ruby_event_store-outbox 0.1.0 → 0.2.0

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: 72cede48801be6a8a32295f690fc6a7a4091ddda2613791765b0d6127ea9eaec
4
- data.tar.gz: 7a20be4c5a340e2305a760ecb104547dd7cf728d68e97717c38fde97ef404dac
3
+ metadata.gz: 67529756ee35bbaf8447ed5ed8afbc6b6e80e2eaae62a1c396492fe4f6fcb7e0
4
+ data.tar.gz: 617091c19ad0d7bd1a688178740b43aa41165ed7cd9a5a371da33ff7734d3873
5
5
  SHA512:
6
- metadata.gz: e5412c791d66024632f3ff638d0bc72b7d4b6fe7535c5f736ecaff420e19d8059d8b97e8e0c34de5032433320df2fd329affb7a8c6356f60ed406d8dfaeb0953
7
- data.tar.gz: f5375242631ef96645463bf23d1507b491a9259231d3082158a5ddcb4b2a35e2afbe473aea2dd7cec7c73629f735525a972ab673a1e6779d6e7a88b53ec7c378
6
+ metadata.gz: 2ec5edfc468a99b05f38a9fc6f2db83bb30f2fd86a1c995f98476dee542a99b8f811a83e46e6915b20873eb1c291e5dc7f95a996542c3017a026fbad93eeb383
7
+ data.tar.gz: 63ee434baae965c530ac947820d0b1f5d2cf682268da3b7c377dde42eedd8eea91b3d56dbd6a96a0e10ec288d4ce6bedbb3cbac9bc874ea03642572f977f7b2a
@@ -4,7 +4,8 @@ module RubyEventStore
4
4
  module Outbox
5
5
  module CleanupStrategies
6
6
  class None
7
- def initialize; end
7
+ def initialize
8
+ end
8
9
 
9
10
  def call(_fetch_specification)
10
11
  :ok
@@ -14,7 +14,7 @@ module RubyEventStore
14
14
  CleanOldEnqueued.new(
15
15
  repository,
16
16
  ActiveSupport::Duration.parse(configuration.cleanup),
17
- configuration.cleanup_limit
17
+ configuration.cleanup_limit,
18
18
  )
19
19
  end
20
20
  end
@@ -32,10 +32,9 @@ module RubyEventStore
32
32
  .new do |option_parser|
33
33
  option_parser.banner = "Usage: res_outbox [options]"
34
34
 
35
- option_parser.on(
36
- "--database-url=DATABASE_URL",
37
- "Database where outbox table is stored"
38
- ) { |database_url| options.database_url = database_url }
35
+ option_parser.on("--database-url=DATABASE_URL", "Database where outbox table is stored") do |database_url|
36
+ options.database_url = database_url
37
+ end
39
38
 
40
39
  option_parser.on("--redis-url=REDIS_URL", "URL to redis database") do |redis_url|
41
40
  options.redis_url = redis_url
@@ -44,25 +43,25 @@ module RubyEventStore
44
43
  option_parser.on(
45
44
  "--log-level=LOG_LEVEL",
46
45
  %i[fatal error warn info debug],
47
- "Logging level, one of: fatal, error, warn, info, debug. Default: warn"
46
+ "Logging level, one of: fatal, error, warn, info, debug. Default: warn",
48
47
  ) { |log_level| options.log_level = log_level.to_sym }
49
48
 
50
49
  option_parser.on(
51
50
  "--message-format=FORMAT",
52
51
  ["sidekiq5"],
53
- "Message format, supported: sidekiq5. Default: sidekiq5"
52
+ "Message format, supported: sidekiq5. Default: sidekiq5",
54
53
  ) { |message_format| options.message_format = message_format }
55
54
 
56
55
  option_parser.on(
57
56
  "--split-keys=SPLIT_KEYS",
58
57
  Array,
59
- "Split keys which should be handled, all if not specified"
58
+ "Split keys which should be handled, all if not specified",
60
59
  ) { |split_keys| options.split_keys = split_keys if !split_keys.empty? }
