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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: '0839d268607d201c9f77bbbca404f32aa5af02621b7582256eb384b42b9d3985'
4
- data.tar.gz: 0ad3d2cfdce3ce99eb5d0b95a4292496927befaca2a8e2d83b865af411be2988
3
+ metadata.gz: ce57b6483f8a2f3fb1a65a2b12a19dd3d7e8ce75afea225ead398980dbfbbf5c
4
+ data.tar.gz: b86d05d225cb4729c5299d7cd8711fe7523f2b7eb851fd1229e99a6caa008c3d
5
5
  SHA512:
6
- metadata.gz: c86be499e21572b796060fc07f5d95191971b87366bbebe3b22737d5eb906f25a6e850f4b4b6f2c08f2790541169f36401fb67b95df0c5da365fda1f76e8751f
7
- data.tar.gz: 3e91e306f83eb4ba8dd5c494aadc90b36a702935d7fb716424af80efee6fe6bce9ff895ae6231aa1c32327d7f468ed2148aa69a5f37842c10e0f50e206dad82a
6
+ metadata.gz: ce613eb94bde316e2a8211f2efb1f0afcc86542607f80a7f53476afde59d6b6757793de4d2ca658a5c39b690c46a7bc8399e913119f632e06353f71278a55e2c
7
+ data.tar.gz: d012f277cb251a96e5baee5292c40dd63fdf4710e7fd900a93a1b642ff2b33f2f11f80b4663ea0867fe44f60cfba33886910b49d90d95f853e2fed3aece5ac02
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- pub_sub_model_sync (1.5.0)
4
+ pub_sub_model_sync (1.6.0)
5
5
  rails
6
6
 
7
7
  GEM
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 (more details in #transactions_max_buffer).
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
- retry_process?(e, retries += 1) ? retry : notify_error(e)
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
- connection_lost_classes = %w[ActiveRecord::ConnectionTimeoutError PG::UnableToSend]
64
- connection_lost_classes.include?(error.class.name) || error.message.match?(/lost connection/i)
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
- payload.headers[:ordering_key] = ordering_key_for(payload)
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 ordering_key_for(payload)
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
- items = self.class.ps_cache_publish_callbacks.select { |item| item[:actions].include?(action) }
43
- raise(StandardError, "No callback found for action :#{action}") if items.empty?
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)
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module PubSubModelSync
4
- VERSION = '1.5.0'
4
+ VERSION = '1.6.0'
5
5
  end
@@ -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 = 'Permit to sync models between apps through pub/sub'
15
- spec.description = 'Permit to sync models between apps through pub/sub'
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.5.0
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-06-02 00:00:00.000000000 Z
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: Permit to sync models between apps through pub/sub
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: Permit to sync models between apps through pub/sub
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: []