deimos-ruby 1.6.1 → 1.8.0.pre.beta1

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.
Files changed (67) 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 +30 -0
  6. data/Gemfile.lock +87 -80
  7. data/README.md +139 -15
  8. data/Rakefile +1 -1
  9. data/deimos-ruby.gemspec +3 -2
  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 +148 -0
  22. data/lib/deimos/consume/message_consumption.rb +93 -0
  23. data/lib/deimos/consumer.rb +79 -69
  24. data/lib/deimos/kafka_message.rb +1 -1
  25. data/lib/deimos/kafka_source.rb +29 -23
  26. data/lib/deimos/kafka_topic_info.rb +1 -1
  27. data/lib/deimos/message.rb +6 -1
  28. data/lib/deimos/metrics/provider.rb +0 -2
  29. data/lib/deimos/poll_info.rb +9 -0
  30. data/lib/deimos/tracing/provider.rb +0 -2
  31. data/lib/deimos/utils/db_poller.rb +149 -0
  32. data/lib/deimos/utils/db_producer.rb +8 -3
  33. data/lib/deimos/utils/deadlock_retry.rb +68 -0
  34. data/lib/deimos/utils/lag_reporter.rb +19 -26
  35. data/lib/deimos/version.rb +1 -1
  36. data/lib/generators/deimos/db_poller/templates/migration +11 -0
  37. data/lib/generators/deimos/db_poller/templates/rails3_migration +16 -0
  38. data/lib/generators/deimos/db_poller_generator.rb +48 -0
  39. data/lib/tasks/deimos.rake +7 -0
  40. data/spec/active_record_batch_consumer_spec.rb +481 -0
  41. data/spec/active_record_consume/batch_slicer_spec.rb +42 -0
  42. data/spec/active_record_consume/schema_model_converter_spec.rb +105 -0
  43. data/spec/active_record_consumer_spec.rb +22 -11
  44. data/spec/active_record_producer_spec.rb +66 -88
  45. data/spec/batch_consumer_spec.rb +23 -7
  46. data/spec/config/configuration_spec.rb +4 -0
  47. data/spec/consumer_spec.rb +8 -8
  48. data/spec/deimos_spec.rb +57 -49
  49. data/spec/handlers/my_batch_consumer.rb +6 -1
  50. data/spec/handlers/my_consumer.rb +6 -1
  51. data/spec/kafka_source_spec.rb +53 -0
  52. data/spec/message_spec.rb +19 -0
  53. data/spec/producer_spec.rb +3 -3
  54. data/spec/rake_spec.rb +1 -1
  55. data/spec/schemas/com/my-namespace/MySchemaCompound-key.avsc +18 -0
  56. data/spec/schemas/com/my-namespace/Wibble.avsc +43 -0
  57. data/spec/spec_helper.rb +61 -6
  58. data/spec/utils/db_poller_spec.rb +320 -0
  59. data/spec/utils/deadlock_retry_spec.rb +74 -0
  60. data/spec/utils/lag_reporter_spec.rb +29 -22
  61. metadata +61 -20
  62. data/lib/deimos/base_consumer.rb +0 -104
  63. data/lib/deimos/utils/executor.rb +0 -124
  64. data/lib/deimos/utils/platform_schema_validation.rb +0 -0
  65. data/lib/deimos/utils/signal_handler.rb +0 -68
  66. data/spec/utils/executor_spec.rb +0 -53
  67. data/spec/utils/signal_handler_spec.rb +0 -16
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.6.1
4
+ version: 1.8.0.pre.beta1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Daniel Orner
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-04-20 00:00:00.000000000 Z
11
+ date: 2020-07-06 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: avro_turf
@@ -52,6 +52,20 @@ dependencies:
52
52
  - - "~>"
53
53
  - !ruby/object:Gem::Version
54
54
  version: '0.7'
55
+ - !ruby/object:Gem::Dependency
56
+ name: sigurd
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - '='
60
+ - !ruby/object:Gem::Version
61
+ version: 0.0.1
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - '='
67
+ - !ruby/object:Gem::Version
68
+ version: 0.0.1
55
69
  - !ruby/object:Gem::Dependency
56
70
  name: activerecord
57
71
  requirement: !ruby/object:Gem::Requirement
@@ -95,19 +109,19 @@ dependencies:
95
109
  - !ruby/object:Gem::Version
96
110
  version: '1.9'
97
111
  - !ruby/object:Gem::Dependency
