ruby_event_store-outbox 0.0.7 → 0.0.12

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: c8e2e739e68444fa47e69b09357b61b1d7b7136db13663028b97eaff9c8f3375
4
- data.tar.gz: fbf657458847ca98c43698c4b40acea4993e0a9c752f788e112490e190d2415b
3
+ metadata.gz: c1d9945d7b34ce9254c8150dc671453cdf3c72a5799bc39b11f1e86e109baa8c
4
+ data.tar.gz: 58fb2365dd712495a8681fb78ed444662422cfddf3b2b165fda5e4299082dc91
5
5
  SHA512:
6
- metadata.gz: a2a08df4a60ba2e339e2914c20fe4a5638f18b20a55a580e2b048758bf273d20fbff1a05fe0eb183d38d625ce6ce94a2e8cc034a1136320ade6bd0e3adeec165
7
- data.tar.gz: 8c6114269f96e992a134b5b731a7be3ba870c4a57d75bfc74129cd385b5100c93d450a9574a53fd3d1bf0489801a0839fc9d3fc9e35237d96672dc4c62baf0da
6
+ metadata.gz: 01fbff71356a4095838f197b96e9673762d02f9677d0d0df3075757861c273131b636b6e181db0ce165fa7d04f2d45b6450e4cd19a49261eb6f9e87d6782b8f7
7
+ data.tar.gz: 53bea6ab8e0e3c2bfad41018fbc65d7f2de33af168b05d5578cf6349a56614be4c4e00448b3954ed59ca42f637908e635701f5c2eb2ef7d4958f1078c8eac18d
@@ -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,7 +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")
62
+ consumer_uuid = SecureRandom.uuid
63
+ logger = Logger.new(STDOUT, level: options.log_level, progname: "RES-Outbox #{consumer_uuid}")
63
64
  consumer_configuration = Consumer::Configuration.new(
64
65
  split_keys: options.split_keys,
65
66
  message_format: options.message_format,
@@ -69,6 +70,7 @@ module RubyEventStore
69
70
  )
70
71
  metrics = Metrics.from_url(options.metrics_url)
