ruby_event_store-outbox 0.0.8 → 0.0.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 9457e402dabad2f00ec38ce0a6aecb12dcfdd303614b0ed6c8e391214e2eece1
4
- data.tar.gz: 8545183b1d4720f5674cfdd5cf385666f2cfcd38ed2cc9fdd4bf8aa79c750835
3
+ metadata.gz: 1b73edc767b3c0caa9ff76cd06a58468e18aa2556f25a7c170bd9f0da6bdce9a
4
+ data.tar.gz: b96f01c515c3975694b7ba54ab9b8057bb506534d22c313823488f0150b98ce3
5
5
  SHA512:
6
- metadata.gz: dc5d885558ac2c2d3b446cc1499a9ceb8aaa541b29c1b85e8019cd832ca2572d883c4fa165abbcedc9470f36f29667becc0b7b937c49fc00b3a3aed2714699db
7
- data.tar.gz: 4371dcb52922553adebe91a4fb36472c27bb0a4df0a76ec6782aaf4c48b57e424d55e7f8ba262b5f694d8d5c32e6062bbc504f8ca5e2edfb04767d0278cccc95
6
+ metadata.gz: d9c3e4f4d5733cb1afbea5ffa9ae91af3c1e4ef17f6d371a9dff483e95a4034f57c0bbe1166a87b5e44f562773f683d5442eaa60b0a704439d974c0718a74a49
7
+ data.tar.gz: 5a1e56fd33a9f296246ed9c429dc9abf35e7fed714c9a2b30cb6aaf926babf505a09c8a093a3aba77076aa6f6727f3568d4677035bec47289181811878534a0a
@@ -11,5 +11,13 @@ class CreateEventStoreOutbox < ActiveRecord::Migration<%= migration_version %>
11
11
  end
12
12
  add_index :event_store_outbox, [:format, :enqueued_at, :split_key], name: "index_event_store_outbox_for_pool"
13
13
  add_index :event_store_outbox, [:created_at, :enqueued_at], name: "index_event_store_outbox_for_clear"
14
+
15
+ create_table(:event_store_outbox_locks, force: false) do |t|
16
+ t.string :format, null: false
17
+ t.string :split_key, null: false
18
+ t.datetime :locked_at, null: true
19
+ t.string :locked_by, null: true, limit: 36
20
+ end
21
+ add_index :event_store_outbox_locks, [:format, :split_key], name: "index_event_store_outbox_locks_for_locking", unique: true
14
22
  end
15
23
  end
@@ -5,6 +5,7 @@ module RubyEventStore
5
5
  end
6
6
  end
7
7
 
8
+ require_relative 'outbox/fetch_specification'
8
9
  require_relative 'outbox/record'
9
10
  require_relative 'outbox/sidekiq_scheduler'
10
11
  require_relative 'outbox/consumer'
@@ -59,9 +59,8 @@ module RubyEventStore
59
59
  end
60
60
 
61
61
  def build_consumer(options)
62
- logger = Logger.new(STDOUT, level: options.log_level, progname: "RES-Outbox")
63
- # ActiveRecord::Base.logger = logger if options.log_level == :debug
64
- # ActiveSupport::LogSubscriber.colorize_logging = false
62
+ consumer_uuid = SecureRandom.uuid
63
+ logger = Logger.new(STDOUT, level: options.log_level, progname: "RES-Outbox #{consumer_uuid}")
65
64
  consumer_configuration = Consumer::Configuration.new(
66
65
  split_keys: options.split_keys,
67
66
  message_format: options.message_format,
@@ -71,6 +70,7 @@ module RubyEventStore
71
70
  )
72
71
  metrics = Metrics.from_url(options.metrics_url)
