deimos-ruby 1.6.2 → 1.8.0.pre.beta2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (65) hide show
  1. checksums.yaml +4 -4
  2. data/.circleci/config.yml +9 -0
  3. data/.rubocop.yml +15 -13
  4. data/.ruby-version +1 -1
  5. data/CHANGELOG.md +31 -0
  6. data/Gemfile.lock +43 -36
  7. data/README.md +141 -16
  8. data/Rakefile +1 -1
  9. data/deimos-ruby.gemspec +2 -1
  10. data/docs/ARCHITECTURE.md +144 -0
  11. data/docs/CONFIGURATION.md +27 -0
  12. data/lib/deimos.rb +7 -6
  13. data/lib/deimos/active_record_consume/batch_consumption.rb +159 -0
  14. data/lib/deimos/active_record_consume/batch_slicer.rb +27 -0
  15. data/lib/deimos/active_record_consume/message_consumption.rb +58 -0
  16. data/lib/deimos/active_record_consume/schema_model_converter.rb +52 -0
  17. data/lib/deimos/active_record_consumer.rb +33 -75
  18. data/lib/deimos/active_record_producer.rb +23 -0
  19. data/lib/deimos/batch_consumer.rb +2 -140
  20. data/lib/deimos/config/configuration.rb +28 -10
  21. data/lib/deimos/consume/batch_consumption.rb +150 -0
  22. data/lib/deimos/consume/message_consumption.rb +94 -0
  23. data/lib/deimos/consumer.rb +79 -69
  24. data/lib/deimos/kafka_message.rb +1 -1
  25. data/lib/deimos/kafka_topic_info.rb +1 -1
  26. data/lib/deimos/message.rb +6 -1
  27. data/lib/deimos/metrics/provider.rb +0 -2
  28. data/lib/deimos/poll_info.rb +9 -0
  29. data/lib/deimos/tracing/provider.rb +0 -2
  30. data/lib/deimos/utils/db_poller.rb +149 -0
  31. data/lib/deimos/utils/db_producer.rb +8 -3
  32. data/lib/deimos/utils/deadlock_retry.rb +68 -0
  33. data/lib/deimos/utils/lag_reporter.rb +19 -26
  34. data/lib/deimos/version.rb +1 -1
  35. data/lib/generators/deimos/db_poller/templates/migration +11 -0
  36. data/lib/generators/deimos/db_poller/templates/rails3_migration +16 -0
  37. data/lib/generators/deimos/db_poller_generator.rb +48 -0
  38. data/lib/tasks/deimos.rake +7 -0
  39. data/spec/active_record_batch_consumer_spec.rb +481 -0
  40. data/spec/active_record_consume/batch_slicer_spec.rb +42 -0
  41. data/spec/active_record_consume/schema_model_converter_spec.rb +105 -0
  42. data/spec/active_record_consumer_spec.rb +3 -11
  43. data/spec/active_record_producer_spec.rb +66 -88
  44. data/spec/batch_consumer_spec.rb +24 -7
  45. data/spec/config/configuration_spec.rb +4 -0
  46. data/spec/consumer_spec.rb +8 -8
  47. data/spec/deimos_spec.rb +57 -49
  48. data/spec/handlers/my_batch_consumer.rb +6 -1
  49. data/spec/handlers/my_consumer.rb +6 -1
  50. data/spec/message_spec.rb +19 -0
  51. data/spec/producer_spec.rb +3 -3
  52. data/spec/rake_spec.rb +1 -1
  53. data/spec/schemas/com/my-namespace/MySchemaCompound-key.avsc +18 -0
  54. data/spec/schemas/com/my-namespace/Wibble.avsc +43 -0
  55. data/spec/spec_helper.rb +61 -6
  56. data/spec/utils/db_poller_spec.rb +320 -0
  57. data/spec/utils/deadlock_retry_spec.rb +74 -0
  58. data/spec/utils/lag_reporter_spec.rb +29 -22
  59. metadata +55 -20
  60. data/lib/deimos/base_consumer.rb +0 -104
  61. data/lib/deimos/utils/executor.rb +0 -124
  62. data/lib/deimos/utils/platform_schema_validation.rb +0 -0
  63. data/lib/deimos/utils/signal_handler.rb +0 -68
  64. data/spec/utils/executor_spec.rb +0 -53
  65. data/spec/utils/signal_handler_spec.rb +0 -16
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 53aab4ddcc19808cf7149ed226de9b7ddda07d12ed4237c157cce75780379c6c
4
- data.tar.gz: 74ebf879894b88fc4ce5082d4f8b82f8f79a0ef581bf368f25423a755d71d635
3
+ metadata.gz: 5c184b8f182ecfa304684f4e8e522e93c43c12fe50b34300326c2e1ef206b6d1
4
+ data.tar.gz: 5861974e036c92470a4a801bed477e5cebefa679cc9996a5d6f0258eaa484150
5
5
  SHA512:
6
- metadata.gz: 4c0fa67f0b958ddc3083c278c5c195f896b134f822168df245e906e89f5f5414f063175e2ed1a444891a210d28e7a0d96836806b6ba2efd6f55bf46d929450c1
7
- data.tar.gz: 9fc31899551ec75dc0ca1e7ac02cc3f4ed429ec8eb25f42057024d2dc9cde5bef70eabafcf8276e7a8a4ae2b7a88b4efff5efe047715612bd5f5480ce1f52576
6
+ metadata.gz: f19a799d333485b6461e2d8cdeaec5643ec6ddf8f6a2ce1725f3c39269cc193cc5b1bc1e81266b6a33af87ec5d09b3ac2f857b48da61c3baded00a98d6935624
7
+ data.tar.gz: a08f6519a635e76944b3b7c2f08d5369b6f327e60e968391fcd5e09e703e8ed526d35d8e124f91cbff8c885fbf8d620f90e0a99b48e4289868a0c11925b43d51
@@ -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
@@ -1,10 +1,11 @@
1
1
  require: rubocop-rspec
2
2
 
3
3
  AllCops:
4
- TargetRubyVersion: 2.3
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
 
@@ -1 +1 @@
1
- 2.5.1
1
+ 2.5.3
@@ -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
@@ -1,10 +1,11 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- deimos-ruby (1.6.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.3)
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.1)
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.5)
65
- concurrent-ruby-ext (1.1.5)
66
- concurrent-ruby (= 1.1.5)
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
- ddtrace (0.30.0)
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.5.0)
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.11.3)
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.1)
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.0.13)
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.9.2)
116
- mimemagic (0.3.4)
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.1)
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.6.5.0)
133
+ parser (2.7.1.2)
132
134
  ast (~> 2.4.0)
133
- pg (1.1.4)
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.12.2)
143
- coderay (~> 1.1.0)
144
- method_source (~> 0.9.0)
145
- rack (2.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.3)
175
- rb-inotify (0.10.0)
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.0)
182
- rspec-support (~> 3.9.0)
183
- rspec-expectations (3.9.0)
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.0)
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.0)
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.77.0)
195
+ rubocop (0.82.0)
193
196
  jaro_winkler (~> 1.5.1)
194
197
  parallel (~> 1.10)
195
- parser (>= 2.6)
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, < 1.7)
199
- rubocop-rspec (1.37.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.1)
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.6.0)
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.4)
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
- bundler (~> 1)
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.17.3
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 is set to `inline_batch`. For example:
317
+ To enable batch consumption, ensure that the `delivery` property of your
318
+ consumer is set to `inline_batch`.
317
319
 
318
- ```ruby
319
- Deimos.configure do
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::BatchConsumer
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 and :topic.
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