pub_sub_model_sync 1.5.0 → 1.6.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/Gemfile.lock +1 -1
- data/README.md +10 -4
- data/lib/pub_sub_model_sync/message_processor.rb +9 -6
- data/lib/pub_sub_model_sync/message_publisher.rb +8 -3
- data/lib/pub_sub_model_sync/publisher_concern.rb +4 -4
- data/lib/pub_sub_model_sync/service_base.rb +6 -1
- data/lib/pub_sub_model_sync/transaction.rb +3 -2
- data/lib/pub_sub_model_sync/version.rb +1 -1
- data/pub_sub_model_sync.gemspec +6 -3
- metadata +12 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ce57b6483f8a2f3fb1a65a2b12a19dd3d7e8ce75afea225ead398980dbfbbf5c
|
4
|
+
data.tar.gz: b86d05d225cb4729c5299d7cd8711fe7523f2b7eb851fd1229e99a6caa008c3d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ce613eb94bde316e2a8211f2efb1f0afcc86542607f80a7f53476afde59d6b6757793de4d2ca658a5c39b690c46a7bc8399e913119f632e06353f71278a55e2c
|
7
|
+
data.tar.gz: d012f277cb251a96e5baee5292c40dd63fdf4710e7fd900a93a1b642ff2b33f2f11f80b4663ea0867fe44f60cfba33886910b49d90d95f853e2fed3aece5ac02
|
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
@@ -271,8 +271,10 @@ PubSubModelSync::Payload.new({ ids: [my_user.id] }, { klass: 'User', action: :ba
|
|
271
271
|
- `as_klass:` (String, default current class name): Class name of the notification
|
272
272
|
- `headers:` (Hash, optional): header settings (More in Payload.headers)
|
273
273
|
|
274
|
-
- `ps_perform_publish(action = :create)` Permits to perform manually the callback of a specific `ps_after_action`
|
274
|
+
- `ps_perform_publish(action = :create, parents_actions: false)` Permits to perform manually the callback of a specific `ps_after_action`
|
275
275
|
- `action` (Symbol, default: :create) Only :create|:update|:destroy
|
276
|
+
- `parents_actions` (Boolean, default: false) When `true`, includes inherited PubSub-callbacks from parent classes
|
277
|
+
|
276
278
|
|
277
279
|
#### **Publisher helpers**
|
278
280
|
- Publish or republish a notification
|
@@ -317,6 +319,7 @@ Any notification before delivering is transformed as a Payload for a better port
|
|
317
319
|
Note: Final `ordering_key` is calculated as: `payload.headers[:forced_ordering_key] || current_transaction&.key || payload.headers[:ordering_key]`
|
318
320
|
- `topic_name`: (String|Array<String>, optional): Specific topic name where to deliver the notification (default `PubSubModelSync::Config.topic_name`).
|
319
321
|
- `forced_ordering_key`: (String, optional): Overrides `ordering_key` with the provided value even withing transactions. Default `nil`.
|
322
|
+
- `target_app_key`: (String, optional): Allows to send the notification to a specific app (includes the application key, separated by comma when multiple apps). Default `nil`.
|
320
323
|
- `cache` (Boolean | Hash, Default false) Cache settings
|
321
324
|
- `true`: Skip publishing similar payloads
|
322
325
|
- `Hash<required: Array<Symbol>>`: Same as `true` and enables payload optimization to exclude unchanged non important attributes. Sample: `{ required: %i[id email] }`
|
@@ -379,16 +382,17 @@ Note: To reduce Payload size, some header info are not delivered (Enable debug m
|
|
379
382
|
- Manual transactions
|
380
383
|
`PubSubModelSync::MessagePublisher::transaction(key, max_buffer: , &block)`
|
381
384
|
- `key` (String|nil) Key used as the ordering_key for all inner notifications (When nil, will use `ordering_key` of the first notification)
|
382
|
-
- `max_buffer:` (Integer, default: `PubSubModelSync::Config.transactions_max_buffer`) Transaction buffer size (
|
385
|
+
- `max_buffer:` (Integer, default: `PubSubModelSync::Config.transactions_max_buffer`) Transaction buffer size (DEPRECATED).
|
386
|
+
- `headers:` (Hash) Header settings to be added to each Payload's header inside this transaction
|
383
387
|
Sample:
|
384
388
|
```ruby
|
385
|
-
PubSubModelSync::MessagePublisher::transaction('my-custom-key') do
|
389
|
+
PubSubModelSync::MessagePublisher::transaction('my-custom-key', headers: { key: 'my-key' }) do
|
386
390
|
user = User.create(name: 'test') # `User`:`:create` notification
|
387
391
|
post = Post.create(title: 'sample') # `Post`:`:create` notification
|
388
392
|
PubSubModelSync::Payload.new({ ids: [user.id] }, { klass: 'User', action: :send_welcome, mode: :klass }).publish! # `User`:`:send_welcome` notification
|
389
393
|
end
|
390
394
|
```
|
391
|
-
All notifications uses `ordering_key: 'my-custom-key'` and will be processed in the same order they were published.
|
395
|
+
All notifications uses `ordering_key: 'my-custom-key'` and will be processed in the same order they were published (Payload headers will include `key="my-key"`).
|
392
396
|
|
393
397
|
## **Testing with RSpec**
|
394
398
|
- Config: (spec/rails_helper.rb)
|
@@ -536,6 +540,8 @@ config.debug = true
|
|
536
540
|
(Proc) => called after publishing a notification
|
537
541
|
- ```.on_error_publish = ->(exception, {payload:}) { payload.delay(...).publish! }```
|
538
542
|
(Proc) => called when failed publishing a notification (delayed_job or similar can be used for retrying)
|
543
|
+
- ```.skip_cache = false```
|
544
|
+
(true/false*) => Allow to skip payload optimization (cache settings)
|
539
545
|
- ```.transactions_max_buffer = 1``` (Integer, default 1) Controls the maximum quantity of notifications to be enqueued to the transaction-buffer before delivering them and thus adds the ability to rollback notifications if the transaction fails.
|
540
546
|
Once this quantity of notifications is reached, then all notifications of the current transaction will immediately be delivered (can be customized per transaction).
|
541
547
|
Note: There is no way to rollback delivered notifications if current transaction fails later.
|
@@ -23,15 +23,15 @@ module PubSubModelSync
|
|
23
23
|
end
|
24
24
|
|
25
25
|
def process
|
26
|
-
retries ||= 0
|
27
26
|
process!
|
28
27
|
rescue => e
|
29
|
-
|
28
|
+
notify_error(e)
|
30
29
|
end
|
31
30
|
|
32
31
|
private
|
33
32
|
|
34
|
-
def run_subscriber(subscriber)
|
33
|
+
def run_subscriber(subscriber) # rubocop:disable Metrics/AbcSize
|
34
|
+
retries ||= 0
|
35
35
|
processor = PubSubModelSync::RunSubscriber.new(subscriber, payload)
|
36
36
|
return unless processable?(subscriber)
|
37
37
|
|
@@ -39,6 +39,8 @@ module PubSubModelSync
|
|
39
39
|
processor.call
|
40
40
|
res = config.on_success_processing.call(payload, { subscriber: subscriber })
|
41
41
|
log "processed message with: #{payload.inspect}" if res != :skip_log
|
42
|
+
rescue => e
|
43
|
+
retry_process?(e, retries += 1) ? retry : raise(e)
|
42
44
|
end
|
43
45
|
|
44
46
|
def processable?(subscriber)
|
@@ -47,7 +49,7 @@ module PubSubModelSync
|
|
47
49
|
!cancel
|
48
50
|
end
|
49
51
|
|
50
|
-
# @param error (StandardError)
|
52
|
+
# @param error (StandardError, Exception)
|
51
53
|
def notify_error(error)
|
52
54
|
error_msg = 'Error processing message: '
|
53
55
|
error_details = [payload, error.message, error.backtrace]
|
@@ -59,9 +61,10 @@ module PubSubModelSync
|
|
59
61
|
raise(e)
|
60
62
|
end
|
61
63
|
|
64
|
+
# @param error [StandardError]
|
62
65
|
def lost_db_connection?(error)
|
63
|
-
|
64
|
-
|
66
|
+
classes = %w[ActiveRecord::ConnectionTimeoutError PG::UnableToSend ActiveRecord::ConnectionNotEstablished]
|
67
|
+
classes.include?(error.class.name) || error.message.match?(/Lost connection to MySQL server/i)
|
65
68
|
end
|
66
69
|
|
67
70
|
def retry_process?(error, retries) # rubocop:disable Metrics/MethodLength
|
@@ -18,6 +18,9 @@ module PubSubModelSync
|
|
18
18
|
# worker/thread.
|
19
19
|
# @see Transaction.new(...)
|
20
20
|
# @param key (String|Nil)
|
21
|
+
# @param settings (Hash<:headers, :max_buffer>)
|
22
|
+
# @option headers [Hash] Headers to be merged for each payload inside this transaction
|
23
|
+
# @option max_buffer [Integer] Deprecated
|
21
24
|
# @param block (Yield) block to be executed
|
22
25
|
def transaction(key, settings = {}, &block)
|
23
26
|
t = init_transaction(key, settings)
|
@@ -71,7 +74,7 @@ module PubSubModelSync
|
|
71
74
|
# @return Payload
|
72
75
|
# Raises error if exist
|
73
76
|
def publish!(payload, &block)
|
74
|
-
|
77
|
+
add_transaction_headers(payload)
|
75
78
|
return unless ensure_publish(payload)
|
76
79
|
|
77
80
|
current_transaction ? current_transaction.add_payload(payload) : connector_publish(payload)
|
@@ -106,8 +109,10 @@ module PubSubModelSync
|
|
106
109
|
!cancelled
|
107
110
|
end
|
108
111
|
|
109
|
-
def
|
110
|
-
payload.headers[:forced_ordering_key] || current_transaction&.key || payload.headers[:ordering_key]
|
112
|
+
def add_transaction_headers(payload)
|
113
|
+
key = payload.headers[:forced_ordering_key] || current_transaction&.key || payload.headers[:ordering_key]
|
114
|
+
payload.headers[:ordering_key] = key
|
115
|
+
payload.headers.merge!(current_transaction.headers) if current_transaction
|
111
116
|
end
|
112
117
|
|
113
118
|
def ensure_model_publish(model, action, payload)
|
@@ -38,10 +38,10 @@ module PubSubModelSync
|
|
38
38
|
|
39
39
|
# Permits to perform manually the callback for a specific action
|
40
40
|
# @param action (Symbol, default: :create) Only :create|:update|:destroy
|
41
|
-
def ps_perform_publish(action = :create)
|
42
|
-
|
43
|
-
|
44
|
-
|
41
|
+
def ps_perform_publish(action = :create, parents_actions: false)
|
42
|
+
callbacks = self.class.ps_cache_publish_callbacks
|
43
|
+
callbacks = self.class.ancestors.map { |p| p.try(:ps_cache_publish_callbacks) }.compact.flatten if parents_actions
|
44
|
+
items = callbacks.select { |item| item[:actions].include?(action) }
|
45
45
|
items.each { |item| instance_exec(action, &item[:callback]) }
|
46
46
|
self
|
47
47
|
end
|
@@ -34,7 +34,7 @@ module PubSubModelSync
|
|
34
34
|
def process_message(payload_info)
|
35
35
|
payload = decode_payload(payload_info)
|
36
36
|
return unless payload
|
37
|
-
return if same_app_message?(payload)
|
37
|
+
return if same_app_message?(payload) || !target_app_message?(payload)
|
38
38
|
|
39
39
|
payload.process
|
40
40
|
end
|
@@ -57,5 +57,10 @@ module PubSubModelSync
|
|
57
57
|
log("Skipping message from same origin: #{[payload]}") if res && config.debug
|
58
58
|
res
|
59
59
|
end
|
60
|
+
|
61
|
+
def target_app_message?(payload)
|
62
|
+
key = payload.headers[:target_app_key].to_s
|
63
|
+
!key.present? || key.split(',').include?(config.subscription_key)
|
64
|
+
end
|
60
65
|
end
|
61
66
|
end
|
@@ -3,17 +3,18 @@
|
|
3
3
|
module PubSubModelSync
|
4
4
|
class Transaction < Base
|
5
5
|
PUBLISHER_KLASS = PubSubModelSync::MessagePublisher
|
6
|
-
attr_accessor :key, :payloads, :max_buffer, :root, :children, :finished
|
6
|
+
attr_accessor :key, :payloads, :max_buffer, :root, :children, :finished, :headers
|
7
7
|
|
8
8
|
# @param key (String,Nil) Transaction key, if empty will use the ordering_key from first payload
|
9
9
|
# @param max_buffer (Integer) Once this quantity of notifications is reached, then all notifications
|
10
10
|
# will immediately be delivered.
|
11
11
|
# Note: There is no way to rollback delivered notifications if current transaction fails
|
12
|
-
def initialize(key, max_buffer: config.transactions_max_buffer)
|
12
|
+
def initialize(key, max_buffer: config.transactions_max_buffer, headers: {})
|
13
13
|
@key = key
|
14
14
|
@max_buffer = max_buffer
|
15
15
|
@children = []
|
16
16
|
@payloads = []
|
17
|
+
@headers = headers
|
17
18
|
end
|
18
19
|
|
19
20
|
# @param payload (Payload)
|
data/pub_sub_model_sync.gemspec
CHANGED
@@ -4,15 +4,18 @@ lib = File.expand_path('lib', __dir__)
|
|
4
4
|
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
5
5
|
require 'pub_sub_model_sync/version'
|
6
6
|
|
7
|
-
Gem::Specification.new do |spec|
|
7
|
+
Gem::Specification.new do |spec| # rubocop:disable Metrics/BlockLength
|
8
8
|
spec.required_ruby_version = '>= 2.4' # rubocop:disable Gemspec/RequiredRubyVersion
|
9
9
|
spec.name = 'pub_sub_model_sync'
|
10
10
|
spec.version = PubSubModelSync::VERSION
|
11
11
|
spec.authors = ['Owen']
|
12
12
|
spec.email = ['owenperedo@gmail.com']
|
13
13
|
|
14
|
-
spec.summary = '
|
15
|
-
|
14
|
+
spec.summary = 'This gem permits to sync automatically models and custom data between multiple Rails
|
15
|
+
applications by publishing notifications via pubsub (Google PubSub, RabbitMQ, or Apache Kafka) and automatically
|
16
|
+
processed by all connected applications. Out of the scope, this gem includes transactions to keep Data consistency
|
17
|
+
by processing notifications in the order they were delivered.'
|
18
|
+
spec.description = spec.summary
|
16
19
|
spec.homepage = 'https://github.com/owen2345/pub_sub_model_sync'
|
17
20
|
spec.license = 'MIT'
|
18
21
|
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: pub_sub_model_sync
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.6.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Owen
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-
|
11
|
+
date: 2022-10-17 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|
@@ -80,7 +80,11 @@ dependencies:
|
|
80
80
|
- - ">="
|
81
81
|
- !ruby/object:Gem::Version
|
82
82
|
version: '0'
|
83
|
-
description:
|
83
|
+
description: This gem permits to sync automatically models and custom data between
|
84
|
+
multiple Rails applications by publishing notifications via pubsub (Google PubSub,
|
85
|
+
RabbitMQ, or Apache Kafka) and automatically processed by all connected applications.
|
86
|
+
Out of the scope, this gem includes transactions to keep Data consistency by processing
|
87
|
+
notifications in the order they were delivered.
|
84
88
|
email:
|
85
89
|
- owenperedo@gmail.com
|
86
90
|
executables: []
|
@@ -159,5 +163,9 @@ requirements: []
|
|
159
163
|
rubygems_version: 3.0.8
|
160
164
|
signing_key:
|
161
165
|
specification_version: 4
|
162
|
-
summary:
|
166
|
+
summary: This gem permits to sync automatically models and custom data between multiple
|
167
|
+
Rails applications by publishing notifications via pubsub (Google PubSub, RabbitMQ,
|
168
|
+
or Apache Kafka) and automatically processed by all connected applications. Out
|
169
|
+
of the scope, this gem includes transactions to keep Data consistency by processing
|
170
|
+
notifications in the order they were delivered.
|
163
171
|
test_files: []
|