73
72
  outbox_consumer = RubyEventStore::Outbox::Consumer.new(
73
+ consumer_uuid,
74
74
  options,
75
75
  logger: logger,
76
76
  metrics: metrics,
@@ -3,11 +3,14 @@ require "redis"
3
3
  require "active_record"
4
4
  require "ruby_event_store/outbox/record"
5
5
  require "ruby_event_store/outbox/sidekiq5_format"
6
+ require "ruby_event_store/outbox/sidekiq_processor"
7
+ require "ruby_event_store/outbox/fetch_specification"
6
8
 
7
9
  module RubyEventStore
8
10
  module Outbox
9
11
  class Consumer
10
12
  SLEEP_TIME_WHEN_NOTHING_TO_DO = 0.1
13
+ MAXIMUM_BATCH_FETCHES_IN_ONE_LOCK = 10
11
14
 
12
15
  class Configuration
13
16
  def initialize(
@@ -38,25 +41,26 @@ module RubyEventStore
38
41
  attr_reader :split_keys, :message_format, :batch_size, :database_url, :redis_url
39
42
  end
40
43
 
41
- def initialize(configuration, clock: Time, logger:, metrics:)
44
+ def initialize(consumer_uuid, configuration, clock: Time, logger:, metrics:)
42
45
  @split_keys = configuration.split_keys
43
46
  @clock = clock
44
- @redis = Redis.new(url: configuration.redis_url)
45
47
  @logger = logger
46
48
  @metrics = metrics
47
49
  @batch_size = configuration.batch_size
50
+ @consumer_uuid = consumer_uuid
48
51
  ActiveRecord::Base.establish_connection(configuration.database_url) unless ActiveRecord::Base.connected?
49
- ActiveRecord::Base.connection.execute("SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;")
52
+ if ActiveRecord::Base.connection.adapter_name == "Mysql2"
53
+ ActiveRecord::Base.connection.execute("SET SESSION innodb_lock_wait_timeout = 1;")
54
+ end
50
55
 
51
56
  raise "Unknown format" if configuration.message_format != SIDEKIQ5_FORMAT
52
- @message_format = SIDEKIQ5_FORMAT
57
+ @processor = SidekiqProcessor.new(Redis.new(url: configuration.redis_url))
53
58
 
54
59
  @gracefully_shutting_down = false
55
60
  prepare_traps
56
61
  end
57
62
 
58
63
  def init
59
- @redis.sadd("queues", split_keys)
60
64
  logger.info("Initiated RubyEventStore::Outbox v#{VERSION}")
61
65
  logger.info("Handling split keys: #{split_keys ? split_keys.join(", ") : "(all of them)"}")
62
66
  end
@@ -73,51 +77,129 @@ module RubyEventStore
73
77
  end
74
78
 
75
79
  def one_loop
76
- Record.transaction do
77
- records_scope = Record.lock.where(format: message_format, enqueued_at: nil)
78
- records_scope = records_scope.where(split_key: split_keys) if !split_keys.nil?
79
- records = records_scope.order("id ASC").limit(batch_size).to_a
80
- if records.empty?
81
- metrics.write_point_queue(status: "ok")
82
- return false
80
+ remaining_split_keys = @split_keys.dup
81
+
82
+ was_something_changed = false
83
+ while (split_key = remaining_split_keys.shift)
84
+ was_something_changed |= handle_split(FetchSpecification.new(processor.message_format, split_key))
85
+ end
86
+ was_something_changed
87
+ end
88
+
89
+ def handle_split(fetch_specification)
90
+ obtained_lock = obtain_lock_for_process(fetch_specification)
91
+ return false unless obtained_lock
92
+
93
+ something_processed = false
94
+
95
+ MAXIMUM_BATCH_FETCHES_IN_ONE_LOCK.times do
96
+ batch = retrieve_batch(fetch_specification)
97
+ if batch.empty?
98
+ break
83
99
  end
84
100
 
85
- now = @clock.now.utc
86
101
  failed_record_ids = []
87
- records.each do |record|
102
+ updated_record_ids = []
103
+ batch.each do |record|
88
104
  begin
89
- handle_one_record(now, record)
105
+ now = @clock.now.utc
106
+ processor.process(record, now)
107
+
108
+ record.update_column(:enqueued_at, now)
109
+ something_processed |= true
110
+ updated_record_ids << record.id
90
111
  rescue => e
91
112
  failed_record_ids << record.id
92
113
  e.full_message.split($/).each {|line| logger.error(line) }
93
114
  end
94
115
  end
95
116
 
96
- updated_record_ids = records.map(&:id) - failed_record_ids
97
- Record.where(id: updated_record_ids).update_all(enqueued_at: now)
98
- metrics.write_point_queue(status: "ok", enqueued: updated_record_ids.size, failed: failed_record_ids.size)
117
+ metrics.write_point_queue(
118
+ enqueued: updated_record_ids.size,
119
+ failed: failed_record_ids.size,
120
+ format: fetch_specification.message_format,
121
+ split_key: fetch_specification.split_key,
122
+ remaining: get_remaining_count(fetch_specification)
123
+ )
124
+
125
+ logger.info "Sent #{updated_record_ids.size} messages from outbox table"
99
126
 
100
- logger.info "Sent #{records.size} messages from outbox table"
101
- true
127
+ obtained_lock = refresh_lock_for_process(obtained_lock)
128
+ break unless obtained_lock
102
129
  end
103
- rescue ActiveRecord::Deadlocked
104
- logger.warn "Outbox fetch deadlocked"
105
- metrics.write_point_queue(status: "deadlocked")
106
- false
107
- rescue ActiveRecord::LockWaitTimeout
108
- logger.warn "Outbox fetch lock timeout"
109
- metrics.write_point_queue(status: "lock_timeout")
110
- false
130
+
131
+ metrics.write_point_queue(
132
+ format: fetch_specification.message_format,
133
+ split_key: fetch_specification.split_key,
134
+ remaining: get_remaining_count(fetch_specification)
135
+ ) unless something_processed
136
+
137
+ release_lock_for_process(fetch_specification)
138
+
139
+ processor.after_batch
140
+
141
+ something_processed
111
142
  end
112
143
 
113
144
  private
114
- attr_reader :split_keys, :logger, :message_format, :batch_size, :metrics
145
+ attr_reader :split_keys, :logger, :batch_size, :metrics, :processor, :consumer_uuid
146
+
147
+ def obtain_lock_for_process(fetch_specification)
148
+ result = Lock.obtain(fetch_specification, consumer_uuid, clock: @clock)
149
+ case result
150
+ when :deadlocked
151
+ logger.warn "Obtaining lock for split_key '#{fetch_specification.split_key}' failed (deadlock)"
152
+ metrics.write_operation_result("obtain", "deadlocked")
153
+ return false
154
+ when :lock_timeout
155
+ logger.warn "Obtaining lock for split_key '#{fetch_specification.split_key}' failed (lock timeout)"
156
+ metrics.write_operation_result("obtain", "lock_timeout")
157
+ return false
158
+ when :taken
159
+ logger.debug "Obtaining lock for split_key '#{fetch_specification.split_key}' unsuccessful (taken)"
160
+ metrics.write_operation_result("obtain", "taken")
161
+ return false
162
+ else
163
+ return result
164
+ end
165
+ end
166
+
167
+ def release_lock_for_process(fetch_specification)
168
+ result = Lock.release(fetch_specification, consumer_uuid)
169
+ case result
170
+ when :ok
171
+ when :deadlocked
172
+ logger.warn "Releasing lock for split_key '#{fetch_specification.split_key}' failed (deadlock)"
173
+ metrics.write_operation_result("release", "deadlocked")
174
+ when :lock_timeout
175
+ logger.warn "Releasing lock for split_key '#{fetch_specification.split_key}' failed (lock timeout)"
176
+ metrics.write_operation_result("release", "lock_timeout")
177
+ when :not_taken_by_this_process
178
+ logger.debug "Releasing lock for split_key '#{fetch_specification.split_key}' failed (not taken by this process)"
179
+ metrics.write_operation_result("release", "not_taken_by_this_process")
180
+ else
181
+ raise "Unexpected result #{result}"
182
+ end
183
+ end
115
184
 
116
- def handle_one_record(now, record)
117
- hash_payload = JSON.parse(record.payload)
118
- @redis.lpush("queue:#{hash_payload.fetch("queue")}", JSON.generate(JSON.parse(record.payload).merge({
119
- "enqueued_at" => now.to_f,
120
- })))
185
+ def refresh_lock_for_process(lock)
186
+ result = lock.refresh(clock: @clock)
187
+ case result
188
+ when :deadlocked
189
+ logger.warn "Refreshing lock for split_key '#{lock.split_key}' failed (deadlock)"
190
+ metrics.write_operation_result("refresh", "deadlocked")
191
+ return false
192
+ when :lock_timeout
193
+ logger.warn "Refreshing lock for split_key '#{lock.split_key}' failed (lock timeout)"
194
+ metrics.write_operation_result("refresh", "lock_timeout")
195
+ return false
196
+ when :stolen
197
+ logger.debug "Refreshing lock for split_key '#{lock.split_key}' unsuccessful (stolen)"
198
+ metrics.write_operation_result("refresh", "stolen")
199
+ return false
200
+ else
201
+ return result
202
+ end
121
203
  end
122
204
 
123
205
  def prepare_traps
@@ -132,6 +214,14 @@ module RubyEventStore
132
214
  def initiate_graceful_shutdown
133
215
  @gracefully_shutting_down = true
134
216
  end
217
+
218
+ def retrieve_batch(fetch_specification)
219
+ Record.remaining_for(fetch_specification).order("id ASC").limit(batch_size).to_a
220
+ end
221
+
222
+ def get_remaining_count(fetch_specification)
223
+ Record.remaining_for(fetch_specification).count
224
+ end
135
225
  end
136
226
  end
137
227
  end
@@ -0,0 +1,6 @@
1
+ module RubyEventStore
2
+ module Outbox
3
+ class ConsumerProcess
4
+ end
5
+ end
6
+ end
@@ -0,0 +1,13 @@
1
+ module RubyEventStore
2
+ module Outbox
3
+ class FetchSpecification
4
+ def initialize(message_format, split_key)
5
+ @message_format = message_format
6
+ @split_key = split_key
7
+ freeze
8
+ end
9
+
10
+ attr_reader :message_format, :split_key
11
+ end
12
+ end
13
+ end
@@ -1,13 +1,12 @@
1
- require "ruby_event_store/outbox/metrics/null"
2
- require "ruby_event_store/outbox/metrics/influx"
3
-
4
1
  module RubyEventStore
5
2
  module Outbox
6
3
  module Metrics
7
4
  def self.from_url(metrics_url)
8
5
  if metrics_url.nil?
6
+ require "ruby_event_store/outbox/metrics/null"
9
7
  Null.new
10
8
  else
9
+ require "ruby_event_store/outbox/metrics/influx"
11
10
  Influx.new(metrics_url)
12
11
  end
13
12
  end
@@ -12,26 +12,40 @@ module RubyEventStore
12
12
  async: true,
13
13
  time_precision: 'ns',
14
14
  }
15
- options[:username] = params["username"]&.first if params["username"].present?
16
- options[:password] = params["password"]&.first if params["password"].present?
15
+ options[:username] = params.fetch("username").first if params.key?("username")
16
+ options[:password] = params.fetch("password").first if params.key?("password")
17
17
  @influxdb_client = InfluxDB::Client.new(**options)
18
18
  end
19
19
 
20
- def write_point_queue(status:, enqueued: 0, failed: 0)
20
+ def write_operation_result(operation, result)
21
+ write_point("ruby_event_store.outbox.lock", {
22
+ values: {
23
+ value: 1,
24
+ },
25
+ tags: {
26
+ operation: operation,
27
+ result: result,
28
+ }
29
+ })
30
+ end
31
+
32
+ def write_point_queue(enqueued: 0, failed: 0, remaining: 0, format: nil, split_key: nil)
21
33
  write_point("ruby_event_store.outbox.queue", {
22
34
  values: {
23
35
  enqueued: enqueued,
24
36
  failed: failed,
37
+ remaining: remaining,
25
38
  },
26
39
  tags: {
27
- status: status,
40
+ format: format,
41
+ split_key: split_key,
28
42
  }
29
43
  })
30
44
  end
31
45
 
32
46
  def write_point(series, data)
33
- data[:timestamp] ||= Process.clock_gettime(Process::CLOCK_REALTIME, :nanosecond).to_i
34
- @influxdb_client.write_point(series, data)
47
+ data[:timestamp] = Process.clock_gettime(Process::CLOCK_REALTIME, :nanosecond)
48
+ influxdb_client.write_point(series, data)
35
49
  end
36
50
 
37
51
  attr_reader :influxdb_client
@@ -2,7 +2,10 @@ module RubyEventStore
2
2
  module Outbox
3
3
  module Metrics
4
4
  class Null
5
- def write_point_queue(status:, enqueued: 0, failed: 0)
5
+ def write_operation_result(operation, result)
6
+ end
7
+
8
+ def write_point_queue(**kwargs)
6
9
  end
7
10
  end
8
11
  end
@@ -8,9 +8,97 @@ module RubyEventStore
8
8
  self.primary_key = :id
9
9
  self.table_name = 'event_store_outbox'
10
10
 
11
+ def self.remaining_for(fetch_specification)
12
+ where(format: fetch_specification.message_format, split_key: fetch_specification.split_key, enqueued_at: nil)
13
+ end
14
+
11
15
  def hash_payload
12
16
  JSON.parse(payload).deep_symbolize_keys
13
17
  end
18
+
19
+ def enqueued?
20
+ !enqueued_at.nil?
21
+ end
22
+ end
23
+
24
+ class Lock < ::ActiveRecord::Base
25
+ self.primary_key = :split_key
26
+ self.table_name = 'event_store_outbox_locks'
27
+
28
+ def self.obtain(fetch_specification, process_uuid, clock:)
29
+ l = nil
30
+ transaction do
31
+ l = get_lock_record(fetch_specification)
32
+
33
+ return :taken if l.recently_locked?
34
+
35
+ l.update!(
36
+ locked_by: process_uuid,
37
+ locked_at: clock.now,
38
+ )
39
+ end
40
+ l
41
+ rescue ActiveRecord::Deadlocked
42
+ :deadlocked
43
+ rescue ActiveRecord::LockWaitTimeout
44
+ :lock_timeout
45
+ end
46
+
47
+ def refresh(clock:)
48
+ transaction do
49
+ current_process_uuid = locked_by
50
+ lock!
51
+ if locked_by == current_process_uuid
52
+ update!(locked_at: clock.now)
53
+ return self
54
+ else
55
+ return :stolen
56
+ end
57
+ end
58
+ rescue ActiveRecord::Deadlocked
59
+ :deadlocked
60
+ rescue ActiveRecord::LockWaitTimeout
61
+ :lock_timeout
62
+ end
63
+
64
+ def self.release(fetch_specification, process_uuid)
65
+ transaction do
66
+ l = get_lock_record(fetch_specification)
67
+ return :not_taken_by_this_process if !l.locked_by?(process_uuid)
68
+
69
+ l.update!(locked_by: nil, locked_at: nil)
70
+ end
71
+ :ok
72
+ rescue ActiveRecord::Deadlocked
73
+ :deadlocked
74
+ rescue ActiveRecord::LockWaitTimeout
75
+ :lock_timeout
76
+ end
77
+
78
+ def locked_by?(process_uuid)
79
+ locked_by.eql?(process_uuid)
80
+ end
81
+
82
+ def recently_locked?
83
+ locked_by && locked_at > 10.minutes.ago
84
+ end
85
+
86
+ private
87
+ def self.lock_for_split_key(fetch_specification)
88
+ lock.find_by(format: fetch_specification.message_format, split_key: fetch_specification.split_key)
89
+ end
90
+
91
+ def self.get_lock_record(fetch_specification)
92
+ l = lock_for_split_key(fetch_specification)
93
+ if l.nil?
94
+ begin
95
+ l = create!(format: fetch_specification.message_format, split_key: fetch_specification.split_key)
96
+ rescue ActiveRecord::RecordNotUnique
97
+ l = lock_for_split_key(fetch_specification)
98
+ end
99
+ end
100
+ l
101
+ end
14
102
  end
15
103
  end
16
104
  end
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "ruby_event_store/outbox/sidekiq5_format"
4
+
5
+ module RubyEventStore
6
+ module Outbox
7
+ class SidekiqProcessor
8
+ InvalidPayload = Class.new(StandardError)
9
+
10
+ def initialize(redis)
11
+ @redis = redis
12
+ @recently_used_queues = Set.new
13
+ end
14
+
15
+ def process(record, now)
16
+ parsed_record = JSON.parse(record.payload)
17
+
18
+ queue = parsed_record["queue"]
19
+ raise InvalidPayload.new("Missing queue") if queue.nil? || queue.empty?
20
+ payload = JSON.generate(parsed_record.merge({
21
+ "enqueued_at" => now.to_f,
22
+ }))
23
+
24
+ redis.lpush("queue:#{queue}", payload)
25
+
26
+ @recently_used_queues << queue
27
+ end
28
+
29
+ def after_batch
30
+ if !@recently_used_queues.empty?
31
+ redis.sadd("queues", @recently_used_queues.to_a)
32
+ @recently_used_queues.clear
33
+ end
34
+ end
35
+
36
+ def message_format
37
+ SIDEKIQ5_FORMAT
38
+ end
39
+
40
+ private
41
+ attr_reader :redis
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'sidekiq'
4
+ require "ruby_event_store/outbox/sidekiq5_format"
5
+
6
+ module RubyEventStore
7
+ module Outbox
8
+ class SidekiqProducer
9
+ def call(klass, args)
10
+ sidekiq_client = Sidekiq::Client.new(Sidekiq.redis_pool)
11
+ item = {
12
+ 'class' => klass,
13
+ 'args' => args,
14
+ }
15
+ normalized_item = sidekiq_client.__send__(:normalize_item, item)
16
+ payload = sidekiq_client.__send__(:process_single, normalized_item.fetch('class'), normalized_item)
17
+ if payload
18
+ Record.create!(
19
+ format: SIDEKIQ5_FORMAT,
20
+ split_key: payload.fetch('queue'),
21
+ payload: payload.to_json
22
+ )
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -1,31 +1,24 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'sidekiq'
4
- require "ruby_event_store/outbox/sidekiq5_format"
3
+ require "ruby_event_store/outbox/sidekiq_producer"
5
4
 
6
5
  module RubyEventStore
7
6
  module Outbox
8
7
  class SidekiqScheduler
8
+ def initialize
9
+ @sidekiq_producer = SidekiqProducer.new
10
+ end
11
+
9
12
  def call(klass, serialized_event)
10
- sidekiq_client = Sidekiq::Client.new(Sidekiq.redis_pool)
11
- item = {
12
- 'class' => klass,
13
- 'args' => [serialized_event.to_h],
14
- }
15
- normalized_item = sidekiq_client.__send__(:normalize_item, item)
16
- payload = sidekiq_client.__send__(:process_single, normalized_item.fetch('class'), normalized_item)
17
- if payload
18
- Record.create!(
19
- format: SIDEKIQ5_FORMAT,
20
- split_key: payload.fetch('queue'),
21
- payload: payload.to_json
22
- )
23
- end
13
+ sidekiq_producer.call(klass, [serialized_event.to_h])
24
14
  end
25
15
 
26
16
  def verify(subscriber)
27
17
  Class === subscriber && subscriber.respond_to?(:through_outbox?) && subscriber.through_outbox?
28
18
  end
19
+
20
+ private
21
+ attr_reader :sidekiq_producer
29
22
  end
30
23
  end
31
24
  end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module RubyEventStore
4
4
  module Outbox
5
- VERSION = "0.0.8"
5
+ VERSION = "0.0.13"
6
6
  end
7
7
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ruby_event_store-outbox
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.8
4
+ version: 0.0.13
5
5
  platform: ruby
6
6
  authors:
7
7
  - Arkency
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-06-25 00:00:00.000000000 Z
11
+ date: 2020-08-30 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: ruby_event_store
@@ -53,12 +53,16 @@ files:
53
53
  - lib/ruby_event_store/outbox.rb
54
54
  - lib/ruby_event_store/outbox/cli.rb
55
55
  - lib/ruby_event_store/outbox/consumer.rb
56
+ - lib/ruby_event_store/outbox/consumer_process.rb
57
+ - lib/ruby_event_store/outbox/fetch_specification.rb
56
58
  - lib/ruby_event_store/outbox/metrics.rb
57
59
  - lib/ruby_event_store/outbox/metrics/influx.rb
58
60
  - lib/ruby_event_store/outbox/metrics/null.rb
59
61
  - lib/ruby_event_store/outbox/record.rb
60
62
  - lib/ruby_event_store/outbox/sidekiq5_format.rb
61
63
  - lib/ruby_event_store/outbox/sidekiq_message_handler.rb
64
+ - lib/ruby_event_store/outbox/sidekiq_processor.rb
65
+ - lib/ruby_event_store/outbox/sidekiq_producer.rb
62
66
  - lib/ruby_event_store/outbox/sidekiq_scheduler.rb
63
67
  - lib/ruby_event_store/outbox/version.rb
64
68
  homepage: https://railseventstore.org