rails-transactional-outbox 0.2.1 → 0.3.1

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