pub_sub_model_sync 0.5.7.1 → 0.5.9.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: cd5c9fd9bd9c35d62c1eeea4fc91024a5c14f136a757c82bde849501e0445c9d
4
- data.tar.gz: 20ea1422d130daa72863743cae0716ba01455e445ee96a9410ec050f0b7f5e52
3
+ metadata.gz: 585431eec8ef5f6f9d4b0ccea9069d03ea2849d94f7a8f43464f588da8cfae6c
4
+ data.tar.gz: 4ebf42b8ca72c0bf150f3c27a9ca9b791532954deb1663c571927324be952589
5
5
  SHA512:
6
- metadata.gz: 4c1c1afba624ef824b269ab7e6aeb112b40bc932b9125ffb90b46b03f7aea96dfc3359fd3ba780b74f9145d37b7fc01be279c8dcf2ecbadce23ac4faad821441
7
- data.tar.gz: c908efda5d92bcf8b262586feb22cadc884c24033a43318d22bdd16fb90036d139994fefc02a7b33335dd38f176cb93096e17aca4cd519d4ed8222d911848f69
6
+ metadata.gz: 1f0c786ff6de119c0add336c0182158f4c5ab3b9b74d2f9e3f180b3753ac288ecc8e2c6c88149ad82a627691c924f049eeb5e02c39280ec7ce3d04831b99dd18
7
+ data.tar.gz: f7ae9286d95af747dd8bd5e21c5f4f87e8aec469077663782103738f7ae9d07fa39b4884f5741e572f17b57a1ce6c358fe8ded7334451aa703559b7367218653
data/CHANGELOG.md CHANGED
@@ -1,5 +1,21 @@
1
1
  # Change Log
2
2
 
3
+ # 0.5.9.1 (February 10, 2021)
4
+ - feat: move :key into headers
5
+
6
+ # 0.5.9 (February 10, 2021)
7
+ - feat: reformat :publish and :process methods to include non silence methods
8
+ - feat: add notification key to payloads (can be used for caching strategies)
9
+
10
+ # 0.5.8.2 (February 05, 2021)
11
+ - fix: restore google pubsub topic settings
12
+
13
+ # 0.5.8.1 (February 05, 2021)
14
+ - fix: keep message ordering with google pubsub
15
+
16
+ # 0.5.8 (January 29, 2021)
17
+ - fix: keep message ordering with google pubsub
18
+
3
19
  # 0.5.7.1 (January 26, 2021)
4
20
  - fix: does not call :on_error_processing when processing a message
5
21
 
data/Gemfile CHANGED
@@ -2,7 +2,7 @@ source "https://rubygems.org"
2
2
 
3
3
  gem 'rubocop', '~> 1.6.0', require: false
4
4
  gem 'bunny' # rabbit-mq
5
- gem 'google-cloud-pubsub' # google pub/sub
5
+ gem 'google-cloud-pubsub', '> 2.0' # google pub/sub
6
6
  gem 'ruby-kafka' # kafka pub/sub
7
7
 
8
8
  group :test do
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- pub_sub_model_sync (0.5.7.1)
4
+ pub_sub_model_sync (0.5.9.1)
5
5
  rails
6
6
 
7
7
  GEM
@@ -78,49 +78,55 @@ GEM
78
78
  diff-lcs (1.3)
79
79
  digest-crc (0.5.1)
80
80
  erubi (1.10.0)
81
- faraday (0.17.3)
81
+ faraday (1.1.0)
82
82
  multipart-post (>= 1.2, < 3)
83
+ ruby2_keywords
84
+ gapic-common (0.3.4)
85
+ google-protobuf (~> 3.12, >= 3.12.2)
86
+ googleapis-common-protos (>= 1.3.9, < 2.0)
87
+ googleapis-common-protos-types (>= 1.0.4, < 2.0)
88
+ googleauth (~> 0.9)
89
+ grpc (~> 1.25)
83
90
  globalid (0.4.2)
84
91
  activesupport (>= 4.2.0)
85
- google-cloud-core (1.3.2)
92
+ google-cloud-core (1.5.0)
86
93
  google-cloud-env (~> 1.0)
87
- google-cloud-env (1.2.1)
88
- faraday (~> 0.11)
89
- google-cloud-pubsub (1.0.2)
94
+ google-cloud-errors (~> 1.0)
95
+ google-cloud-env (1.4.0)
96
+ faraday (>= 0.17.3, < 2.0)
97
+ google-cloud-errors (1.0.1)
98
+ google-cloud-pubsub (2.3.0)
90
99
  concurrent-ruby (~> 1.1)