98
- name: bundler
112
+ name: database_cleaner
99
113
  requirement: !ruby/object:Gem::Requirement
100
114
  requirements:
101
115
  - - "~>"
102
116
  - !ruby/object:Gem::Version
103
- version: '1'
117
+ version: '1.7'
104
118
  type: :development
105
119
  prerelease: false
106
120
  version_requirements: !ruby/object:Gem::Requirement
107
121
  requirements:
108
122
  - - "~>"
109
123
  - !ruby/object:Gem::Version
110
- version: '1'
124
+ version: '1.7'
111
125
  - !ruby/object:Gem::Dependency
112
126
  name: ddtrace
113
127
  requirement: !ruby/object:Gem::Requirement
@@ -213,6 +227,9 @@ dependencies:
213
227
  - - "~>"
214
228
  - !ruby/object:Gem::Version
215
229
  version: '5.2'
230
+ - - ">="
231
+ - !ruby/object:Gem::Version
232
+ version: 5.2.4.2
216
233
  type: :development
217
234
  prerelease: false
218
235
  version_requirements: !ruby/object:Gem::Requirement
@@ -220,6 +237,9 @@ dependencies:
220
237
  - - "~>"
221
238
  - !ruby/object:Gem::Version
222
239
  version: '5.2'
240
+ - - ">="
241
+ - !ruby/object:Gem::Version
242
+ version: 5.2.4.2
223
243
  - !ruby/object:Gem::Dependency
224
244
  name: rake
225
245
  requirement: !ruby/object:Gem::Requirement
@@ -304,7 +324,7 @@ dependencies:
304
324
  - - "~>"
305
325
  - !ruby/object:Gem::Version
306
326
  version: '1.3'
307
- description:
327
+ description:
308
328
  email:
309
329
  - daniel.orner@wishabi.com
310
330
  executables:
@@ -331,10 +351,15 @@ files:
331
351
  - bin/deimos
332
352
  - deimos-ruby.gemspec
333
353
  - docker-compose.yml
354
+ - docs/ARCHITECTURE.md
334
355
  - docs/CONFIGURATION.md
335
356
  - docs/DATABASE_BACKEND.md
336
357
  - docs/PULL_REQUEST_TEMPLATE.md
337
358
  - lib/deimos.rb
359
+ - lib/deimos/active_record_consume/batch_consumption.rb
360
+ - lib/deimos/active_record_consume/batch_slicer.rb
361
+ - lib/deimos/active_record_consume/message_consumption.rb
362
+ - lib/deimos/active_record_consume/schema_model_converter.rb
338
363
  - lib/deimos/active_record_consumer.rb
339
364
  - lib/deimos/active_record_producer.rb
340
365
  - lib/deimos/backends/base.rb
@@ -342,11 +367,12 @@ files:
342
367
  - lib/deimos/backends/kafka.rb
343
368
  - lib/deimos/backends/kafka_async.rb
344
369
  - lib/deimos/backends/test.rb
345
- - lib/deimos/base_consumer.rb
346
370
  - lib/deimos/batch_consumer.rb
347
371
  - lib/deimos/config/configurable.rb
348
372
  - lib/deimos/config/configuration.rb
349
373
  - lib/deimos/config/phobos_config.rb
374
+ - lib/deimos/consume/batch_consumption.rb
375
+ - lib/deimos/consume/message_consumption.rb
350
376
  - lib/deimos/consumer.rb
351
377
  - lib/deimos/instrumentation.rb
352
378
  - lib/deimos/kafka_message.rb
@@ -359,6 +385,7 @@ files:
359
385
  - lib/deimos/monkey_patches/phobos_cli.rb
360
386
  - lib/deimos/monkey_patches/phobos_producer.rb
361
387
  - lib/deimos/monkey_patches/ruby_kafka_heartbeat.rb
388
+ - lib/deimos/poll_info.rb
362
389
  - lib/deimos/producer.rb
363
390
  - lib/deimos/railtie.rb
364
391
  - lib/deimos/schema_backends/avro_base.rb
@@ -373,17 +400,22 @@ files:
373
400
  - lib/deimos/tracing/datadog.rb
374
401
  - lib/deimos/tracing/mock.rb
375
402
  - lib/deimos/tracing/provider.rb
403
+ - lib/deimos/utils/db_poller.rb
376
404
  - lib/deimos/utils/db_producer.rb
377
- - lib/deimos/utils/executor.rb
405
+ - lib/deimos/utils/deadlock_retry.rb
378
406
  - lib/deimos/utils/inline_consumer.rb
