deimos-ruby 1.8.2 → 1.8.7
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 +35 -0
- data/Gemfile.lock +89 -84
- data/README.md +10 -0
- data/deimos-ruby.gemspec +3 -3
- data/docs/INTEGRATION_TESTS.md +52 -0
- data/docs/PULL_REQUEST_TEMPLATE.md +1 -0
- data/lib/deimos/active_record_consume/message_consumption.rb +9 -0
- data/lib/deimos/active_record_consumer.rb +8 -0
- data/lib/deimos/backends/db.rb +10 -1
- data/lib/deimos/config/configurable.rb +5 -0
- data/lib/deimos/config/configuration.rb +1 -1
- data/lib/deimos/kafka_source.rb +3 -2
- data/lib/deimos/kafka_topic_info.rb +2 -5
- data/lib/deimos/schema_backends/avro_schema_coercer.rb +5 -3
- data/lib/deimos/utils/db_poller.rb +2 -1
- data/lib/deimos/utils/db_producer.rb +5 -1
- data/lib/deimos/utils/inline_consumer.rb +8 -2
- data/lib/deimos/version.rb +1 -1
- data/spec/active_record_consumer_spec.rb +13 -0
- data/spec/backends/db_spec.rb +6 -0
- data/spec/generators/active_record_generator_spec.rb +1 -1
- data/spec/kafka_source_spec.rb +83 -0
- data/spec/kafka_topic_info_spec.rb +6 -6
- data/spec/schema_backends/avro_base_shared.rb +26 -1
- data/spec/spec_helper.rb +1 -1
- data/spec/utils/db_producer_spec.rb +27 -0
- data/spec/utils/inline_consumer_spec.rb +31 -0
- metadata +22 -13
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 3c12e06b13f4b1910a93d955ddca456bdfaa0d8c5ee1a925cc1aea5c2d0f4df3
|
|
4
|
+
data.tar.gz: b4ef92834678153a746b585a0c6c5ff36cb32fc65cd3fc94accc8a35e7fd6feb
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 4c3eaf327c4483302847733e1a5134b2f559f4e0ed2e7b090d893292d0cde80ad7f9677c54b0f3b415d3933e377013c286fc133399705803419f0df6cb446f75
|
|
7
|
+
data.tar.gz: a3bb3aa32da8ed98ee4bb763cd5c123f1289042219e85b219629c9e6b4a3ddd75870e0c7d9ea74b0e7d24a6edc707c5d0af39b3e6847de3da3d271679113040f
|
data/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,41 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## UNRELEASED
|
|
9
9
|
|
|
10
|
+
## 1.8.7 - 2021-01-14
|
|
11
|
+
|
|
12
|
+
- ### Roadmap :car:
|
|
13
|
+
- Update Phobos version to allow version 1.9 or 2.x.
|
|
14
|
+
|
|
15
|
+
## 1.8.6 - 2021-01-14
|
|
16
|
+
|
|
17
|
+
- ### Fixes :wrench:
|
|
18
|
+
- Fix for configuration bug with Ruby 3.0 (** instead of passing hash)
|
|
19
|
+
|
|
20
|
+
## 1.8.5 - 2021-01-13
|
|
21
|
+
|
|
22
|
+
- ### Fixes :wrench:
|
|
23
|
+
- Fixes for Rails 6.1 (remove usage of `update_attributes!`)
|
|
24
|
+
|
|
25
|
+
## 1.8.4 - 2020-12-02
|
|
26
|
+
|
|
27
|
+
### Features :star:
|
|
28
|
+
- Add overridable "process_message?" method to ActiveRecordConsumer to allow for skipping of saving/updating records
|
|
29
|
+
|
|
30
|
+
### Fixes :wrench:
|
|
31
|
+
|
|
32
|
+
- Do not apply type coercion to `timestamp-millis` and `timestamp-micros` logical types (fixes [#97](https://github.com/flipp-oss/deimos/issues/97))
|
|
33
|
+
|
|
34
|
+
## 1.8.3 - 2020-11-18
|
|
35
|
+
|
|
36
|
+
### Fixes :wrench:
|
|
37
|
+
- Do not resend already sent messages when splitting up batches
|
|
38
|
+
(fixes [#24](https://github.com/flipp-oss/deimos/issues/24))
|
|
39
|
+
- KafkaSource crashing on bulk-imports if import hooks are disabled
|
|
40
|
+
(fixes [#73](https://github.com/flipp-oss/deimos/issues/73))
|
|
41
|
+
- #96 Use string-safe encoding for partition keys
|
|
42
|
+
- Retry on offset seek failures in inline consumer
|
|
43
|
+
(fixes [#5](Inline consumer should use retries when seeking))
|
|
44
|
+
|
|
10
45
|
## 1.8.2 - 2020-09-25
|
|
11
46
|
|
|
12
47
|
### Features :star:
|
data/Gemfile.lock
CHANGED
|
@@ -1,72 +1,76 @@
|
|
|
1
1
|
PATH
|
|
2
2
|
remote: .
|
|
3
3
|
specs:
|
|
4
|
-
deimos-ruby (1.8.
|
|
4
|
+
deimos-ruby (1.8.7)
|
|
5
5
|
avro_turf (~> 0.11)
|
|
6
|
-
phobos (
|
|
6
|
+
phobos (>= 1.9, < 3.0)
|
|
7
7
|
ruby-kafka (~> 0.7)
|
|
8
8
|
sigurd (= 0.0.1)
|
|
9
9
|
|
|
10
10
|
GEM
|
|
11
11
|
remote: https://rubygems.org/
|
|
12
12
|
specs:
|
|
13
|
-
actioncable (6.
|
|
14
|
-
actionpack (= 6.
|
|
13
|
+
actioncable (6.1.1)
|
|
14
|
+
actionpack (= 6.1.1)
|
|
15
|
+
activesupport (= 6.1.1)
|
|
15
16
|
nio4r (~> 2.0)
|
|
16
17
|
websocket-driver (>= 0.6.1)
|
|
17
|
-
actionmailbox (6.
|
|
18
|
-
actionpack (= 6.
|
|
19
|
-
activejob (= 6.
|
|
20
|
-
activerecord (= 6.
|
|
21
|
-
activestorage (= 6.
|
|
22
|
-
activesupport (= 6.
|
|
18
|
+
actionmailbox (6.1.1)
|
|
19
|
+
actionpack (= 6.1.1)
|
|
20
|
+
activejob (= 6.1.1)
|
|
21
|
+
activerecord (= 6.1.1)
|
|
22
|
+
activestorage (= 6.1.1)
|
|
23
|
+
activesupport (= 6.1.1)
|
|
23
24
|
mail (>= 2.7.1)
|
|
24
|
-
actionmailer (6.
|
|
25
|
-
actionpack (= 6.
|
|
26
|
-
actionview (= 6.
|
|
27
|
-
activejob (= 6.
|
|
25
|
+
actionmailer (6.1.1)
|
|
26
|
+
actionpack (= 6.1.1)
|
|
27
|
+
actionview (= 6.1.1)
|
|
28
|
+
activejob (= 6.1.1)
|
|
29
|
+
activesupport (= 6.1.1)
|
|
28
30
|
mail (~> 2.5, >= 2.5.4)
|
|
29
31
|
rails-dom-testing (~> 2.0)
|
|
30
|
-
actionpack (6.
|
|
31
|
-
actionview (= 6.
|
|
32
|
-
activesupport (= 6.
|
|
33
|
-
rack (~> 2.0, >= 2.0.
|
|
32
|
+
actionpack (6.1.1)
|
|
33
|
+
actionview (= 6.1.1)
|
|
34
|
+
activesupport (= 6.1.1)
|
|
35
|
+
rack (~> 2.0, >= 2.0.9)
|
|
34
36
|
rack-test (>= 0.6.3)
|
|
35
37
|
rails-dom-testing (~> 2.0)
|
|
36
38
|
rails-html-sanitizer (~> 1.0, >= 1.2.0)
|
|
37
|
-
actiontext (6.
|
|
38
|
-
actionpack (= 6.
|
|
39
|
-
activerecord (= 6.
|
|
40
|
-
activestorage (= 6.
|
|
41
|
-
activesupport (= 6.
|
|
39
|
+
actiontext (6.1.1)
|
|
40
|
+
actionpack (= 6.1.1)
|
|
41
|
+
activerecord (= 6.1.1)
|
|
42
|
+
activestorage (= 6.1.1)
|
|
43
|
+
activesupport (= 6.1.1)
|
|
42
44
|
nokogiri (>= 1.8.5)
|
|
43
|
-
actionview (6.
|
|
44
|
-
activesupport (= 6.
|
|
45
|
+
actionview (6.1.1)
|
|
46
|
+
activesupport (= 6.1.1)
|
|
45
47
|
builder (~> 3.1)
|
|
46
48
|
erubi (~> 1.4)
|
|
47
49
|
rails-dom-testing (~> 2.0)
|
|
48
50
|
rails-html-sanitizer (~> 1.1, >= 1.2.0)
|
|
49
|
-
activejob (6.
|
|
50
|
-
activesupport (= 6.
|
|
51
|
+
activejob (6.1.1)
|
|
52
|
+
activesupport (= 6.1.1)
|
|
51
53
|
globalid (>= 0.3.6)
|
|
52
|
-
activemodel (6.
|
|
53
|
-
activesupport (= 6.
|
|
54
|
-
activerecord (6.
|
|
55
|
-
activemodel (= 6.
|
|
56
|
-
activesupport (= 6.
|
|
57
|
-
activerecord-import (1.0.
|
|
54
|
+
activemodel (6.1.1)
|
|
55
|
+
activesupport (= 6.1.1)
|
|
56
|
+
activerecord (6.1.1)
|
|
57
|
+
activemodel (= 6.1.1)
|
|
58
|
+
activesupport (= 6.1.1)
|
|
59
|
+
activerecord-import (1.0.7)
|
|
58
60
|
activerecord (>= 3.2)
|
|
59
|
-
activestorage (6.
|
|
60
|
-
actionpack (= 6.
|
|
61
|
-
activejob (= 6.
|
|
62
|
-
activerecord (= 6.
|
|
61
|
+
activestorage (6.1.1)
|
|
62
|
+
actionpack (= 6.1.1)
|
|
63
|
+
activejob (= 6.1.1)
|
|
64
|
+
activerecord (= 6.1.1)
|
|
65
|
+
activesupport (= 6.1.1)
|
|
63
66
|
marcel (~> 0.3.1)
|
|
64
|
-
|
|
67
|
+
mimemagic (~> 0.3.2)
|
|
68
|
+
activesupport (6.1.1)
|
|
65
69
|
concurrent-ruby (~> 1.0, >= 1.0.2)
|
|
66
|
-
i18n (>=
|
|
67
|
-
minitest (
|
|
68
|
-
tzinfo (~>
|
|
69
|
-
zeitwerk (~> 2.
|
|
70
|
+
i18n (>= 1.6, < 2)
|
|
71
|
+
minitest (>= 5.1)
|
|
72
|
+
tzinfo (~> 2.0)
|
|
73
|
+
zeitwerk (~> 2.3)
|
|
70
74
|
ast (2.4.1)
|
|
71
75
|
avro (1.9.2)
|
|
72
76
|
multi_json
|
|
@@ -75,19 +79,19 @@ GEM
|
|
|
75
79
|
excon (~> 0.45)
|
|
76
80
|
builder (3.2.4)
|
|
77
81
|
coderay (1.1.3)
|
|
78
|
-
concurrent-ruby (1.1.
|
|
79
|
-
concurrent-ruby-ext (1.1.
|
|
80
|
-
concurrent-ruby (= 1.1.
|
|
82
|
+
concurrent-ruby (1.1.7)
|
|
83
|
+
concurrent-ruby-ext (1.1.7)
|
|
84
|
+
concurrent-ruby (= 1.1.7)
|
|
81
85
|
crass (1.0.6)
|
|
82
86
|
database_cleaner (1.8.5)
|
|
83
87
|
ddtrace (0.37.0)
|
|
84
88
|
msgpack
|
|
85
89
|
diff-lcs (1.4.4)
|
|
86
|
-
digest-crc (0.6.
|
|
87
|
-
rake (
|
|
90
|
+
digest-crc (0.6.3)
|
|
91
|
+
rake (>= 12.0.0, < 14.0.0)
|
|
88
92
|
dogstatsd-ruby (4.8.1)
|
|
89
|
-
erubi (1.
|
|
90
|
-
excon (0.
|
|
93
|
+
erubi (1.10.0)
|
|
94
|
+
excon (0.78.1)
|
|
91
95
|
exponential-backoff (0.0.4)
|
|
92
96
|
ffi (1.13.1)
|
|
93
97
|
formatador (0.2.5)
|
|
@@ -110,7 +114,7 @@ GEM
|
|
|
110
114
|
guard-rubocop (1.3.0)
|
|
111
115
|
guard (~> 2.0)
|
|
112
116
|
rubocop (~> 0.20)
|
|
113
|
-
i18n (1.8.
|
|
117
|
+
i18n (1.8.7)
|
|
114
118
|
concurrent-ruby (~> 1.0)
|
|
115
119
|
listen (3.2.1)
|
|
116
120
|
rb-fsevent (~> 0.10, >= 0.10.3)
|
|
@@ -119,7 +123,7 @@ GEM
|
|
|
119
123
|
logging (2.3.0)
|
|
120
124
|
little-plugger (~> 1.1)
|
|
121
125
|
multi_json (~> 1.14)
|
|
122
|
-
loofah (2.
|
|
126
|
+
loofah (2.8.0)
|
|
123
127
|
crass (~> 1.0.2)
|
|
124
128
|
nokogiri (>= 1.5.9)
|
|
125
129
|
lumberjack (1.2.6)
|
|
@@ -130,15 +134,16 @@ GEM
|
|
|
130
134
|
method_source (1.0.0)
|
|
131
135
|
mimemagic (0.3.5)
|
|
132
136
|
mini_mime (1.0.2)
|
|
133
|
-
mini_portile2 (2.
|
|
134
|
-
minitest (5.14.
|
|
137
|
+
mini_portile2 (2.5.0)
|
|
138
|
+
minitest (5.14.3)
|
|
135
139
|
msgpack (1.3.3)
|
|
136
140
|
multi_json (1.15.0)
|
|
137
141
|
mysql2 (0.5.3)
|
|
138
142
|
nenv (0.3.0)
|
|
139
|
-
nio4r (2.5.
|
|
140
|
-
nokogiri (1.
|
|
141
|
-
mini_portile2 (~> 2.
|
|
143
|
+
nio4r (2.5.4)
|
|
144
|
+
nokogiri (1.11.1)
|
|
145
|
+
mini_portile2 (~> 2.5.0)
|
|
146
|
+
racc (~> 1.4)
|
|
142
147
|
notiffany (0.1.3)
|
|
143
148
|
nenv (~> 0.1)
|
|
144
149
|
shellany (~> 0.0)
|
|
@@ -146,7 +151,7 @@ GEM
|
|
|
146
151
|
parser (2.7.1.4)
|
|
147
152
|
ast (~> 2.4.1)
|
|
148
153
|
pg (1.2.3)
|
|
149
|
-
phobos (
|
|
154
|
+
phobos (2.0.1)
|
|
150
155
|
activesupport (>= 3.0.0)
|
|
151
156
|
concurrent-ruby (>= 1.0.2)
|
|
152
157
|
concurrent-ruby-ext (>= 1.0.2)
|
|
@@ -157,37 +162,38 @@ GEM
|
|
|
157
162
|
pry (0.13.1)
|
|
158
163
|
coderay (~> 1.1)
|
|
159
164
|
method_source (~> 1.0)
|
|
165
|
+
racc (1.5.2)
|
|
160
166
|
rack (2.2.3)
|
|
161
167
|
rack-test (1.1.0)
|
|
162
168
|
rack (>= 1.0, < 3)
|
|
163
|
-
rails (6.
|
|
164
|
-
actioncable (= 6.
|
|
165
|
-
actionmailbox (= 6.
|
|
166
|
-
actionmailer (= 6.
|
|
167
|
-
actionpack (= 6.
|
|
168
|
-
actiontext (= 6.
|
|
169
|
-
actionview (= 6.
|
|
170
|
-
activejob (= 6.
|
|
171
|
-
activemodel (= 6.
|
|
172
|
-
activerecord (= 6.
|
|
173
|
-
activestorage (= 6.
|
|
174
|
-
activesupport (= 6.
|
|
175
|
-
bundler (>= 1.
|
|
176
|
-
railties (= 6.
|
|
169
|
+
rails (6.1.1)
|
|
170
|
+
actioncable (= 6.1.1)
|
|
171
|
+
actionmailbox (= 6.1.1)
|
|
172
|
+
actionmailer (= 6.1.1)
|
|
173
|
+
actionpack (= 6.1.1)
|
|
174
|
+
actiontext (= 6.1.1)
|
|
175
|
+
actionview (= 6.1.1)
|
|
176
|
+
activejob (= 6.1.1)
|
|
177
|
+
activemodel (= 6.1.1)
|
|
178
|
+
activerecord (= 6.1.1)
|
|
179
|
+
activestorage (= 6.1.1)
|
|
180
|
+
activesupport (= 6.1.1)
|
|
181
|
+
bundler (>= 1.15.0)
|
|
182
|
+
railties (= 6.1.1)
|
|
177
183
|
sprockets-rails (>= 2.0.0)
|
|
178
184
|
rails-dom-testing (2.0.3)
|
|
179
185
|
activesupport (>= 4.2.0)
|
|
180
186
|
nokogiri (>= 1.6)
|
|
181
187
|
rails-html-sanitizer (1.3.0)
|
|
182
188
|
loofah (~> 2.3)
|
|
183
|
-
railties (6.
|
|
184
|
-
actionpack (= 6.
|
|
185
|
-
activesupport (= 6.
|
|
189
|
+
railties (6.1.1)
|
|
190
|
+
actionpack (= 6.1.1)
|
|
191
|
+
activesupport (= 6.1.1)
|
|
186
192
|
method_source
|
|
187
193
|
rake (>= 0.8.7)
|
|
188
|
-
thor (
|
|
194
|
+
thor (~> 1.0)
|
|
189
195
|
rainbow (3.0.0)
|
|
190
|
-
rake (13.0.
|
|
196
|
+
rake (13.0.3)
|
|
191
197
|
rb-fsevent (0.10.4)
|
|
192
198
|
rb-inotify (0.10.1)
|
|
193
199
|
ffi (~> 1.0)
|
|
@@ -239,20 +245,19 @@ GEM
|
|
|
239
245
|
sprockets (4.0.2)
|
|
240
246
|
concurrent-ruby (~> 1.0)
|
|
241
247
|
rack (> 1, < 3)
|
|
242
|
-
sprockets-rails (3.2.
|
|
248
|
+
sprockets-rails (3.2.2)
|
|
243
249
|
actionpack (>= 4.0)
|
|
244
250
|
activesupport (>= 4.0)
|
|
245
251
|
sprockets (>= 3.0.0)
|
|
246
252
|
sqlite3 (1.4.2)
|
|
247
253
|
thor (1.0.1)
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
thread_safe (~> 0.1)
|
|
254
|
+
tzinfo (2.0.4)
|
|
255
|
+
concurrent-ruby (~> 1.0)
|
|
251
256
|
unicode-display_width (1.7.0)
|
|
252
257
|
websocket-driver (0.7.3)
|
|
253
258
|
websocket-extensions (>= 0.1.0)
|
|
254
259
|
websocket-extensions (0.1.5)
|
|
255
|
-
zeitwerk (2.4.
|
|
260
|
+
zeitwerk (2.4.2)
|
|
256
261
|
|
|
257
262
|
PLATFORMS
|
|
258
263
|
ruby
|
|
@@ -274,9 +279,9 @@ DEPENDENCIES
|
|
|
274
279
|
rspec (~> 3)
|
|
275
280
|
rspec-rails (~> 4)
|
|
276
281
|
rspec_junit_formatter (~> 0.3)
|
|
277
|
-
rubocop (
|
|
278
|
-
rubocop-rspec (
|
|
282
|
+
rubocop (= 0.88.0)
|
|
283
|
+
rubocop-rspec (= 1.42.0)
|
|
279
284
|
sqlite3 (~> 1.3)
|
|
280
285
|
|
|
281
286
|
BUNDLED WITH
|
|
282
|
-
2.
|
|
287
|
+
2.2.5
|
data/README.md
CHANGED
|
@@ -42,6 +42,7 @@ Please see the following for further information not covered by this readme:
|
|
|
42
42
|
* [Configuration Reference](docs/CONFIGURATION.md)
|
|
43
43
|
* [Database Backend Feature](docs/DATABASE_BACKEND.md)
|
|
44
44
|
* [Upgrading Deimos](docs/UPGRADING.md)
|
|
45
|
+
* [Contributing to Integration Tests](docs/INTEGRATION_TESTS.md)
|
|
45
46
|
|
|
46
47
|
# Installation
|
|
47
48
|
|
|
@@ -625,6 +626,15 @@ class MyConsumer < Deimos::ActiveRecordConsumer
|
|
|
625
626
|
def record_key(payload)
|
|
626
627
|
super
|
|
627
628
|
end
|
|
629
|
+
|
|
630
|
+
# Optional override, returns true by default.
|
|
631
|
+
# When this method returns true, a record corresponding to the message
|
|
632
|
+
# is created/updated.
|
|
633
|
+
# When this method returns false, message processing is skipped and a
|
|
634
|
+
# corresponding record will NOT be created/updated.
|
|
635
|
+
def process_message?(payload)
|
|
636
|
+
super
|
|
637
|
+
end
|
|
628
638
|
end
|
|
629
639
|
```
|
|
630
640
|
|
data/deimos-ruby.gemspec
CHANGED
|
@@ -19,7 +19,7 @@ Gem::Specification.new do |spec|
|
|
|
19
19
|
spec.require_paths = ['lib']
|
|
20
20
|
|
|
21
21
|
spec.add_runtime_dependency('avro_turf', '~> 0.11')
|
|
22
|
-
spec.add_runtime_dependency('phobos', '
|
|
22
|
+
spec.add_runtime_dependency('phobos', '>= 1.9', '< 3.0')
|
|
23
23
|
spec.add_runtime_dependency('ruby-kafka', '~> 0.7')
|
|
24
24
|
spec.add_runtime_dependency('sigurd', '0.0.1')
|
|
25
25
|
|
|
@@ -38,7 +38,7 @@ Gem::Specification.new do |spec|
|
|
|
38
38
|
spec.add_development_dependency('rspec', '~> 3')
|
|
39
39
|
spec.add_development_dependency('rspec_junit_formatter', '~>0.3')
|
|
40
40
|
spec.add_development_dependency('rspec-rails', '~> 4')
|
|
41
|
-
spec.add_development_dependency('rubocop', '
|
|
42
|
-
spec.add_development_dependency('rubocop-rspec', '
|
|
41
|
+
spec.add_development_dependency('rubocop', '0.88.0')
|
|
42
|
+
spec.add_development_dependency('rubocop-rspec', '1.42.0')
|
|
43
43
|
spec.add_development_dependency('sqlite3', '~> 1.3')
|
|
44
44
|
end
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# Running Integration Tests
|
|
2
|
+
|
|
3
|
+
This repo includes integration tests in the [spec/utils](spec/utils) directory.
|
|
4
|
+
Here, there are tests for more deimos features that include a database integration like
|
|
5
|
+
* [Database Poller](README.md#Database Poller)
|
|
6
|
+
* [Database Backend](docs/DATABASE_BACKEND.md)
|
|
7
|
+
* [Deadlock Retrying](lib/deimos/utils/deadlock_retry.rb)
|
|
8
|
+
|
|
9
|
+
You will need to set up the following databases to develop and create unit tests in these test suites.
|
|
10
|
+
* [SQLite](#SQLite)
|
|
11
|
+
* [MySQL](#MySQL)
|
|
12
|
+
* [PostgreSQL](#PostgreSQL)
|
|
13
|
+
|
|
14
|
+
### SQLite
|
|
15
|
+
This database is covered through the `sqlite3` gem.
|
|
16
|
+
|
|
17
|
+
## MySQL
|
|
18
|
+
### Setting up a local MySQL server (Mac)
|
|
19
|
+
```bash
|
|
20
|
+
# Download MySQL (Optionally, choose a version you are comfortable with)
|
|
21
|
+
brew install mysql
|
|
22
|
+
# Start automatically after rebooting your machine
|
|
23
|
+
brew services start mysql
|
|
24
|
+
|
|
25
|
+
# Cleanup once you are done with MySQL
|
|
26
|
+
brew services stop mysql
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## PostgreSQL
|
|
30
|
+
### Setting up a local PostgreSQL server (Mac)
|
|
31
|
+
```bash
|
|
32
|
+
# Install postgres if it's not already installed
|
|
33
|
+
brew install postgres
|
|
34
|
+
|
|
35
|
+
# Initialize and Start up postgres db
|
|
36
|
+
brew services start postgres
|
|
37
|
+
initdb /usr/local/var/postgres
|
|
38
|
+
# Create the default database and user
|
|
39
|
+
# Use the password "root"
|
|
40
|
+
createuser -s --password postgres
|
|
41
|
+
|
|
42
|
+
# Cleanup once done with Postgres
|
|
43
|
+
killall postgres
|
|
44
|
+
brew services stop postgres
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## Running Integration Tests
|
|
48
|
+
You must specify the tag "integration" when running these these test suites.
|
|
49
|
+
This can be done through the CLI with the `--tag integration` argument.
|
|
50
|
+
```bash
|
|
51
|
+
rspec spec/utils/ --tag integration
|
|
52
|
+
```
|
|
@@ -28,6 +28,7 @@ Please describe the tests that you ran to verify your changes. Provide instructi
|
|
|
28
28
|
- [ ] I have performed a self-review of my own code
|
|
29
29
|
- [ ] I have commented my code, particularly in hard-to-understand areas
|
|
30
30
|
- [ ] I have made corresponding changes to the documentation
|
|
31
|
+
- [ ] I have added a line in the CHANGELOG describing this change, under the UNRELEASED heading
|
|
31
32
|
- [ ] My changes generate no new warnings
|
|
32
33
|
- [ ] I have added tests that prove my fix is effective or that my feature works
|
|
33
34
|
- [ ] New and existing unit tests pass locally with my changes
|
|
@@ -26,6 +26,15 @@ module Deimos
|
|
|
26
26
|
|
|
27
27
|
# :nodoc:
|
|
28
28
|
def consume(payload, metadata)
|
|
29
|
+
unless self.process_message?(payload)
|
|
30
|
+
Deimos.config.logger.debug(
|
|
31
|
+
message: 'Skipping processing of message',
|
|
32
|
+
payload: payload,
|
|
33
|
+
metadata: metadata
|
|
34
|
+
)
|
|
35
|
+
return
|
|
36
|
+
end
|
|
37
|
+
|
|
29
38
|
key = metadata.with_indifferent_access[:key]
|
|
30
39
|
klass = self.class.config[:record_class]
|
|
31
40
|
record = fetch_record(klass, (payload || {}).with_indifferent_access, key)
|
|
@@ -55,5 +55,13 @@ module Deimos
|
|
|
55
55
|
def record_attributes(payload, _key=nil)
|
|
56
56
|
@converter.convert(payload)
|
|
57
57
|
end
|
|
58
|
+
|
|
59
|
+
# Override this message to conditionally save records
|
|
60
|
+
# @param payload [Hash] The kafka message as a hash
|
|
61
|
+
# @return [Boolean] if true, record is created/update.
|
|
62
|
+
# If false, record processing is skipped but message offset is still committed.
|
|
63
|
+
def process_message?(_payload)
|
|
64
|
+
true
|
|
65
|
+
end
|
|
58
66
|
end
|
|
59
67
|
end
|
data/lib/deimos/backends/db.rb
CHANGED
|
@@ -14,7 +14,7 @@ module Deimos
|
|
|
14
14
|
message = Deimos::KafkaMessage.new(
|
|
15
15
|
message: m.encoded_payload ? m.encoded_payload.to_s.b : nil,
|
|
16
16
|
topic: m.topic,
|
|
17
|
-
partition_key: m
|
|
17
|
+
partition_key: partition_key_for(m)
|
|
18
18
|
)
|
|
19
19
|
message.key = m.encoded_key.to_s.b unless producer_class.config[:no_keys]
|
|
20
20
|
message
|
|
@@ -26,6 +26,15 @@ module Deimos
|
|
|
26
26
|
by: records.size
|
|
27
27
|
)
|
|
28
28
|
end
|
|
29
|
+
|
|
30
|
+
# @param message [Deimos::Message]
|
|
31
|
+
# @return [String] the partition key to use for this message
|
|
32
|
+
def partition_key_for(message)
|
|
33
|
+
return message.partition_key if message.partition_key.present?
|
|
34
|
+
return message.key unless message.key.is_a?(Hash)
|
|
35
|
+
|
|
36
|
+
message.key.to_yaml
|
|
37
|
+
end
|
|
29
38
|
end
|
|
30
39
|
end
|
|
31
40
|
end
|
|
@@ -67,7 +67,7 @@ module Deimos
|
|
|
67
67
|
topic(kafka_config.topic) if kafka_config.topic.present? && klass.respond_to?(:topic)
|
|
68
68
|
schema(kafka_config.schema) if kafka_config.schema.present?
|
|
69
69
|
namespace(kafka_config.namespace) if kafka_config.namespace.present?
|
|
70
|
-
key_config(kafka_config.key_config) if kafka_config.key_config.present?
|
|
70
|
+
key_config(**kafka_config.key_config) if kafka_config.key_config.present?
|
|
71
71
|
end
|
|
72
72
|
end
|
|
73
73
|
|
data/lib/deimos/kafka_source.rb
CHANGED
|
@@ -88,8 +88,9 @@ module Deimos
|
|
|
88
88
|
array_of_attributes,
|
|
89
89
|
options={})
|
|
90
90
|
results = super
|
|
91
|
-
|
|
92
|
-
|
|
91
|
+
if !self.kafka_config[:import] || array_of_attributes.empty?
|
|
92
|
+
return results
|
|
93
|
+
end
|
|
93
94
|
|
|
94
95
|
# This will contain an array of hashes, where each hash is the actual
|
|
95
96
|
# attribute hash that created the object.
|
|
@@ -85,11 +85,8 @@ module Deimos
|
|
|
85
85
|
locked_at: Time.zone.now,
|
|
86
86
|
error: true,
|
|
87
87
|
retries: record.retries + 1 }
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
else
|
|
91
|
-
record.update_attributes!(attr_hash)
|
|
92
|
-
end
|
|
88
|
+
record.attributes = attr_hash
|
|
89
|
+
record.save!
|
|
93
90
|
end
|
|
94
91
|
|
|
95
92
|
# Update the locked_at timestamp to indicate that the producer is still
|
|
@@ -44,9 +44,11 @@ module Deimos
|
|
|
44
44
|
|
|
45
45
|
case field_type
|
|
46
46
|
when :int, :long
|
|
47
|
-
if
|
|
48
|
-
|
|
49
|
-
|
|
47
|
+
if %w(timestamp-millis timestamp-micros).include?(type.logical_type)
|
|
48
|
+
val
|
|
49
|
+
elsif val.is_a?(Integer) ||
|
|
50
|
+
_is_integer_string?(val) ||
|
|
51
|
+
int_classes.any? { |klass| val.is_a?(klass) }
|
|
50
52
|
val.to_i
|
|
51
53
|
else
|
|
52
54
|
val # this will fail
|
|
@@ -142,7 +142,8 @@ module Deimos
|
|
|
142
142
|
last_id = record.public_send(id_method)
|
|
143
143
|
last_updated_at = last_updated(record)
|
|
144
144
|
@producer.send_events(batch)
|
|
145
|
-
@info.
|
|
145
|
+
@info.attributes = { last_sent: last_updated_at, last_sent_id: last_id }
|
|
146
|
+
@info.save!
|
|
146
147
|
end
|
|
147
148
|
end
|
|
148
149
|
end
|
|
@@ -190,11 +190,14 @@ module Deimos
|
|
|
190
190
|
end
|
|
191
191
|
end
|
|
192
192
|
|
|
193
|
+
# Produce messages in batches, reducing the size 1/10 if the batch is too
|
|
194
|
+
# large. Does not retry batches of messages that have already been sent.
|
|
193
195
|
# @param batch [Array<Hash>]
|
|
194
196
|
def produce_messages(batch)
|
|
195
197
|
batch_size = batch.size
|
|
198
|
+
current_index = 0
|
|
196
199
|
begin
|
|
197
|
-
batch.in_groups_of(batch_size, false).each do |group|
|
|
200
|
+
batch[current_index..-1].in_groups_of(batch_size, false).each do |group|
|
|
198
201
|
@logger.debug("Publishing #{group.size} messages to #{@current_topic}")
|
|
199
202
|
producer.publish_list(group)
|
|
200
203
|
Deimos.config.metrics&.increment(
|
|
@@ -202,6 +205,7 @@ module Deimos
|
|
|
202
205
|
tags: %W(status:success topic:#{@current_topic}),
|
|
203
206
|
by: group.size
|
|
204
207
|
)
|
|
208
|
+
current_index += group.size
|
|
205
209
|
@logger.info("Sent #{group.size} messages to #{@current_topic}")
|
|
206
210
|
end
|
|
207
211
|
rescue Kafka::BufferOverflow, Kafka::MessageSizeTooLarge,
|
|
@@ -6,6 +6,7 @@ module Deimos
|
|
|
6
6
|
module Utils
|
|
7
7
|
# Listener that can seek to get the last X messages in a topic.
|
|
8
8
|
class SeekListener < Phobos::Listener
|
|
9
|
+
MAX_SEEK_RETRIES = 3
|
|
9
10
|
attr_accessor :num_messages
|
|
10
11
|
|
|
11
12
|
# :nodoc:
|
|
@@ -13,8 +14,10 @@ module Deimos
|
|
|
13
14
|
@num_messages ||= 10
|
|
14
15
|
@consumer = create_kafka_consumer
|
|
15
16
|
@consumer.subscribe(topic, @subscribe_opts)
|
|
17
|
+
attempt = 0
|
|
16
18
|
|
|
17
19
|
begin
|
|
20
|
+
attempt += 1
|
|
18
21
|
last_offset = @kafka_client.last_offset_for(topic, 0)
|
|
19
22
|
offset = last_offset - num_messages
|
|
20
23
|
if offset.positive?
|
|
@@ -22,7 +25,11 @@ module Deimos
|
|
|
22
25
|
@consumer.seek(topic, 0, offset)
|
|
23
26
|
end
|
|
24
27
|
rescue StandardError => e
|
|
25
|
-
|
|
28
|
+
if attempt < MAX_SEEK_RETRIES
|
|
29
|
+
sleep(1.seconds * attempt)
|
|
30
|
+
retry
|
|
31
|
+
end
|
|
32
|
+
log_error("Could not seek to offset: #{e.message} after #{MAX_SEEK_RETRIES} retries", listener_metadata)
|
|
26
33
|
end
|
|
27
34
|
|
|
28
35
|
instrument('listener.start_handler', listener_metadata) do
|
|
@@ -50,7 +57,6 @@ module Deimos
|
|
|
50
57
|
|
|
51
58
|
# :nodoc:
|
|
52
59
|
def consume(payload, metadata)
|
|
53
|
-
puts "Got #{payload}"
|
|
54
60
|
self.class.total_messages << {
|
|
55
61
|
key: metadata[:key],
|
|
56
62
|
payload: payload
|
data/lib/deimos/version.rb
CHANGED
|
@@ -137,5 +137,18 @@ module ActiveRecordConsumerTest
|
|
|
137
137
|
expect(Widget.find_by_test_id('id1').some_int).to eq(3)
|
|
138
138
|
expect(Widget.find_by_test_id('id2').some_int).to eq(4)
|
|
139
139
|
end
|
|
140
|
+
|
|
141
|
+
it 'should not create record of process_message returns false' do
|
|
142
|
+
MyConsumer.any_instance.stub(:process_message?).and_return(false)
|
|
143
|
+
expect(Widget.count).to eq(0)
|
|
144
|
+
test_consume_message(MyConsumer, {
|
|
145
|
+
test_id: 'abc',
|
|
146
|
+
some_int: 3,
|
|
147
|
+
updated_at: 1.day.ago.to_i,
|
|
148
|
+
some_datetime_int: Time.zone.now.to_i,
|
|
149
|
+
timestamp: 2.minutes.ago.to_s
|
|
150
|
+
}, { call_original: true, key: 5 })
|
|
151
|
+
expect(Widget.count).to eq(0)
|
|
152
|
+
end
|
|
140
153
|
end
|
|
141
154
|
end
|
data/spec/backends/db_spec.rb
CHANGED
|
@@ -43,6 +43,12 @@ each_db_config(Deimos::Backends::Db) do
|
|
|
43
43
|
described_class.publish(producer_class: MyNoKeyProducer,
|
|
44
44
|
messages: [messages.first])
|
|
45
45
|
expect(Deimos::KafkaMessage.count).to eq(4)
|
|
46
|
+
end
|
|
46
47
|
|
|
48
|
+
it 'should add messages with Hash keys with JSON encoding' do
|
|
49
|
+
described_class.publish(producer_class: MyProducer,
|
|
50
|
+
messages: [build_message({ foo: 0 }, 'my-topic', { 'test_id' => 0 })])
|
|
51
|
+
expect(Deimos::KafkaMessage.count).to eq(1)
|
|
52
|
+
expect(Deimos::KafkaMessage.last.partition_key).to eq(%(---\ntest_id: 0\n))
|
|
47
53
|
end
|
|
48
54
|
end
|
|
@@ -16,7 +16,7 @@ RSpec.describe Deimos::Generators::ActiveRecordGenerator do
|
|
|
16
16
|
files = Dir['db/migrate/*.rb']
|
|
17
17
|
expect(files.length).to eq(1)
|
|
18
18
|
results = <<~MIGRATION
|
|
19
|
-
class CreateGeneratedTable < ActiveRecord::Migration[6.
|
|
19
|
+
class CreateGeneratedTable < ActiveRecord::Migration[6.1]
|
|
20
20
|
def up
|
|
21
21
|
if table_exists?(:generated_table)
|
|
22
22
|
warn "generated_table already exists, exiting"
|
data/spec/kafka_source_spec.rb
CHANGED
|
@@ -225,5 +225,88 @@ module KafkaSourceSpec
|
|
|
225
225
|
expect(Deimos::KafkaMessage.count).to eq(0)
|
|
226
226
|
end
|
|
227
227
|
end
|
|
228
|
+
|
|
229
|
+
context 'with import hooks disabled' do
|
|
230
|
+
before(:each) do
|
|
231
|
+
# Dummy class we can include the mixin in. Has a backing table created
|
|
232
|
+
# earlier and has the import hook disabled
|
|
233
|
+
class WidgetNoImportHook < ActiveRecord::Base
|
|
234
|
+
include Deimos::KafkaSource
|
|
235
|
+
self.table_name = 'widgets'
|
|
236
|
+
|
|
237
|
+
# :nodoc:
|
|
238
|
+
def self.kafka_config
|
|
239
|
+
{
|
|
240
|
+
update: true,
|
|
241
|
+
delete: true,
|
|
242
|
+
import: false,
|
|
243
|
+
create: true
|
|
244
|
+
}
|
|
245
|
+
end
|
|
246
|
+
|
|
247
|
+
# :nodoc:
|
|
248
|
+
def self.kafka_producers
|
|
249
|
+
[WidgetProducer]
|
|
250
|
+
end
|
|
251
|
+
end
|
|
252
|
+
WidgetNoImportHook.reset_column_information
|
|
253
|
+
end
|
|
254
|
+
|
|
255
|
+
it 'should not fail when bulk-importing with existing records' do
|
|
256
|
+
widget1 = WidgetNoImportHook.create(widget_id: 1, name: 'Widget 1')
|
|
257
|
+
widget2 = WidgetNoImportHook.create(widget_id: 2, name: 'Widget 2')
|
|
258
|
+
widget1.name = 'New Widget No Import Hook 1'
|
|
259
|
+
widget2.name = 'New Widget No Import Hook 2'
|
|
260
|
+
|
|
261
|
+
expect {
|
|
262
|
+
WidgetNoImportHook.import([widget1, widget2], on_duplicate_key_update: %i(widget_id name))
|
|
263
|
+
}.not_to raise_error
|
|
264
|
+
|
|
265
|
+
expect('my-topic').not_to have_sent({
|
|
266
|
+
widget_id: 1,
|
|
267
|
+
name: 'New Widget No Import Hook 1',
|
|
268
|
+
id: widget1.id,
|
|
269
|
+
created_at: anything,
|
|
270
|
+
updated_at: anything
|
|
271
|
+
}, widget1.id)
|
|
272
|
+
expect('my-topic').not_to have_sent({
|
|
273
|
+
widget_id: 2,
|
|
274
|
+
name: 'New Widget No Import Hook 2',
|
|
275
|
+
id: widget2.id,
|
|
276
|
+
created_at: anything,
|
|
277
|
+
updated_at: anything
|
|
278
|
+
}, widget2.id)
|
|
279
|
+
end
|
|
280
|
+
|
|
281
|
+
it 'should not fail when mixing existing and new records' do
|
|
282
|
+
widget1 = WidgetNoImportHook.create(widget_id: 1, name: 'Widget 1')
|
|
283
|
+
expect('my-topic').to have_sent({
|
|
284
|
+
widget_id: 1,
|
|
285
|
+
name: 'Widget 1',
|
|
286
|
+
id: widget1.id,
|
|
287
|
+
created_at: anything,
|
|
288
|
+
updated_at: anything
|
|
289
|
+
}, widget1.id)
|
|
290
|
+
|
|
291
|
+
widget2 = WidgetNoImportHook.new(widget_id: 2, name: 'Widget 2')
|
|
292
|
+
widget1.name = 'New Widget 1'
|
|
293
|
+
WidgetNoImportHook.import([widget1, widget2], on_duplicate_key_update: %i(widget_id))
|
|
294
|
+
widgets = WidgetNoImportHook.all
|
|
295
|
+
expect('my-topic').not_to have_sent({
|
|
296
|
+
widget_id: 1,
|
|
297
|
+
name: 'New Widget 1',
|
|
298
|
+
id: widgets[0].id,
|
|
299
|
+
created_at: anything,
|
|
300
|
+
updated_at: anything
|
|
301
|
+
}, widgets[0].id)
|
|
302
|
+
expect('my-topic').not_to have_sent({
|
|
303
|
+
widget_id: 2,
|
|
304
|
+
name: 'Widget 2',
|
|
305
|
+
id: widgets[1].id,
|
|
306
|
+
created_at: anything,
|
|
307
|
+
updated_at: anything
|
|
308
|
+
}, widgets[1].id)
|
|
309
|
+
end
|
|
310
|
+
end
|
|
228
311
|
end
|
|
229
312
|
end
|
|
@@ -51,13 +51,13 @@ each_db_config(Deimos::KafkaTopicInfo) do
|
|
|
51
51
|
expect(record.locked_at).to eq(nil)
|
|
52
52
|
expect(record.error).to eq(false)
|
|
53
53
|
expect(record.retries).to eq(0)
|
|
54
|
-
expect(record.last_processed_at.to_s).to eq(Time.zone.now.to_s)
|
|
54
|
+
expect(record.last_processed_at.in_time_zone.to_s).to eq(Time.zone.now.to_s)
|
|
55
55
|
record = Deimos::KafkaTopicInfo.last
|
|
56
56
|
expect(record.locked_by).not_to eq(nil)
|
|
57
57
|
expect(record.locked_at).not_to eq(nil)
|
|
58
58
|
expect(record.error).not_to eq(false)
|
|
59
59
|
expect(record.retries).not_to eq(0)
|
|
60
|
-
expect(record.last_processed_at.to_s).to eq(20.seconds.ago.to_s)
|
|
60
|
+
expect(record.last_processed_at.in_time_zone.to_s).to eq(20.seconds.ago.to_s)
|
|
61
61
|
end
|
|
62
62
|
end
|
|
63
63
|
|
|
@@ -70,11 +70,11 @@ each_db_config(Deimos::KafkaTopicInfo) do
|
|
|
70
70
|
locked_by: 'me', locked_at: 1.minute.ago)
|
|
71
71
|
|
|
72
72
|
expect(Deimos::KafkaTopicInfo.count).to eq(3)
|
|
73
|
-
Deimos::KafkaTopicInfo.all.each { |t| expect(t.last_processed_at.to_s).to eq(old_time) }
|
|
73
|
+
Deimos::KafkaTopicInfo.all.each { |t| expect(t.last_processed_at.in_time_zone.to_s).to eq(old_time) }
|
|
74
74
|
Deimos::KafkaTopicInfo.ping_empty_topics(%w(topic1))
|
|
75
|
-
expect(t1.reload.last_processed_at.to_s).to eq(old_time) # was passed as an exception
|
|
76
|
-
expect(t2.reload.last_processed_at.to_s).to eq(Time.zone.now.to_s)
|
|
77
|
-
expect(t3.reload.last_processed_at.to_s).to eq(old_time) # is locked
|
|
75
|
+
expect(t1.reload.last_processed_at.in_time_zone.to_s).to eq(old_time) # was passed as an exception
|
|
76
|
+
expect(t2.reload.last_processed_at.in_time_zone.to_s).to eq(Time.zone.now.to_s)
|
|
77
|
+
expect(t3.reload.last_processed_at.in_time_zone.to_s).to eq(old_time) # is locked
|
|
78
78
|
end
|
|
79
79
|
end
|
|
80
80
|
|
|
@@ -42,6 +42,20 @@ RSpec.shared_examples_for('an Avro backend') do
|
|
|
42
42
|
{
|
|
43
43
|
'name' => 'union-int-field',
|
|
44
44
|
'type' => %w(null int)
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
'name' => 'timestamp-millis-field',
|
|
48
|
+
'type' => {
|
|
49
|
+
'type' => 'long',
|
|
50
|
+
'logicalType' => 'timestamp-millis'
|
|
51
|
+
}
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
'name' => 'timestamp-micros-field',
|
|
55
|
+
'type' => {
|
|
56
|
+
'type' => 'long',
|
|
57
|
+
'logicalType' => 'timestamp-micros'
|
|
58
|
+
}
|
|
45
59
|
}
|
|
46
60
|
]
|
|
47
61
|
}
|
|
@@ -95,7 +109,9 @@ RSpec.shared_examples_for('an Avro backend') do
|
|
|
95
109
|
'string-field' => 'hi mom',
|
|
96
110
|
'boolean-field' => true,
|
|
97
111
|
'union-field' => nil,
|
|
98
|
-
'union-int-field' => nil
|
|
112
|
+
'union-int-field' => nil,
|
|
113
|
+
'timestamp-millis-field' => Time.utc(2020, 11, 12, 13, 14, 15, 909_090),
|
|
114
|
+
'timestamp-micros-field' => Time.utc(2020, 11, 12, 13, 14, 15, 909_090)
|
|
99
115
|
}
|
|
100
116
|
end
|
|
101
117
|
|
|
@@ -169,6 +185,15 @@ RSpec.shared_examples_for('an Avro backend') do
|
|
|
169
185
|
expect(result['union-field']).to eq('itsme')
|
|
170
186
|
end
|
|
171
187
|
|
|
188
|
+
it 'should not convert timestamp-millis' do
|
|
189
|
+
result = backend.coerce(payload)
|
|
190
|
+
expect(result['timestamp-millis-field']).to eq(Time.utc(2020, 11, 12, 13, 14, 15, 909_090))
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
it 'should not convert timestamp-micros' do
|
|
194
|
+
result = backend.coerce(payload)
|
|
195
|
+
expect(result['timestamp-micros-field']).to eq(Time.utc(2020, 11, 12, 13, 14, 15, 909_090))
|
|
196
|
+
end
|
|
172
197
|
end
|
|
173
198
|
|
|
174
199
|
end
|
data/spec/spec_helper.rb
CHANGED
|
@@ -96,7 +96,32 @@ each_db_config(Deimos::Utils::DbProducer) do
|
|
|
96
96
|
expect(phobos_producer).to have_received(:publish_list).with(['A'] * 100).once
|
|
97
97
|
expect(phobos_producer).to have_received(:publish_list).with(['A'] * 10).once
|
|
98
98
|
expect(phobos_producer).to have_received(:publish_list).with(['A']).once
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
it 'should not resend batches of sent messages' do
|
|
102
|
+
allow(phobos_producer).to receive(:publish_list) do |group|
|
|
103
|
+
raise Kafka::BufferOverflow if group.any?('A') && group.size >= 1000
|
|
104
|
+
raise Kafka::BufferOverflow if group.any?('BIG') && group.size >= 10
|
|
105
|
+
end
|
|
106
|
+
allow(Deimos.config.metrics).to receive(:increment)
|
|
107
|
+
batch = ['A'] * 450 + ['BIG'] * 550
|
|
108
|
+
producer.produce_messages(batch)
|
|
109
|
+
|
|
110
|
+
expect(phobos_producer).to have_received(:publish_list).with(batch)
|
|
111
|
+
expect(phobos_producer).to have_received(:publish_list).with(['A'] * 100).exactly(4).times
|
|
112
|
+
expect(phobos_producer).to have_received(:publish_list).with(['A'] * 50 + ['BIG'] * 50)
|
|
113
|
+
expect(phobos_producer).to have_received(:publish_list).with(['A'] * 10).exactly(5).times
|
|
114
|
+
expect(phobos_producer).to have_received(:publish_list).with(['BIG'] * 1).exactly(550).times
|
|
99
115
|
|
|
116
|
+
expect(Deimos.config.metrics).to have_received(:increment).with('publish',
|
|
117
|
+
tags: %w(status:success topic:),
|
|
118
|
+
by: 100).exactly(4).times
|
|
119
|
+
expect(Deimos.config.metrics).to have_received(:increment).with('publish',
|
|
120
|
+
tags: %w(status:success topic:),
|
|
121
|
+
by: 10).exactly(5).times
|
|
122
|
+
expect(Deimos.config.metrics).to have_received(:increment).with('publish',
|
|
123
|
+
tags: %w(status:success topic:),
|
|
124
|
+
by: 1).exactly(550).times
|
|
100
125
|
end
|
|
101
126
|
|
|
102
127
|
describe '#compact_messages' do
|
|
@@ -289,6 +314,8 @@ each_db_config(Deimos::Utils::DbProducer) do
|
|
|
289
314
|
message: "mess#{i}",
|
|
290
315
|
partition_key: "key#{i}"
|
|
291
316
|
)
|
|
317
|
+
end
|
|
318
|
+
(5..8).each do |i|
|
|
292
319
|
Deimos::KafkaMessage.create!(
|
|
293
320
|
id: i,
|
|
294
321
|
topic: 'my-topic2',
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
describe Deimos::Utils::SeekListener do
|
|
4
|
+
|
|
5
|
+
describe '#start_listener' do
|
|
6
|
+
let(:consumer) { instance_double(Kafka::Consumer) }
|
|
7
|
+
let(:handler) { class_double(Deimos::Utils::MessageBankHandler) }
|
|
8
|
+
|
|
9
|
+
before(:each) do
|
|
10
|
+
allow(handler).to receive(:start)
|
|
11
|
+
allow(consumer).to receive(:subscribe)
|
|
12
|
+
allow_any_instance_of(Phobos::Listener).to receive(:create_kafka_consumer).and_return(consumer)
|
|
13
|
+
allow_any_instance_of(Kafka::Client).to receive(:last_offset_for).and_return(100)
|
|
14
|
+
stub_const('Deimos::Utils::SeekListener::MAX_SEEK_RETRIES', 2)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
it 'should seek offset' do
|
|
18
|
+
allow(consumer).to receive(:seek)
|
|
19
|
+
expect(consumer).to receive(:seek).once
|
|
20
|
+
seek_listener = described_class.new({ handler: handler, group_id: 999, topic: 'test_topic' })
|
|
21
|
+
seek_listener.start_listener
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
it 'should retry on errors when seeking offset' do
|
|
25
|
+
allow(consumer).to receive(:seek).and_raise(StandardError)
|
|
26
|
+
expect(consumer).to receive(:seek).twice
|
|
27
|
+
seek_listener = described_class.new({ handler: handler, group_id: 999, topic: 'test_topic' })
|
|
28
|
+
seek_listener.start_listener
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: deimos-ruby
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.8.
|
|
4
|
+
version: 1.8.7
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Daniel Orner
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date:
|
|
11
|
+
date: 2021-01-14 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: avro_turf
|
|
@@ -28,16 +28,22 @@ dependencies:
|
|
|
28
28
|
name: phobos
|
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
|
30
30
|
requirements:
|
|
31
|
-
- - "
|
|
31
|
+
- - ">="
|
|
32
32
|
- !ruby/object:Gem::Version
|
|
33
33
|
version: '1.9'
|
|
34
|
+
- - "<"
|
|
35
|
+
- !ruby/object:Gem::Version
|
|
36
|
+
version: '3.0'
|
|
34
37
|
type: :runtime
|
|
35
38
|
prerelease: false
|
|
36
39
|
version_requirements: !ruby/object:Gem::Requirement
|
|
37
40
|
requirements:
|
|
38
|
-
- - "
|
|
41
|
+
- - ">="
|
|
39
42
|
- !ruby/object:Gem::Version
|
|
40
43
|
version: '1.9'
|
|
44
|
+
- - "<"
|
|
45
|
+
- !ruby/object:Gem::Version
|
|
46
|
+
version: '3.0'
|
|
41
47
|
- !ruby/object:Gem::Dependency
|
|
42
48
|
name: ruby-kafka
|
|
43
49
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -280,30 +286,30 @@ dependencies:
|
|
|
280
286
|
name: rubocop
|
|
281
287
|
requirement: !ruby/object:Gem::Requirement
|
|
282
288
|
requirements:
|
|
283
|
-
- -
|
|
289
|
+
- - '='
|
|
284
290
|
- !ruby/object:Gem::Version
|
|
285
|
-
version:
|
|
291
|
+
version: 0.88.0
|
|
286
292
|
type: :development
|
|
287
293
|
prerelease: false
|
|
288
294
|
version_requirements: !ruby/object:Gem::Requirement
|
|
289
295
|
requirements:
|
|
290
|
-
- -
|
|
296
|
+
- - '='
|
|
291
297
|
- !ruby/object:Gem::Version
|
|
292
|
-
version:
|
|
298
|
+
version: 0.88.0
|
|
293
299
|
- !ruby/object:Gem::Dependency
|
|
294
300
|
name: rubocop-rspec
|
|
295
301
|
requirement: !ruby/object:Gem::Requirement
|
|
296
302
|
requirements:
|
|
297
|
-
- -
|
|
303
|
+
- - '='
|
|
298
304
|
- !ruby/object:Gem::Version
|
|
299
|
-
version:
|
|
305
|
+
version: 1.42.0
|
|
300
306
|
type: :development
|
|
301
307
|
prerelease: false
|
|
302
308
|
version_requirements: !ruby/object:Gem::Requirement
|
|
303
309
|
requirements:
|
|
304
|
-
- -
|
|
310
|
+
- - '='
|
|
305
311
|
- !ruby/object:Gem::Version
|
|
306
|
-
version:
|
|
312
|
+
version: 1.42.0
|
|
307
313
|
- !ruby/object:Gem::Dependency
|
|
308
314
|
name: sqlite3
|
|
309
315
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -348,6 +354,7 @@ files:
|
|
|
348
354
|
- docs/ARCHITECTURE.md
|
|
349
355
|
- docs/CONFIGURATION.md
|
|
350
356
|
- docs/DATABASE_BACKEND.md
|
|
357
|
+
- docs/INTEGRATION_TESTS.md
|
|
351
358
|
- docs/PULL_REQUEST_TEMPLATE.md
|
|
352
359
|
- docs/UPGRADING.md
|
|
353
360
|
- lib/deimos.rb
|
|
@@ -464,6 +471,7 @@ files:
|
|
|
464
471
|
- spec/utils/db_poller_spec.rb
|
|
465
472
|
- spec/utils/db_producer_spec.rb
|
|
466
473
|
- spec/utils/deadlock_retry_spec.rb
|
|
474
|
+
- spec/utils/inline_consumer_spec.rb
|
|
467
475
|
- spec/utils/lag_reporter_spec.rb
|
|
468
476
|
- spec/utils/platform_schema_validation_spec.rb
|
|
469
477
|
- spec/utils/schema_controller_mixin_spec.rb
|
|
@@ -490,7 +498,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
490
498
|
- !ruby/object:Gem::Version
|
|
491
499
|
version: '0'
|
|
492
500
|
requirements: []
|
|
493
|
-
rubygems_version: 3.
|
|
501
|
+
rubygems_version: 3.0.9
|
|
494
502
|
signing_key:
|
|
495
503
|
specification_version: 4
|
|
496
504
|
summary: Kafka libraries for Ruby.
|
|
@@ -547,6 +555,7 @@ test_files:
|
|
|
547
555
|
- spec/utils/db_poller_spec.rb
|
|
548
556
|
- spec/utils/db_producer_spec.rb
|
|
549
557
|
- spec/utils/deadlock_retry_spec.rb
|
|
558
|
+
- spec/utils/inline_consumer_spec.rb
|
|
550
559
|
- spec/utils/lag_reporter_spec.rb
|
|
551
560
|
- spec/utils/platform_schema_validation_spec.rb
|
|
552
561
|
- spec/utils/schema_controller_mixin_spec.rb
|