61
60
 
62
61
  option_parser.on(
63
62
  "--batch-size=BATCH_SIZE",
64
63
  Integer,
65
- "Amount of records fetched in one fetch. Bigger value means more duplicated messages when network problems occur. Default: 100"
64
+ "Amount of records fetched in one fetch. Bigger value means more duplicated messages when network problems occur. Default: 100",
66
65
  ) { |batch_size| options.batch_size = batch_size }
67
66
 
68
67
  option_parser.on("--metrics-url=METRICS_URL", "URI to metrics collector, optional") do |metrics_url|
@@ -71,18 +70,18 @@ module RubyEventStore
71
70
 
72
71
  option_parser.on(
73
72
  "--cleanup=STRATEGY",
74
- "A strategy for cleaning old records. One of: none or iso8601 duration format how old enqueued records should be removed. Default: none"
73
+ "A strategy for cleaning old records. One of: none or iso8601 duration format how old enqueued records should be removed. Default: none",
75
74
  ) { |cleanup_strategy| options.cleanup_strategy = cleanup_strategy }
76
75
 
77
76
  option_parser.on(
78
77
  "--cleanup-limit=LIMIT",
79
- "Amount of records removed in single cleanup run. One of: all or number of records that should be removed. Default: all"
78
+ "Amount of records removed in single cleanup run. One of: all or number of records that should be removed. Default: all",
80
79
  ) { |cleanup_limit| options.cleanup_limit = cleanup_limit }
81
80
 
82
81
  option_parser.on(
83
82
  "--sleep-on-empty=SLEEP_TIME",
84
83
  Float,
85
- "How long to sleep before next check when there was nothing to do. Default: 0.5"
84
+ "How long to sleep before next check when there was nothing to do. Default: 0.5",
86
85
  ) { |sleep_on_empty| options.sleep_on_empty = sleep_on_empty }
87
86
 
88
87
  option_parser.on("-l", "--[no-]lock", "Lock split key in consumer") do |locking|
@@ -101,27 +100,26 @@ module RubyEventStore
101
100
 
102
101
  def run(argv)
103
102
  options = Parser.parse(argv)
104
- build_runner(options)
105
- .run
103
+ build_runner(options).run
106
104
  end
107
105
 
108
106
  def build_runner(options)
109
107
  consumer_uuid = SecureRandom.uuid
110
108
  logger = Logger.new(STDOUT, level: options.log_level, progname: "RES-Outbox #{consumer_uuid}")
111
- consumer_configuration = Configuration.new(
112
- split_keys: options.split_keys,
113
- message_format: options.message_format,
114
- batch_size: options.batch_size,
115
- database_url: options.database_url,
116
- redis_url: options.redis_url,
117
- cleanup: options.cleanup_strategy,
118
- cleanup_limit: options.cleanup_limit,
119
- sleep_on_empty: options.sleep_on_empty,
120
- locking: options.locking
121
- )
109
+ consumer_configuration =
110
+ Configuration.new(
111
+ split_keys: options.split_keys,
112
+ message_format: options.message_format,
113
+ batch_size: options.batch_size,
114
+ database_url: options.database_url,
115
+ redis_url: options.redis_url,
116
+ cleanup: options.cleanup_strategy,
117
+ cleanup_limit: options.cleanup_limit,
118
+ sleep_on_empty: options.sleep_on_empty,
119
+ locking: options.locking,
120
+ )
122
121
  metrics = Metrics.from_url(options.metrics_url)
123
- outbox_consumer =
124
- Outbox::Consumer.new(consumer_uuid, consumer_configuration, logger: logger, metrics: metrics)
122
+ outbox_consumer = Outbox::Consumer.new(consumer_uuid, consumer_configuration, logger: logger, metrics: metrics)
125
123
  Runner.new(outbox_consumer, consumer_configuration, logger: logger)