379
407
  - lib/deimos/utils/lag_reporter.rb
380
- - lib/deimos/utils/platform_schema_validation.rb
381
- - lib/deimos/utils/signal_handler.rb
382
408
  - lib/deimos/version.rb
383
409
  - lib/generators/deimos/db_backend/templates/migration
384
410
  - lib/generators/deimos/db_backend/templates/rails3_migration
385
411
  - lib/generators/deimos/db_backend_generator.rb
412
+ - lib/generators/deimos/db_poller/templates/migration
413
+ - lib/generators/deimos/db_poller/templates/rails3_migration
414
+ - lib/generators/deimos/db_poller_generator.rb
386
415
  - lib/tasks/deimos.rake
416
+ - spec/active_record_batch_consumer_spec.rb
417
+ - spec/active_record_consume/batch_slicer_spec.rb
418
+ - spec/active_record_consume/schema_model_converter_spec.rb
387
419
  - spec/active_record_consumer_spec.rb
388
420
  - spec/active_record_producer_spec.rb
389
421
  - spec/backends/base_spec.rb
@@ -399,6 +431,7 @@ files:
399
431
  - spec/handlers/my_consumer.rb
400
432
  - spec/kafka_source_spec.rb
401
433
  - spec/kafka_topic_info_spec.rb
434
+ - spec/message_spec.rb
402
435
  - spec/phobos.bad_db.yml
403
436
  - spec/phobos.yml
404
437
  - spec/producer_spec.rb
@@ -410,18 +443,20 @@ files:
410
443
  - spec/schema_backends/base_spec.rb
411
444
  - spec/schemas/com/my-namespace/MySchema-key.avsc
412
445
  - spec/schemas/com/my-namespace/MySchema.avsc
446
+ - spec/schemas/com/my-namespace/MySchemaCompound-key.avsc
413
447
  - spec/schemas/com/my-namespace/MySchemaWithBooleans.avsc
414
448
  - spec/schemas/com/my-namespace/MySchemaWithDateTimes.avsc
415
449
  - spec/schemas/com/my-namespace/MySchemaWithId.avsc
416
450
  - spec/schemas/com/my-namespace/MySchemaWithUniqueId.avsc
451
+ - spec/schemas/com/my-namespace/Wibble.avsc
417
452
  - spec/schemas/com/my-namespace/Widget.avsc
418
453
  - spec/schemas/com/my-namespace/WidgetTheSecond.avsc
419
454
  - spec/spec_helper.rb
455
+ - spec/utils/db_poller_spec.rb
420
456
  - spec/utils/db_producer_spec.rb
421
- - spec/utils/executor_spec.rb
457
+ - spec/utils/deadlock_retry_spec.rb
422
458
  - spec/utils/lag_reporter_spec.rb
423
459
  - spec/utils/platform_schema_validation_spec.rb
424
- - spec/utils/signal_handler_spec.rb
425
460
  - support/deimos-solo.png
426
461
  - support/deimos-with-name-next.png
427
462
  - support/deimos-with-name.png
@@ -430,7 +465,7 @@ homepage: ''
430
465
  licenses:
431
466
  - Apache-2.0
432
467
  metadata: {}
433
- post_install_message:
468
+ post_install_message:
434
469
  rdoc_options: []
435
470
  require_paths:
436
471
  - lib
@@ -441,15 +476,18 @@ required_ruby_version: !ruby/object:Gem::Requirement
441
476
  version: '0'
442
477
  required_rubygems_version: !ruby/object:Gem::Requirement
443
478
  requirements:
444
- - - ">="
479
+ - - ">"
445
480
  - !ruby/object:Gem::Version
446
- version: '0'
481
+ version: 1.3.1
447
482
  requirements: []
448
- rubygems_version: 3.1.2
449
- signing_key:
483
+ rubygems_version: 3.1.3
484
+ signing_key:
450
485
  specification_version: 4
451
486
  summary: Kafka libraries for Ruby.
452
487
  test_files:
488
+ - spec/active_record_batch_consumer_spec.rb
489
+ - spec/active_record_consume/batch_slicer_spec.rb
490
+ - spec/active_record_consume/schema_model_converter_spec.rb
453
491
  - spec/active_record_consumer_spec.rb
454
492
  - spec/active_record_producer_spec.rb
455
493
  - spec/backends/base_spec.rb
@@ -465,6 +503,7 @@ test_files:
465
503
  - spec/handlers/my_consumer.rb
466
504
  - spec/kafka_source_spec.rb