71
72
  outbox_consumer = RubyEventStore::Outbox::Consumer.new(
73
+ consumer_uuid,
72
74
  options,
73
75
  logger: logger,
74
76
  metrics: metrics,
@@ -3,11 +3,13 @@ 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"
6
7
 
7
8
  module RubyEventStore
8
9
  module Outbox
9
10
  class Consumer
10
11
  SLEEP_TIME_WHEN_NOTHING_TO_DO = 0.1
12
+ MAXIMUM_BATCH_FETCHES_IN_ONE_LOCK = 10
11
13
 
12
14
  class Configuration
13
15
  def initialize(
@@ -38,24 +40,26 @@ module RubyEventStore
38
40
  attr_reader :split_keys, :message_format, :batch_size, :database_url, :redis_url
39
41
  end
40
42
 
41
- def initialize(configuration, clock: Time, logger:, metrics:)
43
+ def initialize(consumer_uuid, configuration, clock: Time, logger:, metrics:)
42
44
  @split_keys = configuration.split_keys
43
45
  @clock = clock
44
- @redis = Redis.new(url: configuration.redis_url)
45
46
  @logger = logger
46
47
  @metrics = metrics
47
48
  @batch_size = configuration.batch_size
49
+ @consumer_uuid = consumer_uuid
48
50
  ActiveRecord::Base.establish_connection(configuration.database_url) unless ActiveRecord::Base.connected?
51
+ if ActiveRecord::Base.connection.adapter_name == "Mysql2"
52
+ ActiveRecord::Base.connection.execute("SET SESSION innodb_lock_wait_timeout = 1;")
53
+ end
49
54
 
50
55
  raise "Unknown format" if configuration.message_format != SIDEKIQ5_FORMAT
51
- @message_format = SIDEKIQ5_FORMAT
56
+ @processor = SidekiqProcessor.new(Redis.new(url: configuration.redis_url))
52
57
 
53
58
  @gracefully_shutting_down = false
54
59
  prepare_traps
55
60
  end
56
61
 
57
62
  def init
58
- @redis.sadd("queues", split_keys)
59
63
  logger.info("Initiated RubyEventStore::Outbox v#{VERSION}")
60
64
  logger.info("Handling split keys: #{split_keys ? split_keys.join(", ") : "(all of them)"}")
61
65
  end
@@ -72,51 +76,129 @@ module RubyEventStore
72
76
  end
73
77
 
74
78
  def one_loop
75
- Record.transaction do
76
- records_scope = Record.lock.where(format: message_format, enqueued_at: nil)
77
- records_scope = records_scope.where(split_key: split_keys) if !split_keys.nil?
78
- records = records_scope.order("id ASC").limit(batch_size).to_a
79
- if records.empty?
80
- metrics.write_point_queue(status: "ok")
81
- return false
79
+ remaining_split_keys = @split_keys.dup
80
+
81
+ was_something_changed = false
82
+ while (split_key = remaining_split_keys.shift)
83
+ was_something_changed |= handle_split(FetchSpecification.new(processor.message_format, split_key))
84
+ end
85
+ was_something_changed
86
+ end
87
+
88
+ def handle_split(fetch_specification)
89
+ obtained_lock = obtain_lock_for_process(fetch_specification)
90
+ return false unless obtained_lock
91
+
92
+ something_processed = false
93
+
94
+ MAXIMUM_BATCH_FETCHES_IN_ONE_LOCK.times do
95
+ batch = retrieve_batch(fetch_specification)
96
+ if batch.empty?
97
+ break
82
98
  end
83
99
 
84
- now = @clock.now.utc
85
100
  failed_record_ids = []
86
- records.each do |record|
101
+ updated_record_ids = []
102
+ batch.each do |record|
87
103
  begin
88
- handle_one_record(now, record)
104
+ now = @clock.now.utc
105
+ processor.process(record, now)
106
+
107
+ record.update_column(:enqueued_at, now)
108
+ something_processed |= true
109
+ updated_record_ids << record.id
89
110
  rescue => e
90
111
  failed_record_ids << record.id
91
112
  e.full_message.split($/).each {|line| logger.error(line) }
92
113
  end
93
114
  end
94
115
 
95
- updated_record_ids = records.map(&:id) - failed_record_ids
96
- Record.where(id: updated_record_ids).update_all(enqueued_at: now)
97
- metrics.write_point_queue(status: "ok", enqueued: updated_record_ids.size, failed: failed_record_ids.size)
116
+ metrics.write_point_queue(
117
+ enqueued: updated_record_ids.size,
118
+ failed: failed_record_ids.size,
119
+ format: fetch_specification.message_format,
120
+ split_key: fetch_specification.split_key,
121
+ remaining: get_remaining_count(fetch_specification)
122
+ )
123
+
124
+ logger.info "Sent #{updated_record_ids.size} messages from outbox table"
98
125
 
99
- logger.info "Sent #{records.size} messages from outbox table"
100
- true
126
+ obtained_lock = refresh_lock_for_process(obtained_lock)
127
+ break unless obtained_lock
101
128
  end
102
- rescue ActiveRecord::Deadlocked
103
- logger.warn "Outbox fetch deadlocked"
104
- metrics.write_point_queue(status: "deadlocked")
105
- false
106
- rescue ActiveRecord::LockWaitTimeout
107
- logger.warn "Outbox fetch lock timeout"
108
- metrics.write_point_queue(status: "lock_timeout")
109
- false
129
+
130
+ metrics.write_point_queue(
131
+ format: fetch_specification.message_format,
132
+ split_key: fetch_specification.split_key,
133
+ remaining: get_remaining_count(fetch_specification)
134
+ ) unless something_processed
135
+
136
+ release_lock_for_process(fetch_specification)
137
+
138
+ processor.after_batch
139
+
140
+ something_processed
110
141
  end
111
142
 
112
143
  private
113
- attr_reader :split_keys, :logger, :message_format, :batch_size, :metrics
144
+ attr_reader :split_keys, :logger, :batch_size, :metrics, :processor, :consumer_uuid
145
+
146
+ def obtain_lock_for_process(fetch_specification)
147
+ result = Lock.obtain(fetch_specification, consumer_uuid, clock: @clock)
148
+ case result
149
+ when :deadlocked
150
+ logger.warn "Obtaining lock for split_key '#{fetch_specification.split_key}' failed (deadlock)"
151
+ metrics.write_operation_result("obtain", "deadlocked")
152
+ return false
153
+ when :lock_timeout
154
+ logger.warn "Obtaining lock for split_key '#{fetch_specification.split_key}' failed (lock timeout)"
155
+ metrics.write_operation_result("obtain", "lock_timeout")
156
+ return false
157
+ when :taken
158
+ logger.debug "Obtaining lock for split_key '#{fetch_specification.split_key}' unsuccessful (taken)"
159
+ metrics.write_operation_result("obtain", "taken")
160
+ return false
161
+ else
162
+ return result
163
+ end
164
+ end
165
+
166
+ def release_lock_for_process(fetch_specification)
167
+ result = Lock.release(fetch_specification, consumer_uuid)
168
+ case result
169
+ when :ok
170
+ when :deadlocked
171
+ logger.warn "Releasing lock for split_key '#{fetch_specification.split_key}' failed (deadlock)"
172
+ metrics.write_operation_result("release", "deadlocked")
173
+ when :lock_timeout
174
+ logger.warn "Releasing lock for split_key '#{fetch_specification.split_key}' failed (lock timeout)"
175
+ metrics.write_operation_result("release", "lock_timeout")
176
+ when :not_taken_by_this_process
177
+ logger.debug "Releasing lock for split_key '#{fetch_specification.split_key}' failed (not taken by this process)"
178
+ metrics.write_operation_result("release", "not_taken_by_this_process")
179
+ else
180
+ raise "Unexpected result #{result}"
181
+ end
182
+ end
114
183
 
115
- def handle_one_record(now, record)
116
- hash_payload = JSON.parse(record.payload)
117
- @redis.lpush("queue:#{hash_payload.fetch("queue")}", JSON.generate(JSON.parse(record.payload).merge({
118
- "enqueued_at" => now.to_f,
119
- })))
184
+ def refresh_lock_for_process(lock)
185
+ result = lock.refresh(clock: @clock)
186
+ case result
187
+ when :deadlocked
188
+ logger.warn "Refreshing lock for split_key '#{lock.split_key}' failed (deadlock)"
189
+ metrics.write_operation_result("refresh", "deadlocked")
190
+ return false
191
+ when :lock_timeout
192
+ logger.warn "Refreshing lock for split_key '#{lock.split_key}' failed (lock timeout)"
193
+ metrics.write_operation_result("refresh", "lock_timeout")
194
+ return false
195
+ when :stolen
196
+ logger.debug "Refreshing lock for split_key '#{lock.split_key}' unsuccessful (stolen)"
197
+ metrics.write_operation_result("refresh", "stolen")
198
+ return false
199
+ else
200
+ return result
201
+ end
120
202
  end
121
203
 
122
204
  def prepare_traps
@@ -131,6 +213,14 @@ module RubyEventStore
131
213
  def initiate_graceful_shutdown
132
214
  @gracefully_shutting_down = true
133
215
  end
216
+
217
+ def retrieve_batch(fetch_specification)
218
+ Record.remaining_for(fetch_specification).order("id ASC").limit(batch_size).to_a
219
+ end
220
+
221
+ def get_remaining_count(fetch_specification)
222
+ Record.remaining_for(fetch_specification).count
223
+ end
134
224
  end
135
225
  end
136
226
  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.7"
5
+ VERSION = "0.0.12"
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.7
4
+ version: 0.0.12
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