deimos-ruby 1.6.2 → 1.8.0.pre.beta2
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/.circleci/config.yml +9 -0
- data/.rubocop.yml +15 -13
- data/.ruby-version +1 -1
- data/CHANGELOG.md +31 -0
- data/Gemfile.lock +43 -36
- data/README.md +141 -16
- data/Rakefile +1 -1
- data/deimos-ruby.gemspec +2 -1
- data/docs/ARCHITECTURE.md +144 -0
- data/docs/CONFIGURATION.md +27 -0
- data/lib/deimos.rb +7 -6
- data/lib/deimos/active_record_consume/batch_consumption.rb +159 -0
- data/lib/deimos/active_record_consume/batch_slicer.rb +27 -0
- data/lib/deimos/active_record_consume/message_consumption.rb +58 -0
- data/lib/deimos/active_record_consume/schema_model_converter.rb +52 -0
- data/lib/deimos/active_record_consumer.rb +33 -75
- data/lib/deimos/active_record_producer.rb +23 -0
- data/lib/deimos/batch_consumer.rb +2 -140
- data/lib/deimos/config/configuration.rb +28 -10
- data/lib/deimos/consume/batch_consumption.rb +150 -0
- data/lib/deimos/consume/message_consumption.rb +94 -0
- data/lib/deimos/consumer.rb +79 -69
- data/lib/deimos/kafka_message.rb +1 -1
- data/lib/deimos/kafka_topic_info.rb +1 -1
- data/lib/deimos/message.rb +6 -1
- data/lib/deimos/metrics/provider.rb +0 -2
- data/lib/deimos/poll_info.rb +9 -0
- data/lib/deimos/tracing/provider.rb +0 -2
- data/lib/deimos/utils/db_poller.rb +149 -0
- data/lib/deimos/utils/db_producer.rb +8 -3
- data/lib/deimos/utils/deadlock_retry.rb +68 -0
- data/lib/deimos/utils/lag_reporter.rb +19 -26
- data/lib/deimos/version.rb +1 -1
- data/lib/generators/deimos/db_poller/templates/migration +11 -0
- data/lib/generators/deimos/db_poller/templates/rails3_migration +16 -0
- data/lib/generators/deimos/db_poller_generator.rb +48 -0
- data/lib/tasks/deimos.rake +7 -0
- data/spec/active_record_batch_consumer_spec.rb +481 -0
- data/spec/active_record_consume/batch_slicer_spec.rb +42 -0
- data/spec/active_record_consume/schema_model_converter_spec.rb +105 -0
- data/spec/active_record_consumer_spec.rb +3 -11
- data/spec/active_record_producer_spec.rb +66 -88
- data/spec/batch_consumer_spec.rb +24 -7
- data/spec/config/configuration_spec.rb +4 -0
- data/spec/consumer_spec.rb +8 -8
- data/spec/deimos_spec.rb +57 -49
- data/spec/handlers/my_batch_consumer.rb +6 -1
- data/spec/handlers/my_consumer.rb +6 -1
- data/spec/message_spec.rb +19 -0
- data/spec/producer_spec.rb +3 -3
- data/spec/rake_spec.rb +1 -1
- data/spec/schemas/com/my-namespace/MySchemaCompound-key.avsc +18 -0
- data/spec/schemas/com/my-namespace/Wibble.avsc +43 -0
- data/spec/spec_helper.rb +61 -6
- data/spec/utils/db_poller_spec.rb +320 -0
- data/spec/utils/deadlock_retry_spec.rb +74 -0
- data/spec/utils/lag_reporter_spec.rb +29 -22
- metadata +55 -20
- data/lib/deimos/base_consumer.rb +0 -104
- data/lib/deimos/utils/executor.rb +0 -124
- data/lib/deimos/utils/platform_schema_validation.rb +0 -0
- data/lib/deimos/utils/signal_handler.rb +0 -68
- data/spec/utils/executor_spec.rb +0 -53
- data/spec/utils/signal_handler_spec.rb +0 -16
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5c184b8f182ecfa304684f4e8e522e93c43c12fe50b34300326c2e1ef206b6d1
|
4
|
+
data.tar.gz: 5861974e036c92470a4a801bed477e5cebefa679cc9996a5d6f0258eaa484150
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f19a799d333485b6461e2d8cdeaec5643ec6ddf8f6a2ce1725f3c39269cc193cc5b1bc1e81266b6a33af87ec5d09b3ac2f857b48da61c3baded00a98d6935624
|
7
|
+
data.tar.gz: a08f6519a635e76944b3b7c2f08d5369b6f327e60e968391fcd5e09e703e8ed526d35d8e124f91cbff8c885fbf8d620f90e0a99b48e4289868a0c11925b43d51
|
data/.circleci/config.yml
CHANGED
@@ -20,6 +20,9 @@ jobs:
|
|
20
20
|
# Bundle install dependencies in /tmp/
|
21
21
|
# so Dockerfile does not copy them since
|
22
22
|
# its base image is different than CircleCI
|
23
|
+
- run:
|
24
|
+
name: Install bundler
|
25
|
+
command: gem install bundler:2.1.4
|
23
26
|
- run:
|
24
27
|
name: Bundle install
|
25
28
|
command: bundle install --path vendor/bundle --jobs=4 --retry=3
|
@@ -40,6 +43,9 @@ jobs:
|
|
40
43
|
steps:
|
41
44
|
- attach_workspace:
|
42
45
|
at: ~/workspace
|
46
|
+
- run:
|
47
|
+
name: Install bundler
|
48
|
+
command: gem install bundler:2.1.4
|
43
49
|
- run:
|
44
50
|
name: Point bundle to vendor/bundle
|
45
51
|
command: bundle --path vendor/bundle
|
@@ -50,6 +56,9 @@ jobs:
|
|
50
56
|
steps:
|
51
57
|
- attach_workspace:
|
52
58
|
at: ~/workspace
|
59
|
+
- run:
|
60
|
+
name: Install bundler
|
61
|
+
command: gem install bundler:2.1.4
|
53
62
|
- run:
|
54
63
|
name: Point bundle to vendor/bundle
|
55
64
|
command: bundle --path vendor/bundle
|
data/.rubocop.yml
CHANGED
@@ -1,10 +1,11 @@
|
|
1
1
|
require: rubocop-rspec
|
2
2
|
|
3
3
|
AllCops:
|
4
|
-
TargetRubyVersion: 2.
|
4
|
+
TargetRubyVersion: 2.4
|
5
5
|
Exclude:
|
6
6
|
- lib/deimos/monkey_patches/*.rb
|
7
7
|
- vendor/**/*
|
8
|
+
NewCops: enable
|
8
9
|
|
9
10
|
# class Plumbus
|
10
11
|
# private
|
@@ -34,6 +35,12 @@ Layout/DotPosition:
|
|
34
35
|
Layout/EmptyLinesAroundBlockBody:
|
35
36
|
Enabled: false
|
36
37
|
|
38
|
+
Layout/LineLength:
|
39
|
+
Max: 100
|
40
|
+
Severity: refactor
|
41
|
+
Exclude:
|
42
|
+
- 'spec/**/*'
|
43
|
+
|
37
44
|
# foo = if expression
|
38
45
|
# 'bar'
|
39
46
|
# end
|
@@ -82,12 +89,6 @@ Metrics/CyclomaticComplexity:
|
|
82
89
|
Severity: refactor
|
83
90
|
Max: 20
|
84
91
|
|
85
|
-
Metrics/LineLength:
|
86
|
-
Max: 100
|
87
|
-
Severity: refactor
|
88
|
-
Exclude:
|
89
|
-
- 'spec/**/*'
|
90
|
-
|
91
92
|
Metrics/MethodLength:
|
92
93
|
Severity: refactor
|
93
94
|
Max: 30
|
@@ -123,12 +124,6 @@ Style/BlockDelimiters:
|
|
123
124
|
# some_method(x, y, {a: 1, b: 2})
|
124
125
|
# some_method(x, y, {a: 1, b: 2}, a: 1, b: 2)
|
125
126
|
|
126
|
-
# good
|
127
|
-
# some_method(x, y, a: 1, b: 2)
|
128
|
-
# some_method(x, y, {a: 1, b: 2}, {a: 1, b: 2})
|
129
|
-
Style/BracesAroundHashParameters:
|
130
|
-
EnforcedStyle: context_dependent
|
131
|
-
|
132
127
|
# Enable both this:
|
133
128
|
# MyModule::MyClass
|
134
129
|
# and this:
|
@@ -179,6 +174,13 @@ Style/GuardClause:
|
|
179
174
|
Style/HashSyntax:
|
180
175
|
EnforcedStyle: ruby19_no_mixed_keys
|
181
176
|
|
177
|
+
# We are still unofficially targeting Ruby 2.3
|
178
|
+
Style/HashTransformKeys:
|
179
|
+
Enabled: false
|
180
|
+
|
181
|
+
Style/HashTransformValues:
|
182
|
+
Enabled: false
|
183
|
+
|
182
184
|
Style/IfUnlessModifier:
|
183
185
|
Enabled: false
|
184
186
|
|
data/.ruby-version
CHANGED
@@ -1 +1 @@
|
|
1
|
-
2.5.
|
1
|
+
2.5.3
|
data/CHANGELOG.md
CHANGED
@@ -7,6 +7,37 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
7
7
|
|
8
8
|
## UNRELEASED
|
9
9
|
|
10
|
+
## 1.8.0-beta2 - 2020-07-08
|
11
|
+
|
12
|
+
### Fixes :wrench:
|
13
|
+
- Fix crash with batch consumption due to not having ActiveSupport::Concern
|
14
|
+
|
15
|
+
### Features :star:
|
16
|
+
- Add `first_offset` to the metadata sent via the batch
|
17
|
+
|
18
|
+
## 1.8.0-beta1 - 2020-07-06
|
19
|
+
### Features :star:
|
20
|
+
- Added `ActiveRecordConsumer` batch mode
|
21
|
+
|
22
|
+
### Fixes :wrench:
|
23
|
+
- Lag calculation can be incorrect if no messages are being consumed.
|
24
|
+
- Fixed bug where printing messages on a MessageSizeTooLarge
|
25
|
+
error didn't work.
|
26
|
+
|
27
|
+
### Roadmap
|
28
|
+
- Moved SignalHandler and Executor to the `sigurd` gem.
|
29
|
+
|
30
|
+
## 1.7.0-beta1 - 2020-05-12
|
31
|
+
### Features :star:
|
32
|
+
- Added the DB Poller feature / process.
|
33
|
+
|
34
|
+
## 1.6.4 - 2020-05-11
|
35
|
+
- Fixed the payload logging fix for errored messages as well.
|
36
|
+
|
37
|
+
## 1.6.3 - 2020-05-04
|
38
|
+
### Fixes :wrench:
|
39
|
+
- Fixed the payload logging fix.
|
40
|
+
|
10
41
|
## 1.6.2 - 2020-05-04
|
11
42
|
### Fixes :wrench:
|
12
43
|
- When saving records via `ActiveRecordConsumer`, update `updated_at` to today's time
|
data/Gemfile.lock
CHANGED
@@ -1,10 +1,11 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
deimos-ruby (1.
|
4
|
+
deimos-ruby (1.8.0.pre.beta1)
|
5
5
|
avro_turf (~> 0.11)
|
6
6
|
phobos (~> 1.9)
|
7
7
|
ruby-kafka (~> 0.7)
|
8
|
+
sigurd (= 0.0.1)
|
8
9
|
|
9
10
|
GEM
|
10
11
|
remote: https://rubygems.org/
|
@@ -41,7 +42,7 @@ GEM
|
|
41
42
|
activemodel (= 5.2.4.2)
|
42
43
|
activesupport (= 5.2.4.2)
|
43
44
|
arel (>= 9.0)
|
44
|
-
activerecord-import (1.0.
|
45
|
+
activerecord-import (1.0.4)
|
45
46
|
activerecord (>= 3.2)
|
46
47
|
activestorage (5.2.4.2)
|
47
48
|
actionpack (= 5.2.4.2)
|
@@ -54,30 +55,31 @@ GEM
|
|
54
55
|
tzinfo (~> 1.1)
|
55
56
|
arel (9.0.0)
|
56
57
|
ast (2.4.0)
|
57
|
-
avro (1.9.
|
58
|
+
avro (1.9.2)
|
58
59
|
multi_json
|
59
60
|
avro_turf (0.11.0)
|
60
61
|
avro (>= 1.7.7, < 1.10)
|
61
62
|
excon (~> 0.45)
|
62
63
|
builder (3.2.4)
|
63
64
|
coderay (1.1.2)
|
64
|
-
concurrent-ruby (1.1.
|
65
|
-
concurrent-ruby-ext (1.1.
|
66
|
-
concurrent-ruby (= 1.1.
|
65
|
+
concurrent-ruby (1.1.6)
|
66
|
+
concurrent-ruby-ext (1.1.6)
|
67
|
+
concurrent-ruby (= 1.1.6)
|
67
68
|
crass (1.0.6)
|
68
|
-
|
69
|
+
database_cleaner (1.8.5)
|
70
|
+
ddtrace (0.35.1)
|
69
71
|
msgpack
|
70
72
|
diff-lcs (1.3)
|
71
73
|
digest-crc (0.5.1)
|
72
|
-
dogstatsd-ruby (4.
|
74
|
+
dogstatsd-ruby (4.8.0)
|
73
75
|
erubi (1.9.0)
|
74
76
|
excon (0.73.0)
|
75
77
|
exponential-backoff (0.0.4)
|
76
|
-
ffi (1.
|
78
|
+
ffi (1.12.2)
|
77
79
|
formatador (0.2.5)
|
78
80
|
globalid (0.4.2)
|
79
81
|
activesupport (>= 4.2.0)
|
80
|
-
guard (2.16.
|
82
|
+
guard (2.16.2)
|
81
83
|
formatador (>= 0.2.4)
|
82
84
|
listen (>= 2.7, < 4.0)
|
83
85
|
lumberjack (>= 1.0.12, < 2.0)
|
@@ -107,17 +109,17 @@ GEM
|
|
107
109
|
loofah (2.5.0)
|
108
110
|
crass (~> 1.0.2)
|
109
111
|
nokogiri (>= 1.5.9)
|
110
|
-
lumberjack (1.
|
112
|
+
lumberjack (1.2.4)
|
111
113
|
mail (2.7.1)
|
112
114
|
mini_mime (>= 0.1.1)
|
113
115
|
marcel (0.3.3)
|
114
116
|
mimemagic (~> 0.3.2)
|
115
|
-
method_source (0.
|
116
|
-
mimemagic (0.3.
|
117
|
+
method_source (1.0.0)
|
118
|
+
mimemagic (0.3.5)
|
117
119
|
mini_mime (1.0.2)
|
118
120
|
mini_portile2 (2.4.0)
|
119
121
|
minitest (5.14.0)
|
120
|
-
msgpack (1.3.
|
122
|
+
msgpack (1.3.3)
|
121
123
|
multi_json (1.14.1)
|
122
124
|
mysql2 (0.5.3)
|
123
125
|
nenv (0.3.0)
|
@@ -128,9 +130,9 @@ GEM
|
|
128
130
|
nenv (~> 0.1)
|
129
131
|
shellany (~> 0.0)
|
130
132
|
parallel (1.19.1)
|
131
|
-
parser (2.
|
133
|
+
parser (2.7.1.2)
|
132
134
|
ast (~> 2.4.0)
|
133
|
-
pg (1.
|
135
|
+
pg (1.2.3)
|
134
136
|
phobos (1.9.0)
|
135
137
|
activesupport (>= 3.0.0)
|
136
138
|
concurrent-ruby (>= 1.0.2)
|
@@ -139,10 +141,10 @@ GEM
|
|
139
141
|
logging
|
140
142
|
ruby-kafka
|
141
143
|
thor
|
142
|
-
pry (0.
|
143
|
-
coderay (~> 1.1
|
144
|
-
method_source (~>
|
145
|
-
rack (2.2.
|
144
|
+
pry (0.13.1)
|
145
|
+
coderay (~> 1.1)
|
146
|
+
method_source (~> 1.0)
|
147
|
+
rack (2.2.3)
|
146
148
|
rack-test (1.1.0)
|
147
149
|
rack (>= 1.0, < 3)
|
148
150
|
rails (5.2.4.2)
|
@@ -171,37 +173,42 @@ GEM
|
|
171
173
|
thor (>= 0.19.0, < 2.0)
|
172
174
|
rainbow (3.0.0)
|
173
175
|
rake (13.0.1)
|
174
|
-
rb-fsevent (0.10.
|
175
|
-
rb-inotify (0.10.
|
176
|
+
rb-fsevent (0.10.4)
|
177
|
+
rb-inotify (0.10.1)
|
176
178
|
ffi (~> 1.0)
|
179
|
+
rexml (3.2.4)
|
177
180
|
rspec (3.9.0)
|
178
181
|
rspec-core (~> 3.9.0)
|
179
182
|
rspec-expectations (~> 3.9.0)
|
180
183
|
rspec-mocks (~> 3.9.0)
|
181
|
-
rspec-core (3.9.
|
182
|
-
rspec-support (~> 3.9.
|
183
|
-
rspec-expectations (3.9.
|
184
|
+
rspec-core (3.9.2)
|
185
|
+
rspec-support (~> 3.9.3)
|
186
|
+
rspec-expectations (3.9.1)
|
184
187
|
diff-lcs (>= 1.2.0, < 2.0)
|
185
188
|
rspec-support (~> 3.9.0)
|
186
|
-
rspec-mocks (3.9.
|
189
|
+
rspec-mocks (3.9.1)
|
187
190
|
diff-lcs (>= 1.2.0, < 2.0)
|
188
191
|
rspec-support (~> 3.9.0)
|
189
|
-
rspec-support (3.9.
|
192
|
+
rspec-support (3.9.3)
|
190
193
|
rspec_junit_formatter (0.4.1)
|
191
194
|
rspec-core (>= 2, < 4, != 2.12.0)
|
192
|
-
rubocop (0.
|
195
|
+
rubocop (0.82.0)
|
193
196
|
jaro_winkler (~> 1.5.1)
|
194
197
|
parallel (~> 1.10)
|
195
|
-
parser (>= 2.
|
198
|
+
parser (>= 2.7.0.1)
|
196
199
|
rainbow (>= 2.2.2, < 4.0)
|
200
|
+
rexml
|
197
201
|
ruby-progressbar (~> 1.7)
|
198
|
-
unicode-display_width (>= 1.4.0, <
|
199
|
-
rubocop-rspec (1.
|
202
|
+
unicode-display_width (>= 1.4.0, < 2.0)
|
203
|
+
rubocop-rspec (1.39.0)
|
200
204
|
rubocop (>= 0.68.1)
|
201
205
|
ruby-kafka (0.7.10)
|
202
206
|
digest-crc
|
203
207
|
ruby-progressbar (1.10.1)
|
204
208
|
shellany (0.0.1)
|
209
|
+
sigurd (0.0.1)
|
210
|
+
concurrent-ruby (~> 1)
|
211
|
+
exponential-backoff
|
205
212
|
sprockets (4.0.0)
|
206
213
|
concurrent-ruby (~> 1.0)
|
207
214
|
rack (> 1, < 3)
|
@@ -209,15 +216,15 @@ GEM
|
|
209
216
|
actionpack (>= 4.0)
|
210
217
|
activesupport (>= 4.0)
|
211
218
|
sprockets (>= 3.0.0)
|
212
|
-
sqlite3 (1.4.
|
219
|
+
sqlite3 (1.4.2)
|
213
220
|
thor (1.0.1)
|
214
221
|
thread_safe (0.3.6)
|
215
222
|
tzinfo (1.2.7)
|
216
223
|
thread_safe (~> 0.1)
|
217
|
-
unicode-display_width (1.
|
224
|
+
unicode-display_width (1.7.0)
|
218
225
|
websocket-driver (0.7.1)
|
219
226
|
websocket-extensions (>= 0.1.0)
|
220
|
-
websocket-extensions (0.1.
|
227
|
+
websocket-extensions (0.1.5)
|
221
228
|
|
222
229
|
PLATFORMS
|
223
230
|
ruby
|
@@ -226,7 +233,7 @@ DEPENDENCIES
|
|
226
233
|
activerecord (~> 5.2)
|
227
234
|
activerecord-import
|
228
235
|
avro (~> 1.9)
|
229
|
-
|
236
|
+
database_cleaner (~> 1.7)
|
230
237
|
ddtrace (~> 0.11)
|
231
238
|
deimos-ruby!
|
232
239
|
dogstatsd-ruby (~> 4.2)
|
@@ -244,4 +251,4 @@ DEPENDENCIES
|
|
244
251
|
sqlite3 (~> 1.3)
|
245
252
|
|
246
253
|
BUNDLED WITH
|
247
|
-
1.
|
254
|
+
2.1.4
|
data/README.md
CHANGED
@@ -23,6 +23,7 @@ Built on Phobos and hence Ruby-Kafka.
|
|
23
23
|
* [Consumers](#consumers)
|
24
24
|
* [Rails Integration](#rails-integration)
|
25
25
|
* [Database Backend](#database-backend)
|
26
|
+
* [Database Poller](#database-poller)
|
26
27
|
* [Running Consumers](#running-consumers)
|
27
28
|
* [Metrics](#metrics)
|
28
29
|
* [Testing](#testing)
|
@@ -313,28 +314,19 @@ messages as an array and then process them together. This can improve
|
|
313
314
|
consumer throughput, depending on the use case. Batch consumers behave like
|
314
315
|
other consumers in regards to key and payload decoding, etc.
|
315
316
|
|
316
|
-
To enable batch consumption, ensure that the `delivery` property
|
317
|
+
To enable batch consumption, ensure that the `delivery` property of your
|
318
|
+
consumer is set to `inline_batch`.
|
317
319
|
|
318
|
-
|
319
|
-
|
320
|
-
consumer do
|
321
|
-
class_name 'Consumers::MyBatchConsumer'
|
322
|
-
topic 'my_batched_topic'
|
323
|
-
group_id 'my_group_id'
|
324
|
-
delivery :inline_batch
|
325
|
-
end
|
326
|
-
end
|
327
|
-
```
|
328
|
-
|
329
|
-
Batch consumers must inherit from the Deimos::BatchConsumer class as in
|
330
|
-
this sample:
|
320
|
+
Batch consumers will invoke the `consume_batch` method instead of `consume`
|
321
|
+
as in this example:
|
331
322
|
|
332
323
|
```ruby
|
333
|
-
class MyBatchConsumer < Deimos::
|
324
|
+
class MyBatchConsumer < Deimos::Consumer
|
334
325
|
|
335
326
|
def consume_batch(payloads, metadata)
|
336
327
|
# payloads is an array of schema-decoded hashes.
|
337
|
-
# metadata is a hash that contains information like :keys
|
328
|
+
# metadata is a hash that contains information like :keys, :topic,
|
329
|
+
# and :first_offset.
|
338
330
|
# Keys are automatically decoded and available as an array with
|
339
331
|
# the same cardinality as the payloads. If you need to iterate
|
340
332
|
# over payloads and keys together, you can use something like this:
|
@@ -532,12 +524,14 @@ class MyConsumer < Deimos::ActiveRecordConsumer
|
|
532
524
|
|
533
525
|
# Optional override of the way to fetch records based on payload and
|
534
526
|
# key. Default is to use the key to search the primary key of the table.
|
527
|
+
# Only used in non-batch mode.
|
535
528
|
def fetch_record(klass, payload, key)
|
536
529
|
super
|
537
530
|
end
|
538
531
|
|
539
532
|
# Optional override on how to set primary key for new records.
|
540
533
|
# Default is to set the class's primary key to the message's decoded key.
|
534
|
+
# Only used in non-batch mode.
|
541
535
|
def assign_key(record, payload, key)
|
542
536
|
super
|
543
537
|
end
|
@@ -545,6 +539,7 @@ class MyConsumer < Deimos::ActiveRecordConsumer
|
|
545
539
|
# Optional override of the default behavior, which is to call `destroy`
|
546
540
|
# on the record - e.g. you can replace this with "archiving" the record
|
547
541
|
# in some way.
|
542
|
+
# Only used in non-batch mode.
|
548
543
|
def destroy_record(record)
|
549
544
|
super
|
550
545
|
end
|
@@ -554,9 +549,136 @@ class MyConsumer < Deimos::ActiveRecordConsumer
|
|
554
549
|
def record_attributes(payload)
|
555
550
|
super.merge(:some_field => 'some_value')
|
556
551
|
end
|
552
|
+
|
553
|
+
# Optional override to change the attributes used for identifying records
|
554
|
+
def record_key(payload)
|
555
|
+
super
|
556
|
+
end
|
557
557
|
end
|
558
558
|
```
|
559
559
|
|
560
|
+
#### Batch Consumers
|
561
|
+
|
562
|
+
Deimos also provides a batch consumption mode for `ActiveRecordConsumer` which
|
563
|
+
processes groups of messages at once using the ActiveRecord backend.
|
564
|
+
|
565
|
+
Batch ActiveRecord consumers make use of the
|
566
|
+
[activerecord-import](https://github.com/zdennis/activerecord-import) to insert
|
567
|
+
or update multiple records in bulk SQL statements. This reduces processing
|
568
|
+
time at the cost of skipping ActiveRecord callbacks for individual records.
|
569
|
+
Deleted records (tombstones) are grouped into `delete_all` calls and thus also
|
570
|
+
skip `destroy` callbacks.
|
571
|
+
|
572
|
+
Batch consumption is used when the `delivery` setting for your consumer is set to `inline_batch`.
|
573
|
+
|
574
|
+
**Note**: Currently, batch consumption only supports only primary keys as identifiers out of the box. See
|
575
|
+
[the specs](spec/active_record_batch_consumer_spec.rb) for an example of how to use compound keys.
|
576
|
+
|
577
|
+
By default, batches will be compacted before processing, i.e. only the last
|
578
|
+
message for each unique key in a batch will actually be processed. To change
|
579
|
+
this behaviour, call `compacted false` inside of your consumer definition.
|
580
|
+
|
581
|
+
A sample batch consumer would look as follows:
|
582
|
+
|
583
|
+
```ruby
|
584
|
+
class MyConsumer < Deimos::ActiveRecordConsumer
|
585
|
+
schema 'MySchema'
|
586
|
+
key_config field: 'my_field'
|
587
|
+
record_class Widget
|
588
|
+
|
589
|
+
# Controls whether the batch is compacted before consuming.
|
590
|
+
# If true, only the last message for each unique key in a batch will be
|
591
|
+
# processed.
|
592
|
+
# If false, messages will be grouped into "slices" of independent keys
|
593
|
+
# and each slice will be imported separately.
|
594
|
+
#
|
595
|
+
# compacted false
|
596
|
+
|
597
|
+
|
598
|
+
# Optional override of the default behavior, which is to call `delete_all`
|
599
|
+
# on the associated records - e.g. you can replace this with setting a deleted
|
600
|
+
# flag on the record.
|
601
|
+
def remove_records(records)
|
602
|
+
super
|
603
|
+
end
|
604
|
+
|
605
|
+
# Optional override to change the attributes of the record before they
|
606
|
+
# are saved.
|
607
|
+
def record_attributes(payload)
|
608
|
+
super.merge(:some_field => 'some_value')
|
609
|
+
end
|
610
|
+
end
|
611
|
+
```
|
612
|
+
|
613
|
+
## Database Poller
|
614
|
+
|
615
|
+
Another method of fetching updates from the database to Kafka is by polling
|
616
|
+
the database (a process popularized by [Kafka Connect](https://docs.confluent.io/current/connect/index.html)).
|
617
|
+
Deimos provides a database poller, which allows you the same pattern but
|
618
|
+
with all the flexibility of real Ruby code, and the added advantage of having
|
619
|
+
a single consistent framework to talk to Kafka.
|
620
|
+
|
621
|
+
One of the disadvantages of polling the database is that it can't detect deletions.
|
622
|
+
You can get over this by configuring a mixin to send messages *only* on deletion,
|
623
|
+
and use the poller to handle all other updates. You can reuse the same producer
|
624
|
+
for both cases to handle joins, changes/mappings, business logic, etc.
|
625
|
+
|
626
|
+
To enable the poller, generate the migration:
|
627
|
+
|
628
|
+
```ruby
|
629
|
+
rails g deimos:db_poller
|
630
|
+
```
|
631
|
+
|
632
|
+
Run the migration:
|
633
|
+
|
634
|
+
```ruby
|
635
|
+
rails db:migrate
|
636
|
+
```
|
637
|
+
|
638
|
+
Add the following configuration:
|
639
|
+
|
640
|
+
```ruby
|
641
|
+
Deimos.configure do
|
642
|
+
db_poller do
|
643
|
+
producer_class 'MyProducer' # an ActiveRecordProducer
|
644
|
+
end
|
645
|
+
db_poller do
|
646
|
+
producer_class 'MyOtherProducer'
|
647
|
+
run_every 2.minutes
|
648
|
+
delay 5.seconds # to allow for transactions to finish
|
649
|
+
full_table true # if set, dump the entire table every run; use for small tables
|
650
|
+
end
|
651
|
+
end
|
652
|
+
```
|
653
|
+
|
654
|
+
All the information around connecting and querying the database lives in the
|
655
|
+
producer itself, so you don't need to write any additional code. You can
|
656
|
+
define one additional method on the producer:
|
657
|
+
|
658
|
+
```ruby
|
659
|
+
class MyProducer < Deimos::ActiveRecordProducer
|
660
|
+
...
|
661
|
+
def poll_query(time_from:, time_to:, column_name:, min_id:)
|
662
|
+
# Default is to use the timestamp `column_name` to find all records
|
663
|
+
# between time_from and time_to, or records where `updated_at` is equal to
|
664
|
+
# `time_from` but its ID is greater than `min_id`. This is called
|
665
|
+
# successively as the DB is polled to ensure even if a batch ends in the
|
666
|
+
# middle of a timestamp, we won't miss any records.
|
667
|
+
# You can override or change this behavior if necessary.
|
668
|
+
end
|
669
|
+
end
|
670
|
+
```
|
671
|
+
|
672
|
+
To run the DB poller:
|
673
|
+
|
674
|
+
rake deimos:db_poller
|
675
|
+
|
676
|
+
Note that the DB poller creates one thread per configured poller, and is
|
677
|
+
currently designed *not* to be scaled out - i.e. it assumes you will only
|
678
|
+
have one process running at a time. If a particular poll takes longer than
|
679
|
+
the poll interval (i.e. interval is set at 1 minute but it takes 75 seconds)
|
680
|
+
the next poll will begin immediately following the first one completing.
|
681
|
+
|
560
682
|
## Running consumers
|
561
683
|
|
562
684
|
Deimos includes a rake task. Once it's in your gemfile, just run
|
@@ -783,6 +905,9 @@ Deimos::Utils::InlineConsumer.get_messages_for(
|
|
783
905
|
|
784
906
|
Bug reports and pull requests are welcome on GitHub at https://github.com/flipp-oss/deimos .
|
785
907
|
|
908
|
+
We have more information on the [internal architecture](docs/ARCHITECTURE.md) of Deimos
|
909
|
+
for contributors!
|
910
|
+
|
786
911
|
### Linting
|
787
912
|
|
788
913
|
Deimos uses Rubocop to lint the code. Please run Rubocop on your code
|