pub_sub_model_sync 0.5.5 → 0.5.8.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 +4 -4
- data/CHANGELOG.md +16 -0
- data/Gemfile +1 -1
- data/Gemfile.lock +45 -39
- data/README.md +30 -6
- data/lib/pub_sub_model_sync/mock_google_service.rb +8 -0
- data/lib/pub_sub_model_sync/payload.rb +22 -0
- data/lib/pub_sub_model_sync/runner.rb +7 -6
- data/lib/pub_sub_model_sync/service_base.rb +1 -1
- data/lib/pub_sub_model_sync/service_google.rb +11 -3
- data/lib/pub_sub_model_sync/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: f0a2485c0e567e6c012020de909833a5597f5280680b633ea8e9427c51776ddd
|
|
4
|
+
data.tar.gz: ba82cc15b42bd39928fc8eb186c08b811bd1f2de8ca9cae1afc8df28653e284b
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 7751c9ffd368b27cd0e6dcbfd5a79900a2e2bb003eb3be7a210be4182fef4bef950a2d793f9edb4280e82fa3a4c602f67842289df286d9c32a69895f6f488613
|
|
7
|
+
data.tar.gz: 0f4cfca2f370bd4082537619c9654cf21739e07efaa15ee4b7cedbf3892c3141ce722076edcaae9bda6e65f08028a6819aca8dccc7f8a9ed99d3d0703ee9242b
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,21 @@
|
|
|
1
1
|
# Change Log
|
|
2
2
|
|
|
3
|
+
# 0.5.8.1 (February 05, 2021)
|
|
4
|
+
- fix: keep message ordering with google pubsub
|
|
5
|
+
|
|
6
|
+
# 0.5.8 (January 29, 2021)
|
|
7
|
+
- fix: keep message ordering with google pubsub
|
|
8
|
+
|
|
9
|
+
# 0.5.7.1 (January 26, 2021)
|
|
10
|
+
- fix: does not call :on_error_processing when processing a message
|
|
11
|
+
|
|
12
|
+
# 0.5.7 (January 13, 2021)
|
|
13
|
+
- feat: add method to preload sync listeners
|
|
14
|
+
|
|
15
|
+
# 0.5.6 (January 12, 2021)
|
|
16
|
+
- feat: add payload validation
|
|
17
|
+
- feat: add method to rebuild payload
|
|
18
|
+
|
|
3
19
|
# 0.5.5 (January 11, 2021)
|
|
4
20
|
- feat: google-pub/sub receive messages in the same order they were delivered
|
|
5
21
|
|
data/Gemfile
CHANGED
data/Gemfile.lock
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
PATH
|
|
2
2
|
remote: .
|
|
3
3
|
specs:
|
|
4
|
-
pub_sub_model_sync (0.5.
|
|
4
|
+
pub_sub_model_sync (0.5.8.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 (
|
|
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.
|
|
92
|
+
google-cloud-core (1.5.0)
|
|
86
93
|
google-cloud-env (~> 1.0)
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
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.
|
|
92
|
-
google-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
google-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
googleapis-common-protos (1.
|
|
103
|
-
google-protobuf (~> 3.
|
|
104
|
-
|
|
105
|
-
|
|
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.
|
|
115
|
-
grpc (1.
|
|
116
|
-
google-protobuf (~> 3.
|
|
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.
|
|
119
|
-
|
|
120
|
-
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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
|
-
|
|
213
|
+
ruby2_keywords (0.0.2)
|
|
214
|
+
signet (0.14.0)
|
|
209
215
|
addressable (~> 2.3)
|
|
210
|
-
faraday (
|
|
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
|
```
|
|
@@ -59,10 +59,10 @@ And then execute: $ bundle install
|
|
|
59
59
|
rake pub_sub_model_sync:start
|
|
60
60
|
```
|
|
61
61
|
Note: Publishers do not need todo this
|
|
62
|
-
Note2 (Rails 6+): Due to Zeitwerk, you need to load listeners manually when syncing
|
|
62
|
+
Note2 (Rails 6+): Due to Zeitwerk, you need to load listeners manually when syncing without mentioned task (like rails console)
|
|
63
63
|
```ruby
|
|
64
64
|
# PubSubModelSync::Config.subscribers ==> []
|
|
65
|
-
|
|
65
|
+
PubSubModelSync::Runner.preload_listeners
|
|
66
66
|
# PubSubModelSync::Config.subscribers ==> [#<PubSubModelSync::Subscriber:0x000.. @klass="Article", @action=:create..., ....]
|
|
67
67
|
```
|
|
68
68
|
|
|
@@ -308,10 +308,13 @@ config.debug = true
|
|
|
308
308
|
|
|
309
309
|
## TODO
|
|
310
310
|
- Add alias attributes when subscribing (similar to publisher)
|
|
311
|
-
- Add flag ```model.
|
|
311
|
+
- Add flag ```model.ps_process_payload``` to retrieve the payload used to process the pub/sub sync
|
|
312
312
|
- Auto publish update only if payload has changed
|
|
313
313
|
- On delete, payload must only be composed by ids
|
|
314
|
-
-
|
|
314
|
+
- Feature to publish multiple message at a time with the ability to exclude similar messages by klass and action (use the last one)
|
|
315
|
+
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 })
|
|
316
|
+
- Add DB table to use as a shield to skip publishing similar notifications or publish partial notifications (similar idea when processing notif)
|
|
317
|
+
- add callback: on_message_received(payload)
|
|
315
318
|
|
|
316
319
|
## Q&A
|
|
317
320
|
- Error "could not obtain a connection from the pool within 5.000 seconds"
|
|
@@ -319,7 +322,28 @@ config.debug = true
|
|
|
319
322
|
To fix the problem, edit config/database.yml and increase the quantity of ```pool: 10```
|
|
320
323
|
- Google pubsub: How to process notifications parallely and not sequentially (default 1 thread)?
|
|
321
324
|
```ruby PubSubModelSync::ServiceGoogle::LISTEN_SETTINGS = { threads: { callback: qty_threads } } ```
|
|
322
|
-
Note: by this way some notifications can be processed before others thus missing relationship errors can appear
|
|
325
|
+
Note: by this way some notifications can be processed before others thus missing relationship errors can appear
|
|
326
|
+
- How to retry failed syncs with sidekiq?
|
|
327
|
+
```ruby
|
|
328
|
+
# lib/initializers/pub_sub_config.rb
|
|
329
|
+
|
|
330
|
+
class PubSubRecovery
|
|
331
|
+
include Sidekiq::Worker
|
|
332
|
+
sidekiq_options queue: :pubsub, retry: 2, backtrace: true
|
|
333
|
+
|
|
334
|
+
def perform(payload_data, action)
|
|
335
|
+
payload = PubSubModelSync::Payload.from_payload_data(payload_data)
|
|
336
|
+
payload.send(action)
|
|
337
|
+
end
|
|
338
|
+
end
|
|
339
|
+
|
|
340
|
+
PubSubModelSync::Config.on_error_publish = lambda do |_e, data|
|
|
341
|
+
PubSubRecovery.perform_async(data[:payload].to_h, :publish!)
|
|
342
|
+
end
|
|
343
|
+
PubSubModelSync::Config.on_error_processing = lambda do |_e, data|
|
|
344
|
+
PubSubRecovery.perform_async(data[:payload].to_h, :process!)
|
|
345
|
+
end
|
|
346
|
+
```
|
|
323
347
|
|
|
324
348
|
## Contributing
|
|
325
349
|
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
module PubSubModelSync
|
|
4
4
|
class Payload
|
|
5
|
+
class MissingInfo < StandardError; end
|
|
5
6
|
attr_reader :data, :attributes, :headers
|
|
6
7
|
|
|
7
8
|
# @param data (Hash: { any value }):
|
|
@@ -11,8 +12,10 @@ module PubSubModelSync
|
|
|
11
12
|
@attributes = attributes
|
|
12
13
|
@headers = headers
|
|
13
14
|
build_headers
|
|
15
|
+
validate!
|
|
14
16
|
end
|
|
15
17
|
|
|
18
|
+
# @return Hash: payload data
|
|
16
19
|
def to_h
|
|
17
20
|
{ data: data, attributes: attributes, headers: headers }
|
|
18
21
|
end
|
|
@@ -25,33 +28,52 @@ module PubSubModelSync
|
|
|
25
28
|
attributes[:action]
|
|
26
29
|
end
|
|
27
30
|
|
|
31
|
+
# Process payload data
|
|
32
|
+
# (If error will raise exception and wont call on_error_processing callback)
|
|
28
33
|
def process!
|
|
29
34
|
process do |publisher|
|
|
30
35
|
publisher.raise_error = true
|
|
31
36
|
end
|
|
32
37
|
end
|
|
33
38
|
|
|
39
|
+
# Process payload data
|
|
40
|
+
# (If error will call on_error_processing callback)
|
|
34
41
|
def process
|
|
35
42
|
publisher = PubSubModelSync::MessageProcessor.new(self)
|
|
36
43
|
yield(publisher) if block_given?
|
|
37
44
|
publisher.process
|
|
38
45
|
end
|
|
39
46
|
|
|
47
|
+
# Publish payload to pubsub
|
|
48
|
+
# (If error will raise exception and wont call on_error_publish callback)
|
|
40
49
|
def publish!
|
|
41
50
|
klass = PubSubModelSync::MessagePublisher
|
|
42
51
|
klass.publish(self, raise_error: true)
|
|
43
52
|
end
|
|
44
53
|
|
|
54
|
+
# Publish payload to pubsub
|
|
55
|
+
# (If error will call on_error_publish callback)
|
|
45
56
|
def publish
|
|
46
57
|
klass = PubSubModelSync::MessagePublisher
|
|
47
58
|
klass.publish(self)
|
|
48
59
|
end
|
|
49
60
|
|
|
61
|
+
# convert payload data into Payload
|
|
62
|
+
# @param data [Hash]: payload data (:data, :attributes, :headers)
|
|
63
|
+
def self.from_payload_data(data)
|
|
64
|
+
data = data.deep_symbolize_keys
|
|
65
|
+
new(data[:data], data[:attributes], data[:headers])
|
|
66
|
+
end
|
|
67
|
+
|
|
50
68
|
private
|
|
51
69
|
|
|
52
70
|
def build_headers
|
|
53
71
|
headers[:uuid] ||= SecureRandom.uuid
|
|
54
72
|
headers[:app_key] ||= PubSubModelSync::Config.subscription_key
|
|
55
73
|
end
|
|
74
|
+
|
|
75
|
+
def validate!
|
|
76
|
+
raise MissingInfo if !attributes[:klass] || !attributes[:action]
|
|
77
|
+
end
|
|
56
78
|
end
|
|
57
79
|
end
|
|
@@ -4,6 +4,7 @@ require 'active_support/core_ext/module'
|
|
|
4
4
|
module PubSubModelSync
|
|
5
5
|
class Runner
|
|
6
6
|
class ShutDown < StandardError; end
|
|
7
|
+
delegate :preload_listeners, to: :class
|
|
7
8
|
attr_accessor :connector
|
|
8
9
|
|
|
9
10
|
def initialize
|
|
@@ -12,12 +13,17 @@ module PubSubModelSync
|
|
|
12
13
|
|
|
13
14
|
def run
|
|
14
15
|
trap_signals!
|
|
15
|
-
|
|
16
|
+
preload_listeners
|
|
16
17
|
start_listeners
|
|
17
18
|
rescue ShutDown
|
|
18
19
|
connector.stop
|
|
19
20
|
end
|
|
20
21
|
|
|
22
|
+
def self.preload_listeners
|
|
23
|
+
Rails.application.try(:eager_load!) if defined?(Rails)
|
|
24
|
+
Zeitwerk::Loader.eager_load_all if defined?(Zeitwerk::Loader)
|
|
25
|
+
end
|
|
26
|
+
|
|
21
27
|
private
|
|
22
28
|
|
|
23
29
|
def start_listeners
|
|
@@ -31,10 +37,5 @@ module PubSubModelSync
|
|
|
31
37
|
end
|
|
32
38
|
%w[INT QUIT TERM].each { |signal| Signal.trap(signal, handler) }
|
|
33
39
|
end
|
|
34
|
-
|
|
35
|
-
def preload_framework!
|
|
36
|
-
Rails.application.try(:eager_load!) if defined?(Rails)
|
|
37
|
-
Zeitwerk::Loader.eager_load_all if defined?(Zeitwerk::Loader)
|
|
38
|
-
end
|
|
39
40
|
end
|
|
40
41
|
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, streams: 1 }.freeze
|
|
11
|
+
TOPIC_SETTINGS = { async: { threads: { publish: 1, callback: 1 } } }.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.
|
|
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)
|
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.
|
|
4
|
+
version: 0.5.8.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-
|
|
11
|
+
date: 2021-02-05 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: rails
|