91
- google-cloud-core (~> 1.2)
92
- google-gax (~> 1.7)
93
- googleapis-common-protos (>= 1.3.9, < 2.0)
94
- grpc-google-iam-v1 (~> 0.6.9)
95
- google-gax (1.7.1)
96
- google-protobuf (~> 3.2)
97
- googleapis-common-protos (>= 1.3.5, < 2.0)
98
- googleauth (>= 0.6.2, < 0.10.0)
99
- grpc (>= 1.7.2, < 2.0)
100
- rly (~> 0.2.3)
101
- google-protobuf (3.11.4)
102
- googleapis-common-protos (1.3.9)
103
- google-protobuf (~> 3.0)
104
- googleapis-common-protos-types (~> 1.0)
105
- grpc (~> 1.0)
106
- googleapis-common-protos-types (1.0.4)
107
- google-protobuf (~> 3.0)
108
- googleauth (0.9.0)
109
- faraday (~> 0.12)
100
+ google-cloud-core (~> 1.5)
101
+ google-cloud-pubsub-v1 (~> 0.0)
102
+ google-cloud-pubsub-v1 (0.1.2)
103
+ gapic-common (~> 0.3)
104
+ google-cloud-errors (~> 1.0)
105
+ grpc-google-iam-v1 (>= 0.6.10, < 2.0)
106
+ google-protobuf (3.14.0-universal-darwin)
107
+ googleapis-common-protos (1.3.10)
108
+ google-protobuf (~> 3.11)
109
+ googleapis-common-protos-types (>= 1.0.5, < 2.0)
110
+ grpc (~> 1.27)
111
+ googleapis-common-protos-types (1.0.5)
112
+ google-protobuf (~> 3.11)
113
+ googleauth (0.14.0)
114
+ faraday (>= 0.17.3, < 2.0)
110
115
  jwt (>= 1.4, < 3.0)
111
116
  memoist (~> 0.16)
112
117
  multi_json (~> 1.11)
113
118
  os (>= 0.9, < 2.0)
114
- signet (~> 0.7)
115
- grpc (1.27.0)
116
- google-protobuf (~> 3.11)
119
+ signet (~> 0.14)
120
+ grpc (1.34.0-universal-darwin)
121
+ google-protobuf (~> 3.13)
117
122
  googleapis-common-protos-types (~> 1.0)
118
- grpc-google-iam-v1 (0.6.9)
119
- googleapis-common-protos (>= 1.3.1, < 2.0)
120
- grpc (~> 1.0)
123
+ grpc-google-iam-v1 (0.6.10)
124
+ google-protobuf (~> 3.11)
125
+ googleapis-common-protos (>= 1.3.10, < 2.0)
126
+ grpc (~> 1.27)
121
127
  i18n (1.8.2)
122
128
  concurrent-ruby (~> 1.0)
123
- jwt (2.2.1)
129
+ jwt (2.2.2)
124
130
  loofah (2.8.0)
125
131
  crass (~> 1.0.2)
126
132
  nokogiri (>= 1.5.9)
@@ -134,16 +140,16 @@ GEM
134
140
  mini_mime (1.0.2)
135
141
  mini_portile2 (2.4.0)
136
142
  minitest (5.14.0)
137
- multi_json (1.14.1)
143
+ multi_json (1.15.0)
138
144
  multipart-post (2.1.1)
139
145
  nio4r (2.5.4)
140
146
  nokogiri (1.10.10)
141
147
  mini_portile2 (~> 2.4.0)
142
- os (1.0.1)
148
+ os (1.1.1)
143
149
  parallel (1.20.1)
144
150
  parser (2.7.2.0)
145
151
  ast (~> 2.4.1)
146
- public_suffix (4.0.3)
152
+ public_suffix (4.0.6)
147
153
  rack (2.2.3)
148
154
  rack-test (1.1.0)
149
155
  rack (>= 1.0, < 3)
@@ -177,7 +183,6 @@ GEM
177
183
  rake (13.0.1)
178
184
  regexp_parser (2.0.1)
179
185
  rexml (3.2.4)
180
- rly (0.2.3)
181
186
  rspec (3.9.0)
182
187
  rspec-core (~> 3.9.0)
183
188
  rspec-expectations (~> 3.9.0)
@@ -205,9 +210,10 @@ GEM
205
210
  ruby-kafka (1.0.0)
206
211
  digest-crc
207
212
  ruby-progressbar (1.10.1)