126
124
  end
127
125
  end
@@ -41,14 +41,14 @@ module RubyEventStore
41
41
  end
42
42
 
43
43
  attr_reader :split_keys,
44
- :message_format,
45
- :batch_size,
46
- :database_url,
47
- :redis_url,
48
- :cleanup,
49
- :cleanup_limit,
50
- :sleep_on_empty,
51
- :locking
44
+ :message_format,
45
+ :batch_size,
46
+ :database_url,
47
+ :redis_url,
48
+ :cleanup,
49
+ :cleanup_limit,
50
+ :sleep_on_empty,
51
+ :locking
52
52
  end
53
53
  end
54
54
  end
@@ -44,14 +44,17 @@ module RubyEventStore
44
44
  end
45
45
 
46
46
  def handle_split(fetch_specification)
47
- repository.with_next_batch(fetch_specification, tempo.batch_size, consumer_uuid, locking, @clock) do |record|
48
- now = @clock.now.utc
49
- processor.process(record, now)
50
- repository.mark_as_enqueued(record, now)
51
- end.tap do
52
- cleanup(fetch_specification)
53
- processor.after_batch
54
- end.success_count > 0
47
+ repository
48
+ .with_next_batch(fetch_specification, tempo.batch_size, consumer_uuid, locking, @clock) do |record|
49
+ now = @clock.now.utc
50
+ processor.process(record, now)
51
+ repository.mark_as_enqueued(record, now)
52
+ end
53
+ .tap do
54
+ cleanup(fetch_specification)
55
+ processor.after_batch
56
+ end
57
+ .success_count > 0
55
58
  end
56
59
 
57
60
  private
@@ -7,7 +7,6 @@ module RubyEventStore
7
7
  module Metrics
8
8
  class Influx
9
9
  def initialize(url)
10
- uri = URI.parse(url)
11
10
  options = { url: url, async: true, time_precision: "ns" }
12
11
  @influxdb_client = InfluxDB::Client.new(**options)
13
12
  end
@@ -15,7 +14,7 @@ module RubyEventStore
15
14
  def write_operation_result(operation, result)
16
15
  write_point(
17
16
  "ruby_event_store.outbox.lock",
18
- { values: { value: 1 }, tags: { operation: operation, result: result } }
17
+ { values: { value: 1 }, tags: { operation: operation, result: result } },
19
18
  )
20
19
  end
21
20
 
@@ -26,13 +25,13 @@ module RubyEventStore
26
25
  values: {
27
26
  enqueued: enqueued,
28
27
  failed: failed,
29
- remaining: remaining
28
+ remaining: remaining,
30
29
  },
31
30
  tags: {
32
31
  format: format,
33
- split_key: split_key
34
- }
35
- }
32
+ split_key: split_key,
33
+ },
34
+ },
36
35
  )
37
36
  end
38
37
 
@@ -4,9 +4,11 @@ module RubyEventStore
4
4
  module Outbox
5
5
  module Metrics
6
6
  class Null
7
- def write_operation_result(operation, result); end
7
+ def write_operation_result(operation, result)
8
+ end
8
9
 
9
- def write_point_queue(**kwargs); end
10
+ def write_point_queue(**kwargs)
11
+ end
10
12
  end
11
13
  end
12
14
  end
@@ -21,7 +21,7 @@ module RubyEventStore
21
21
  failed: failed,
22
22
  remaining: remaining,
23
23
  format: format,
24
- split_key: split_key
24
+ split_key: split_key,
25
25
  }
26
26
  end
27
27
 
@@ -151,57 +151,53 @@ module RubyEventStore
151
151
  BatchResult.empty.tap do |result|
152
152
  obtained_lock = obtain_lock_for_process(fetch_specification, consumer_uuid, clock: clock)
153
153
  case obtained_lock
