pub_sub_model_sync 1.0 → 1.2.0

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: caaffd468399e03dbf5582bf6973339ec61348a6dcc67a87bda1d930f9271aae
4
- data.tar.gz: 9db2cb5f4a70c218d720130761a24ad7f6450531d845f9517e74968f409c9743
3
+ metadata.gz: ec006984fe2bf9c33e9992facdd25aa9fdaa3494b6bcaf656592593829429017
4
+ data.tar.gz: 8ca60de929eb7465c95ff819682af73a9094c60294661e5c823394aeb02ed6a9
5
5
  SHA512:
6
- metadata.gz: ac6c2b0087e411969e4a0fbe6fa4aa57e3fe46b6ff93f4d675e2a213cd0fd75786b5e16e1a4c1df6aee92296eac0c94a054a6b62543a0f7f643ba2ea0bc8ff7b
7
- data.tar.gz: 9816c7cedd0d6289d0d73a55f35ede63a46bca7f38805c4c566012fed04a58595498a80fd272ce89b617e34b1d1487ff2ee7f6e050c9712bfd6d75b364ad910a
6
+ metadata.gz: d799e1e27c071e18d5d7e15460a7d481b9991b469599356e23c86669fd8de95046b236d4596a5fbc22e93d278548d2b17255262e4f5c67eece7a4f6898ff03ca
7
+ data.tar.gz: 89f6e95f040919994a0f0397b61b09ac83567b69472ce41aa722b240dba66e8a66d4e7a60cedb0de5f59d671525c4d7ad38b02255e558728f8e34e71e227e1e1
data/CHANGELOG.md CHANGED
@@ -1,5 +1,19 @@
1
1
  # Change Log
2
2
 
3
+ # 1.2.0 (October 28, 2021)
4
+ - feat: rename Payload `:key` into `:internal_key` to avoid confusions while debugging
5
+
6
+ # 1.1.1 (October 25, 2021)
7
+ - feat: include `ordering_key topic_name` when delivering a notification for debugging purposes
8
+ - doc: improve docs
9
+
10
+ # 1.1.0 (October 25, 2021)
11
+ - feat: change `transactions_max_buffer` default value to 1 to deliver notifications once they were called
12
+ - feat: use `after_commit` instead of `before_commit` callback and remove the horrible AR patch for rails 4
13
+
14
+ # 1.0.1 (August 20, 2021)
15
+ - refactor: improve service exit when running in k8s
16
+
3
17
  # 1.0 (June 13, 2021)
4
18
  This version includes many changes that was refactored from previous version, and thus it needs manual changes to migrate into this version.
5
19
  - Refactor: Subscribers param renamed `from_action` into `to_action` and added support for block or lambda
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- pub_sub_model_sync (1.0)
4
+ pub_sub_model_sync (1.2.0)
5
5
  rails
6
6
 
7
7
  GEM
@@ -116,7 +116,6 @@ GEM
116
116
  google-cloud-errors (~> 1.0)
117
117
  grpc-google-iam-v1 (>= 0.6.10, < 2.0)
118
118
  google-protobuf (3.17.0)
119
- google-protobuf (3.17.0-x86_64-linux)
120
119
  googleapis-common-protos (1.3.11)
121
120
  google-protobuf (~> 3.14)
122
121
  googleapis-common-protos-types (>= 1.0.6, < 2.0)
@@ -133,9 +132,6 @@ GEM
133
132
  grpc (1.37.1)
134
133
  google-protobuf (~> 3.15)
135
134
  googleapis-common-protos-types (~> 1.0)
136
- grpc (1.37.1-x86_64-linux)
137
- google-protobuf (~> 3.15)
138
- googleapis-common-protos-types (~> 1.0)
139
135
  grpc-google-iam-v1 (0.6.11)
140
136
  google-protobuf (~> 3.14)
141
137
  googleapis-common-protos (>= 1.3.11, < 2.0)
@@ -158,8 +154,6 @@ GEM
158
154
  nio4r (2.5.7)
159
155
  nokogiri (1.11.3-x86_64-darwin)
160
156
  racc (~> 1.4)
161
- nokogiri (1.11.3-x86_64-linux)
162
- racc (~> 1.4)
163
157
  os (1.1.1)
164
158
  parallel (1.20.1)
165
159
  parser (3.0.1.1)
@@ -266,4 +260,4 @@ DEPENDENCIES
266
260
  sqlite3
267
261
 
268
262
  BUNDLED WITH