467
505
  - spec/kafka_topic_info_spec.rb
506
+ - spec/message_spec.rb
468
507
  - spec/phobos.bad_db.yml
469
508
  - spec/phobos.yml
470
509
  - spec/producer_spec.rb
@@ -476,15 +515,17 @@ test_files:
476
515
  - spec/schema_backends/base_spec.rb
477
516
  - spec/schemas/com/my-namespace/MySchema-key.avsc
478
517
  - spec/schemas/com/my-namespace/MySchema.avsc
518
+ - spec/schemas/com/my-namespace/MySchemaCompound-key.avsc
479
519
  - spec/schemas/com/my-namespace/MySchemaWithBooleans.avsc
480
520
  - spec/schemas/com/my-namespace/MySchemaWithDateTimes.avsc
481
521
  - spec/schemas/com/my-namespace/MySchemaWithId.avsc
482
522
  - spec/schemas/com/my-namespace/MySchemaWithUniqueId.avsc
523
+ - spec/schemas/com/my-namespace/Wibble.avsc
483
524
  - spec/schemas/com/my-namespace/Widget.avsc
484
525
  - spec/schemas/com/my-namespace/WidgetTheSecond.avsc
485
526
  - spec/spec_helper.rb
527
+ - spec/utils/db_poller_spec.rb
486
528
  - spec/utils/db_producer_spec.rb
487
- - spec/utils/executor_spec.rb
529
+ - spec/utils/deadlock_retry_spec.rb
488
530
  - spec/utils/lag_reporter_spec.rb
489
531
  - spec/utils/platform_schema_validation_spec.rb