154
- when :deadlocked
155
- logger.warn "Obtaining lock for split_key '#{fetch_specification.split_key}' failed (deadlock)"
156
- metrics.write_operation_result("obtain", "deadlocked")
157
- return BatchResult.empty
158
- when :lock_timeout
159
- logger.warn "Obtaining lock for split_key '#{fetch_specification.split_key}' failed (lock timeout)"
160
- metrics.write_operation_result("obtain", "lock_timeout")
161
- return BatchResult.empty
162
- when :taken
163
- logger.debug "Obtaining lock for split_key '#{fetch_specification.split_key}' unsuccessful (taken)"
164
- metrics.write_operation_result("obtain", "taken")
165
- return BatchResult.empty
154
+ when :deadlocked
155
+ logger.warn "Obtaining lock for split_key '#{fetch_specification.split_key}' failed (deadlock)"
156
+ metrics.write_operation_result("obtain", "deadlocked")
157
+ return BatchResult.empty
158
+ when :lock_timeout
159
+ logger.warn "Obtaining lock for split_key '#{fetch_specification.split_key}' failed (lock timeout)"
160
+ metrics.write_operation_result("obtain", "lock_timeout")
161
+ return BatchResult.empty
162
+ when :taken
163
+ logger.debug "Obtaining lock for split_key '#{fetch_specification.split_key}' unsuccessful (taken)"
164
+ metrics.write_operation_result("obtain", "taken")
165
+ return BatchResult.empty
166
166
  end
167
167
 
168
168
  Consumer::MAXIMUM_BATCH_FETCHES_IN_ONE_LOCK.times do
169
169
  batch = retrieve_batch(fetch_specification, batch_size).to_a
170
170
  break if batch.empty?
171
- batch.each do |record|
172
- handle_execution(result) do
173
- block.call(record)
174
- end
175
- end
171
+ batch.each { |record| handle_execution(result) { block.call(record) } }
176
172
  case (refresh_result = obtained_lock.refresh(clock: clock))
177
- when :ok
178
- when :deadlocked
179
- logger.warn "Refreshing lock for split_key '#{lock.split_key}' failed (deadlock)"
180
- metrics.write_operation_result("refresh", "deadlocked")
181
- break
182
- when :lock_timeout
183
- logger.warn "Refreshing lock for split_key '#{lock.split_key}' failed (lock timeout)"
184
- metrics.write_operation_result("refresh", "lock_timeout")
185
- break
186
- when :stolen
187
- logger.debug "Refreshing lock for split_key '#{lock.split_key}' unsuccessful (stolen)"
188
- metrics.write_operation_result("refresh", "stolen")
189
- break
190
- else
191
- raise "Unexpected result #{refresh_result}"
173
+ when :ok
174
+ when :deadlocked
175
+ logger.warn "Refreshing lock for split_key '#{lock.split_key}' failed (deadlock)"
176
+ metrics.write_operation_result("refresh", "deadlocked")
177
+ break
178
+ when :lock_timeout
179
+ logger.warn "Refreshing lock for split_key '#{lock.split_key}' failed (lock timeout)"
180
+ metrics.write_operation_result("refresh", "lock_timeout")
181
+ break
182
+ when :stolen
183
+ logger.debug "Refreshing lock for split_key '#{lock.split_key}' unsuccessful (stolen)"
184
+ metrics.write_operation_result("refresh", "stolen")
185
+ break
186
+ else
187
+ raise "Unexpected result #{refresh_result}"
192
188
  end
193
189
  end
194
190
 
195
191
  case release_lock_for_process(fetch_specification, consumer_uuid)
