rails-transactional-outbox 0.2.1 → 0.3.1

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: fd2d8339f85250e27d0ba1de331c1c29feb38b2521bd5f2f82b221f80722f5e5
4
- data.tar.gz: 5c968efad34540cf5b0d02dab303d90fb492bdea617e25176880e7c50b1e6755
3
+ metadata.gz: 3e55a23c67288d7804bffc02cf8bfa1f36fc93562600f2002cce9393ca097b95
4
+ data.tar.gz: 64693c24b6affcf0357973f439aa0907636537c74ffd247b93ddf77984c0f6fa
5
5
  SHA512:
6
- metadata.gz: 89643bbb880f86e8d2f9d92ba6f9c95cad1f1df505bf66af82f47a2fd65a11ed0be23a76aaf9b973dbb81101db34178fbee1b8c441e844fcf0ec07704db1207f
7
- data.tar.gz: f9e45e9d83b6eb28e7f58b61e23868b50256546619f0b1dba4a16a388045d9842c69b361a865ed3c5a60dd02dc45353f3e6be7f8eda11b393dcf75e2bad87c0b
6
+ metadata.gz: 2ce32e65ec889efa9e3c56931c9a8146ffeff13178ab67aaf242b4dad72d22d33c9949bfde0d8609bbd610a1309d9fb26b5eef73be949b14ec9803444f181810
7
+ data.tar.gz: 1484e44ad3673815550375b95e78dfc08f235535dd47f519c7a26bf2ed38e781b16d9ff9e1acd69b8dfa029967e55f1308302d1989c36a03b97b37cb9fd077fd
data/CHANGELOG.md CHANGED
@@ -1,12 +1,20 @@
1
1
  ## [Unreleased]
2
2
 
3
+ ## [0.3.1] - 2023-05-24
4
+
5
+ - add config option whether to raise error when outbox entry record is not found
6
+
7
+ ## [0.3.0] - 2022-12-20
8
+
9
+ - Move to file-based healthchecks, instead of using Redis-based ones.
10
+
3
11
  ## [0.2.1] - 2022-09-08
4
12
 
5
13
  - Simplify update to 0.2 (`causality_key` is not required if `outbox_entry_causality_key_resolver` is not used)
6
14
 
7
15
  ## [0.2.0] - 2022-09-08
8
16
 
9
- - Introduce `RailsTransactionalOutbox::OutboxEntriesProcessors::OrderedByCausalityKeyProcessor`
17
+ - Introduce `RailsTransactionalOutbox::OutboxEntriesProcessors::OrderedByCausalityKeyProcessor`
10
18
 
11
19
  ## [0.1.0] - 2022-08-23
12
20
 
data/Gemfile.lock CHANGED
@@ -9,12 +9,12 @@ GIT
9
9
  PATH
10
10
  remote: .
11
11
  specs:
12
- rails-transactional-outbox (0.2.1)
12
+ rails-transactional-outbox (0.3.1)
13
13
  activerecord (>= 5)
14
14
  activesupport (>= 3.2)
15
15
  concurrent-ruby
16
16
  dry-monitor
17
- redis
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.15.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
- dry-core (~> 0.6)
46
- dry-core (0.8.1)
48
+ zeitwerk (~> 2.6)
49
+ dry-events (1.0.1)
47
50
  concurrent-ruby (~> 1.0)
48
- dry-events (0.3.0)
49
- concurrent-ruby (~> 1.0)
50
- dry-core (~> 0.5, >= 0.5)
51
- dry-monitor (0.6.3)
52
- dry-configurable (~> 0.13, >= 0.13.0)
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.2.0)
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.0)
124
+ zeitwerk (2.6.8)
123
125
 
124
126
  PLATFORMS
125
127
  x86_64-darwin-18
data/README.md CHANGED
@@ -29,21 +29,22 @@ 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
+ config.raise_not_found_model_error = true # optional, defaults to true. Should the error be raised if outbox entry model is not found
43
+
43
44
  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
45
  config.lock_expiry_time = 10_000 # not required, defaults to 10_000, the unit is milliseconds
45
46
  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`
47
+ 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
48
  end
48
49
  end
49
50
  ```
