deimos-ruby 1.6.1 → 1.8.0.pre.beta1

Sign up to get free protection for your applications and to get access to all the features.
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