196
- when :deadlocked
197
- logger.warn "Releasing lock for split_key '#{fetch_specification.split_key}' failed (deadlock)"
198
- metrics.write_operation_result("release", "deadlocked")
199
- when :lock_timeout
200
- logger.warn "Releasing lock for split_key '#{fetch_specification.split_key}' failed (lock timeout)"
201
- metrics.write_operation_result("release", "lock_timeout")
202
- when :not_taken_by_this_process
203
- logger.debug "Releasing lock for split_key '#{fetch_specification.split_key}' failed (not taken by this process)"
204
- metrics.write_operation_result("release", "not_taken_by_this_process")
192
+ when :deadlocked
193
+ logger.warn "Releasing lock for split_key '#{fetch_specification.split_key}' failed (deadlock)"
194
+ metrics.write_operation_result("release", "deadlocked")
195
+ when :lock_timeout
196
+ logger.warn "Releasing lock for split_key '#{fetch_specification.split_key}' failed (lock timeout)"
197
+ metrics.write_operation_result("release", "lock_timeout")
198
+ when :not_taken_by_this_process
199
+ logger.debug "Releasing lock for split_key '#{fetch_specification.split_key}' failed (not taken by this process)"
200
+ metrics.write_operation_result("release", "not_taken_by_this_process")
205
201
  end
206
202
  instrument_batch_result(fetch_specification, result)
207
203
  end
@@ -212,11 +208,7 @@ module RubyEventStore
212
208
  Record.transaction do
213
209
  batch = retrieve_batch(fetch_specification, batch_size).lock("FOR UPDATE SKIP LOCKED")
214
210
  break if batch.empty?
215
- batch.each do |record|
216
- handle_execution(result) do
217
- block.call(record)
218
- end
219
- end
211
+ batch.each { |record| handle_execution(result) { block.call(record) } }
220
212
  end
221
213
 
222
214
  instrument_batch_result(fetch_specification, result)
@@ -229,7 +221,7 @@ module RubyEventStore
229
221
  failed: result.failed_count,
230
222
  format: fetch_specification.message_format,
231
223
  split_key: fetch_specification.split_key,
232
- remaining: Record.remaining_for(fetch_specification).count
224
+ remaining: Record.remaining_for(fetch_specification).count,
233
225
  )
234
226
 
235
227
  logger.info "Sent #{result.success_count} messages from outbox table"
@@ -30,6 +30,7 @@ module RubyEventStore
30
30
  end
31
31
 
32
32
  private
33
+
33
34
  attr_reader :consumer, :logger, :sleep_on_empty, :split_keys
34
35
 
35
36
  def prepare_traps
@@ -20,7 +20,7 @@ module RubyEventStore
20
20
 
21
21
  queue = parsed_record["queue"]
22
22
  raise InvalidPayload.new("Missing queue") if queue.nil? || queue.empty?
23
- payload = JSON.generate(parsed_record.merge({ "enqueued_at" => now.to_f }))
23
+ payload = JSON.generate(parsed_record.merge({ "enqueued_at" => record.created_at.to_f }))
24
24
 
25
25
  redis.call("LPUSH", "queue:#{queue}", payload)
26
26
 
@@ -20,7 +20,7 @@ module RubyEventStore
20
20
  Repository::Record.create!(
21
21
  format: SIDEKIQ5_FORMAT,
22
22
  split_key: payload.fetch("queue"),
23
- payload: payload.to_json
23
+ payload: payload.to_json,
24
24
  )
25
25
  end
26
26
  end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module RubyEventStore
4
4
  module Outbox
5
- VERSION = "0.1.0"
5
+ VERSION = "0.2.0"
6
6
  end
7
7
  end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ruby_event_store-outbox
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Arkency
8
8
  bindir: bin
9
9
  cert_chain: []
10
- date: 2025-03-28 00:00:00.000000000 Z
10
+ date: 1980-01-02 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
13
  name: ruby_event_store
@@ -91,7 +91,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
91
91
  - !ruby/object:Gem::Version
92
92
  version: '0'
93
93
  requirements: []
94
- rubygems_version: 3.6.6
94
+ rubygems_version: 3.6.8
95
95
  specification_version: 4
96
96
  summary: Active Record based outbox for Ruby Event Store
97
97
  test_files: []