ruby_event_store-outbox 0.0.8 → 0.0.13

Sign up to get free protection for your applications and to get access to all the features.
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