208
- signet (0.11.0)
213
+ ruby2_keywords (0.0.2)
214
+ signet (0.14.0)
209
215
  addressable (~> 2.3)
210
- faraday (~> 0.9)
216
+ faraday (>= 0.17.3, < 2.0)
211
217
  jwt (>= 1.5, < 3.0)
212
218
  multi_json (~> 1.10)
213
219
  sprockets (4.0.2)
@@ -235,7 +241,7 @@ DEPENDENCIES
235
241
  bundler
236
242
  bunny
237
243
  database_cleaner-active_record
238
- google-cloud-pubsub
244
+ google-cloud-pubsub (> 2.0)
239
245
  pub_sub_model_sync!
240
246
  rake
241
247
  rspec
data/README.md CHANGED
@@ -15,7 +15,7 @@ Add this line to your application's Gemfile:
15
15
  ```ruby
16
16
  gem 'pub_sub_model_sync'
17
17
 
18
- gem 'google-cloud-pubsub' # to use google pub/sub service
18
+ gem 'google-cloud-pubsub', '>= 1.9' # to use google pub/sub service
19
19
  gem 'bunny' # to use rabbit-mq pub/sub service
20
20
  gem 'ruby-kafka' # to use apache kafka pub/sub service
21
21
  ```
@@ -50,7 +50,19 @@ And then execute: $ bundle install
50
50
  PubSubModelSync::Config.kafka_connection = [["kafka1:9092", "localhost:2121"], { logger: Rails.logger }]
51
51
  PubSubModelSync::Config.topic_name = 'sample-topic'
