ruby_event_store-outbox 0.0.24 → 0.0.26
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +3 -3
- data/lib/generators/ruby_event_store/outbox/migration_generator.rb +17 -15
- data/lib/generators/ruby_event_store/outbox/templates/{create_event_store_outbox_template.rb → create_event_store_outbox_template.erb} +4 -4
- data/lib/ruby_event_store/outbox/batch_result.rb +26 -0
- data/lib/ruby_event_store/outbox/cleanup_strategies/clean_old_enqueued.rb +3 -0
- data/lib/ruby_event_store/outbox/cleanup_strategies/none.rb +3 -2
- data/lib/ruby_event_store/outbox/cleanup_strategies.rb +20 -0
- data/lib/ruby_event_store/outbox/cli.rb +64 -48
- data/lib/ruby_event_store/outbox/configuration.rb +50 -0
- data/lib/ruby_event_store/outbox/consumer.rb +53 -102
- data/lib/ruby_event_store/outbox/fetch_specification.rb +4 -10
- data/lib/ruby_event_store/outbox/metrics/influx.rb +21 -25
- data/lib/ruby_event_store/outbox/metrics/null.rb +4 -4
- data/lib/ruby_event_store/outbox/metrics/test.rb +10 -2
- data/lib/ruby_event_store/outbox/metrics.rb +2 -0
- data/lib/ruby_event_store/outbox/repository.rb +20 -24
- data/lib/ruby_event_store/outbox/runner.rb +43 -0
- data/lib/ruby_event_store/outbox/sidekiq_processor.rb +13 -8
- data/lib/ruby_event_store/outbox/sidekiq_producer.rb +20 -9
- data/lib/ruby_event_store/outbox/sidekiq_scheduler.rb +2 -1
- data/lib/ruby_event_store/outbox/tempo.rb +25 -0
- data/lib/ruby_event_store/outbox/version.rb +1 -1
- data/lib/ruby_event_store/outbox.rb +9 -5
- metadata +16 -12
- data/lib/ruby_event_store/outbox/legacy_sidekiq_scheduler.rb +0 -24
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e26efaae4895206e93089d60869e01dd1a0f8182e6f41e49edef000a2833b5bb
|
4
|
+
data.tar.gz: 1ab71712967fa31437d538c787721f6bd27f0a65d1b0474e95cd1428945d9b62
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3189203160620f90690f35b511d2e60471c6652e13ef510464952aa40ba2b35c650d66477f1ab5e3360e6bf31ab6a1fdf40aadd8916ff9aa802dbe069034a013
|
7
|
+
data.tar.gz: 765725def129ea5368be1fd5fcb2ae013e45efe17ed48b9d58eb4d1c1356582fb76fcafd9885d9fc527556a6351993709250c434b8ee586708f9b8514d617e2f
|
data/README.md
CHANGED
@@ -34,11 +34,12 @@ Additionally, your handler's `through_outbox?` method should return `true`, for
|
|
34
34
|
|
35
35
|
```ruby
|
36
36
|
class SomeHandler
|
37
|
-
def self.through_outbox
|
37
|
+
def self.through_outbox?
|
38
|
+
true
|
39
|
+
end
|
38
40
|
end
|
39
41
|
```
|
40
42
|
|
41
|
-
|
42
43
|
## Installation (outbox process)
|
43
44
|
|
44
45
|
Run following process in any way you prefer:
|
@@ -65,7 +66,6 @@ res_outbox --database-url="mysql2://root@0.0.0.0:3306/my_database" \
|
|
65
66
|
--metrics-url=http://user:password@localhost:8086/dbname"
|
66
67
|
```
|
67
68
|
|
68
|
-
|
69
69
|
## Contributing
|
70
70
|
|
71
71
|
Bug reports and pull requests are welcome on GitHub at https://github.com/RailsEventStore/rails_event_store.
|
@@ -1,28 +1,30 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
begin
|
4
|
-
require
|
4
|
+
require "rails/generators"
|
5
5
|
rescue LoadError
|
6
6
|
end
|
7
7
|
|
8
|
-
|
9
|
-
module
|
10
|
-
|
11
|
-
|
8
|
+
if defined?(Rails::Generators::Base)
|
9
|
+
module RubyEventStore
|
10
|
+
module Outbox
|
11
|
+
class MigrationGenerator < Rails::Generators::Base
|
12
|
+
source_root File.expand_path(File.join(File.dirname(__FILE__), "./templates"))
|
12
13
|
|
13
|
-
|
14
|
-
|
15
|
-
|
14
|
+
def create_migration
|
15
|
+
template "create_event_store_outbox_template.erb", "db/migrate/#{timestamp}_create_event_store_outbox.rb"
|
16
|
+
end
|
16
17
|
|
17
|
-
|
18
|
+
private
|
18
19
|
|
19
|
-
|
20
|
-
|
21
|
-
|
20
|
+
def migration_version
|
21
|
+
::ActiveRecord::Migration.current_version
|
22
|
+
end
|
22
23
|
|
23
|
-
|
24
|
-
|
24
|
+
def timestamp
|
25
|
+
Time.now.strftime("%Y%m%d%H%M%S")
|
26
|
+
end
|
25
27
|
end
|
26
28
|
end
|
27
29
|
end
|
28
|
-
end
|
30
|
+
end
|
@@ -1,13 +1,13 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
class CreateEventStoreOutbox < ActiveRecord::Migration<%= migration_version %>
|
3
|
+
class CreateEventStoreOutbox < ActiveRecord::Migration[<%= migration_version %>]
|
4
4
|
def change
|
5
5
|
create_table(:event_store_outbox, force: false) do |t|
|
6
6
|
t.string :split_key, null: true
|
7
7
|
t.string :format, null: false
|
8
8
|
t.binary :payload, null: false
|
9
|
-
t.datetime :created_at, null: false
|
10
|
-
t.datetime :enqueued_at, null: true
|
9
|
+
t.datetime :created_at, null: false, precision: 6
|
10
|
+
t.datetime :enqueued_at, null: true, precision: 6
|
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"
|
@@ -15,7 +15,7 @@ class CreateEventStoreOutbox < ActiveRecord::Migration<%= migration_version %>
|
|
15
15
|
create_table(:event_store_outbox_locks, force: false) do |t|
|
16
16
|
t.string :format, null: false
|
17
17
|
t.string :split_key, null: false
|
18
|
-
t.datetime :locked_at, null: true
|
18
|
+
t.datetime :locked_at, null: true, precision: 6
|
19
19
|
t.string :locked_by, null: true, limit: 36
|
20
20
|
end
|
21
21
|
add_index :event_store_outbox_locks, [:format, :split_key], name: "index_event_store_outbox_locks_for_locking", unique: true
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RubyEventStore
|
4
|
+
module Outbox
|
5
|
+
class BatchResult
|
6
|
+
def self.empty
|
7
|
+
new
|
8
|
+
end
|
9
|
+
|
10
|
+
def initialize
|
11
|
+
@success_count = 0
|
12
|
+
@failed_count = 0
|
13
|
+
end
|
14
|
+
|
15
|
+
attr_reader :success_count, :failed_count
|
16
|
+
|
17
|
+
def count_success!
|
18
|
+
@success_count += 1
|
19
|
+
end
|
20
|
+
|
21
|
+
def count_failed!
|
22
|
+
@failed_count += 1
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RubyEventStore
|
4
|
+
module Outbox
|
5
|
+
module CleanupStrategies
|
6
|
+
def self.build(configuration, repository)
|
7
|
+
case configuration.cleanup
|
8
|
+
when :none
|
9
|
+
None.new
|
10
|
+
else
|
11
|
+
CleanOldEnqueued.new(
|
12
|
+
repository,
|
13
|
+
ActiveSupport::Duration.parse(configuration.cleanup),
|
14
|
+
configuration.cleanup_limit
|
15
|
+
)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -1,7 +1,11 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require "optparse"
|
2
4
|
require_relative "version"
|
3
5
|
require_relative "consumer"
|
6
|
+
require_relative "runner"
|
4
7
|
require_relative "metrics"
|
8
|
+
require_relative "configuration"
|
5
9
|
|
6
10
|
module RubyEventStore
|
7
11
|
module Outbox
|
@@ -11,7 +15,7 @@ module RubyEventStore
|
|
11
15
|
redis_url: nil,
|
12
16
|
log_level: :warn,
|
13
17
|
split_keys: nil,
|
14
|
-
message_format:
|
18
|
+
message_format: "sidekiq5",
|
15
19
|
batch_size: 100,
|
16
20
|
metrics_url: nil,
|
17
21
|
cleanup_strategy: :none,
|
@@ -23,69 +27,84 @@ module RubyEventStore
|
|
23
27
|
class Parser
|
24
28
|
def self.parse(argv)
|
25
29
|
options = Options.new(*DEFAULTS.values)
|
26
|
-
OptionParser
|
27
|
-
|
30
|
+
OptionParser
|
31
|
+
.new do |option_parser|
|
32
|
+
option_parser.banner = "Usage: res_outbox [options]"
|
28
33
|
|
29
|
-
|
30
|
-
|
31
|
-
|
34
|
+
option_parser.on(
|
35
|
+
"--database-url=DATABASE_URL",
|
36
|
+
"Database where outbox table is stored"
|
37
|
+
) { |database_url| options.database_url = database_url }
|
32
38
|
|
33
|
-
|
34
|
-
|
35
|
-
|
39
|
+
option_parser.on("--redis-url=REDIS_URL", "URL to redis database") do |redis_url|
|
40
|
+
options.redis_url = redis_url
|
41
|
+
end
|
36
42
|
|
37
|
-
|
38
|
-
|
39
|
-
|
43
|
+
option_parser.on(
|
44
|
+
"--log-level=LOG_LEVEL",
|
45
|
+
%i[fatal error warn info debug],
|
46
|
+
"Logging level, one of: fatal, error, warn, info, debug. Default: warn"
|
47
|
+
) { |log_level| options.log_level = log_level.to_sym }
|
40
48
|
|
41
|
-
|
42
|
-
|
43
|
-
|
49
|
+
option_parser.on(
|
50
|
+
"--message-format=FORMAT",
|
51
|
+
["sidekiq5"],
|
52
|
+
"Message format, supported: sidekiq5. Default: sidekiq5"
|
53
|
+
) { |message_format| options.message_format = message_format }
|
44
54
|
|
45
|
-
|
46
|
-
|
47
|
-
|
55
|
+
option_parser.on(
|
56
|
+
"--split-keys=SPLIT_KEYS",
|
57
|
+
Array,
|
58
|
+
"Split keys which should be handled, all if not specified"
|
59
|
+
) { |split_keys| options.split_keys = split_keys if !split_keys.empty? }
|
48
60
|
|
49
|
-
|
50
|
-
|
51
|
-
|
61
|
+
option_parser.on(
|
62
|
+
"--batch-size=BATCH_SIZE",
|
63
|
+
Integer,
|
64
|
+
"Amount of records fetched in one fetch. Bigger value means more duplicated messages when network problems occur. Default: 100"
|
65
|
+
) { |batch_size| options.batch_size = batch_size }
|
52
66
|
|
53
|
-
|
54
|
-
|
55
|
-
|
67
|
+
option_parser.on("--metrics-url=METRICS_URL", "URI to metrics collector, optional") do |metrics_url|
|
68
|
+
options.metrics_url = metrics_url
|
69
|
+
end
|
56
70
|
|
57
|
-
|
58
|
-
|
59
|
-
|
71
|
+
option_parser.on(
|
72
|
+
"--cleanup=STRATEGY",
|
73
|
+
"A strategy for cleaning old records. One of: none or iso8601 duration format how old enqueued records should be removed. Default: none"
|
74
|
+
) { |cleanup_strategy| options.cleanup_strategy = cleanup_strategy }
|
60
75
|
|
61
|
-
|
62
|
-
|
63
|
-
|
76
|
+
option_parser.on(
|
77
|
+
"--cleanup-limit=LIMIT",
|
78
|
+
"Amount of records removed in single cleanup run. One of: all or number of records that should be removed. Default: all"
|
79
|
+
) { |cleanup_limit| options.cleanup_limit = cleanup_limit }
|
64
80
|
|
65
|
-
|
66
|
-
|
67
|
-
|
81
|
+
option_parser.on(
|
82
|
+
"--sleep-on-empty=SLEEP_TIME",
|
83
|
+
Float,
|
84
|
+
"How long to sleep before next check when there was nothing to do. Default: 0.5"
|
85
|
+
) { |sleep_on_empty| options.sleep_on_empty = sleep_on_empty }
|
68
86
|
|
69
|
-
|
70
|
-
|
71
|
-
|
87
|
+
option_parser.on_tail("--version", "Show version") do
|
88
|
+
puts VERSION
|
89
|
+
exit
|
90
|
+
end
|
72
91
|
end
|
73
|
-
|
92
|
+
.parse(argv)
|
74
93
|
return options
|
75
94
|
end
|
76
95
|
end
|
77
96
|
|
78
97
|
def run(argv)
|
79
98
|
options = Parser.parse(argv)
|
80
|
-
|
81
|
-
|
82
|
-
|
99
|
+
build_runner(options)
|
100
|
+
.init
|
101
|
+
.run
|
83
102
|
end
|
84
103
|
|
85
|
-
def
|
104
|
+
def build_runner(options)
|
86
105
|
consumer_uuid = SecureRandom.uuid
|
87
106
|
logger = Logger.new(STDOUT, level: options.log_level, progname: "RES-Outbox #{consumer_uuid}")
|
88
|
-
consumer_configuration =
|
107
|
+
consumer_configuration = Configuration.new(
|
89
108
|
split_keys: options.split_keys,
|
90
109
|
message_format: options.message_format,
|
91
110
|
batch_size: options.batch_size,
|
@@ -96,12 +115,9 @@ module RubyEventStore
|
|
96
115
|
sleep_on_empty: options.sleep_on_empty,
|
97
116
|
)
|
98
117
|
metrics = Metrics.from_url(options.metrics_url)
|
99
|
-
outbox_consumer =
|
100
|
-
consumer_uuid,
|
101
|
-
|
102
|
-
logger: logger,
|
103
|
-
metrics: metrics,
|
104
|
-
)
|
118
|
+
outbox_consumer =
|
119
|
+
Outbox::Consumer.new(consumer_uuid, consumer_configuration, logger: logger, metrics: metrics)
|
120
|
+
Runner.new(outbox_consumer, consumer_configuration, logger: logger)
|
105
121
|
end
|
106
122
|
end
|
107
123
|
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RubyEventStore
|
4
|
+
module Outbox
|
5
|
+
class Configuration
|
6
|
+
def initialize(
|
7
|
+
split_keys:,
|
8
|
+
message_format:,
|
9
|
+
batch_size:,
|
10
|
+
database_url:,
|
11
|
+
redis_url:,
|
12
|
+
cleanup:,
|
13
|
+
cleanup_limit:,
|
14
|
+
sleep_on_empty:
|
15
|
+
)
|
16
|
+
@split_keys = split_keys
|
17
|
+
@message_format = message_format
|
18
|
+
@batch_size = batch_size || 100
|
19
|
+
@database_url = database_url
|
20
|
+
@redis_url = redis_url
|
21
|
+
@cleanup = cleanup
|
22
|
+
@cleanup_limit = cleanup_limit
|
23
|
+
@sleep_on_empty = sleep_on_empty
|
24
|
+
freeze
|
25
|
+
end
|
26
|
+
|
27
|
+
def with(overriden_options)
|
28
|
+
self.class.new(
|
29
|
+
split_keys: overriden_options.fetch(:split_keys, split_keys),
|
30
|
+
message_format: overriden_options.fetch(:message_format, message_format),
|
31
|
+
batch_size: overriden_options.fetch(:batch_size, batch_size),
|
32
|
+
database_url: overriden_options.fetch(:database_url, database_url),
|
33
|
+
redis_url: overriden_options.fetch(:redis_url, redis_url),
|
34
|
+
cleanup: overriden_options.fetch(:cleanup, cleanup),
|
35
|
+
cleanup_limit: overriden_options.fetch(:cleanup_limit, cleanup_limit),
|
36
|
+
sleep_on_empty: overriden_options.fetch(:sleep_on_empty, sleep_on_empty)
|
37
|
+
)
|
38
|
+
end
|
39
|
+
|
40
|
+
attr_reader :split_keys,
|
41
|
+
:message_format,
|
42
|
+
:batch_size,
|
43
|
+
:database_url,
|
44
|
+
:redis_url,
|
45
|
+
:cleanup,
|
46
|
+
:cleanup_limit,
|
47
|
+
:sleep_on_empty
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -1,5 +1,7 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require "logger"
|
2
|
-
require "redis"
|
4
|
+
require "redis-client"
|
3
5
|
require "active_record"
|
4
6
|
require_relative "repository"
|
5
7
|
require_relative "sidekiq5_format"
|
@@ -13,86 +15,24 @@ module RubyEventStore
|
|
13
15
|
class Consumer
|
14
16
|
MAXIMUM_BATCH_FETCHES_IN_ONE_LOCK = 10
|
15
17
|
|
16
|
-
class Configuration
|
17
|
-
def initialize(
|
18
|
-
split_keys:,
|
19
|
-
message_format:,
|
20
|
-
batch_size:,
|
21
|
-
database_url:,
|
22
|
-
redis_url:,
|
23
|
-
cleanup:,
|
24
|
-
cleanup_limit:,
|
25
|
-
sleep_on_empty:
|
26
|
-
)
|
27
|
-
@split_keys = split_keys
|
28
|
-
@message_format = message_format
|
29
|
-
@batch_size = batch_size || 100
|
30
|
-
@database_url = database_url
|
31
|
-
@redis_url = redis_url
|
32
|
-
@cleanup = cleanup
|
33
|
-
@cleanup_limit = cleanup_limit
|
34
|
-
@sleep_on_empty = sleep_on_empty
|
35
|
-
freeze
|
36
|
-
end
|
37
|
-
|
38
|
-
def with(overriden_options)
|
39
|
-
self.class.new(
|
40
|
-
split_keys: overriden_options.fetch(:split_keys, split_keys),
|
41
|
-
message_format: overriden_options.fetch(:message_format, message_format),
|
42
|
-
batch_size: overriden_options.fetch(:batch_size, batch_size),
|
43
|
-
database_url: overriden_options.fetch(:database_url, database_url),
|
44
|
-
redis_url: overriden_options.fetch(:redis_url, redis_url),
|
45
|
-
cleanup: overriden_options.fetch(:cleanup, cleanup),
|
46
|
-
cleanup_limit: overriden_options.fetch(:cleanup_limit, cleanup_limit),
|
47
|
-
sleep_on_empty: overriden_options.fetch(:sleep_on_empty, sleep_on_empty)
|
48
|
-
)
|
49
|
-
end
|
50
|
-
|
51
|
-
attr_reader :split_keys, :message_format, :batch_size, :database_url, :redis_url, :cleanup, :cleanup_limit, :sleep_on_empty
|
52
|
-
end
|
53
|
-
|
54
18
|
def initialize(consumer_uuid, configuration, clock: Time, logger:, metrics:)
|
55
19
|
@split_keys = configuration.split_keys
|
56
20
|
@clock = clock
|
57
21
|
@logger = logger
|
58
22
|
@metrics = metrics
|
59
|
-
@
|
60
|
-
@sleep_on_empty = configuration.sleep_on_empty
|
23
|
+
@tempo = Tempo.new(configuration.batch_size)
|
61
24
|
@consumer_uuid = consumer_uuid
|
62
25
|
|
63
26
|
raise "Unknown format" if configuration.message_format != SIDEKIQ5_FORMAT
|
64
|
-
|
65
|
-
|
66
|
-
@gracefully_shutting_down = false
|
67
|
-
prepare_traps
|
27
|
+
redis_config = RedisClient.config(url: configuration.redis_url)
|
28
|
+
@processor = SidekiqProcessor.new(redis_config.new_client)
|
68
29
|
|
69
30
|
@repository = Repository.new(configuration.database_url)
|
70
|
-
@cleanup_strategy =
|
71
|
-
when :none
|
72
|
-
CleanupStrategies::None.new
|
73
|
-
else
|
74
|
-
CleanupStrategies::CleanOldEnqueued.new(repository, ActiveSupport::Duration.parse(configuration.cleanup), configuration.cleanup_limit)
|
75
|
-
end
|
31
|
+
@cleanup_strategy = CleanupStrategies.build(configuration, repository)
|
76
32
|
end
|
77
33
|
|
78
|
-
def
|
79
|
-
|
80
|
-
logger.info("Handling split keys: #{split_keys ? split_keys.join(", ") : "(all of them)"}")
|
81
|
-
end
|
82
|
-
|
83
|
-
def run
|
84
|
-
while !@gracefully_shutting_down do
|
85
|
-
was_something_changed = one_loop
|
86
|
-
if !was_something_changed
|
87
|
-
STDOUT.flush
|
88
|
-
sleep sleep_on_empty
|
89
|
-
end
|
90
|
-
end
|
91
|
-
logger.info "Gracefully shutting down"
|
92
|
-
end
|
93
|
-
|
94
|
-
def one_loop
|
95
|
-
remaining_split_keys = @split_keys.dup
|
34
|
+
def process
|
35
|
+
remaining_split_keys = split_keys.dup
|
96
36
|
|
97
37
|
was_something_changed = false
|
98
38
|
while (split_key = remaining_split_keys.shift)
|
@@ -109,45 +49,41 @@ module RubyEventStore
|
|
109
49
|
|
110
50
|
MAXIMUM_BATCH_FETCHES_IN_ONE_LOCK.times do
|
111
51
|
batch = retrieve_batch(fetch_specification)
|
112
|
-
if batch.empty?
|
113
|
-
break
|
114
|
-
end
|
52
|
+
break if batch.empty?
|
115
53
|
|
116
|
-
|
117
|
-
updated_record_ids = []
|
54
|
+
batch_result = BatchResult.empty
|
118
55
|
batch.each do |record|
|
119
|
-
|
56
|
+
handle_failure(batch_result) do
|
120
57
|
now = @clock.now.utc
|
121
58
|
processor.process(record, now)
|
122
59
|
|
123
60
|
repository.mark_as_enqueued(record, now)
|
124
61
|
something_processed |= true
|
125
|
-
|
126
|
-
rescue => e
|
127
|
-
failed_record_ids << record.id
|
128
|
-
e.full_message.split($/).each {|line| logger.error(line) }
|
62
|
+
batch_result.count_success!
|
129
63
|
end
|
130
64
|
end
|
131
65
|
|
132
66
|
metrics.write_point_queue(
|
133
|
-
enqueued:
|
134
|
-
failed:
|
67
|
+
enqueued: batch_result.success_count,
|
68
|
+
failed: batch_result.failed_count,
|
135
69
|
format: fetch_specification.message_format,
|
136
70
|
split_key: fetch_specification.split_key,
|
137
71
|
remaining: get_remaining_count(fetch_specification)
|
138
72
|
)
|
139
73
|
|
140
|
-
logger.info "Sent #{
|
74
|
+
logger.info "Sent #{batch_result.success_count} messages from outbox table"
|
141
75
|
|
142
76
|
refresh_successful = refresh_lock_for_process(obtained_lock)
|
143
77
|
break unless refresh_successful
|
144
78
|
end
|
145
79
|
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
80
|
+
unless something_processed
|
81
|
+
metrics.write_point_queue(
|
82
|
+
format: fetch_specification.message_format,
|
83
|
+
split_key: fetch_specification.split_key,
|
84
|
+
remaining: get_remaining_count(fetch_specification)
|
85
|
+
)
|
86
|
+
end
|
151
87
|
|
152
88
|
release_lock_for_process(fetch_specification)
|
153
89
|
|
@@ -159,7 +95,35 @@ module RubyEventStore
|
|
159
95
|
end
|
160
96
|
|
161
97
|
private
|
162
|
-
|
98
|
+
|
99
|
+
attr_reader :split_keys,
|
100
|
+
:logger,
|
101
|
+
:metrics,
|
102
|
+
:processor,
|
103
|
+
:consumer_uuid,
|
104
|
+
:repository,
|
105
|
+
:cleanup_strategy,
|
106
|
+
:tempo
|
107
|
+
|
108
|
+
def handle_failure(batch_result)
|
109
|
+
retried = false
|
110
|
+
yield
|
111
|
+
rescue RetriableError => error
|
112
|
+
if retried
|
113
|
+
batch_result.count_failed!
|
114
|
+
log_error(error)
|
115
|
+
else
|
116
|
+
retried = true
|
117
|
+
retry
|
118
|
+
end
|
119
|
+
rescue => error
|
120
|
+
batch_result.count_failed!
|
121
|
+
log_error(error)
|
122
|
+
end
|
123
|
+
|
124
|
+
def log_error(e)
|
125
|
+
e.full_message.split($/).each { |line| logger.error(line) }
|
126
|
+
end
|
163
127
|
|
164
128
|
def obtain_lock_for_process(fetch_specification)
|
165
129
|
result = repository.obtain_lock_for_process(fetch_specification, consumer_uuid, clock: @clock)
|
@@ -230,21 +194,8 @@ module RubyEventStore
|
|
230
194
|
end
|
231
195
|
end
|
232
196
|
|
233
|
-
def prepare_traps
|
234
|
-
Signal.trap("INT") do
|
235
|
-
initiate_graceful_shutdown
|
236
|
-
end
|
237
|
-
Signal.trap("TERM") do
|
238
|
-
initiate_graceful_shutdown
|
239
|
-
end
|
240
|
-
end
|
241
|
-
|
242
|
-
def initiate_graceful_shutdown
|
243
|
-
@gracefully_shutting_down = true
|
244
|
-
end
|
245
|
-
|
246
197
|
def retrieve_batch(fetch_specification)
|
247
|
-
repository.retrieve_batch(fetch_specification, batch_size)
|
198
|
+
repository.retrieve_batch(fetch_specification, tempo.batch_size)
|
248
199
|
end
|
249
200
|
|
250
201
|
def get_remaining_count(fetch_specification)
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module RubyEventStore
|
2
4
|
module Outbox
|
3
5
|
class FetchSpecification
|
@@ -10,19 +12,11 @@ module RubyEventStore
|
|
10
12
|
attr_reader :message_format, :split_key
|
11
13
|
|
12
14
|
def ==(other)
|
13
|
-
other.instance_of?(self.class) &&
|
14
|
-
other.message_format.eql?(message_format) &&
|
15
|
-
other.split_key.eql?(split_key)
|
15
|
+
other.instance_of?(self.class) && other.message_format.eql?(message_format) && other.split_key.eql?(split_key)
|
16
16
|
end
|
17
17
|
|
18
|
-
BIG_VALUE = 0b111111100100010000010010110010101011011101110101001100100110000
|
19
|
-
|
20
18
|
def hash
|
21
|
-
[
|
22
|
-
self.class,
|
23
|
-
message_format,
|
24
|
-
split_key,
|
25
|
-
].hash ^ BIG_VALUE
|
19
|
+
[message_format, split_key].hash ^ self.class.hash
|
26
20
|
end
|
27
21
|
|
28
22
|
alias_method :eql?, :==
|
@@ -1,4 +1,6 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "influxdb"
|
2
4
|
|
3
5
|
module RubyEventStore
|
4
6
|
module Outbox
|
@@ -6,38 +8,32 @@ module RubyEventStore
|
|
6
8
|
class Influx
|
7
9
|
def initialize(url)
|
8
10
|
uri = URI.parse(url)
|
9
|
-
options = {
|
10
|
-
url: url,
|
11
|
-
async: true,
|
12
|
-
time_precision: 'ns',
|
13
|
-
}
|
11
|
+
options = { url: url, async: true, time_precision: "ns" }
|
14
12
|
@influxdb_client = InfluxDB::Client.new(**options)
|
15
13
|
end
|
16
14
|
|
17
15
|
def write_operation_result(operation, result)
|
18
|
-
write_point(
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
tags: {
|
23
|
-
operation: operation,
|
24
|
-
result: result,
|
25
|
-
}
|
26
|
-
})
|
16
|
+
write_point(
|
17
|
+
"ruby_event_store.outbox.lock",
|
18
|
+
{ values: { value: 1 }, tags: { operation: operation, result: result } }
|
19
|
+
)
|
27
20
|
end
|
28
21
|
|
29
22
|
def write_point_queue(enqueued: 0, failed: 0, remaining: 0, format: nil, split_key: nil)
|
30
|
-
write_point(
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
23
|
+
write_point(
|
24
|
+
"ruby_event_store.outbox.queue",
|
25
|
+
{
|
26
|
+
values: {
|
27
|
+
enqueued: enqueued,
|
28
|
+
failed: failed,
|
29
|
+
remaining: remaining
|
30
|
+
},
|
31
|
+
tags: {
|
32
|
+
format: format,
|
33
|
+
split_key: split_key
|
34
|
+
}
|
39
35
|
}
|
40
|
-
|
36
|
+
)
|
41
37
|
end
|
42
38
|
|
43
39
|
def write_point(series, data)
|
@@ -1,12 +1,12 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module RubyEventStore
|
2
4
|
module Outbox
|
3
5
|
module Metrics
|
4
6
|
class Null
|
5
|
-
def write_operation_result(operation, result)
|
6
|
-
end
|
7
|
+
def write_operation_result(operation, result); end
|
7
8
|
|
8
|
-
def write_point_queue(**kwargs)
|
9
|
-
end
|
9
|
+
def write_point_queue(**kwargs); end
|
10
10
|
end
|
11
11
|
end
|
12
12
|
end
|
@@ -1,4 +1,6 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "influxdb"
|
2
4
|
|
3
5
|
module RubyEventStore
|
4
6
|
module Outbox
|
@@ -14,7 +16,13 @@ module RubyEventStore
|
|
14
16
|
end
|
15
17
|
|
16
18
|
def write_point_queue(enqueued: 0, failed: 0, remaining: 0, format: nil, split_key: nil)
|
17
|
-
@queue_stats << {
|
19
|
+
@queue_stats << {
|
20
|
+
enqueued: enqueued,
|
21
|
+
failed: failed,
|
22
|
+
remaining: remaining,
|
23
|
+
format: format,
|
24
|
+
split_key: split_key
|
25
|
+
}
|
18
26
|
end
|
19
27
|
|
20
28
|
attr_reader :operation_results, :queue_stats
|
@@ -1,7 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require
|
4
|
-
require
|
3
|
+
require "active_record"
|
4
|
+
require "active_support/core_ext/numeric/time.rb"
|
5
5
|
|
6
6
|
module RubyEventStore
|
7
7
|
module Outbox
|
@@ -10,7 +10,7 @@ module RubyEventStore
|
|
10
10
|
|
11
11
|
class Record < ::ActiveRecord::Base
|
12
12
|
self.primary_key = :id
|
13
|
-
self.table_name =
|
13
|
+
self.table_name = "event_store_outbox"
|
14
14
|
|
15
15
|
def self.remaining_for(fetch_specification)
|
16
16
|
where(format: fetch_specification.message_format, split_key: fetch_specification.split_key, enqueued_at: nil)
|
@@ -30,7 +30,7 @@ module RubyEventStore
|
|
30
30
|
end
|
31
31
|
|
32
32
|
class Lock < ::ActiveRecord::Base
|
33
|
-
self.table_name =
|
33
|
+
self.table_name = "event_store_outbox_locks"
|
34
34
|
|
35
35
|
def self.obtain(fetch_specification, process_uuid, clock:)
|
36
36
|
transaction do
|
@@ -39,16 +39,13 @@ module RubyEventStore
|
|
39
39
|
if l.recently_locked?(clock: clock)
|
40
40
|
:taken
|
41
41
|
else
|
42
|
-
l.update!(
|
43
|
-
locked_by: process_uuid,
|
44
|
-
locked_at: clock.now,
|
45
|
-
)
|
42
|
+
l.update!(locked_by: process_uuid, locked_at: clock.now)
|
46
43
|
l
|
47
44
|
end
|
48
45
|
end
|
49
|
-
rescue ActiveRecord::Deadlocked
|
46
|
+
rescue ::ActiveRecord::Deadlocked
|
50
47
|
:deadlocked
|
51
|
-
rescue ActiveRecord::LockWaitTimeout
|
48
|
+
rescue ::ActiveRecord::LockWaitTimeout
|
52
49
|
:lock_timeout
|
53
50
|
end
|
54
51
|
|
@@ -63,9 +60,9 @@ module RubyEventStore
|
|
63
60
|
:stolen
|
64
61
|
end
|
65
62
|
end
|
66
|
-
rescue ActiveRecord::Deadlocked
|
63
|
+
rescue ::ActiveRecord::Deadlocked
|
67
64
|
:deadlocked
|
68
|
-
rescue ActiveRecord::LockWaitTimeout
|
65
|
+
rescue ::ActiveRecord::LockWaitTimeout
|
69
66
|
:lock_timeout
|
70
67
|
end
|
71
68
|
|
@@ -79,9 +76,9 @@ module RubyEventStore
|
|
79
76
|
:ok
|
80
77
|
end
|
81
78
|
end
|
82
|
-
rescue ActiveRecord::Deadlocked
|
79
|
+
rescue ::ActiveRecord::Deadlocked
|
83
80
|
:deadlocked
|
84
|
-
rescue ActiveRecord::LockWaitTimeout
|
81
|
+
rescue ::ActiveRecord::LockWaitTimeout
|
85
82
|
:lock_timeout
|
86
83
|
end
|
87
84
|
|
@@ -98,6 +95,7 @@ module RubyEventStore
|
|
98
95
|
end
|
99
96
|
|
100
97
|
private
|
98
|
+
|
101
99
|
def self.lock_for_split_key(fetch_specification)
|
102
100
|
lock.find_by(format: fetch_specification.message_format, split_key: fetch_specification.split_key)
|
103
101
|
end
|
@@ -107,7 +105,7 @@ module RubyEventStore
|
|
107
105
|
if l.nil?
|
108
106
|
begin
|
109
107
|
l = create!(format: fetch_specification.message_format, split_key: fetch_specification.split_key)
|
110
|
-
rescue ActiveRecord::RecordNotUnique
|
108
|
+
rescue ::ActiveRecord::RecordNotUnique
|
111
109
|
l = lock_for_split_key(fetch_specification)
|
112
110
|
end
|
113
111
|
end
|
@@ -116,9 +114,9 @@ module RubyEventStore
|
|
116
114
|
end
|
117
115
|
|
118
116
|
def initialize(database_url)
|
119
|
-
ActiveRecord::Base.establish_connection(database_url) unless ActiveRecord::Base.connected?
|
120
|
-
if ActiveRecord::Base.connection.adapter_name == "Mysql2"
|
121
|
-
ActiveRecord::Base.connection.execute("SET SESSION innodb_lock_wait_timeout = 1;")
|
117
|
+
::ActiveRecord::Base.establish_connection(database_url) unless ::ActiveRecord::Base.connected?
|
118
|
+
if ::ActiveRecord::Base.connection.adapter_name == "Mysql2"
|
119
|
+
::ActiveRecord::Base.connection.execute("SET SESSION innodb_lock_wait_timeout = 1;")
|
122
120
|
end
|
123
121
|
end
|
124
122
|
|
@@ -143,15 +141,13 @@ module RubyEventStore
|
|
143
141
|
end
|
144
142
|
|
145
143
|
def delete_enqueued_older_than(fetch_specification, duration, limit)
|
146
|
-
scope = Record
|
147
|
-
|
148
|
-
.where("enqueued_at < ?", duration.ago)
|
149
|
-
scope = scope.limit(limit) unless limit == :all
|
144
|
+
scope = Record.for_fetch_specification(fetch_specification).where("enqueued_at < ?", duration.ago)
|
145
|
+
scope = scope.limit(limit).order(:id) unless limit == :all
|
150
146
|
scope.delete_all
|
151
147
|
:ok
|
152
|
-
rescue ActiveRecord::Deadlocked
|
148
|
+
rescue ::ActiveRecord::Deadlocked
|
153
149
|
:deadlocked
|
154
|
-
rescue ActiveRecord::LockWaitTimeout
|
150
|
+
rescue ::ActiveRecord::LockWaitTimeout
|
155
151
|
:lock_timeout
|
156
152
|
end
|
157
153
|
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RubyEventStore
|
4
|
+
module Outbox
|
5
|
+
class Runner
|
6
|
+
def initialize(consumer, configuration, logger:)
|
7
|
+
@consumer = consumer
|
8
|
+
@logger = logger
|
9
|
+
@sleep_on_empty = configuration.sleep_on_empty
|
10
|
+
@split_keys = configuration.split_keys
|
11
|
+
@gracefully_shutting_down = false
|
12
|
+
prepare_traps
|
13
|
+
end
|
14
|
+
|
15
|
+
def run
|
16
|
+
logger.info("Initiated RubyEventStore::Outbox v#{VERSION}")
|
17
|
+
logger.info("Handling split keys: #{split_keys ? split_keys.join(", ") : "(all of them)"}")
|
18
|
+
|
19
|
+
while !@gracefully_shutting_down
|
20
|
+
was_something_changed = consumer.process
|
21
|
+
if !was_something_changed
|
22
|
+
STDOUT.flush
|
23
|
+
sleep sleep_on_empty
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
logger.info "Gracefully shutting down"
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
attr_reader :consumer, :logger, :sleep_on_empty, :split_keys
|
32
|
+
|
33
|
+
def prepare_traps
|
34
|
+
Signal.trap("INT") { initiate_graceful_shutdown }
|
35
|
+
Signal.trap("TERM") { initiate_graceful_shutdown }
|
36
|
+
end
|
37
|
+
|
38
|
+
def initiate_graceful_shutdown
|
39
|
+
@gracefully_shutting_down = true
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -17,20 +17,17 @@ module RubyEventStore
|
|
17
17
|
|
18
18
|
queue = parsed_record["queue"]
|
19
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
|
-
}))
|
20
|
+
payload = JSON.generate(parsed_record.merge({ "enqueued_at" => now.to_f }))
|
23
21
|
|
24
|
-
redis.
|
22
|
+
redis.call("LPUSH", "queue:#{queue}", payload)
|
25
23
|
|
26
24
|
@recently_used_queues << queue
|
25
|
+
rescue RedisClient::TimeoutError, RedisClient::ConnectionError
|
26
|
+
raise RetriableError
|
27
27
|
end
|
28
28
|
|
29
29
|
def after_batch
|
30
|
-
|
31
|
-
redis.sadd("queues", @recently_used_queues.to_a)
|
32
|
-
@recently_used_queues.clear
|
33
|
-
end
|
30
|
+
ensure_that_sidekiq_knows_about_all_queues
|
34
31
|
end
|
35
32
|
|
36
33
|
def message_format
|
@@ -38,6 +35,14 @@ module RubyEventStore
|
|
38
35
|
end
|
39
36
|
|
40
37
|
private
|
38
|
+
|
39
|
+
def ensure_that_sidekiq_knows_about_all_queues
|
40
|
+
if !@recently_used_queues.empty?
|
41
|
+
redis.call("SADD", "queues", @recently_used_queues.to_a)
|
42
|
+
@recently_used_queues.clear
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
41
46
|
attr_reader :redis
|
42
47
|
end
|
43
48
|
end
|
@@ -1,31 +1,42 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require
|
4
|
-
require_relative
|
3
|
+
require "sidekiq"
|
4
|
+
require_relative "sidekiq5_format"
|
5
5
|
require_relative "repository"
|
6
6
|
|
7
7
|
module RubyEventStore
|
8
8
|
module Outbox
|
9
9
|
class SidekiqProducer
|
10
10
|
def call(klass, args)
|
11
|
-
|
12
|
-
item = {
|
13
|
-
'args' => args.map(&:to_h).map {|h| h.transform_keys(&:to_s)},
|
14
|
-
'class' => klass,
|
15
|
-
}
|
11
|
+
item = { "args" => args.map(&:to_h).map { |h| h.transform_keys(&:to_s) }, "class" => klass }
|
16
12
|
normalized_item = sidekiq_client.__send__(:normalize_item, item)
|
17
|
-
payload =
|
13
|
+
payload =
|
14
|
+
sidekiq_client
|
15
|
+
.middleware
|
16
|
+
.invoke(normalized_item["class"], normalized_item, normalized_item["queue"], Sidekiq.redis_pool) do
|
17
|
+
normalized_item
|
18
|
+
end
|
18
19
|
if payload
|
19
20
|
Repository::Record.create!(
|
20
21
|
format: SIDEKIQ5_FORMAT,
|
21
|
-
split_key: payload.fetch(
|
22
|
+
split_key: payload.fetch("queue"),
|
22
23
|
payload: payload.to_json
|
23
24
|
)
|
24
25
|
end
|
25
26
|
end
|
26
27
|
|
27
28
|
private
|
29
|
+
|
28
30
|
attr_reader :repository
|
31
|
+
|
32
|
+
def sidekiq_client
|
33
|
+
@sidekiq_client ||=
|
34
|
+
if Gem::Version.new(Sidekiq::VERSION) < Gem::Version.new("7.0.0")
|
35
|
+
Sidekiq::Client.new(Sidekiq.redis_pool)
|
36
|
+
else
|
37
|
+
Sidekiq::Client.new(pool: Sidekiq.redis_pool)
|
38
|
+
end
|
39
|
+
end
|
29
40
|
end
|
30
41
|
end
|
31
42
|
end
|
@@ -5,7 +5,7 @@ require_relative "sidekiq_producer"
|
|
5
5
|
module RubyEventStore
|
6
6
|
module Outbox
|
7
7
|
class SidekiqScheduler
|
8
|
-
def initialize(serializer:
|
8
|
+
def initialize(serializer: Serializers::YAML)
|
9
9
|
@serializer = serializer
|
10
10
|
@sidekiq_producer = SidekiqProducer.new
|
11
11
|
end
|
@@ -19,6 +19,7 @@ module RubyEventStore
|
|
19
19
|
end
|
20
20
|
|
21
21
|
private
|
22
|
+
|
22
23
|
attr_reader :serializer, :sidekiq_producer
|
23
24
|
end
|
24
25
|
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RubyEventStore
|
4
|
+
module Outbox
|
5
|
+
class Tempo
|
6
|
+
EXPONENTIAL_MULTIPLIER = 2
|
7
|
+
|
8
|
+
def initialize(max_batch_size)
|
9
|
+
raise ArgumentError if max_batch_size < 1
|
10
|
+
@max_batch_size = max_batch_size
|
11
|
+
end
|
12
|
+
|
13
|
+
def batch_size
|
14
|
+
@batch_size = next_batch_size
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
def next_batch_size
|
20
|
+
return 1 if @batch_size.nil?
|
21
|
+
[@batch_size * EXPONENTIAL_MULTIPLIER, @max_batch_size].min
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -2,11 +2,15 @@
|
|
2
2
|
|
3
3
|
module RubyEventStore
|
4
4
|
module Outbox
|
5
|
+
Error = Class.new(StandardError)
|
6
|
+
RetriableError = Class.new(Error)
|
5
7
|
end
|
6
8
|
end
|
7
9
|
|
8
|
-
require_relative
|
9
|
-
require_relative
|
10
|
-
require_relative
|
11
|
-
require_relative
|
12
|
-
require_relative
|
10
|
+
require_relative "outbox/fetch_specification"
|
11
|
+
require_relative "outbox/repository"
|
12
|
+
require_relative "outbox/sidekiq_scheduler"
|
13
|
+
require_relative "outbox/version"
|
14
|
+
require_relative "outbox/tempo"
|
15
|
+
require_relative "outbox/batch_result"
|
16
|
+
require_relative "outbox/cleanup_strategies"
|
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.
|
4
|
+
version: 0.0.26
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Arkency
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2024-04-12 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: ruby_event_store
|
@@ -30,15 +30,15 @@ dependencies:
|
|
30
30
|
requirements:
|
31
31
|
- - ">="
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version: '
|
33
|
+
version: '6.0'
|
34
34
|
type: :runtime
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
38
|
- - ">="
|
39
39
|
- !ruby/object:Gem::Version
|
40
|
-
version: '
|
41
|
-
description:
|
40
|
+
version: '6.0'
|
41
|
+
description:
|
42
42
|
email: dev@arkency.com
|
43
43
|
executables:
|
44
44
|
- res_outbox
|
@@ -49,23 +49,27 @@ files:
|
|
49
49
|
- README.md
|
50
50
|
- bin/res_outbox
|
51
51
|
- lib/generators/ruby_event_store/outbox/migration_generator.rb
|
52
|
-
- lib/generators/ruby_event_store/outbox/templates/create_event_store_outbox_template.
|
52
|
+
- lib/generators/ruby_event_store/outbox/templates/create_event_store_outbox_template.erb
|
53
53
|
- lib/ruby_event_store/outbox.rb
|
54
|
+
- lib/ruby_event_store/outbox/batch_result.rb
|
55
|
+
- lib/ruby_event_store/outbox/cleanup_strategies.rb
|
54
56
|
- lib/ruby_event_store/outbox/cleanup_strategies/clean_old_enqueued.rb
|
55
57
|
- lib/ruby_event_store/outbox/cleanup_strategies/none.rb
|
56
58
|
- lib/ruby_event_store/outbox/cli.rb
|
59
|
+
- lib/ruby_event_store/outbox/configuration.rb
|
57
60
|
- lib/ruby_event_store/outbox/consumer.rb
|
58
61
|
- lib/ruby_event_store/outbox/fetch_specification.rb
|
59
|
-
- lib/ruby_event_store/outbox/legacy_sidekiq_scheduler.rb
|
60
62
|
- lib/ruby_event_store/outbox/metrics.rb
|
61
63
|
- lib/ruby_event_store/outbox/metrics/influx.rb
|
62
64
|
- lib/ruby_event_store/outbox/metrics/null.rb
|
63
65
|
- lib/ruby_event_store/outbox/metrics/test.rb
|
64
66
|
- lib/ruby_event_store/outbox/repository.rb
|
67
|
+
- lib/ruby_event_store/outbox/runner.rb
|
65
68
|
- lib/ruby_event_store/outbox/sidekiq5_format.rb
|
66
69
|
- lib/ruby_event_store/outbox/sidekiq_processor.rb
|
67
70
|
- lib/ruby_event_store/outbox/sidekiq_producer.rb
|
68
71
|
- lib/ruby_event_store/outbox/sidekiq_scheduler.rb
|
72
|
+
- lib/ruby_event_store/outbox/tempo.rb
|
69
73
|
- lib/ruby_event_store/outbox/version.rb
|
70
74
|
homepage: https://railseventstore.org
|
71
75
|
licenses:
|
@@ -75,7 +79,7 @@ metadata:
|
|
75
79
|
source_code_uri: https://github.com/RailsEventStore/rails_event_store
|
76
80
|
bug_tracker_uri: https://github.com/RailsEventStore/rails_event_store/issues
|
77
81
|
rubygems_mfa_required: 'true'
|
78
|
-
post_install_message:
|
82
|
+
post_install_message:
|
79
83
|
rdoc_options: []
|
80
84
|
require_paths:
|
81
85
|
- lib
|
@@ -83,15 +87,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
83
87
|
requirements:
|
84
88
|
- - ">="
|
85
89
|
- !ruby/object:Gem::Version
|
86
|
-
version: '2.
|
90
|
+
version: '2.7'
|
87
91
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
88
92
|
requirements:
|
89
93
|
- - ">="
|
90
94
|
- !ruby/object:Gem::Version
|
91
95
|
version: '0'
|
92
96
|
requirements: []
|
93
|
-
rubygems_version: 3.
|
94
|
-
signing_key:
|
97
|
+
rubygems_version: 3.4.10
|
98
|
+
signing_key:
|
95
99
|
specification_version: 4
|
96
100
|
summary: Active Record based outbox for Ruby Event Store
|
97
101
|
test_files: []
|
@@ -1,24 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require_relative "sidekiq_producer"
|
4
|
-
|
5
|
-
module RubyEventStore
|
6
|
-
module Outbox
|
7
|
-
class LegacySidekiqScheduler
|
8
|
-
def initialize
|
9
|
-
@sidekiq_producer = SidekiqProducer.new
|
10
|
-
end
|
11
|
-
|
12
|
-
def call(klass, serialized_record)
|
13
|
-
sidekiq_producer.call(klass, [serialized_record])
|
14
|
-
end
|
15
|
-
|
16
|
-
def verify(subscriber)
|
17
|
-
Class === subscriber && subscriber.respond_to?(:through_outbox?) && subscriber.through_outbox?
|
18
|
-
end
|
19
|
-
|
20
|
-
private
|
21
|
-
attr_reader :sidekiq_producer
|
22
|
-
end
|
23
|
-
end
|
24
|
-
end
|