490
- - spec/utils/signal_handler_spec.rb
@@ -1,104 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Deimos
4
- # Shared methods for Kafka Consumers
5
- class BaseConsumer
6
- include SharedConfig
7
-
8
- class << self
9
- # @return [Deimos::SchemaBackends::Base]
10
- def decoder
11
- @decoder ||= Deimos.schema_backend(schema: config[:schema],
12
- namespace: config[:namespace])
13
- end
14
-
15
- # @return [Deimos::SchemaBackends::Base]
16
- def key_decoder
17
- @key_decoder ||= Deimos.schema_backend(schema: config[:key_schema],
18
- namespace: config[:namespace])
19
- end
20
- end
21
-
22
- # Helper method to decode an encoded key.
23
- # @param key [String]
24
- # @return [Object] the decoded key.
25
- def decode_key(key)
26
- return nil if key.nil?
27
-
28
- config = self.class.config
29
- unless config[:key_configured]
30
- raise 'No key config given - if you are not decoding keys, please use '\
31
- '`key_config plain: true`'
32
- end
33
-
34
- if config[:key_field]
35
- self.class.decoder.decode_key(key, config[:key_field])
36
- elsif config[:key_schema]
37
- self.class.key_decoder.decode(key, schema: config[:key_schema])
38
- else # no encoding
39
- key
40
- end
41
- end
42
-
43
- protected
44
-
45
- # @param payload [Hash|String]
46
- # @param metadata [Hash]
47
- def _with_error_span(payload, metadata)
48
- @span = Deimos.config.tracer&.start(
49
- 'deimos-consumer',
50
- resource: self.class.name.gsub('::', '-')
51
- )
52
- yield
53
- rescue StandardError => e
54
- _handle_error(e, payload, metadata)
55
- ensure
56
- Deimos.config.tracer&.finish(@span)
57
- end
58
-
59
- def _report_time_delayed(payload, metadata)
60
- return if payload.nil? || payload['timestamp'].blank?
61
-
62
- begin
63
- time_delayed = Time.now.in_time_zone - payload['timestamp'].to_datetime
64
- rescue ArgumentError
65
- Deimos.config.logger.info(
66
- message: "Error parsing timestamp! #{payload['timestamp']}"
67
- )
68
- return
69
- end
70
- Deimos.config.metrics&.histogram('handler', time_delayed, tags: %W(
71
- time:time_delayed
72
- topic:#{metadata[:topic]}
73
- ))
74
- end
75
-
76
- # Overrideable method to determine if a given error should be considered
77
- # "fatal" and always be reraised.
78
- # @param error [Exception]
79
- # @param payload [Hash]
80
- # @param metadata [Hash]
81
- # @return [Boolean]
82
- def fatal_error?(_error, _payload, _metadata)
83
- false
84
- end
85
-
86
- # @param exception [Exception]
87
- # @param payload [Hash]
88
- # @param metadata [Hash]
89
- def _handle_error(exception, payload, metadata)
90
- Deimos.config.tracer&.set_error(@span, exception)
91
-
92
- raise if Deimos.config.consumers.reraise_errors ||
93
- Deimos.config.consumers.fatal_error&.call(exception, payload, metadata) ||
94
- fatal_error?(exception, payload, metadata)
95
- end
96
-
97
- # @param _time_taken [Float]
98
- # @param _payload [Hash]
99
- # @param _metadata [Hash]
100
- def _handle_success(_time_taken, _payload, _metadata)
101
- raise NotImplementedError
102
- end
103
- end
104
- end
@@ -1,124 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # rubocop:disable Lint/RescueException
4
- module Deimos
5
- module Utils
6
- # Mostly copied from Phobos::Executor. We should DRY this up by putting in a
7
- # PR to make it more generic. Might even make sense to move to a separate
8
- # gem.
9
- class Executor
10
- # @return [Array<#start, #stop, #id>]
11
- attr_accessor :runners
12
-
13
- # @param runners [Array<#start, #stop, #id>] A list of objects that can be
14
- # started or stopped.
15
- # @param logger [Logger]
16
- # @param sleep_seconds [Integer] Use a fixed time to sleep between
17
- # failed runs instead of using an exponential backoff.
18
- def initialize(runners, sleep_seconds: nil, logger: Logger.new(STDOUT))
19
- @threads = Concurrent::Array.new
20
- @runners = runners
21
- @logger = logger
22
- @sleep_seconds = sleep_seconds
23
- end
24
-
25
- # Start the executor.
26
- def start
27
- @logger.info('Starting executor')
28
- @signal_to_stop = false
29
- @threads.clear
30
- @thread_pool = Concurrent::FixedThreadPool.new(@runners.size)
31
-
32
- @runners.each do |runner|
33
- @thread_pool.post do
34
- thread = Thread.current
35
- thread.abort_on_exception = true
36
- @threads << thread
37
- run_object(runner)
38
- end
39
- end
40
-
41
- true
42
- end
43
-
44
- # Stop the executor.
45
- def stop
46
- return if @signal_to_stop
47
-
48
- @logger.info('Stopping executor')
49
- @signal_to_stop = true
50
- @runners.each(&:stop)
51
- @threads.select(&:alive?).each do |thread|
52
- begin
53
- thread.wakeup
54
- rescue StandardError
55
- nil
56
- end
57
- end
58
- @thread_pool&.shutdown
59
- @thread_pool&.wait_for_termination
60
- @logger.info('Executor stopped')
61
- end
62
-
63
- private
64
-
65
- # @param exception [Throwable]
66
- # @return [Hash]
67
- def error_metadata(exception)
68
- {
69
- exception_class: exception.class.name,
70
- exception_message: exception.message,
71
- backtrace: exception.backtrace
72
- }
73
- end
74
-
75
- def run_object(runner)
76
- retry_count = 0
77
-
78
- begin
79
- @logger.info("Running #{runner.id}")
80
- runner.start
81
- retry_count = 0 # success - reset retry count
82
- rescue Exception => e
83
- handle_crashed_runner(runner, e, retry_count)
84
- retry_count += 1
85
- retry unless @signal_to_stop
86
- end
87
- rescue Exception => e
88
- @logger.error("Failed to run listener (#{e.message}) #{error_metadata(e)}")
89
- raise e
90
- end
91
-
92
- # @return [ExponentialBackoff]
93
- def create_exponential_backoff
94
- min = 1
95
- max = 60
96
- ExponentialBackoff.new(min, max).tap do |backoff|
97
- backoff.randomize_factor = rand
98
- end
99
- end
100
-
101
- # When "runner#start" is interrupted / crashes we assume it's
102
- # safe to be called again
103
- def handle_crashed_runner(runner, error, retry_count)
104
- interval = if @sleep_seconds
105
- @sleep_seconds
106
- else
107
- backoff = create_exponential_backoff
108
- backoff.interval_at(retry_count).round(2)
109
- end
110
-
111
- metadata = {
112
- listener_id: runner.id,
113
- retry_count: retry_count,
114
- waiting_time: interval
115
- }.merge(error_metadata(error))
116
-
117
- @logger.error("Runner crashed, waiting #{interval}s (#{error.message}) #{metadata}")
118
- sleep(interval)
119
- end
120
- end
121
- end
122
- end
123
-
124
- # rubocop:enable Lint/RescueException