pub_sub_model_sync 1.3.1 → 1.4.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: 2df220f5f010c22b60791382718383661ae2d4200bb63d4cf3e03a2b096cc740
4
- data.tar.gz: 84244d1c98800f15d54e3fbfdd3897fa05aea6f7285a8da5f1fcfb67d61513be
3
+ metadata.gz: 1740843df35081aaeb35e2be5c1c27cf1ff4d219af0bcf9ae4394495ab8bba5e
4
+ data.tar.gz: ed506905c6b29b554f4a1cd100ddf4c8df2bf0a99590eb26901fae8feef862ce
5
5
  SHA512:
6
- metadata.gz: eb2df15c0f909204e165b6d4e69d97434fcd50bc3ce3169086840753879b5ca0bf4de3942da0354e2528a203563084258ab3c6b003969336ae12311105e3a316
7
- data.tar.gz: d3f4660cc54518d11bb66880a9c99100eb9e757cc9c1087b8a4a59cff976178d3f9d5fe97beddac55395b392083a381df9828a8dace92087f129a7130265759f
6
+ metadata.gz: bff308ce421ae321d2a9e7b53df194d3678e5f6103eb4f2048d4ed064302c042fb7768f96a3c639190b8d452a0636fded9e72394a5ffe4731b4e24ba0b2f5b53
7
+ data.tar.gz: e60c4c78c5fbde7c40d68adb1ed029d74bb10d1759a27682850aae6015c84f938cbccccaadc976878afa26d97621d360937cdeb5cc028b2393facb12fa0d4831
data/CHANGELOG.md CHANGED
@@ -1,5 +1,7 @@
1
1
  # Change Log
2
2
 
3
+ For recent releases list go to: https://github.com/owen2345/pub_sub_model_sync/releases
4
+
3
5
  # 1.2.1 (October 28, 2021)
4
6
  chore: improve logs
5
7
 
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- pub_sub_model_sync (1.3.1)
4
+ pub_sub_model_sync (1.4.0)
5
5
  rails
6
6
 
7
7
  GEM
data/README.md CHANGED
@@ -313,10 +313,15 @@ Any notification before delivering is transformed as a Payload for a better port
313
313
  * `headers`: (Hash) Notification settings that defines how the notification will be processed or delivered.
314
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
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
316
  - `topic_name`: (String|Array<String>, optional): Specific topic name where to deliver the notification (default `PubSubModelSync::Config.topic_name`).
