ruby_event_store-outbox 0.0.4 → 0.0.5

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: db60ea5281225f61b150abd5c7e6cc8e7379f09f20c58ed5f10b234534e43612
4
- data.tar.gz: 6a4d1015845ce306d2d9663d3b70fe284fa75ea783960e3c345d5d315d79cc61
3
+ metadata.gz: 4a53d0609414ba7c53da1a8602042de223631437392a9a29b0e3dbb3d2015a61
4
+ data.tar.gz: fa4dde249eecb5701f311d91a772fc12394b5412c6af18649d5ba604b11c9567
5
5
  SHA512:
6
- metadata.gz: f3629cebca8b80cf62979c5a1cdcb27e53c63552cbdc73e3b0296d8a4fed1b2715e9a8de5f2eafa69fd3f68a3cb2b77aa2e6209be26f3fa724fc70a59575031a
7
- data.tar.gz: 52d9e07cad234988c58453a700fe80a9325e22d68265821afd66ced82d2cbae27d2e0ab0d22b14b761a9faf908e49e766dedb99eca82aa825712e3362b5181a6
6
+ metadata.gz: f8d236f0e1cb2236f3740e7f92ea19662df5af2350e729266c50635208e3d714ccef6dfdd1f9080ac9237467a88be351e07f656cd5ae2361223f4eb6c96fb3cc
7
+ data.tar.gz: eb6718caa012a1d6f5ed52374108c0a74f2f7faf73ddb8208d17c2f0bdb636712ee30249865b8eb39d410c1f273721e0495fa53d2b3f32d06a8f2be98eb4459f
@@ -1,15 +1,16 @@
1
1
  require "optparse"
2
2
  require "ruby_event_store/outbox/version"
3
3
  require "ruby_event_store/outbox/consumer"
4
+ require "ruby_event_store/outbox/metrics"
4
5
 
5
6
  module RubyEventStore
6
7
  module Outbox
7
8
  class CLI
8
- Options = Struct.new(:database_url, :redis_url, :log_level, :split_keys, :message_format)
9
+ Options = Struct.new(:database_url, :redis_url, :log_level, :split_keys, :message_format, :batch_size, :metrics_url)
9
10
 
10
11
  class Parser
11
12
  def self.parse(argv)
12
- options = Options.new(nil, nil, :warn, nil, nil)
13
+ options = Options.new(nil, nil, :warn, nil, nil, 100)
13
14
  OptionParser.new do |option_parser|
14
15
  option_parser.banner = "Usage: res_outbox [options]"
15
16
 
@@ -33,6 +34,14 @@ module RubyEventStore
33
34
  options.split_keys = split_keys if !split_keys.empty?
34
35
  end
35
36
 
37
+ option_parser.on("--batch-size BATCH_SIZE", Integer, "Amount of records fetched in one fetch. Bigger value means more duplicated messages when network problems occur.") do |batch_size|
38
+ options.batch_size = batch_size
39
+ end
40
+
41
+ option_parser.on("--metrics-url METRICS_URL", "URI to metrics collector") do |metrics_url|
42
+ options.metrics_url = metrics_url
43
+ end
44
+
36
45
  option_parser.on_tail("--version", "Show version") do
37
46
  puts VERSION
38
47
  exit
@@ -44,16 +53,26 @@ module RubyEventStore
44
53
 
45
54
  def run(argv)
46
55
  options = Parser.parse(argv)
56
+ outbox_consumer = build_consumer(options)
57
+ outbox_consumer.init
58
+ outbox_consumer.run
59
+ end
60
+
61
+ def build_consumer(options)
47
62
  logger = Logger.new(STDOUT, level: options.log_level, progname: "RES-Outbox")
