rails-transactional-outbox 0.2.0 → 0.3.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 +4 -4
- data/CHANGELOG.md +9 -1
- data/Gemfile.lock +16 -14
- data/README.md +10 -13
- data/lib/rails_transactional_outbox/health_check.rb +15 -16
- data/lib/rails_transactional_outbox/outbox_entry_factory.rb +1 -1
- data/lib/rails_transactional_outbox/version.rb +1 -1
- data/lib/rails_transactional_outbox.rb +1 -0
- data/rails-transactional-outbox.gemspec +1 -1
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7981d03fa1319ccf83fe5f66ac64869ef024e8add13de49f6af43b26ff39ac3e
|
4
|
+
data.tar.gz: e2f5048f5c4f5700b95b247064cc4e2fe9d8601b6df63d39b1e06896aeb1e915
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e903bcb6ee6c1b58d77b7c9b0dff41297e44b81c45457df0888caf6ecbdf2fa0f7254f9424cac14db2d31933e6f9790438cf2a9e232cb6af06405f9985221ceb
|
7
|
+
data.tar.gz: 8296fbab0324e8b0fa6410187e0fb59392121aecfccab2301fecff8fc1b51d815eaa71af78fdbd1d7c67f11573fc06d9598ec9e719d01738fc72d92646a33e0a
|
data/CHANGELOG.md
CHANGED
@@ -1,8 +1,16 @@
|
|
1
1
|
## [Unreleased]
|
2
2
|
|
3
|
+
## [0.3.0] - 2022-12-20
|
4
|
+
|
5
|
+
- Move to file-based healthchecks, instead of using Redis-based ones.
|
6
|
+
|
7
|
+
## [0.2.1] - 2022-09-08
|
8
|
+
|
9
|
+
- Simplify update to 0.2 (`causality_key` is not required if `outbox_entry_causality_key_resolver` is not used)
|
10
|
+
|
3
11
|
## [0.2.0] - 2022-09-08
|
4
12
|
|
5
|
-
- Introduce `RailsTransactionalOutbox::OutboxEntriesProcessors::OrderedByCausalityKeyProcessor`
|
13
|
+
- Introduce `RailsTransactionalOutbox::OutboxEntriesProcessors::OrderedByCausalityKeyProcessor`
|
6
14
|
|
7
15
|
## [0.1.0] - 2022-08-23
|
8
16
|
|
data/Gemfile.lock
CHANGED
@@ -9,12 +9,12 @@ GIT
|
|
9
9
|
PATH
|
10
10
|
remote: .
|
11
11
|
specs:
|
12
|
-
rails-transactional-outbox (0.
|
12
|
+
rails-transactional-outbox (0.3.0)
|
13
13
|
activerecord (>= 5)
|
14
14
|
activesupport (>= 3.2)
|
15
15
|
concurrent-ruby
|
16
16
|
dry-monitor
|
17
|
-
|
17
|
+
file-based-healthcheck
|
18
18
|
sigurd
|
19
19
|
zeitwerk
|
20
20
|
|
@@ -40,21 +40,23 @@ GEM
|
|
40
40
|
msgpack
|
41
41
|
debase-ruby_core_source (0.10.16)
|
42
42
|
diff-lcs (1.5.0)
|
43
|
-
dry-configurable (0.
|
43
|
+
dry-configurable (1.0.1)
|
44
|
+
dry-core (~> 1.0, < 2)
|
45
|
+
zeitwerk (~> 2.6)
|
46
|
+
dry-core (1.0.0)
|
44
47
|
concurrent-ruby (~> 1.0)
|
45
|
-
|
46
|
-
dry-
|
48
|
+
zeitwerk (~> 2.6)
|
49
|
+
dry-events (1.0.1)
|
47
50
|
concurrent-ruby (~> 1.0)
|
48
|
-
|
49
|
-
|
50
|
-
dry-
|
51
|
-
|
52
|
-
dry-
|
53
|
-
dry-core (~> 0.5, >= 0.5)
|
54
|
-
dry-events (~> 0.2)
|
55
|
-
zeitwerk (~> 2.5)
|
51
|
+
dry-core (~> 1.0, < 2)
|
52
|
+
dry-monitor (1.0.1)
|
53
|
+
dry-configurable (~> 1.0, < 2)
|
54
|
+
dry-core (~> 1.0, < 2)
|
55
|
+
dry-events (~> 1.0, < 2)
|
56
56
|
exponential-backoff (0.0.4)
|
57
57
|
ffi (1.15.5)
|
58
|
+
file-based-healthcheck (0.1.1)
|
59
|
+
activesupport (>= 3.2)
|
58
60
|
i18n (1.12.0)
|
59
61
|
concurrent-ruby (~> 1.0)
|
60
62
|
json (2.6.2)
|
@@ -119,7 +121,7 @@ GEM
|
|
119
121
|
tzinfo (2.0.5)
|
120
122
|
concurrent-ruby (~> 1.0)
|
121
123
|
unicode-display_width (2.2.0)
|
122
|
-
zeitwerk (2.6.
|
124
|
+
zeitwerk (2.6.6)
|
123
125
|
|
124
126
|
PLATFORMS
|
125
127
|
x86_64-darwin-18
|
data/README.md
CHANGED
@@ -29,21 +29,21 @@ Create the initializer with the following content:
|
|
29
29
|
``` rb
|
30
30
|
Rails.application.config.to_prepare do
|
31
31
|
RailsTransactionalOutbox.configure do |config|
|
32
|
-
config.database_connection_provider = ActiveRecord::Base # required
|
32
|
+
config.database_connection_provider = ActiveRecord::Base # required
|
33
33
|
config.transaction_provider = ActiveRecord::Base # required
|
34
34
|
config.logger = Rails.logger # required
|
35
35
|
config.outbox_model = OutboxEntry # required
|
36
|
-
config.error_handler = Sentry # non-required, but highly recommended, defaults to RailsTransactionalOutbox::ErrorHandlers::NullErrorHandler
|
37
|
-
|
36
|
+
config.error_handler = Sentry # non-required, but highly recommended, defaults to RailsTransactionalOutbox::ErrorHandlers::NullErrorHandler. When using Sentry, you will probably want to exclude SignalException `config.excluded_exceptions += ["SignalException"]`.
|
37
|
+
|
38
38
|
config.transactional_outbox_worker_sleep_seconds = 1 # optional, defaults to 0.5
|
39
|
-
config.transactional_outbox_worker_idle_delay_multiplier = 5 # optional, defaults to 1, if there are no outbox entries to be processed, then the sleep time for the thread will be equal to transactional_outbox_worker_idle_delay_multiplier * transactional_outbox_worker_sleep_seconds
|
39
|
+
config.transactional_outbox_worker_idle_delay_multiplier = 5 # optional, defaults to 1, if there are no outbox entries to be processed, then the sleep time for the thread will be equal to transactional_outbox_worker_idle_delay_multiplier * transactional_outbox_worker_sleep_seconds
|
40
40
|
config.outbox_batch_size = 100 # optional, defaults to 100
|
41
41
|
config.add_record_processor(MyCustomOperationProcerssor) # optional, by default it contains only one processor for ActiveRecord, but you could add more
|
42
|
-
|
42
|
+
|
43
43
|
config.lock_client = Redlock::Client.new([ENV["REDIS_URL"]]) # required if you want to use RailsTransactionalOutbox::OutboxEntriesProcessors::OrderedByCausalityKeyProcessor, defaults to RailsTransactionalOutbox::NullLockClient. Check its interface and the interface of `redlock` gem. To cut the long story short, when the lock is acquired, a hash with the structure outlined in RailsTransactionalOutbox::NullLockClient should be yielded, if the lock is not acquired, a nil should be yielded.
|
44
44
|
config.lock_expiry_time = 10_000 # not required, defaults to 10_000, the unit is milliseconds
|
45
45
|
config.outbox_entries_processor = `RailsTransactionalOutbox::OutboxEntriesProcessors::OrderedByCausalityKeyProcessor`.new # not required, defaults to RailsTransactionalOutbox::OutboxEntriesProcessors::NonOrderedProcessor.new
|
46
|
-
config.outbox_entry_causality_key_resolver = ->(model) { model.tenant_id } # not required, defaults to a lambda returning nil. Needed when using `outbox_entry_causality_key_resolver`
|
46
|
+
config.outbox_entry_causality_key_resolver = ->(model) { model.tenant_id } # not required, defaults to a lambda returning nil. Needed when using `outbox_entry_causality_key_resolver`
|
47
47
|
end
|
48
48
|
end
|
49
49
|
```
|
@@ -53,7 +53,7 @@ Create OutboxEntry model (or use a different name, just make sure to adjust conf
|
|
53
53
|
``` rb
|
54
54
|
class OutboxEntry < ApplicationRecord
|
55
55
|
include RailsTransactionalOutbox::OutboxModel
|
56
|
-
|
56
|
+
|
57
57
|
# optional, if you want to use encryption
|
58
58
|
crypt_keeper :changeset, :arguments, encryptor: :postgres_pgp, key: ENV.fetch("CRYPT_KEEPER_KEY"), encoding: "UTF-8"
|
59
59
|
outbox_encrypt_json_for :changeset, :arguments
|
@@ -93,7 +93,7 @@ end
|
|
93
93
|
|
94
94
|
Keep in mind that `arguments` and `changeset` are `text` columns here. If you don't want to use encryption, replace them with `jsonb` columns:
|
95
95
|
|
96
|
-
```rb
|
96
|
+
```rb
|
97
97
|
t.jsonb "arguments", null: false, default: {}
|
98
98
|
t.jsonb "changeset", null: false, default: {}
|
99
99
|
```
|
@@ -105,7 +105,7 @@ As the last step, include `RailsTransactionalOutbox::ReliableModel` module in th
|
|
105
105
|
``` ruby
|
106
106
|
class User < ActiveRecord::Base
|
107
107
|
include RailsTransactionalOutbox::ReliableModel
|
108
|
-
end
|
108
|
+
end
|
109
109
|
```
|
110
110
|
|
111
111
|
Now, you can just replace `after_commit` callbacks with `reliable_after_commit`. The interface is going to be the same as for `after_commit`:
|
@@ -247,9 +247,8 @@ end
|
|
247
247
|
|
248
248
|
### Health Checks
|
249
249
|
|
250
|
-
First, you need to set `REDIS_URL` ENV variable to provide the URL for Redis.
|
251
250
|
|
252
|
-
Then,
|
251
|
+
Then, Uou need to explicitly enable the health check (e.g. in the initializer):
|
253
252
|
|
254
253
|
``` rb
|
255
254
|
RailsTransactionalOutbox.enable_outbox_worker_healthcheck
|
@@ -261,8 +260,6 @@ To perform the actual health check, use `bin/rails_transactional_outbox_health_c
|
|
261
260
|
bundle exec rails_transactional_outbox_health_check
|
262
261
|
```
|
263
262
|
|
264
|
-
The logic is based on checking a special value in Redis that is set (and unset) for a given container when Outbox workers are initialized/stopped/processing messages.
|
265
|
-
|
266
263
|
It works for both readiness and liveness checks.
|
267
264
|
|
268
265
|
#### Events, hooks and monitors
|
@@ -1,46 +1,45 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require "redis"
|
4
|
-
|
5
3
|
class RailsTransactionalOutbox
|
6
4
|
class HealthCheck
|
7
5
|
KEY_PREFIX = "__rails_transactional__outbox_worker__running__"
|
8
|
-
|
9
|
-
private_constant :KEY_PREFIX, :
|
6
|
+
TMP_DIR = "/tmp"
|
7
|
+
private_constant :KEY_PREFIX, :TMP_DIR
|
10
8
|
|
11
|
-
def self.check(
|
12
|
-
expiry_time_in_seconds:
|
13
|
-
new(redis_url: redis_url, hostname: hostname, expiry_time_in_seconds: expiry_time_in_seconds).check
|
9
|
+
def self.check(hostname: ENV.fetch("HOSTNAME", nil), expiry_time_in_seconds: 120)
|
10
|
+
new(hostname: hostname, expiry_time_in_seconds: expiry_time_in_seconds).check
|
14
11
|
end
|
15
12
|
|
16
|
-
attr_reader :
|
13
|
+
attr_reader :hostname, :expiry_time_in_seconds
|
17
14
|
|
18
|
-
def initialize(
|
19
|
-
expiry_time_in_seconds: 120)
|
20
|
-
@redis_client = Redis.new(url: redis_url)
|
15
|
+
def initialize(hostname: ENV.fetch("HOSTNAME", nil), expiry_time_in_seconds: 120)
|
21
16
|
@hostname = hostname
|
22
17
|
@expiry_time_in_seconds = expiry_time_in_seconds
|
23
18
|
end
|
24
19
|
|
25
20
|
def check
|
26
|
-
|
27
|
-
if value == VALUE
|
21
|
+
if healthcheck_storage.running?
|
28
22
|
""
|
29
23
|
else
|
30
|
-
"[Rails Transactional Outbox Worker
|
24
|
+
"[Rails Transactional Outbox Worker healthcheck failed]"
|
31
25
|
end
|
32
26
|
end
|
33
27
|
|
34
28
|
def register_heartbeat
|
35
|
-
|
29
|
+
healthcheck_storage.touch
|
36
30
|
end
|
37
31
|
|
38
32
|
def worker_stopped
|
39
|
-
|
33
|
+
healthcheck_storage.remove
|
40
34
|
end
|
41
35
|
|
42
36
|
private
|
43
37
|
|
38
|
+
def healthcheck_storage
|
39
|
+
@healthcheck_storage ||= FileBasedHealthcheck.new(directory: TMP_DIR, filename: key,
|
40
|
+
time_threshold: expiry_time_in_seconds)
|
41
|
+
end
|
42
|
+
|
44
43
|
def key
|
45
44
|
"#{KEY_PREFIX}#{hostname}"
|
46
45
|
end
|
@@ -25,7 +25,7 @@ class RailsTransactionalOutbox
|
|
25
25
|
event_name: "#{model.model_name.singular}_#{event_name_suffix(event_type)}",
|
26
26
|
context: RailsTransactionalOutbox::RecordProcessors::ActiveRecordProcessor.context,
|
27
27
|
causality_key: outbox_entry_causality_key_resolver.call(model)
|
28
|
-
}
|
28
|
+
}.compact
|
29
29
|
end
|
30
30
|
|
31
31
|
def event_name_suffix(event_type)
|
@@ -34,7 +34,7 @@ Gem::Specification.new do |spec|
|
|
34
34
|
spec.add_dependency "activesupport", ">= 3.2"
|
35
35
|
spec.add_dependency "concurrent-ruby"
|
36
36
|
spec.add_dependency "dry-monitor"
|
37
|
-
spec.add_dependency "
|
37
|
+
spec.add_dependency "file-based-healthcheck"
|
38
38
|
spec.add_dependency "sigurd"
|
39
39
|
spec.add_dependency "zeitwerk"
|
40
40
|
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rails-transactional-outbox
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Karol Galanciak
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-
|
11
|
+
date: 2022-12-20 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activerecord
|
@@ -67,7 +67,7 @@ dependencies:
|
|
67
67
|
- !ruby/object:Gem::Version
|
68
68
|
version: '0'
|
69
69
|
- !ruby/object:Gem::Dependency
|
70
|
-
name:
|
70
|
+
name: file-based-healthcheck
|
71
71
|
requirement: !ruby/object:Gem::Requirement
|
72
72
|
requirements:
|
73
73
|
- - ">="
|