@@ -53,7 +54,7 @@ Create OutboxEntry model (or use a different name, just make sure to adjust conf
53
54
  ``` rb
54
55
  class OutboxEntry < ApplicationRecord
55
56
  include RailsTransactionalOutbox::OutboxModel
56
-
57
+
57
58
  # optional, if you want to use encryption
58
59
  crypt_keeper :changeset, :arguments, encryptor: :postgres_pgp, key: ENV.fetch("CRYPT_KEEPER_KEY"), encoding: "UTF-8"
59
60
  outbox_encrypt_json_for :changeset, :arguments
@@ -93,7 +94,7 @@ end
93
94
 
94
95
  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
96
 
96
- ```rb
97
+ ```rb
97
98
  t.jsonb "arguments", null: false, default: {}
98
99
  t.jsonb "changeset", null: false, default: {}
99
100
  ```
@@ -105,7 +106,7 @@ As the last step, include `RailsTransactionalOutbox::ReliableModel` module in th
105
106
  ``` ruby
106
107
  class User < ActiveRecord::Base
107
108
  include RailsTransactionalOutbox::ReliableModel
108
- end
109
+ end
109
110
  ```
110
111
 
111
112
  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 +248,8 @@ end
247
248
 
248
249
  ### Health Checks
249
250
 
250
- First, you need to set `REDIS_URL` ENV variable to provide the URL for Redis.
251
251
 
252
- Then, you need to explicitly enable the health check (e.g. in the initializer):
252
+ Then, Uou need to explicitly enable the health check (e.g. in the initializer):
253
253
 
254
254
  ``` rb
255
255
  RailsTransactionalOutbox.enable_outbox_worker_healthcheck
@@ -261,8 +261,6 @@ To perform the actual health check, use `bin/rails_transactional_outbox_health_c
261
261
  bundle exec rails_transactional_outbox_health_check
262
262
  ```
263
263
 
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
264
  It works for both readiness and liveness checks.
267
265
 
268
266
  #### Events, hooks and monitors
@@ -5,7 +5,8 @@ class RailsTransactionalOutbox
5
5
  attr_accessor :database_connection_provider, :logger, :outbox_model, :transaction_provider
6
6
  attr_writer :error_handler, :transactional_outbox_worker_sleep_seconds,
7
7
  :transactional_outbox_worker_idle_delay_multiplier, :outbox_batch_size, :outbox_entries_processor,
8
- :lock_client, :lock_expiry_time, :outbox_entry_causality_key_resolver
8
+ :lock_client, :lock_expiry_time, :outbox_entry_causality_key_resolver,
9
+ :raise_not_found_model_error
9
10
 
10
11
  def error_handler
11
12
  @error_handler || RailsTransactionalOutbox::ErrorHandlers::NullErrorHandler
@@ -35,6 +36,14 @@ class RailsTransactionalOutbox
35
36
  @outbox_entries_processor ||= RailsTransactionalOutbox::OutboxEntriesProcessors::NonOrderedProcessor.new
36
37
  end
37
38
 
39
+ def raise_not_found_model_error
40
+ return @raise_not_found_model_error if defined?(@raise_not_found_model_error)
41
+
42
+ true
43
+ end
44
+
45
+ alias_method :raise_not_found_model_error?, :raise_not_found_model_error
46
+
38
47
  def lock_client
39
48
  @lock_client || RailsTransactionalOutbox::NullLockClient
40
49
  end
@@ -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
- VALUE = "OK"
9
- private_constant :KEY_PREFIX, :VALUE
6
+ TMP_DIR = "/tmp"
7
+ private_constant :KEY_PREFIX, :TMP_DIR
10
8
 
11
- def self.check(redis_url: ENV.fetch("REDIS_URL", nil), hostname: ENV.fetch("HOSTNAME", nil),
12
- expiry_time_in_seconds: 120)
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 :redis_client, :hostname, :expiry_time_in_seconds
13
+ attr_reader :hostname, :expiry_time_in_seconds
17
14
 
18
- def initialize(redis_url: ENV.fetch("REDIS_URL", nil), hostname: ENV.fetch("HOSTNAME", nil),
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
- value = redis_client.get(key)
27
- if value == VALUE
21
+ if healthcheck_storage.running?
28
22
  ""
29
23
  else
30
- "[Rails Transactional Outbox Worker - expected #{VALUE} under #{key}, found: #{value}] "
24
+ "[Rails Transactional Outbox Worker healthcheck failed]"
31
25
  end
32
26
  end
33
27
 
34
28
  def register_heartbeat
35
- redis_client.set(key, VALUE, ex: expiry_time_in_seconds)
29
+ healthcheck_storage.touch
36
30
  end
37
31
 
38
32
  def worker_stopped
39
- redis_client.del(key)
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
@@ -15,7 +15,14 @@ class RailsTransactionalOutbox
15
15
  end
16
16
 
17
17
  def call(record)
18
- model = record.infer_model or raise CouldNotFindModelError.new(record)
18
+ model = record.infer_model
19
+ if model.nil?
20
+ if RailsTransactionalOutbox.configuration.raise_not_found_model_error?
21
+ raise CouldNotFindModelError.new(record)
22
+ end
23
+
24
+ return
25
+ end
19
26
  model.previous_changes = record.transformed_changeset.with_indifferent_access
20
27
  model.reliable_after_commit_callbacks.for_event_type(record.event_type).each do |callback|
21
28
  callback.call(model)
@@ -3,5 +3,5 @@
3
3
  class RailsTransactionalOutbox
4
4
  module Version
5
5
  end
6
- VERSION = "0.2.1"
6
+ VERSION = "0.3.1"
7
7
  end
@@ -5,6 +5,7 @@ require "logger"
5
5
  require "dry-monitor"
6
6
  require "sigurd"
7
7
  require "concurrent-ruby"
8
+ require "file-based-healthcheck"
8
9
 
9
10
  class RailsTransactionalOutbox
10
11
  def self.loader
@@ -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 "redis"
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.2.1
4
+ version: 0.3.1
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-09-08 00:00:00.000000000 Z
11
+ date: 2023-05-26 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: redis
70
+ name: file-based-healthcheck
71
71
  requirement: !ruby/object:Gem::Requirement
72
72
  requirements:
73
73
  - - ">="