318
317
  - `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.
318
+ - `cache` (Boolean | Hash, Default false) Cache settings
319
+ - `true`: Skip publishing similar payloads
320
+ - `Hash<required: Array<Symbol>>`: Same as `true` and enables payload optimization to exclude unchanged non important attributes. Sample: `{ required: %i[id email] }`
321
+
322
+ ** Read ONLY **
323
+ - `internal_key`: 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).
324
+ - `app_key`: (Auto calculated): Name of the application who delivered the notification.
320
325
  - `uuid`: (Auto calculated): Unique notification identifier (Very useful when debugging).
321
326
  Note: To reduce Payload size, some header info are not delivered (Enable debug mode to deliver all payload info).
322
327
 
@@ -9,6 +9,7 @@ module PubSubModelSync
9
9
  cattr_accessor(:debug) { false }
10
10
  cattr_accessor :logger # LoggerInst
11
11
  cattr_accessor(:transactions_max_buffer) { 1 }
12
+ cattr_accessor(:skip_cache) { false }
12
13
 
13
14
  cattr_accessor(:on_before_processing) { ->(_payload, _info) {} } # return :cancel to skip
14
15
  cattr_accessor(:on_success_processing) { ->(_payload, _info) {} }
@@ -98,8 +98,11 @@ module PubSubModelSync
98
98
  private
99
99
 
100
100
  def ensure_publish(payload)
101
- cancelled = config.on_before_publish.call(payload) == :cancel
102
- log("Publish cancelled by config.on_before_publish: #{[payload]}") if config.debug && cancelled
101
+ cache_klass = PubSubModelSync::PayloadCacheOptimizer
102
+ cancelled = payload.cache_settings ? cache_klass.new(payload).call == :already_sent : false
103
+ cancelled ||= config.on_before_publish.call(payload) == :cancel
104
+ log_msg = "Publish cancelled by config.on_before_publish or cache checker: #{[payload]}"
105
+ log(log_msg) if config.debug && cancelled
103
106
  !cancelled
104
107
  end
105
108
 
@@ -19,9 +19,17 @@ module PubSubModelSync
19
19
  # <klass>: when class message
20
20
  # <klass/id>: when model message
21
21
  # topic_name (String|Array<String>): Specific topic name to be used when delivering the
22
- # message (default first topic)
22
+ # message (default Config.topic_name)
23
23
  # forced_ordering_key (String, optional): Will force to use this value as the ordering_key,
24
24
  # even withing transactions. Default nil.
25
+ # cache (Boolean | Hash, Default false) Cache settings
26
+ # true: Skip publishing similar payloads
27
+ # Hash<required: Array<Symbol>>: Same as true and enables payload optimization to exclude
28
+ # unchanged non important attributes. Sample: { required: %i[id email] }
29
+ # --- READ ONLY ----
30
+ # app_key: (string) Subscriber-Key of the application who delivered the notification
31
+ # internal_key: (String) "<klass>/<action>"
32
+ # uuid: Unique notification identifier
25
33
  def initialize(data, info, headers = {})
26
34
  @data = data.deep_symbolize_keys
27
35
  @info = info.deep_symbolize_keys
@@ -75,6 +83,16 @@ module PubSubModelSync
75
83
  klass.publish(self)
76
84
  end
77
85
 
86
+ # @param attr_keys (Array<Symbol>) List of attributes to be excluded from payload
87
+ def exclude_data_attrs(attr_keys)
88
+ @data = data.except(*attr_keys)
89
+ end
90
+
91
+ # Attributes to always be delivered after cache optimization
92
+ def cache_settings
93
+ headers[:cache]
94
+ end
95
+
78
96
  # convert payload data into Payload
79
97
  # @param data [Hash]: payload data (:data, :info, :headers)
80
98
  def self.from_payload_data(data)
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PubSubModelSync
4
+ class PayloadCacheOptimizer < PubSubModelSync::Base
5
+ # Optimizes payload data to deliver only the required ones and the changed ones and thus avoid
6
+ # delivering unnecessary notifications.
7
+ # Uses Rails.cache to retrieve previous delivered data.
8
+ attr_reader :payload, :required_attrs, :cache_key
9
+
10
+ # @param payload (Payload)
11
+ def initialize(payload)
12
+ @payload = payload
13
+ @cache_key = "pubsub/#{payload.headers[:internal_key]}/#{payload.headers[:topic_name]}"
14
+ end
15
+
16
+ # @return (:already_sent|Payload)
17
+ def call
18
+ backup_data = payload.data.clone
19
+ return payload if cache_disabled?
20
+ return :already_sent if previous_payload_data == payload.data
21
+
22
+ optimize_payload if optimization_enabled?
23
+ Rails.cache.write(cache_key, backup_data, expires_in: 1.week)
24
+ payload
25
+ end
26
+
27
+ private
28
+
29
+ def optimization_enabled?
30
+ previous_payload_data && payload.cache_settings.is_a?(Hash)
31
+ end
32
+
33
+ def cache_disabled?
34
+ res = config.skip_cache || Rails.cache.nil?
35
+ log("Skipping cache, it was disabled: #{[payload]}") if res && debug?
36
+ res
37
+ end
38
+
39
+ def previous_payload_data
40
+ @previous_payload_data ||= Rails.cache.read(cache_key)
41
+ end
42
+
43
+ def optimize_payload # rubocop:disable Metrics/AbcSize
44
+ changed_keys = Hash[(payload.data.to_a - previous_payload_data.to_a)].keys
45
+ invalid_keys = payload.data.keys - (changed_keys + payload.cache_settings[:required])
46
+ log("Excluding non changed attributes: #{invalid_keys} from: #{payload.inspect}") if debug?
47
+ payload.exclude_data_attrs(invalid_keys)
48
+ end
49
+ end
50
+ 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[forced_ordering_key]
27
+ not_important_keys = %i[forced_ordering_key cache]
28
28
  reduce_payload_size = !config.debug
29
29
  data[:headers].except!(*not_important_keys) if reduce_payload_size
30
30
  data.to_json
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module PubSubModelSync
4
- VERSION = '1.3.1'
4
+ VERSION = '1.4.0'
5
5
  end
@@ -16,6 +16,7 @@ require 'pub_sub_model_sync/message_processor'
16
16
  require 'pub_sub_model_sync/run_subscriber'
17
17
 
18
18
  require 'pub_sub_model_sync/payload_builder'
19
+ require 'pub_sub_model_sync/payload_cache_optimizer'
19
20
  require 'pub_sub_model_sync/subscriber'
20
21
 
21
22
  require 'pub_sub_model_sync/service_base'
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.3.1
4
+ version: 1.4.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-02-10 00:00:00.000000000 Z
11
+ date: 2022-03-02 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -119,6 +119,7 @@ files:
119
119
  - lib/pub_sub_model_sync/mock_rabbit_service.rb
120
120
  - lib/pub_sub_model_sync/payload.rb
121
121
  - lib/pub_sub_model_sync/payload_builder.rb
122
+ - lib/pub_sub_model_sync/payload_cache_optimizer.rb
122
123
  - lib/pub_sub_model_sync/publisher_concern.rb
123
124
  - lib/pub_sub_model_sync/railtie.rb
124
125
  - lib/pub_sub_model_sync/run_subscriber.rb