52
52
  ```
53
- See details here: https://github.com/zendesk/ruby-kafka
53
+ See details here: https://github.com/zendesk/ruby-kafka
54
+
55
+ Kafka Confluence example:
56
+ ```ruby
57
+ kafka_settings = {
58
+ sasl_plain_username: '...',
59
+ sasl_plain_password: '...',
60
+ ssl_ca_certs_from_system: true,
61
+ logger: Rails.logger, client_id: 'my-app-name'
62
+ }
63
+ PubSubModelSync::Config.kafka_connection = [['...confluent.cloud:9092'], kafka_settings]
64
+ ```
65
+ Note: You need to create the topic manually on Kafka Confluence
54
66
 
55
67
  - Add publishers/subscribers to your models (See examples below)
56
68
 
@@ -308,10 +320,19 @@ config.debug = true
308
320
 
309
321
  ## TODO
310
322
  - Add alias attributes when subscribing (similar to publisher)
311
- - Add flag ```model.ps_processing``` to indicate that the current transaction is being processed by pub/sub
323
+ - Add flag ```model.ps_process_payload``` to retrieve the payload used to process the pub/sub sync
312
324
  - Auto publish update only if payload has changed
313
325
  - On delete, payload must only be composed by ids
314
- - Change notifications into messages
326
+ - Feature to publish multiple message at a time with the ability to exclude similar messages by klass and action (use the last one)
327
+ PubSubModelSync::MessagePublisher.batch_publish({ 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 })
328
+ - Add DB table to use as a shield to skip publishing similar notifications or publish partial notifications (similar idea when processing notif)
329
+ - add callback: on_message_received(payload)
330
+ - Sometimes the listener service stops listening, debug idea:
331
+ ```subscriber.on_error do
332
+ log('error gogooogle')
333
+ end
334
+ loop{ log(['loooooop:', {l: subscriber.last_error, stopped: subscriber.stopped?, started: subscriber.started?}]); sleep 2 }
335
+ ```
315
336
 
316
337
  ## Q&A
317
338
  - Error "could not obtain a connection from the pool within 5.000 seconds"
@@ -2,7 +2,7 @@
2
2
 
3
3
  module PubSubModelSync
4
4
  class MessageProcessor < PubSubModelSync::Base
5
- attr_accessor :payload, :raise_error
5
+ attr_accessor :payload
6
6
 
7
7
  # @param payload (Payload): payload to be delivered
8
8
  # @Deprecated: def initialize(data, klass, action)
@@ -15,10 +15,16 @@ module PubSubModelSync
15
15
  @payload = PubSubModelSync::Payload.new(payload, { klass: klass, action: action })
16
16
  end
17
17
 
18
- def process
18
+ def process!
19
19
  filter_subscribers.each(&method(:run_subscriber))
20
20
  end
21
21
 
22
+ def process
23
+ process!
24
+ rescue => e
25
+ notify_error(e)
26
+ end
27
+
22
28
  private
23
29
 
24
30
  def run_subscriber(subscriber)
@@ -29,8 +35,6 @@ module PubSubModelSync
29
35
  res = config.on_success_processing.call(payload, { subscriber: subscriber })
30
36
  log "processed message with: #{payload.inspect}" if res != :skip_log
31
37
  end
32
- rescue => e
33
- raise_error ? raise : print_subscriber_error(e, subscriber)
34
38
  end
35
39
 
36
40
  def processable?(subscriber)
@@ -40,9 +44,9 @@ module PubSubModelSync
40
44
  end
41
45
 
42
46
  # @param error (Error)
43
- def print_subscriber_error(error, subscriber)
47
+ def notify_error(error)
44
48
  info = [payload, error.message, error.backtrace]
45
- res = config.on_error_processing.call(error, { payload: payload, subscriber: subscriber })
49
+ res = config.on_error_processing.call(error, { payload: payload })
46
50
  log("Error processing message: #{info}", :error) if res != :skip_log
47
51
  end
48
52
 
@@ -7,14 +7,20 @@ module PubSubModelSync
7
7
  @connector ||= PubSubModelSync::Connector.new
8
8
  end
9
9
 
10
+ # Publishes any value to pubsub
11
+ # @param klass (String): Class name
12
+ # @param data (Hash): Data to be delivered
13
+ # @param action (:symbol): action name
10
14
  def publish_data(klass, data, action)
11
- payload = PubSubModelSync::Payload.new(data, { klass: klass.to_s, action: action.to_sym })
15
+ attrs = { klass: klass.to_s, action: action.to_sym }
16
+ payload = PubSubModelSync::Payload.new(data, attrs)
12
17
  publish(payload)
13
18
  end
14
19
 
15
- # @param model: ActiveRecord model
16
- # @param action: (Sym) Action name
17
- # @param publisher: (Publisher, optional) Publisher to be used
20
+ # Publishes model info to pubsub
21
+ # @param model (ActiveRecord model)
22
+ # @param action (Sym): Action name
23
+ # @param publisher (Publisher, optional): Publisher to be used
18
24
  def publish_model(model, action, publisher = nil)
19
25
  return if model.ps_skip_sync?(action)
20
26
 
@@ -27,7 +33,10 @@ module PubSubModelSync
27
33
  model.ps_after_sync(action, payload.data)
28
34
  end
29
35
 
30
- def publish(payload, raise_error: false)
36
+ # Publishes payload to pubsub
37
+ # @attr payload (PubSubModelSync::Payload)
38
+ # Raises error if exist
39
+ def publish!(payload)
31
40
  if config.on_before_publish.call(payload) == :cancel
32
41
  log("Publish message cancelled: #{payload}") if config.debug
33
42
  return
@@ -36,8 +45,14 @@ module PubSubModelSync
36
45
  log("Publishing message: #{[payload]}")
37
46
  connector.publish(payload)
38
47
  config.on_after_publish.call(payload)
48
+ end
49
+
50
+ # Similar to :publish! method
51
+ # Notifies error via :on_error_publish instead of raising error
52
+ def publish(payload)
53
+ publish!(payload)
39
54
  rescue => e
40
- raise_error ? raise : notify_error(e, payload)
55
+ notify_error(e, payload)
41
56
  end
42
57
 
43
58
  private
@@ -34,6 +34,14 @@ module PubSubModelSync
34
34
  def publish(*_args)
35
35
  true
36
36
  end
37
+
38
+ def publish_async(*_args)
39
+ yield(OpenStruct.new(succeeded?: true)) if block_given?
40
+ end
41
+
42
+ def enable_message_ordering!
43
+ true
44
+ end
37
45
  end
38
46
 
39
47
  def topic(*_args)
@@ -6,7 +6,8 @@ module PubSubModelSync
6
6
  attr_reader :data, :attributes, :headers
7
7
 
8
8
  # @param data (Hash: { any value }):
9
- # @param attributes (Hash: { klass: string, action: :sym }):
9
+ # @param attributes (Hash: { klass*: string, action*: :sym }):
10
+ # @param headers (Hash: { key?: string, ...any_key?: anything }):
10
11
  def initialize(data, attributes, headers = {})
11
12
  @data = data
12
13
  @attributes = attributes
@@ -31,16 +32,14 @@ module PubSubModelSync
31
32
  # Process payload data
32
33
  # (If error will raise exception and wont call on_error_processing callback)
33
34
  def process!
34
- process do |publisher|
35
- publisher.raise_error = true
36
- end
35
+ publisher = PubSubModelSync::MessageProcessor.new(self)
36
+ publisher.process!
37
37
  end
38
38
 
39
39
  # Process payload data
40
40
  # (If error will call on_error_processing callback)
41
41
  def process
42
42
  publisher = PubSubModelSync::MessageProcessor.new(self)
43
- yield(publisher) if block_given?
44
43
  publisher.process
45
44
  end
46
45
 
@@ -48,7 +47,7 @@ module PubSubModelSync
48
47
  # (If error will raise exception and wont call on_error_publish callback)
49
48
  def publish!
50
49
  klass = PubSubModelSync::MessagePublisher
51
- klass.publish(self, raise_error: true)
50
+ klass.publish!(self)
52
51
  end
53
52
 
54
53
  # Publish payload to pubsub
@@ -70,6 +69,7 @@ module PubSubModelSync
70
69
  def build_headers
71
70
  headers[:uuid] ||= SecureRandom.uuid
72
71
  headers[:app_key] ||= PubSubModelSync::Config.subscription_key
72
+ headers[:key] ||= [klass.to_s, action].join('/')
73
73
  end
74
74
 
75
75
  def validate!
@@ -11,8 +11,10 @@ module PubSubModelSync
11
11
  @as_klass = as_klass || klass
12
12
  end
13
13
 
14
+ # Builds the payload with model information defined for :action (:create|:update|:destroy)
14
15
  def payload(model, action)
15
- PubSubModelSync::Payload.new(payload_data(model), payload_attrs(model, action))
16
+ headers = { key: [model.class.name, action, model.id].join('/') }
17
+ PubSubModelSync::Payload.new(payload_data(model), payload_attrs(model, action), headers)
16
18
  end
17
19
 
18
20
  private
@@ -29,7 +31,10 @@ module PubSubModelSync
29
31
  end
30
32
 
31
33
  def payload_attrs(model, action)
32
- { klass: (as_klass || model.class.name).to_s, action: action.to_sym }
34
+ {
35
+ klass: (as_klass || model.class.name).to_s,
36
+ action: action.to_sym
37
+ }
33
38
  end
34
39
  end
35
40
  end
@@ -7,7 +7,8 @@ end
7
7
 
8
8
  module PubSubModelSync
9
9
  class ServiceGoogle < ServiceBase
10
- LISTEN_SETTINGS = { threads: { callback: 1 } }.freeze
10
+ LISTEN_SETTINGS = { threads: { callback: 1 }, message_ordering: true }.freeze
11
+ TOPIC_SETTINGS = {}.freeze
11
12
  SUBSCRIPTION_SETTINGS = { message_ordering: true }.freeze
12
13
  attr_accessor :service, :topic, :subscription, :subscriber
13
14
 
@@ -15,7 +16,8 @@ module PubSubModelSync
15
16
  @service = Google::Cloud::Pubsub.new(project: config.project,
16
17
  credentials: config.credentials)
17
18
  @topic = service.topic(config.topic_name) ||
18
- service.create_topic(config.topic_name)
19
+ service.create_topic(config.topic_name, TOPIC_SETTINGS)
20
+ topic.enable_message_ordering!
19
21
  end
20
22
 
21
23
  def listen_messages
@@ -30,7 +32,9 @@ module PubSubModelSync
30
32
  end
31
33
 
32
34
  def publish(payload)
33
- topic.publish(payload.to_json, { SERVICE_KEY => true }.merge(PUBLISH_SETTINGS))
35
+ topic.publish_async(payload.to_json, message_headers) do |res|
36
+ raise 'Failed to publish the message.' unless res.succeeded?
37
+ end
34
38
  end
35
39
 
36
40
  def stop
@@ -40,6 +44,10 @@ module PubSubModelSync
40
44
 
41
45
  private
42
46
 
47
+ def message_headers
48
+ { SERVICE_KEY => true, ordering_key: SERVICE_KEY }.merge(PUBLISH_SETTINGS)
49
+ end
50
+
43
51
  def subscribe_to_topic
44
52
  topic.subscription(config.subscription_key) ||
45
53
  topic.subscribe(config.subscription_key, SUBSCRIPTION_SETTINGS)
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module PubSubModelSync
4
- VERSION = '0.5.7.1'
4
+ VERSION = '0.5.9.1'
5
5
  end
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: 0.5.7.1
4
+ version: 0.5.9.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Owen
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2021-01-27 00:00:00.000000000 Z
11
+ date: 2021-02-10 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails