ruby_event_store-outbox 0.0.4 → 0.0.5

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: 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