48
- outbox_consumer = RubyEventStore::Outbox::Consumer.new(
49
- options.message_format,
50
- options.split_keys,
63
+ consumer_configuration = Consumer::Configuration.new(
64
+ split_keys: options.split_keys,
65
+ message_format: options.message_format,
66
+ batch_size: options.batch_size,
51
67
  database_url: options.database_url,
52
68
  redis_url: options.redis_url,
69
+ )
70
+ metrics = Metrics.from_url(options.metrics_url)
71
+ outbox_consumer = RubyEventStore::Outbox::Consumer.new(
72
+ options,
53
73
  logger: logger,
74
+ metrics: metrics,
54
75
  )
55
- outbox_consumer.init
56
- outbox_consumer.run
57
76
  end
58
77
  end
59
78
  end
@@ -9,14 +9,45 @@ module RubyEventStore
9
9
  class Consumer
10
10
  SLEEP_TIME_WHEN_NOTHING_TO_DO = 0.1
11
11
 
12
- def initialize(format, split_keys, database_url:, redis_url:, clock: Time, logger:)
13
- @split_keys = split_keys
12
+ class Configuration
13
+ def initialize(
14
+ split_keys:,
15
+ message_format:,
16
+ batch_size:,
17
+ database_url:,
18
+ redis_url:
19
+ )
20
+ @split_keys = split_keys
21
+ @message_format = message_format
22
+ @batch_size = batch_size || 100
23
+ @database_url = database_url
24
+ @redis_url = redis_url
25
+ freeze
26
+ end
27
+
28
+ def with(overriden_options)
29
+ self.class.new(
30
+ split_keys: overriden_options.fetch(:split_keys, split_keys),
31
+ message_format: overriden_options.fetch(:message_format, message_format),
32
+ batch_size: overriden_options.fetch(:batch_size, batch_size),
33
+ database_url: overriden_options.fetch(:database_url, database_url),
34
+ redis_url: overriden_options.fetch(:redis_url, redis_url),
35
+ )
36
+ end
37
+
38
+ attr_reader :split_keys, :message_format, :batch_size, :database_url, :redis_url
39
+ end
40
+
41
+ def initialize(configuration, clock: Time, logger:, metrics:)
42
+ @split_keys = configuration.split_keys
14
43
  @clock = clock
15
- @redis = Redis.new(url: redis_url)
44
+ @redis = Redis.new(url: configuration.redis_url)
16
45
  @logger = logger
17
- ActiveRecord::Base.establish_connection(database_url) unless ActiveRecord::Base.connected?
46
+ @metrics = metrics
47
+ @batch_size = configuration.batch_size
48
+ ActiveRecord::Base.establish_connection(configuration.database_url) unless ActiveRecord::Base.connected?
18
49
 
19
- raise "Unknown format" if format != SIDEKIQ5_FORMAT
50
+ raise "Unknown format" if configuration.message_format != SIDEKIQ5_FORMAT
20
51
  @message_format = SIDEKIQ5_FORMAT
21
52
 
22
53
  @gracefully_shutting_down = false
@@ -44,8 +75,11 @@ module RubyEventStore
44
75
  Record.transaction do
45
76
  records_scope = Record.lock.where(format: message_format, enqueued_at: nil)
46
77
  records_scope = records_scope.where(split_key: split_keys) if !split_keys.nil?
47
- records = records_scope.order("id ASC").limit(100).to_a
48
- return false if records.empty?
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
82
+ end
49
83
 
50
84
  now = @clock.now.utc
51
85
  failed_record_ids = []
@@ -58,18 +92,25 @@ module RubyEventStore
58
92
  end
59
93
  end
60
94
 
61
- Record.where(id: records.map(&:id) - failed_record_ids).update_all(enqueued_at: now)
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)
62
98
 
63
99
  logger.info "Sent #{records.size} messages from outbox table"
64
- return true
100
+ true
65
101
  end
66
102
  rescue ActiveRecord::Deadlocked
67
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")
68
109
  false
69
110
  end
70
111
 
71
112
  private
72
- attr_reader :split_keys, :logger, :message_format
113
+ attr_reader :split_keys, :logger, :message_format, :batch_size, :metrics
73
114
 
74
115
  def handle_one_record(now, record)
75
116
  hash_payload = JSON.parse(record.payload)
@@ -0,0 +1,16 @@
1
+ require "ruby_event_store/outbox/metrics/null"
2
+ require "ruby_event_store/outbox/metrics/influx"
3
+
4
+ module RubyEventStore
5
+ module Outbox
6
+ module Metrics
7
+ def self.from_url(metrics_url)
8
+ if metrics_url.nil?
9
+ Null.new
10
+ else
11
+ Influx.new(metrics_url)
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,33 @@
1
+ require 'influxdb'
2
+
3
+ module RubyEventStore
4
+ module Outbox
5
+ module Metrics
6
+ class Influx
7
+ def initialize(url)
8
+ @influxdb_client = InfluxDB::Client.new(url: url, async: true, time_precision: 'ns')
9
+ end
10
+
11
+ def write_point_queue(status:, enqueued: 0, failed: 0)
12
+ write_point("ruby_event_store.outbox.queue", {
13
+ values: {
14
+ enqueued: enqueued,
15
+ failed: failed,
16
+ },
17
+ tags: {
18
+ status: status,
19
+ }
20
+ })
21
+ end
22
+
23
+ def write_point(series, data)
24
+ data[:timestamp] ||= Process.clock_gettime(Process::CLOCK_REALTIME, :nanosecond).to_i
25
+ @influxdb_client.write_point(series, data)
26
+ end
27
+
28
+ private
29
+ attr_reader :influxdb_client
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,10 @@
1
+ module RubyEventStore
2
+ module Outbox
3
+ module Metrics
4
+ class Null
5
+ def write_point_queue(status:, enqueued: 0, failed: 0)
6
+ end
7
+ end
8
+ end
9
+ end
10
+ end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module RubyEventStore
4
4
  module Outbox
5
- VERSION = "0.0.4"
5
+ VERSION = "0.0.5"
6
6
  end
7
7
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ruby_event_store-outbox
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.4
4
+ version: 0.0.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Arkency
@@ -53,6 +53,9 @@ 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/metrics.rb
57
+ - lib/ruby_event_store/outbox/metrics/influx.rb
58
+ - lib/ruby_event_store/outbox/metrics/null.rb
56
59
  - lib/ruby_event_store/outbox/record.rb
57
60
  - lib/ruby_event_store/outbox/sidekiq5_format.rb
58
61
  - lib/ruby_event_store/outbox/sidekiq_message_handler.rb