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