deimos-ruby 1.16.3 → 1.16.5
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/.github/workflows/ci.yml +3 -3
- data/.gitignore +1 -0
- data/.rubocop.yml +20 -14
- data/.rubocop_todo.yml +364 -0
- data/.ruby-version +2 -1
- data/CHANGELOG.md +9 -0
- data/Gemfile +6 -0
- data/README.md +7 -1
- data/Steepfile +6 -0
- data/deimos-ruby.gemspec +3 -2
- data/lib/deimos/active_record_consume/batch_consumption.rb +7 -2
- data/lib/deimos/active_record_consume/batch_slicer.rb +2 -0
- data/lib/deimos/active_record_consume/message_consumption.rb +8 -4
- data/lib/deimos/active_record_consumer.rb +7 -4
- data/lib/deimos/active_record_producer.rb +3 -0
- data/lib/deimos/backends/base.rb +4 -2
- data/lib/deimos/backends/kafka.rb +1 -0
- data/lib/deimos/backends/kafka_async.rb +1 -0
- data/lib/deimos/config/configuration.rb +4 -0
- data/lib/deimos/config/phobos_config.rb +2 -1
- data/lib/deimos/consume/batch_consumption.rb +8 -1
- data/lib/deimos/consume/message_consumption.rb +4 -1
- data/lib/deimos/instrumentation.rb +11 -4
- data/lib/deimos/kafka_message.rb +1 -0
- data/lib/deimos/kafka_source.rb +5 -0
- data/lib/deimos/kafka_topic_info.rb +4 -0
- data/lib/deimos/message.rb +19 -2
- data/lib/deimos/metrics/datadog.rb +2 -1
- data/lib/deimos/metrics/mock.rb +2 -2
- data/lib/deimos/metrics/provider.rb +6 -0
- data/lib/deimos/monkey_patches/phobos_cli.rb +1 -1
- data/lib/deimos/monkey_patches/phobos_producer.rb +1 -0
- data/lib/deimos/producer.rb +12 -6
- data/lib/deimos/schema_backends/base.rb +31 -17
- data/lib/deimos/schema_backends/mock.rb +2 -2
- data/lib/deimos/schema_class/base.rb +9 -5
- data/lib/deimos/schema_class/enum.rb +4 -2
- data/lib/deimos/schema_class/record.rb +5 -5
- data/lib/deimos/shared_config.rb +6 -2
- data/lib/deimos/test_helpers.rb +21 -4
- data/lib/deimos/tracing/datadog.rb +1 -1
- data/lib/deimos/tracing/mock.rb +4 -3
- data/lib/deimos/tracing/provider.rb +5 -0
- data/lib/deimos/utils/db_poller.rb +9 -1
- data/lib/deimos/utils/db_producer.rb +14 -2
- data/lib/deimos/utils/deadlock_retry.rb +3 -0
- data/lib/deimos/utils/inline_consumer.rb +14 -6
- data/lib/deimos/utils/lag_reporter.rb +11 -0
- data/lib/deimos/utils/schema_controller_mixin.rb +8 -0
- data/lib/deimos/version.rb +1 -1
- data/lib/deimos.rb +3 -2
- data/lib/generators/deimos/active_record_generator.rb +1 -1
- data/lib/generators/deimos/db_backend_generator.rb +1 -0
- data/lib/generators/deimos/db_poller_generator.rb +1 -0
- data/lib/generators/deimos/schema_class/templates/schema_record.rb.tt +1 -1
- data/lib/generators/deimos/schema_class_generator.rb +13 -4
- data/rbs_collection.lock.yaml +176 -0
- data/rbs_collection.yaml +15 -0
- data/sig/avro.rbs +14 -0
- data/sig/defs.rbs +1867 -0
- data/sig/fig_tree.rbs +2 -0
- data/spec/consumer_spec.rb +14 -14
- data/spec/generators/schema_class/my_schema_spec.rb +3 -3
- data/spec/generators/schema_class/my_schema_with_complex_types_spec.rb +1 -1
- data/spec/producer_spec.rb +1 -1
- data/spec/schemas/my_namespace/my_schema_with_complex_type.rb +3 -3
- data/spec/snapshots/consumers-no-nest.snap +1 -1
- data/spec/snapshots/consumers.snap +1 -1
- data/spec/snapshots/consumers_and_producers-no-nest.snap +3 -3
- data/spec/snapshots/consumers_and_producers.snap +3 -3
- data/spec/snapshots/consumers_circular-no-nest.snap +1 -1
- data/spec/snapshots/consumers_circular.snap +1 -1
- data/spec/snapshots/consumers_complex_types-no-nest.snap +1 -1
- data/spec/snapshots/consumers_complex_types.snap +1 -1
- data/spec/snapshots/consumers_nested-no-nest.snap +1 -1
- data/spec/snapshots/consumers_nested.snap +1 -1
- data/spec/snapshots/namespace_folders.snap +3 -3
- data/spec/snapshots/producers_with_key-no-nest.snap +1 -1
- data/spec/snapshots/producers_with_key.snap +1 -1
- metadata +39 -21
- data/.gemfiles/avro_turf-0.gemfile +0 -3
- data/.gemfiles/avro_turf-1.gemfile +0 -3
- data/.ruby-gemset +0 -1
@@ -7,23 +7,26 @@ module Deimos
|
|
7
7
|
# Base Class for Schema Classes generated from Avro.
|
8
8
|
class Base
|
9
9
|
|
10
|
-
#
|
10
|
+
# @param _args [Array<Object>]
|
11
11
|
def initialize(*_args)
|
12
12
|
end
|
13
13
|
|
14
14
|
# Converts the object to a hash which can be used for debugging or comparing objects.
|
15
|
+
# @param _opts [Hash]
|
15
16
|
# @return [Hash] a hash representation of the payload
|
16
17
|
def as_json(_opts={})
|
17
18
|
raise NotImplementedError
|
18
19
|
end
|
19
20
|
|
20
|
-
# @param key [String
|
21
|
+
# @param key [String,Symbol]
|
21
22
|
# @param val [Object]
|
23
|
+
# @return [void]
|
22
24
|
def []=(key, val)
|
23
25
|
self.send("#{key}=", val)
|
24
26
|
end
|
25
27
|
|
26
|
-
#
|
28
|
+
# @param other [SchemaClass::Base]
|
29
|
+
# @return [Boolean]
|
27
30
|
def ==(other)
|
28
31
|
comparison = other
|
29
32
|
if other.class == self.class
|
@@ -33,7 +36,7 @@ module Deimos
|
|
33
36
|
comparison == self.as_json
|
34
37
|
end
|
35
38
|
|
36
|
-
#
|
39
|
+
# @return [String]
|
37
40
|
def inspect
|
38
41
|
klass = self.class
|
39
42
|
"#{klass}(#{self.as_json})"
|
@@ -41,13 +44,14 @@ module Deimos
|
|
41
44
|
|
42
45
|
# Initializes this class from a given value
|
43
46
|
# @param value [Object]
|
47
|
+
# @return [SchemaClass::Base]
|
44
48
|
def self.initialize_from_value(value)
|
45
49
|
raise NotImplementedError
|
46
50
|
end
|
47
51
|
|
48
52
|
protected
|
49
53
|
|
50
|
-
#
|
54
|
+
# @return [Integer]
|
51
55
|
def hash
|
52
56
|
as_json.hash
|
53
57
|
end
|
@@ -8,9 +8,11 @@ module Deimos
|
|
8
8
|
# Base Class for Enum Classes generated from Avro.
|
9
9
|
class Enum < Base
|
10
10
|
|
11
|
+
# @return [String]
|
11
12
|
attr_accessor :value
|
12
13
|
|
13
14
|
# @param other [Deimos::SchemaClass::Enum]
|
15
|
+
# @return [Boolean]
|
14
16
|
def ==(other)
|
15
17
|
other.is_a?(self.class) ? other.value == @value : other == @value
|
16
18
|
end
|
@@ -31,12 +33,12 @@ module Deimos
|
|
31
33
|
raise NotImplementedError
|
32
34
|
end
|
33
35
|
|
34
|
-
#
|
36
|
+
# @return [String]
|
35
37
|
def as_json(_opts={})
|
36
38
|
@value
|
37
39
|
end
|
38
40
|
|
39
|
-
#
|
41
|
+
# @return [SchemaClass::Enum]
|
40
42
|
def self.initialize_from_value(value)
|
41
43
|
return nil if value.nil?
|
42
44
|
|
@@ -21,8 +21,8 @@ module Deimos
|
|
21
21
|
end
|
22
22
|
|
23
23
|
# Merge a hash or an identical schema object with this one and return a new object.
|
24
|
-
# @param other_hash [Hash
|
25
|
-
# @return [
|
24
|
+
# @param other_hash [Hash,SchemaClass::Base]
|
25
|
+
# @return [SchemaClass::Base]
|
26
26
|
def merge(other_hash)
|
27
27
|
obj = self.class.new(**self.to_h.symbolize_keys)
|
28
28
|
other_hash.to_h.each do |k, v|
|
@@ -32,13 +32,13 @@ module Deimos
|
|
32
32
|
end
|
33
33
|
|
34
34
|
# Element access method as if this Object were a hash
|
35
|
-
# @param key[String
|
35
|
+
# @param key[String,Symbol]
|
36
36
|
# @return [Object] The value of the attribute if exists, nil otherwise
|
37
37
|
def [](key)
|
38
38
|
self.try(key.to_sym)
|
39
39
|
end
|
40
40
|
|
41
|
-
#
|
41
|
+
# @return [SchemaClass::Record]
|
42
42
|
def with_indifferent_access
|
43
43
|
self
|
44
44
|
end
|
@@ -72,7 +72,7 @@ module Deimos
|
|
72
72
|
validator.schema_fields.map(&:name)
|
73
73
|
end
|
74
74
|
|
75
|
-
#
|
75
|
+
# @return [SchemaClass::Record]
|
76
76
|
def self.initialize_from_value(value)
|
77
77
|
return nil if value.nil?
|
78
78
|
|
data/lib/deimos/shared_config.rb
CHANGED
@@ -35,12 +35,14 @@ module Deimos
|
|
35
35
|
|
36
36
|
# Set the schema.
|
37
37
|
# @param schema [String]
|
38
|
+
# @return [void]
|
38
39
|
def schema(schema)
|
39
40
|
config[:schema] = schema
|
40
41
|
end
|
41
42
|
|
42
43
|
# Set the namespace.
|
43
44
|
# @param namespace [String]
|
45
|
+
# @return [void]
|
44
46
|
def namespace(namespace)
|
45
47
|
config[:namespace] = namespace
|
46
48
|
end
|
@@ -48,9 +50,10 @@ module Deimos
|
|
48
50
|
# Set key configuration.
|
49
51
|
# @param field [Symbol] the name of a field to use in the value schema as
|
50
52
|
# a generated key schema
|
51
|
-
# @param schema [String
|
53
|
+
# @param schema [String, Symbol] the name of a schema to use for the key
|
52
54
|
# @param plain [Boolean] if true, do not encode keys at all
|
53
55
|
# @param none [Boolean] if true, do not use keys at all
|
56
|
+
# @return [void]
|
54
57
|
def key_config(plain: nil, field: nil, schema: nil, none: nil)
|
55
58
|
config[:key_configured] = true
|
56
59
|
config[:no_keys] = none
|
@@ -59,7 +62,8 @@ module Deimos
|
|
59
62
|
config[:key_schema] = schema
|
60
63
|
end
|
61
64
|
|
62
|
-
# @param
|
65
|
+
# @param use_schema_classes [Boolean]
|
66
|
+
# @return [void]
|
63
67
|
def schema_class_config(use_schema_classes)
|
64
68
|
config[:use_schema_classes] = use_schema_classes
|
65
69
|
end
|
data/lib/deimos/test_helpers.rb
CHANGED
@@ -20,6 +20,7 @@ module Deimos
|
|
20
20
|
end
|
21
21
|
|
22
22
|
# Set the config to the right settings for a unit test
|
23
|
+
# @return [void]
|
23
24
|
def unit_test!
|
24
25
|
Deimos.configure do |deimos_config|
|
25
26
|
deimos_config.logger = Logger.new(STDOUT)
|
@@ -31,6 +32,7 @@ module Deimos
|
|
31
32
|
end
|
32
33
|
|
33
34
|
# Kafka test config with avro schema registry
|
35
|
+
# @return [void]
|
34
36
|
def full_integration_test!
|
35
37
|
Deimos.configure do |deimos_config|
|
36
38
|
deimos_config.producers.backend = :kafka
|
@@ -39,6 +41,7 @@ module Deimos
|
|
39
41
|
end
|
40
42
|
|
41
43
|
# Set the config to the right settings for a kafka test
|
44
|
+
# @return [void]
|
42
45
|
def kafka_test!
|
43
46
|
Deimos.configure do |deimos_config|
|
44
47
|
deimos_config.producers.backend = :kafka
|
@@ -62,21 +65,25 @@ module Deimos
|
|
62
65
|
end
|
63
66
|
|
64
67
|
# @deprecated
|
68
|
+
# @!visibility private
|
65
69
|
def stub_producers_and_consumers!
|
66
70
|
warn('stub_producers_and_consumers! is no longer necessary and this method will be removed in 3.0')
|
67
71
|
end
|
68
72
|
|
69
73
|
# @deprecated
|
74
|
+
# @!visibility private
|
70
75
|
def stub_producer(_klass)
|
71
76
|
warn('Stubbing producers is no longer necessary and this method will be removed in 3.0')
|
72
77
|
end
|
73
78
|
|
74
79
|
# @deprecated
|
80
|
+
# @!visibility private
|
75
81
|
def stub_consumer(_klass)
|
76
82
|
warn('Stubbing consumers is no longer necessary and this method will be removed in 3.0')
|
77
83
|
end
|
78
84
|
|
79
85
|
# @deprecated
|
86
|
+
# @!visibility private
|
80
87
|
def stub_batch_consumer(_klass)
|
81
88
|
warn('Stubbing batch consumers is no longer necessary and this method will be removed in 3.0')
|
82
89
|
end
|
@@ -84,6 +91,7 @@ module Deimos
|
|
84
91
|
# get the difference of 2 hashes.
|
85
92
|
# @param hash1 [Hash]
|
86
93
|
# @param hash2 [Hash]
|
94
|
+
# @!visibility private
|
87
95
|
def _hash_diff(hash1, hash2)
|
88
96
|
if hash1.nil? || !hash1.is_a?(Hash)
|
89
97
|
hash2
|
@@ -96,7 +104,7 @@ module Deimos
|
|
96
104
|
end
|
97
105
|
end
|
98
106
|
|
99
|
-
#
|
107
|
+
# @!visibility private
|
100
108
|
def _frk_failure_message(topic, message, key=nil, partition_key=nil, was_negated=false)
|
101
109
|
messages = Deimos::Backends::Test.sent_messages.
|
102
110
|
select { |m| m[:topic] == topic }.
|
@@ -162,6 +170,7 @@ module Deimos
|
|
162
170
|
|
163
171
|
# Clear all sent messages - e.g. if we want to check that
|
164
172
|
# particular messages were sent or not sent after a point in time.
|
173
|
+
# @return [void]
|
165
174
|
def clear_kafka_messages!
|
166
175
|
Deimos::Backends::Test.sent_messages.clear
|
167
176
|
end
|
@@ -170,15 +179,16 @@ module Deimos
|
|
170
179
|
# that the schema is correct. If
|
171
180
|
# a block is given, that block will be executed when `consume` is called.
|
172
181
|
# Otherwise it will just confirm that `consume` is called at all.
|
173
|
-
# @param handler_class_or_topic [Class
|
182
|
+
# @param handler_class_or_topic [Class, String] Class which inherits from
|
174
183
|
# Deimos::Consumer or the topic as a string
|
175
184
|
# @param payload [Hash] the payload to consume
|
176
185
|
# @param call_original [Boolean] if true, allow the consume handler
|
177
186
|
# to continue as normal. Not compatible with a block.
|
178
|
-
# @param
|
187
|
+
# @param skip_expectation [Boolean] Set to true to not place any
|
179
188
|
# expectations on the consumer. Primarily used internally to Deimos.
|
180
189
|
# @param key [Object] the key to use.
|
181
190
|
# @param partition_key [Object] the partition key to use.
|
191
|
+
# @return [void]
|
182
192
|
def test_consume_message(handler_class_or_topic,
|
183
193
|
payload,
|
184
194
|
call_original: false,
|
@@ -225,6 +235,7 @@ module Deimos
|
|
225
235
|
# Check to see that a given message will fail due to validation errors.
|
226
236
|
# @param handler_class [Class]
|
227
237
|
# @param payload [Hash]
|
238
|
+
# @return [void]
|
228
239
|
def test_consume_invalid_message(handler_class, payload)
|
229
240
|
expect {
|
230
241
|
handler_class.decoder.validate(payload,
|
@@ -236,9 +247,14 @@ module Deimos
|
|
236
247
|
# i.e. that the schema is correct. If
|
237
248
|
# a block is given, that block will be executed when `consume` is called.
|
238
249
|
# Otherwise it will just confirm that `consume` is called at all.
|
239
|
-
# @param handler_class_or_topic [Class
|
250
|
+
# @param handler_class_or_topic [Class, String] Class which inherits from
|
240
251
|
# Deimos::Consumer or the topic as a string
|
241
252
|
# @param payloads [Array<Hash>] the payload to consume
|
253
|
+
# @param keys [Array<Hash,String>]
|
254
|
+
# @param partition_keys [Array<Integer>]
|
255
|
+
# @param call_original [Boolean]
|
256
|
+
# @param skip_expectation [Boolean]
|
257
|
+
# @return [void]
|
242
258
|
def test_consume_batch(handler_class_or_topic,
|
243
259
|
payloads,
|
244
260
|
keys: [],
|
@@ -297,6 +313,7 @@ module Deimos
|
|
297
313
|
# Check to see that a given message will fail due to validation errors.
|
298
314
|
# @param handler_class [Class]
|
299
315
|
# @param payloads [Array<Hash>]
|
316
|
+
# @return [void]
|
300
317
|
def test_consume_batch_invalid_message(handler_class, payloads)
|
301
318
|
topic_name = 'my-topic'
|
302
319
|
handler = handler_class.new
|
data/lib/deimos/tracing/mock.rb
CHANGED
@@ -6,13 +6,14 @@ module Deimos
|
|
6
6
|
module Tracing
|
7
7
|
# Class that mocks out tracing functionality
|
8
8
|
class Mock < Tracing::Provider
|
9
|
-
#
|
9
|
+
# @param logger [Logger]
|
10
10
|
def initialize(logger=nil)
|
11
11
|
@logger = logger || Logger.new(STDOUT)
|
12
12
|
@logger.info('MockTracingProvider initialized')
|
13
13
|
end
|
14
14
|
|
15
|
-
#
|
15
|
+
# @param span_name [String]
|
16
|
+
# @param _options [Hash]
|
16
17
|
def start(span_name, _options={})
|
17
18
|
@logger.info("Mock span '#{span_name}' started")
|
18
19
|
{
|
@@ -35,7 +36,7 @@ module Deimos
|
|
35
36
|
end
|
36
37
|
|
37
38
|
# :nodoc:
|
38
|
-
def set_tag(
|
39
|
+
def set_tag(tag, value, span=nil)
|
39
40
|
nil
|
40
41
|
end
|
41
42
|
|
@@ -14,6 +14,7 @@ module Deimos
|
|
14
14
|
|
15
15
|
# Finishes the trace on the span object.
|
16
16
|
# @param span [Object] The span to finish trace on
|
17
|
+
# @return [void]
|
17
18
|
def finish(span)
|
18
19
|
raise NotImplementedError
|
19
20
|
end
|
@@ -21,11 +22,13 @@ module Deimos
|
|
21
22
|
# Set an error on the span.
|
22
23
|
# @param span [Object] The span to set error on
|
23
24
|
# @param exception [Exception] The exception that occurred
|
25
|
+
# @return [void]
|
24
26
|
def set_error(span, exception)
|
25
27
|
raise NotImplementedError
|
26
28
|
end
|
27
29
|
|
28
30
|
# Get the currently activated span.
|
31
|
+
# @return [Object]
|
29
32
|
def active_span
|
30
33
|
raise NotImplementedError
|
31
34
|
end
|
@@ -33,6 +36,8 @@ module Deimos
|
|
33
36
|
# Set a tag to a span. Use the currently active span if not given.
|
34
37
|
# @param tag [String]
|
35
38
|
# @param value [String]
|
39
|
+
# @param span [Object]
|
40
|
+
# @return [void]
|
36
41
|
def set_tag(tag, value, span=nil)
|
37
42
|
raise NotImplementedError
|
38
43
|
end
|
@@ -7,12 +7,15 @@ module Deimos
|
|
7
7
|
module Utils
|
8
8
|
# Class which continually polls the database and sends Kafka messages.
|
9
9
|
class DbPoller
|
10
|
+
# @return [Integer]
|
10
11
|
BATCH_SIZE = 1000
|
11
12
|
|
12
13
|
# Needed for Executor so it can identify the worker
|
14
|
+
# @return [Integer]
|
13
15
|
attr_reader :id
|
14
16
|
|
15
17
|
# Begin the DB Poller process.
|
18
|
+
# @return [void]
|
16
19
|
def self.start!
|
17
20
|
if Deimos.config.db_poller_objects.empty?
|
18
21
|
raise('No pollers configured!')
|
@@ -28,7 +31,7 @@ module Deimos
|
|
28
31
|
signal_handler.run!
|
29
32
|
end
|
30
33
|
|
31
|
-
# @param config [
|
34
|
+
# @param config [FigTree::ConfigStruct]
|
32
35
|
def initialize(config)
|
33
36
|
@config = config
|
34
37
|
@id = SecureRandom.hex
|
@@ -47,6 +50,7 @@ module Deimos
|
|
47
50
|
# time we ran
|
48
51
|
# 2) On a loop, process all the recent updates between the last time
|
49
52
|
# we ran and now.
|
53
|
+
# @return [void]
|
50
54
|
def start
|
51
55
|
# Don't send asynchronously
|
52
56
|
if Deimos.config.producers.backend == :kafka_async
|
@@ -66,6 +70,7 @@ module Deimos
|
|
66
70
|
end
|
67
71
|
|
68
72
|
# Grab the PollInfo or create if it doesn't exist.
|
73
|
+
# @return [void]
|
69
74
|
def retrieve_poll_info
|
70
75
|
ActiveRecord::Base.connection.reconnect! unless ActiveRecord::Base.connection.open_transactions.positive?
|
71
76
|
new_time = @config.start_from_beginning ? Time.new(0) : Time.zone.now
|
@@ -76,6 +81,7 @@ module Deimos
|
|
76
81
|
end
|
77
82
|
|
78
83
|
# Stop the poll.
|
84
|
+
# @return [void]
|
79
85
|
def stop
|
80
86
|
Deimos.config.logger.info('Received signal to stop')
|
81
87
|
@signal_to_stop = true
|
@@ -95,6 +101,7 @@ module Deimos
|
|
95
101
|
end
|
96
102
|
|
97
103
|
# Send messages for updated data.
|
104
|
+
# @return [void]
|
98
105
|
def process_updates
|
99
106
|
return unless should_run?
|
100
107
|
|
@@ -135,6 +142,7 @@ module Deimos
|
|
135
142
|
end
|
136
143
|
|
137
144
|
# @param batch [Array<ActiveRecord::Base>]
|
145
|
+
# @return [void]
|
138
146
|
def process_batch(batch)
|
139
147
|
record = batch.last
|
140
148
|
id_method = record.class.primary_key
|
@@ -8,8 +8,11 @@ module Deimos
|
|
8
8
|
include Phobos::Producer
|
9
9
|
attr_accessor :id, :current_topic
|
10
10
|
|
11
|
+
# @return [Integer]
|
11
12
|
BATCH_SIZE = 1000
|
13
|
+
# @return [Integer]
|
12
14
|
DELETE_BATCH_SIZE = 10
|
15
|
+
# @return [Integer]
|
13
16
|
MAX_DELETE_ATTEMPTS = 3
|
14
17
|
|
15
18
|
# @param logger [Logger]
|
@@ -19,12 +22,13 @@ module Deimos
|
|
19
22
|
@logger.push_tags("DbProducer #{@id}") if @logger.respond_to?(:push_tags)
|
20
23
|
end
|
21
24
|
|
22
|
-
# @return [
|
25
|
+
# @return [FigTree]
|
23
26
|
def config
|
24
27
|
Deimos.config.db_producer
|
25
28
|
end
|
26
29
|
|
27
30
|
# Start the poll.
|
31
|
+
# @return [void]
|
28
32
|
def start
|
29
33
|
@logger.info('Starting...')
|
30
34
|
@signal_to_stop = false
|
@@ -40,12 +44,14 @@ module Deimos
|
|
40
44
|
end
|
41
45
|
|
42
46
|
# Stop the poll.
|
47
|
+
# @return [void]
|
43
48
|
def stop
|
44
49
|
@logger.info('Received signal to stop')
|
45
50
|
@signal_to_stop = true
|
46
51
|
end
|
47
52
|
|
48
53
|
# Complete one loop of processing all messages in the DB.
|
54
|
+
# @return [void]
|
49
55
|
def process_next_messages
|
50
56
|
topics = retrieve_topics
|
51
57
|
@logger.info("Found topics: #{topics}")
|
@@ -80,6 +86,7 @@ module Deimos
|
|
80
86
|
end
|
81
87
|
|
82
88
|
# Process a single batch in a topic.
|
89
|
+
# @return [void]
|
83
90
|
def process_topic_batch
|
84
91
|
messages = retrieve_messages
|
85
92
|
return false if messages.empty?
|
@@ -111,6 +118,7 @@ module Deimos
|
|
111
118
|
end
|
112
119
|
|
113
120
|
# @param messages [Array<Deimos::KafkaMessage>]
|
121
|
+
# @return [void]
|
114
122
|
def delete_messages(messages)
|
115
123
|
attempts = 1
|
116
124
|
begin
|
@@ -137,6 +145,7 @@ module Deimos
|
|
137
145
|
end
|
138
146
|
|
139
147
|
# @param messages [Array<Deimos::KafkaMessage>]
|
148
|
+
# @return [void]
|
140
149
|
def log_messages(messages)
|
141
150
|
return if config.log_topics != :all && !config.log_topics.include?(@current_topic)
|
142
151
|
|
@@ -146,7 +155,8 @@ module Deimos
|
|
146
155
|
end
|
147
156
|
end
|
148
157
|
|
149
|
-
# Send metrics to
|
158
|
+
# Send metrics related to pending messages.
|
159
|
+
# @return [void]
|
150
160
|
def send_pending_metrics
|
151
161
|
metrics = Deimos.config.metrics
|
152
162
|
return unless metrics
|
@@ -185,6 +195,7 @@ module Deimos
|
|
185
195
|
# Shut down the sync producer if we have to. Phobos will automatically
|
186
196
|
# create a new one. We should call this if the producer can be in a bad
|
187
197
|
# state and e.g. we need to clear the buffer.
|
198
|
+
# @return [void]
|
188
199
|
def shutdown_producer
|
189
200
|
if self.class.producer.respond_to?(:sync_producer_shutdown) # Phobos 1.8.3
|
190
201
|
self.class.producer.sync_producer_shutdown
|
@@ -194,6 +205,7 @@ module Deimos
|
|
194
205
|
# Produce messages in batches, reducing the size 1/10 if the batch is too
|
195
206
|
# large. Does not retry batches of messages that have already been sent.
|
196
207
|
# @param batch [Array<Hash>]
|
208
|
+
# @return [void]
|
197
209
|
def produce_messages(batch)
|
198
210
|
batch_size = batch.size
|
199
211
|
current_index = 0
|
@@ -7,9 +7,11 @@ module Deimos
|
|
7
7
|
class DeadlockRetry
|
8
8
|
class << self
|
9
9
|
# Maximum number of times to retry the block after encountering a deadlock
|
10
|
+
# @return [Integer]
|
10
11
|
RETRY_COUNT = 2
|
11
12
|
|
12
13
|
# Need to match on error messages to support older Rails versions
|
14
|
+
# @return [Array<String>]
|
13
15
|
DEADLOCK_MESSAGES = [
|
14
16
|
# MySQL
|
15
17
|
'Deadlock found when trying to get lock',
|
@@ -28,6 +30,7 @@ module Deimos
|
|
28
30
|
# from retrying at the same time.
|
29
31
|
# @param tags [Array] Tags to attach when logging and reporting metrics.
|
30
32
|
# @yield Yields to the block that may deadlock.
|
33
|
+
# @return [void]
|
31
34
|
def wrap(tags=[])
|
32
35
|
count = RETRY_COUNT
|
33
36
|
|
@@ -6,10 +6,12 @@ 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
|
+
# @return [Integer]
|
9
10
|
MAX_SEEK_RETRIES = 3
|
11
|
+
# @return [Integer]
|
10
12
|
attr_accessor :num_messages
|
11
13
|
|
12
|
-
#
|
14
|
+
# @return [void]
|
13
15
|
def start_listener
|
14
16
|
@num_messages ||= 10
|
15
17
|
@consumer = create_kafka_consumer
|
@@ -45,17 +47,20 @@ module Deimos
|
|
45
47
|
|
46
48
|
cattr_accessor :total_messages
|
47
49
|
|
48
|
-
# @param klass [Class
|
50
|
+
# @param klass [Class<Deimos::Consumer>]
|
51
|
+
# @return [void]
|
49
52
|
def self.config_class=(klass)
|
50
53
|
self.config.merge!(klass.config)
|
51
54
|
end
|
52
55
|
|
53
|
-
#
|
56
|
+
# @param _kafka_client [Kafka::Client]
|
57
|
+
# @return [void]
|
54
58
|
def self.start(_kafka_client)
|
55
59
|
self.total_messages = []
|
56
60
|
end
|
57
61
|
|
58
|
-
#
|
62
|
+
# @param payload [Hash]
|
63
|
+
# @param metadata [Hash]
|
59
64
|
def consume(payload, metadata)
|
60
65
|
self.class.total_messages << {
|
61
66
|
key: metadata[:key],
|
@@ -66,18 +71,20 @@ module Deimos
|
|
66
71
|
|
67
72
|
# Class which can process/consume messages inline.
|
68
73
|
class InlineConsumer
|
74
|
+
# @return [Integer]
|
69
75
|
MAX_MESSAGE_WAIT_TIME = 1.second
|
76
|
+
# @return [Integer]
|
70
77
|
MAX_TOPIC_WAIT_TIME = 10.seconds
|
71
78
|
|
72
79
|
# Get the last X messages from a topic. You can specify a subclass of
|
73
80
|
# Deimos::Consumer or Deimos::Producer, or provide the
|
74
81
|
# schema, namespace and key_config directly.
|
75
82
|
# @param topic [String]
|
76
|
-
# @param config_class [Class
|
83
|
+
# @param config_class [Class<Deimos::Consumer>,Class<Deimos::Producer>]
|
77
84
|
# @param schema [String]
|
78
85
|
# @param namespace [String]
|
79
86
|
# @param key_config [Hash]
|
80
|
-
# @param num_messages [
|
87
|
+
# @param num_messages [Integer]
|
81
88
|
# @return [Array<Hash>]
|
82
89
|
def self.get_messages_for(topic:, schema: nil, namespace: nil, key_config: nil,
|
83
90
|
config_class: nil, num_messages: 10)
|
@@ -106,6 +113,7 @@ module Deimos
|
|
106
113
|
# @param frk_consumer [Class]
|
107
114
|
# @param num_messages [Integer] If this number is >= the number
|
108
115
|
# of messages in the topic, all messages will be consumed.
|
116
|
+
# @return [void]
|
109
117
|
def self.consume(topic:, frk_consumer:, num_messages: 10)
|
110
118
|
listener = SeekListener.new(
|
111
119
|
handler: frk_consumer,
|
@@ -24,6 +24,7 @@ module Deimos
|
|
24
24
|
|
25
25
|
# @param topic [String]
|
26
26
|
# @param partition [Integer]
|
27
|
+
# @return [void]
|
27
28
|
def report_lag(topic, partition)
|
28
29
|
self.topics[topic.to_s] ||= Topic.new(topic, self)
|
29
30
|
self.topics[topic.to_s].report_lag(partition)
|
@@ -32,6 +33,7 @@ module Deimos
|
|
32
33
|
# @param topic [String]
|
33
34
|
# @param partition [Integer]
|
34
35
|
# @param offset [Integer]
|
36
|
+
# @return [void]
|
35
37
|
def assign_current_offset(topic, partition, offset)
|
36
38
|
self.topics[topic.to_s] ||= Topic.new(topic, self)
|
37
39
|
self.topics[topic.to_s].assign_current_offset(partition, offset)
|
@@ -56,11 +58,15 @@ module Deimos
|
|
56
58
|
end
|
57
59
|
|
58
60
|
# @param partition [Integer]
|
61
|
+
# @param offset [Integer]
|
62
|
+
# @return [void]
|
59
63
|
def assign_current_offset(partition, offset)
|
60
64
|
self.partition_current_offsets[partition.to_i] = offset
|
61
65
|
end
|
62
66
|
|
63
67
|
# @param partition [Integer]
|
68
|
+
# @param offset [Integer]
|
69
|
+
# @return [Integer]
|
64
70
|
def compute_lag(partition, offset)
|
65
71
|
begin
|
66
72
|
client = Phobos.create_kafka_client
|
@@ -74,6 +80,7 @@ module Deimos
|
|
74
80
|
end
|
75
81
|
|
76
82
|
# @param partition [Integer]
|
83
|
+
# @return [void]
|
77
84
|
def report_lag(partition)
|
78
85
|
current_offset = self.partition_current_offsets[partition.to_i]
|
79
86
|
return unless current_offset
|
@@ -94,6 +101,7 @@ module Deimos
|
|
94
101
|
|
95
102
|
class << self
|
96
103
|
# Reset all group information.
|
104
|
+
# @return [void]
|
97
105
|
def reset
|
98
106
|
@groups = {}
|
99
107
|
end
|
@@ -103,6 +111,7 @@ module Deimos
|
|
103
111
|
# topic = event.payload.fetch(:topic)
|
104
112
|
# partition = event.payload.fetch(:partition)
|
105
113
|
# @param payload [Hash]
|
114
|
+
# @return [void]
|
106
115
|
def message_processed(payload)
|
107
116
|
offset = payload[:offset] || payload[:last_offset]
|
108
117
|
topic = payload[:topic]
|
@@ -116,6 +125,7 @@ module Deimos
|
|
116
125
|
end
|
117
126
|
|
118
127
|
# @param payload [Hash]
|
128
|
+
# @return [void]
|
119
129
|
def offset_seek(payload)
|
120
130
|
offset = payload[:offset]
|
121
131
|
topic = payload[:topic]
|
@@ -129,6 +139,7 @@ module Deimos
|
|
129
139
|
end
|
130
140
|
|
131
141
|
# @param payload [Hash]
|
142
|
+
# @return [void]
|
132
143
|
def heartbeat(payload)
|
133
144
|
group = payload[:group_id]
|
134
145
|
synchronize do
|