269
- 2.2.17
263
+ 2.2.29
data/README.md CHANGED
@@ -3,8 +3,8 @@
3
3
  ![Ruby badge](https://img.shields.io/badge/Ruby-2.4+-success.png)
4
4
  ![Production badge](https://img.shields.io/badge/Production-ready-success.png)
5
5
 
6
- This gem permits to sync automatically models and custom data between multiple Rails applications by publishing notifications via pubsub (Google PubSub, RabbitMQ, or Apache Kafka) and automatically processed by all connected applications. Out of the scope, this gem includes transactions to keep Data consistency by processing notifications in the order they were delivered.
7
- These notifications use JSON format to easily be decoded by subscribers (Rails applications and even other languages)
6
+ This gem permits to sync automatically models and custom data between multiple Rails applications by publishing notifications via pubsub (Google PubSub, RabbitMQ, or Apache Kafka) and automatically processed by all connected applications. Out of the scope, this gem includes transactions to keep Data consistency by processing notifications in the order they were delivered.
7
+ These notifications use JSON format to easily be decoded by subscribers (Rails applications and even other languages, soon for [Cristal-lang](https://crystal-lang.org/))
8
8
 
9
9
  - [**PubSubModelSync**](#pubsubmodelsync)
10
10
  - [**Features**](#features)
@@ -104,6 +104,7 @@ And then execute: $ bundle install
104
104
  ![Diagram](/docs/notifications-diagram.png?raw=true)
105
105
 
106
106
  ## **Examples**
107
+ See sample apps in [/samples](/samples/)
107
108
  ### **Basic Example**
108
109
  ```ruby
109
110
  # App 1 (Publisher)
@@ -244,7 +245,7 @@ PubSubModelSync::Payload.new({ ids: [my_user.id] }, { klass: 'User', action: :ba
244
245
  - `method_name` (Symbol, optional) method to be called to process action callback, sample: `def my_method(action) ... end`
245
246
  - `block` (Proc, optional) Block to be called to process action callback, sample: `{ |action| ... }`
246
247
 
247
- **Note1**: Due to rails callback ordering, this method uses `before_commit` callback when creating or updating models to ensure expected notifications order (More details [**here**](#transactions)).
248
+ **Note1**: Due to rails callback ordering, this method uses `after_commit on: action {...}` callback when creating or updating models to ensure expected notifications order (More details [**here**](#transactions)).
248
249
  **Note2**: Due to rails callback ordering, this method uses `after_destroy` callback when destroying models to ensure the expected notifications order.
249
250
 
250
251
  - `ps_publish(action, data: {}, mapping: [], headers: {}, as_klass: nil)` Delivers an instance notification via pubsub
@@ -310,11 +311,14 @@ Any notification before delivering is transformed as a Payload for a better port
310
311
  - `klass`: (String) Notification class name
311
312
  - `mode`: (Symbol: `:model`|`:class`) Kind of notification
312
313
  * `headers`: (Hash) Notification settings that defines how the notification will be processed or delivered.
313
- - `key`: (String, optional) identifier of the payload, default: `<klass_name>/<action>` when class message, `<model.class.name>/<action>/<model.id>` when model message (Useful for caching techniques).
314
- - `ordering_key`: (String, optional): messages with the same value are processed in the same order they were delivered, default: `klass_name` when class message, `<model.class.name>/<model.id>` when instance message.
315
- Note: Final `ordering_key` is calculated by this way: `payload.headers[:forced_ordering_key] || current_transaction&.key || payload.headers[:ordering_key]`
316
- - `topic_name`: (String|Array<String>, optional): Specific topic name (can be seen as a channel) to be used when delivering the message (default first topic from config).
317
- - `forced_ordering_key`: (String, optional): Will force to use this value as the `ordering_key`, even withing transactions. Default `nil`.
314
+ - `ordering_key`: (String, optional): notifications with the same `ordering_key` are processed in the same order they were delivered, default: `<model.class.name>/<model.id>` when instance notification and `klass_name` when class notification.
315
+ Note: Final `ordering_key` is calculated as: `payload.headers[:forced_ordering_key] || current_transaction&.key || payload.headers[:ordering_key]`
316
+ - `internal_key`: (String, optional) Internal identifier of the payload, default: `<model.class.name>/<action>/<model.id>` when model notification and `<klass_name>/<action>` when class notification (Useful for caching techniques).
317
+ - `topic_name`: (String|Array<String>, optional): Specific topic name where to deliver the notification (default `PubSubModelSync::Config.topic_name`).
318
+ - `forced_ordering_key`: (String, optional): Overrides `ordering_key` with the provided value even withing transactions. Default `nil`.
319
+ - `app_key`: (Auto calculated): Name of the application who delivered the notification.
320
+ - `uuid`: (Auto calculated): Unique notification identifier (Very useful when debugging).
321
+ Note: To reduce Payload size, some header info are not delivered (Enable debug mode to deliver all payload info).
318
322
 
319
323
  - Actions
320
324
  ```ruby
@@ -339,21 +343,21 @@ Any notification before delivering is transformed as a Payload for a better port
339
343
  ps_after_action([:create, :update, :destroy]) { |action| ps_publish(action, mapping: %i[id user_id title]) }
340
344
  end
341
345
  ```
342
- - When created (all notifications use the same ordering key to be processed in the same order)
346
+ - When created (all notifications use the same ordering_key to be processed in the same order)
343
347
  ```ruby
344
348
  user = User.create!(name: 'test', posts_attributes: [{ title: 'Post 1' }, { title: 'Post 2' }])
345
349
  # notification #1 => <Payload data: {id: 1, name: 'sample'}, info: { klass: 'User', action: :create, mode: :model }, headers: { ordering_key = `User/1` }>
346
350
  # notification #2 => <Payload data: {id: 1, title: 'Post 1', user_id: 1}, info: { klass: 'Post', action: :create, mode: :model }, headers: { ordering_key = `User/1` }>
347
351
  # notification #3 => <Payload data: {id: 2, title: 'Post 2', user_id: 1}, info: { klass: 'Post', action: :create, mode: :model }, headers: { ordering_key = `User/1` }>
348
352
  ```
349
- - When updated (all notifications use the same ordering key to be processed in the same order)
353
+ - When updated (all notifications use the same ordering_key to be processed in the same order)
350
354
  ```ruby
351
355
  user.update!(name: 'changed', posts_attributes: [{ id: 1, title: 'Post 1C' }, { id: 2, title: 'Post 2C' }])
352
356
  # notification #1 => <Payload data: {id: 1, name: 'changed'}, info: { klass: 'User', action: :update, mode: :model }, headers: { ordering_key = `User/1` }>
353
357
  # notification #2 => <Payload data: {id: 1, title: 'Post 1C', user_id: 1}, info: { klass: 'Post', action: :update, mode: :model }, headers: { ordering_key = `User/1` }>
354
358
  # notification #3 => <Payload data: {id: 2, title: 'Post 2C', user_id: 1}, info: { klass: 'Post', action: :update, mode: :model }, headers: { ordering_key = `User/1` }>
355
359
  ```
356
- - When destroyed (all notifications use the same ordering key to be processed in the same order)
360
+ - When destroyed (all notifications use the same ordering_key to be processed in the same order)
357
361
  **Note**: The notifications order were reordered in order to avoid inconsistency in other apps
358
362
  ```ruby
359
363
  user.destroy!
@@ -367,10 +371,8 @@ Any notification before delivering is transformed as a Payload for a better port
367
371
 
368
372
  - Manual transactions
369
373
  `PubSubModelSync::MessagePublisher::transaction(key, max_buffer: , &block)`
370
- - `key` (String|nil) Key used as the ordering key for all inner notifications (When nil, will use `ordering_key` of the first notification)
371
- - `max_buffer:` (Boolean, default: `PubSubModelSync::Config.transactions_max_buffer`)
372
- If true: will save all notifications and deliver all them when transaction has successfully finished. If transaction has failed, then all saved notifications will be discarded (not delivered).
373
- If false: will deliver all notifications immediately (no way to rollback notifications if transaction has failed)
374
+ - `key` (String|nil) Key used as the ordering_key for all inner notifications (When nil, will use `ordering_key` of the first notification)
375
+ - `max_buffer:` (Integer, default: `PubSubModelSync::Config.transactions_max_buffer`) Transaction buffer size (more details in #transactions_max_buffer).
374
376
  Sample:
375
377
  ```ruby
376
378
  PubSubModelSync::MessagePublisher::transaction('my-custom-key') do
@@ -529,7 +531,7 @@ config.debug = true
529
531
  Topic name(s) to be used to listen all notifications from when listening. Additionally first topic name is used as the default topic name when publishing a notification.
530
532
  - `.subscription_name = "my-app-1"`: (String, default Rails.application.name)
531
533
  Subscriber's identifier which helps to:
532
- * skip self messages
534
+ * skip self notifications
533
535
  * continue the sync from the last synced notification when service was restarted.
534
536
  - `.default_topic_name = "my_topic"`: (String|Array<String>, optional(default first topic from `topic_name`))
535
537
  Topic name used as the default topic if not defined in the payload when publishing a notification
@@ -538,29 +540,34 @@ config.debug = true
538
540
  - ```.logger = Rails.logger```
539
541
  (Logger) => define custom logger
540
542
  - ```.on_before_processing = ->(payload, {subscriber:}) { puts payload }```
541
- (Proc) => called before processing received message (:cancel can be returned to skip processing)
543
+ (Proc) => called before processing a received notification (:cancel can be returned to skip processing)
542
544
  - ```.on_success_processing = ->(payload, {subscriber:}) { puts payload }```
543
- (Proc) => called when a message was successfully processed
545
+ (Proc) => called when a notification was successfully processed
544
546
  - ```.on_error_processing = ->(exception, {payload:, subscriber:}) { payload.delay(...).process! }```
545
- (Proc) => called when a message failed when processing (delayed_job or similar can be used for retrying)
547
+ (Proc) => called when a notification has failed when processing (delayed_job or similar can be used for retrying)
546
548
  - ```.on_before_publish = ->(payload) { puts payload }```
547
- (Proc) => called before publishing a message (:cancel can be returned to skip publishing)
549
+ (Proc) => called before publishing a notification (:cancel can be returned to skip publishing)
548
550
  - ```.on_after_publish = ->(payload) { puts payload }```
549
- (Proc) => called after publishing a message
551
+ (Proc) => called after publishing a notification
550
552
  - ```.on_error_publish = ->(exception, {payload:}) { payload.delay(...).publish! }```
551
- (Proc) => called when failed publishing a message (delayed_job or similar can be used for retrying)
552
- - ```.transactions_max_buffer = 100``` (Integer) Once this quantity of notifications is reached, then all notifications will immediately be delivered.
553
- Note: There is no way to rollback delivered notifications if current transaction fails
554
- - ```.enable_rails4_before_commit = true``` (true*|false) When false will disable rails 4 hack compatibility and then CRUD notifications will be prepared using `after_commit` callback instead of `before_commit` (used in `ps_after_action(...)`) which will not rollback sql transactions if failed when publishing pubsub notification.
553
+ (Proc) => called when failed publishing a notification (delayed_job or similar can be used for retrying)
554
+ - ```.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.
555
+ Once this quantity of notifications is reached, then all notifications of the current transaction will immediately be delivered (can be customized per transaction).
556
+ Note: There is no way to rollback delivered notifications if current transaction fails later.
557
+ Note2: Only notifications from the buffer can be rollbacked if the current transaction has failed.
555
558
 
556
559
  ## **TODO**
557
560
  - Auto publish update only if payload has changed (see ways to compare previous payload vs new payload)
558
- - Improve transactions to exclude similar messages by klass and action. Sample:
561
+ - Improve transactions to exclude similar notifications by klass and action. Sample:
559
562
  ```PubSubModelSync::MessagePublisher.transaction(key, { same_keys: :use_last_as_first|:use_last|:use_first_as_last|:keep*, same_data: :use_last_as_first*|:use_last|:use_first_as_last|:keep })```
560
563
  - Add DB table to use as a shield to prevent publishing similar notifications and publish partial notifications (similar idea when processing notif)
561
564
  - Last notification is not being delivered immediately in google pubsub (maybe force with timeout 10secs and service.deliver_messages)
562
565
  - Update folder structure
563
566
  - Services support to deliver multiple payloads from transactions
567
+ - Fix deprecation warnings: pub_sub_model_sync/service_google.rb:39: warning: Splitting the last argument into positional and keyword parameters is deprecated
568
+ - Add if/unless to ps_after_action
569
+ - Add subscription liveness checker using thread without db connection to check periodically pending notifications from google pubsub
570
+ - Unify .stop() and 'Listener stopped'
564
571
 
565
572
  ## **Q&A**
566
573
  - I'm getting error "could not obtain a connection from the pool within 5.000 seconds"... what does this mean?
@@ -8,8 +8,7 @@ module PubSubModelSync
8
8
  # customizable callbacks
9
9
  cattr_accessor(:debug) { false }
10
10
  cattr_accessor :logger # LoggerInst
11
- cattr_accessor(:transactions_max_buffer) { 100 }
12
- cattr_accessor(:enable_rails4_before_commit) { Rails::VERSION::MAJOR == 4 }
11
+ cattr_accessor(:transactions_max_buffer) { 1 }
13
12
 
14
13
  cattr_accessor(:on_before_processing) { ->(_payload, _info) {} } # return :cancel to skip
15
14
  cattr_accessor(:on_success_processing) { ->(_payload, _info) {} }
@@ -31,7 +31,7 @@ module PubSubModelSync
31
31
  end
32
32
 
33
33
  # Starts a new transaction
34
- # @param key (String|Nil)
34
+ # @param key (String, Nil)
35
35
  # @return (Transaction)
36
36
  def init_transaction(key, settings = {})
37
37
  new_transaction = PubSubModelSync::Transaction.new(key, settings)
@@ -54,9 +54,9 @@ module PubSubModelSync
54
54
  publish(payload)
55
55
  end
56
56
 
57
- # @param model (ActiveRecord::Base)
58
- # @param action (Symbol: @see PublishConcern::ps_publish)
59
- # @param settings (Hash: @see PayloadBuilder.settings)
57
+ # @param model (ActiveRecord::Base,PubSubModelSync::PublisherConcern)
58
+ # @param action (Symbol,String @see PublishConcern::ps_publish)
59
+ # @param settings (Hash @see PayloadBuilder.settings)
60
60
  def publish_model(model, action, settings = {})
61
61
  log("Building payload for: #{[model, action].inspect}") if config.debug
62
62
  payload = PubSubModelSync::PayloadBuilder.new(model, action, settings).call
@@ -80,7 +80,7 @@ module PubSubModelSync
80
80
  end
81
81
 
82
82
  def connector_publish(payload)
83
- log("Publishing message #{payload.inspect}...") if config.debug
83
+ log("Publishing message #{[payload]}...") if config.debug
84
84
  connector.publish(payload)
85
85
  log("Published message: #{[payload]}")
86
86
  config.on_after_publish.call(payload)
@@ -99,7 +99,7 @@ module PubSubModelSync
99
99
 
100
100
  def ensure_publish(payload)
101
101
  cancelled = config.on_before_publish.call(payload) == :cancel
102
- log("Publish cancelled by config.on_before_publish: #{payload}") if config.debug && cancelled
102
+ log("Publish cancelled by config.on_before_publish: #{[payload]}") if config.debug && cancelled
103
103
  !cancelled
104
104
  end
105
105
 
@@ -110,7 +110,7 @@ module PubSubModelSync
110
110
  def ensure_model_publish(model, action, payload)
111
111
  res_before = model.ps_before_publish(action, payload)
112
112
  cancelled = res_before == :cancel
113
- log("Publish cancelled by model.ps_before_publish: #{payload}") if config.debug && cancelled
113
+ log("Publish cancelled by model.ps_before_publish: #{[payload]}") if config.debug && cancelled
114
114
  !cancelled
115
115
  end
116
116
 
@@ -86,7 +86,7 @@ module PubSubModelSync
86
86
 
87
87
  def build_headers
88
88
  headers[:app_key] ||= PubSubModelSync::Config.subscription_key
89
- headers[:key] ||= [klass, action].join('/')
89
+ headers[:internal_key] ||= [klass, action].join('/')
90
90
  headers[:ordering_key] ||= klass
91
91
  headers[:uuid] ||= SecureRandom.uuid
92
92
  end
@@ -4,7 +4,7 @@ module PubSubModelSync
4
4
  class PayloadBuilder < PubSubModelSync::Base
5
5
  attr_accessor :model, :action, :data, :mapping, :headers, :as_klass
6
6
 
7
- # @param model (ActiveRecord::Base)
7
+ # @param model (ActiveRecord::Base,PubSubModelSync::PublisherConcern)
8
8
  # @param action (@see PublishConcern::ps_publish)
9
9
  # @param settings (@see PublishConcern::ps_publish): { data:, mapping:, headers:, as_klass: }
10
10
  def initialize(model, action, settings = {})
@@ -43,8 +43,8 @@ module PubSubModelSync
43
43
 
44
44
  def headers_data
45
45
  klass_name = model.class.name
46
- key = [klass_name, action, model.id || SecureRandom.uuid].join('/')
47
- def_data = { ordering_key: self.class.ordering_key_for(model), key: key }
46
+ internal_key = [klass_name, action, model.id || SecureRandom.uuid].join('/')
47
+ def_data = { ordering_key: self.class.ordering_key_for(model), internal_key: internal_key }
48
48
  def_data.merge(compute_value(headers))
49
49
  end
50
50
 
@@ -18,14 +18,14 @@ module PubSubModelSync
18
18
  alias ps_after_sync ps_after_publish # @deprecated
19
19
 
20
20
  # Delivers a notification via pubsub
21
- # @param action (Sym|String) Sample: create|update|save|destroy|<any_other_key>
21
+ # @param action (Symbol,String) Sample: create|update|save|destroy|<any_other_key>
22
22
  # @param mapping? (Array<String>) If present will generate data using the mapping and added to the payload.
23
23
  # Sample: ["id", "full_name:name"]
24
- # @param data? (Hash|Symbol|Proc)
24
+ # @param data? (Hash,Symbol,Proc)
25
25
  # Hash: Data to be added to the payload
26
26
  # Symbol: Method name to be called to retrieve payload data (must return a hash value, receives :action name)
27
27
  # Proc: Block to be called to retrieve payload data
28
- # @param headers? (Hash|Symbol|Proc): (All available attributes in Payload.headers)
28
+ # @param headers? (Hash,Symbol,Proc): (All available attributes in @Payload.headers)
29
29
  # Hash: Data that will be merged with default header values
30
30
  # Symbol: Method name that will be called to retrieve header values (must return a hash, receives :action name)
31
31
  # Proc: Block to be called to retrieve header values
@@ -57,7 +57,7 @@ module PubSubModelSync
57
57
  klass.publish_data((as_klass || name).to_s, data, action.to_sym, headers: headers)
58
58
  end
59
59
 
60
- # @param crud_actions (Symbol|Array<Symbol>): :create, :update, :destroy
60
+ # @param crud_actions (Symbol,Array<Symbol>): :create, :update, :destroy
61
61
  # @param method_name (Symbol, optional) method to be called
62
62
  def ps_after_action(crud_actions, method_name = nil, &block)
63
63
  actions = Array(crud_actions).map(&:to_sym)
@@ -67,7 +67,7 @@ module PubSubModelSync
67
67
  if action == :destroy
68
68
  after_destroy { instance_exec(action, &callback) }
69
69
  else
70
- ps_define_commit_action(action, callback)
70
+ send(:after_commit, on: action) { instance_exec(action, &callback) }
71
71
  end
72
72
  end
73
73
  end
@@ -80,15 +80,6 @@ module PubSubModelSync
80
80
 
81
81
  private
82
82
 
83
- def ps_define_commit_action(action, callback)
84
- if PubSubModelSync::Config.enable_rails4_before_commit # rails 4 compatibility
85
- define_method("ps_before_#{action}_commit") { instance_exec(action, &callback) }
86
- else
87
- commit_name = respond_to?(:before_commit) ? :before_commit : :after_commit
88
- send(commit_name, on: action) { instance_exec(action, &callback) }
89
- end
90
- end
91
-
92
83
  # Initialize calls to start and end pub_sub transactions and deliver all them in the same order
93
84
  def ps_init_transaction_callbacks
94
85
  start_transaction = lambda do
@@ -13,7 +13,6 @@ module PubSubModelSync
13
13
  end
14
14
 
15
15
  configure do
16
- require 'pub_sub_model_sync/initializers/before_commit' if PubSubModelSync::Config.enable_rails4_before_commit
17
16
  end
18
17
  end
19
18
  end
@@ -3,7 +3,6 @@
3
3
  require 'active_support/core_ext/module'
4
4
  module PubSubModelSync
5
5
  class Runner
6
- class ShutDown < StandardError; end
7
6
  delegate :preload_listeners, to: :class
8
7
  attr_accessor :connector
9
8
 
@@ -12,11 +11,10 @@ module PubSubModelSync
12
11
  end
13
12
 
14
13
  def run
14
+ at_exit { connector.stop }
15
15
  trap_signals!
16
16
  preload_listeners
17
17
  start_listeners
18
- rescue ShutDown
19
- connector.stop
20
18
  end
21
19
 
22
20
  def self.preload_listeners
@@ -32,8 +30,8 @@ module PubSubModelSync
32
30
 
33
31
  def trap_signals!
34
32
  handler = proc do |signal|
35
- puts "received #{Signal.signame(signal)}"
36
- raise ShutDown
33
+ puts "PS_MSYNC ==> received #{Signal.signame(signal)}"
34
+ exit
37
35
  end
38
36
  %w[INT QUIT TERM].each { |signal| Signal.trap(signal, handler) }
39
37
  end
@@ -24,7 +24,7 @@ module PubSubModelSync
24
24
  # @return (String): Json Format
25
25
  def encode_payload(payload)
26
26
  data = payload.to_h
27
- not_important_keys = %i[ordering_key topic_name forced_ordering_key]
27
+ not_important_keys = %i[forced_ordering_key]
28
28
  reduce_payload_size = !config.debug
29
29
  data[:headers].except!(*not_important_keys) if reduce_payload_size
30
30
  data.to_json
@@ -44,7 +44,7 @@ module PubSubModelSync
44
44
 
45
45
  def stop
46
46
  log('Listener stopping...')
47
- subscribers.each(&:stop!)
47
+ (subscribers || []).each(&:stop!)
48
48
  end
49
49
 
50
50
  private
@@ -76,8 +76,8 @@ module PubSubModelSync
76
76
  end
77
77
 
78
78
  # Check topic existence, create if missing topic
79
- # @param names (Array<String>|String)
80
- # @return (Array|String) return @param names
79
+ # @param names (Array<String>,String)
80
+ # @return (Array,String) return @param names
81
81
  def ensure_topics(names)
82
82
  missing_topics = Array(names) - (@known_topics || service.topics)
83
83
  missing_topics.each do |name|
@@ -9,16 +9,16 @@ module PubSubModelSync
9
9
  end
10
10
 
11
11
  module ClassMethods
12
- # @param actions (Symbol|Array<Symbol>) Notification.action name: save|create|update|destroy|<any_other_action>
12
+ # @param actions (Symbol,Array<Symbol>) Notification.action name: save|create|update|destroy|<any_other_action>
13
13
  # @param mapping (Array<String,Symbol>) Attributes mapping with aliasing support, sample: ["id", "full_name:name"]
14
14
  # @param settings (Hash<:from_klass, :to_action, :id, :if, :unless>)
15
15
  # from_klass (String) Notification.class name
16
- # to_action (Symbol|Proc):
16
+ # to_action (Symbol,Proc):
17
17
  # Symbol: Method to process the notification
18
18
  # Proc: Block to process the notification
19
- # id (Symbol|Array<Symbol|String>) attribute(s) DB primary identifier(s). Supports for mapping format.
20
- # if (Symbol|Proc|Array<Symbol>) Method or block called as the conformation before calling the callback
21
- # unless (Symbol|Proc|Array<Symbol>) Method or block called as the negation before calling the callback
19
+ # id (Symbol,Array<Symbol,String>) attribute(s) DB primary identifier(s). Supports for mapping format.
20
+ # if (Symbol,Proc,Array<Symbol>) Method or block called as the conformation before calling the callback
21
+ # unless (Symbol,Proc,Array<Symbol>) Method or block called as the negation before calling the callback
22
22
  def ps_subscribe(actions, mapping = [], settings = {}, &block)
23
23
  settings[:to_action] ||= block if block
24
24
  Array(actions).map do |action|
@@ -5,7 +5,7 @@ module PubSubModelSync
5
5
  PUBLISHER_KLASS = PubSubModelSync::MessagePublisher
6
6
  attr_accessor :key, :payloads, :max_buffer, :root, :children, :finished
7
7
 
8
- # @param key (String|nil) Transaction key, if empty will use the ordering_key from first payload
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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module PubSubModelSync
4
- VERSION = '1.0'
4
+ VERSION = '1.2.0'
5
5
  end
data/samples/README.md CHANGED
@@ -6,7 +6,7 @@ This is a sample to sync information between rails applications using RabbitMQ
6
6
  ```docker network create shared_app_services```
7
7
 
8
8
  * Start RabbitMQ server
9
- ```docker-compose up pubsub```
9
+ ```cd samples/app1 && docker-compose up pubsub```
10
10
 
11
11
  * In another tab access to App1 to publish notifications (Wait for step 2)
12
12
  - Access to the application
data/samples/app1/Gemfile CHANGED
@@ -18,7 +18,7 @@ gem 'bootsnap', '>= 1.4.4', require: false
18
18
  # Use Rack CORS for handling Cross-Origin Resource Sharing (CORS), making cross-origin AJAX possible
19
19
  # gem 'rack-cors'
20
20
 
21
- gem 'pub_sub_model_sync'
21
+ gem 'pub_sub_model_sync', '>= 1.0'
22
22
  gem 'bunny' # to use rabbit-mq pub/sub service
23
23
  gem 'annotate'
24
24
 
@@ -96,7 +96,7 @@ GEM
96
96
  nokogiri (1.11.3)
97
97
  mini_portile2 (~> 2.5.0)
98
98
  racc (~> 1.4)
99
- pub_sub_model_sync (1.0.beta1)
99
+ pub_sub_model_sync (1.0)
100
100
  rails
101
101
  puma (5.3.1)
102
102
  nio4r (~> 2.0)
@@ -160,7 +160,7 @@ DEPENDENCIES
160
160
  bunny
161
161
  byebug
162
162
  listen (~> 3.3)
163
- pub_sub_model_sync (= 1.0.beta2)
163
+ pub_sub_model_sync (>= 1.0)
164
164
  puma (~> 5.0)
165
165
  rails (~> 6.1.3, >= 6.1.3.2)
166
166
  spring
@@ -168,4 +168,4 @@ DEPENDENCIES
168
168
  tzinfo-data
169
169
 
170
170
  BUNDLED WITH
171
- 2.1.4
171
+ 2.2.17
@@ -0,0 +1 @@
1
+ f76db08751757e296d844ca18dcdb0a1
data/samples/app2/Gemfile CHANGED
@@ -18,7 +18,7 @@ gem 'bootsnap', '>= 1.4.4', require: false
18
18
  # Use Rack CORS for handling Cross-Origin Resource Sharing (CORS), making cross-origin AJAX possible
19
19
  # gem 'rack-cors'
20
20
 
21
- gem 'pub_sub_model_sync'
21
+ gem 'pub_sub_model_sync', '>= 1.0'
22
22
  gem 'bunny' # to use rabbit-mq pub/sub service
23
23
  gem 'annotate'
24
24
 
@@ -96,7 +96,7 @@ GEM
96
96
  nokogiri (1.11.3)
97
97
  mini_portile2 (~> 2.5.0)
98
98
  racc (~> 1.4)
99
- pub_sub_model_sync (1.0.beta1)
99
+ pub_sub_model_sync (1.0)
100
100
  rails
101
101
  puma (5.3.1)
102
102
  nio4r (~> 2.0)
@@ -160,7 +160,7 @@ DEPENDENCIES
160
160
  bunny
161
161
  byebug
162
162
  listen (~> 3.3)
163
- pub_sub_model_sync (= 1.0.beta2)
163
+ pub_sub_model_sync (>= 1.0)
164
164
  puma (~> 5.0)
165
165
  rails (~> 6.1.3, >= 6.1.3.2)
166
166
  spring
@@ -168,4 +168,4 @@ DEPENDENCIES
168
168
  tzinfo-data
169
169
 
170
170
  BUNDLED WITH
171
- 2.1.4
171
+ 2.2.17
@@ -0,0 +1 @@
1
+ ca702da1b8c8f8405ed03739ca90b0e5
Binary file
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.0'
4
+ version: 1.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Owen
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2021-06-13 00:00:00.000000000 Z
11
+ date: 2021-10-28 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -111,7 +111,6 @@ files:
111
111
  - lib/pub_sub_model_sync/base.rb
112
112
  - lib/pub_sub_model_sync/config.rb
113
113
  - lib/pub_sub_model_sync/connector.rb
114
- - lib/pub_sub_model_sync/initializers/before_commit.rb
115
114
  - lib/pub_sub_model_sync/message_processor.rb
116
115
  - lib/pub_sub_model_sync/message_publisher.rb
117
116
  - lib/pub_sub_model_sync/mock_google_service.rb
@@ -134,8 +133,6 @@ files:
134
133
  - lib/pub_sub_model_sync/version.rb
135
134
  - pub_sub_model_sync.gemspec
136
135
  - samples/README.md
137
- - samples/app1/.gitattributes
138
- - samples/app1/.gitignore
139
136
  - samples/app1/Dockerfile
140
137
  - samples/app1/Gemfile
141
138
  - samples/app1/Gemfile.lock
@@ -168,6 +165,7 @@ files:
168
165
  - samples/app1/config/initializers/pubsub.rb
169
166
  - samples/app1/config/initializers/wrap_parameters.rb
170
167
  - samples/app1/config/locales/en.yml
168
+ - samples/app1/config/master.key
171
169
  - samples/app1/config/puma.rb
172
170
  - samples/app1/config/routes.rb
173
171
  - samples/app1/config/spring.rb
@@ -177,8 +175,6 @@ files:
177
175
  - samples/app1/db/seeds.rb
178
176
  - samples/app1/docker-compose.yml
179
177
  - samples/app1/log/.keep
180
- - samples/app2/.gitattributes
181
- - samples/app2/.gitignore
182
178
  - samples/app2/Dockerfile
183
179
  - samples/app2/Gemfile
184
180
  - samples/app2/Gemfile.lock
@@ -211,9 +207,11 @@ files:
211
207
  - samples/app2/config/initializers/pubsub.rb
212
208
  - samples/app2/config/initializers/wrap_parameters.rb
213
209
  - samples/app2/config/locales/en.yml
210
+ - samples/app2/config/master.key
214
211
  - samples/app2/config/puma.rb
215
212
  - samples/app2/config/routes.rb
216
213
  - samples/app2/config/spring.rb
214
+ - samples/app2/db/development.sqlite3
217
215
  - samples/app2/db/migrate/20210513080956_create_customers.rb
218
216
  - samples/app2/db/migrate/20210513135203_create_posts.rb
219
217
  - samples/app2/db/schema.rb
@@ -1,23 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # Rails 4 backward compatibility (Add "simple" ps_before_*_commit callbacks)
4
- ActiveRecord::ConnectionAdapters::RealTransaction.class_eval do
5
- alias_method :commit_without_before_commit, :commit
6
-
7
- def commit
8
- call_before_commit_records if PubSubModelSync::Config.enable_rails4_before_commit
9
- commit_without_before_commit
10
- end
11
-
12
- private
13
-
14
- def call_before_commit_records
15
- ite = records.uniq
16
- ite.each do |record|
17
- action = record.previous_changes.include?(:id) ? :create : :update
18
- action = :destroy if record.destroyed?
19
- callback_name = "ps_before_#{action}_commit".to_sym
20
- record.send(callback_name) if record.respond_to?(callback_name)
21
- end
22
- end
23
- end
@@ -1,8 +0,0 @@
1
- # See https://git-scm.com/docs/gitattributes for more about git attribute files.
2
-
3
- # Mark the database schema as having been generated.
4
- db/schema.rb linguist-generated
5
-
6
-
7
- # Mark any vendored files as having been vendored.
8
- vendor/* linguist-vendored
@@ -1,28 +0,0 @@
1
- # See https://help.github.com/articles/ignoring-files for more about ignoring files.
2
- #
3
- # If you find yourself ignoring temporary files generated by your text editor
4
- # or operating system, you probably want to add a global ignore instead:
5
- # git config --global core.excludesfile '~/.gitignore_global'
6
-
7
- # Ignore bundler config.
8
- /.bundle
9
-
10
- # Ignore the default SQLite database.
11
- /db/*.sqlite3
12
- /db/*.sqlite3-*
13
-
14
- # Ignore all logfiles and tempfiles.
15
- /log/*
16
- /tmp/*
17
- !/log/.keep
18
- !/tmp/.keep
19
-
20
- # Ignore pidfiles, but keep the directory.
21
- /tmp/pids/*
22
- !/tmp/pids/
23
- !/tmp/pids/.keep
24
-
25
- .byebug_history
26
-
27
- # Ignore master key for decrypting credentials and more.
28
- /config/master.key
@@ -1,8 +0,0 @@
1
- # See https://git-scm.com/docs/gitattributes for more about git attribute files.
2
-
3
- # Mark the database schema as having been generated.
4
- db/schema.rb linguist-generated
5
-
6
-
7
- # Mark any vendored files as having been vendored.
8
- vendor/* linguist-vendored
@@ -1,28 +0,0 @@
1
- # See https://help.github.com/articles/ignoring-files for more about ignoring files.
2
- #
3
- # If you find yourself ignoring temporary files generated by your text editor
4
- # or operating system, you probably want to add a global ignore instead:
5
- # git config --global core.excludesfile '~/.gitignore_global'
6
-
7
- # Ignore bundler config.
8
- /.bundle
9
-
10
- # Ignore the default SQLite database.
11
- /db/*.sqlite3
12
- /db/*.sqlite3-*
13
-
14
- # Ignore all logfiles and tempfiles.
15
- /log/*
16
- /tmp/*
17
- !/log/.keep
18
- !/tmp/.keep
19
-
20
- # Ignore pidfiles, but keep the directory.
21
- /tmp/pids/*
22
- !/tmp/pids/
23
- !/tmp/pids/.keep
24
-
25
- .byebug_history
26
-
27
- # Ignore master key for decrypting credentials and more.
28
- /config/master.key