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.
- checksums.yaml +4 -4
- data/.circleci/config.yml +9 -0
- data/.rubocop.yml +15 -13
- data/.ruby-version +1 -1
- data/CHANGELOG.md +30 -0
- data/Gemfile.lock +87 -80
- data/README.md +139 -15
- data/Rakefile +1 -1
- data/deimos-ruby.gemspec +3 -2
- data/docs/ARCHITECTURE.md +144 -0
- data/docs/CONFIGURATION.md +27 -0
- data/lib/deimos.rb +7 -6
- data/lib/deimos/active_record_consume/batch_consumption.rb +159 -0
- data/lib/deimos/active_record_consume/batch_slicer.rb +27 -0
- data/lib/deimos/active_record_consume/message_consumption.rb +58 -0
- data/lib/deimos/active_record_consume/schema_model_converter.rb +52 -0
- data/lib/deimos/active_record_consumer.rb +33 -75
- data/lib/deimos/active_record_producer.rb +23 -0
- data/lib/deimos/batch_consumer.rb +2 -140
- data/lib/deimos/config/configuration.rb +28 -10
- data/lib/deimos/consume/batch_consumption.rb +148 -0
- data/lib/deimos/consume/message_consumption.rb +93 -0
- data/lib/deimos/consumer.rb +79 -69
- data/lib/deimos/kafka_message.rb +1 -1
- data/lib/deimos/kafka_source.rb +29 -23
- data/lib/deimos/kafka_topic_info.rb +1 -1
- data/lib/deimos/message.rb +6 -1
- data/lib/deimos/metrics/provider.rb +0 -2
- data/lib/deimos/poll_info.rb +9 -0
- data/lib/deimos/tracing/provider.rb +0 -2
- data/lib/deimos/utils/db_poller.rb +149 -0
- data/lib/deimos/utils/db_producer.rb +8 -3
- data/lib/deimos/utils/deadlock_retry.rb +68 -0
- data/lib/deimos/utils/lag_reporter.rb +19 -26
- data/lib/deimos/version.rb +1 -1
- data/lib/generators/deimos/db_poller/templates/migration +11 -0
- data/lib/generators/deimos/db_poller/templates/rails3_migration +16 -0
- data/lib/generators/deimos/db_poller_generator.rb +48 -0
- data/lib/tasks/deimos.rake +7 -0
- data/spec/active_record_batch_consumer_spec.rb +481 -0
- data/spec/active_record_consume/batch_slicer_spec.rb +42 -0
- data/spec/active_record_consume/schema_model_converter_spec.rb +105 -0
- data/spec/active_record_consumer_spec.rb +22 -11
- data/spec/active_record_producer_spec.rb +66 -88
- data/spec/batch_consumer_spec.rb +23 -7
- data/spec/config/configuration_spec.rb +4 -0
- data/spec/consumer_spec.rb +8 -8
- data/spec/deimos_spec.rb +57 -49
- data/spec/handlers/my_batch_consumer.rb +6 -1
- data/spec/handlers/my_consumer.rb +6 -1
- data/spec/kafka_source_spec.rb +53 -0
- data/spec/message_spec.rb +19 -0
- data/spec/producer_spec.rb +3 -3
- data/spec/rake_spec.rb +1 -1
- data/spec/schemas/com/my-namespace/MySchemaCompound-key.avsc +18 -0
- data/spec/schemas/com/my-namespace/Wibble.avsc +43 -0
- data/spec/spec_helper.rb +61 -6
- data/spec/utils/db_poller_spec.rb +320 -0
- data/spec/utils/deadlock_retry_spec.rb +74 -0
- data/spec/utils/lag_reporter_spec.rb +29 -22
- metadata +61 -20
- data/lib/deimos/base_consumer.rb +0 -104
- data/lib/deimos/utils/executor.rb +0 -124
- data/lib/deimos/utils/platform_schema_validation.rb +0 -0
- data/lib/deimos/utils/signal_handler.rb +0 -68
- data/spec/utils/executor_spec.rb +0 -53
- data/spec/utils/signal_handler_spec.rb +0 -16
data/lib/deimos/kafka_message.rb
CHANGED
data/lib/deimos/kafka_source.rb
CHANGED
@@ -93,31 +93,37 @@ module Deimos
|
|
93
93
|
|
94
94
|
# This will contain an array of hashes, where each hash is the actual
|
95
95
|
# attribute hash that created the object.
|
96
|
-
ids = if results.is_a?(Array)
|
97
|
-
results[1]
|
98
|
-
elsif results.respond_to?(:ids)
|
99
|
-
results.ids
|
100
|
-
else
|
101
|
-
[]
|
102
|
-
end
|
103
|
-
if ids.blank?
|
104
|
-
# re-fill IDs based on what was just entered into the DB.
|
105
|
-
if self.connection.adapter_name.downcase =~ /sqlite/
|
106
|
-
last_id = self.connection.select_value('select last_insert_rowid()')
|
107
|
-
ids = ((last_id - array_of_attributes.size + 1)..last_id).to_a
|
108
|
-
else # mysql
|
109
|
-
last_id = self.connection.select_value('select LAST_INSERT_ID()')
|
110
|
-
ids = (last_id..(last_id + array_of_attributes.size)).to_a
|
111
|
-
end
|
112
|
-
end
|
113
96
|
array_of_hashes = []
|
114
|
-
array_of_attributes.
|
115
|
-
|
116
|
-
|
117
|
-
|
97
|
+
array_of_attributes.each do |array|
|
98
|
+
array_of_hashes << column_names.zip(array).to_h.with_indifferent_access
|
99
|
+
end
|
100
|
+
hashes_with_id, hashes_without_id = array_of_hashes.partition { |arr| arr[:id].present? }
|
101
|
+
|
102
|
+
self.kafka_producers.each { |p| p.send_events(hashes_with_id) }
|
103
|
+
|
104
|
+
if hashes_without_id.any?
|
105
|
+
if options[:on_duplicate_key_update].present? &&
|
106
|
+
options[:on_duplicate_key_update] != [:updated_at]
|
107
|
+
unique_columns = column_names.map(&:to_s) -
|
108
|
+
options[:on_duplicate_key_update].map(&:to_s) - %w(id created_at)
|
109
|
+
records = hashes_without_id.map do |hash|
|
110
|
+
self.where(unique_columns.map { |c| [c, hash[c]] }.to_h).first
|
111
|
+
end
|
112
|
+
self.kafka_producers.each { |p| p.send_events(records) }
|
113
|
+
else
|
114
|
+
# re-fill IDs based on what was just entered into the DB.
|
115
|
+
last_id = if self.connection.adapter_name.downcase =~ /sqlite/
|
116
|
+
self.connection.select_value('select last_insert_rowid()') -
|
117
|
+
hashes_without_id.size + 1
|
118
|
+
else # mysql
|
119
|
+
self.connection.select_value('select LAST_INSERT_ID()')
|
120
|
+
end
|
121
|
+
hashes_without_id.each_with_index do |attrs, i|
|
122
|
+
attrs[:id] = last_id + i
|
123
|
+
end
|
124
|
+
self.kafka_producers.each { |p| p.send_events(hashes_without_id) }
|
125
|
+
end
|
118
126
|
end
|
119
|
-
|
120
|
-
self.kafka_producers.each { |p| p.send_events(array_of_hashes) }
|
121
127
|
results
|
122
128
|
end
|
123
129
|
end
|
data/lib/deimos/message.rb
CHANGED
@@ -10,7 +10,7 @@ module Deimos
|
|
10
10
|
# @param producer [Class]
|
11
11
|
def initialize(payload, producer, topic: nil, key: nil, partition_key: nil)
|
12
12
|
@payload = payload&.with_indifferent_access
|
13
|
-
@producer_name = producer
|
13
|
+
@producer_name = producer&.name
|
14
14
|
@topic = topic
|
15
15
|
@key = key
|
16
16
|
@partition_key = partition_key
|
@@ -70,5 +70,10 @@ module Deimos
|
|
70
70
|
def ==(other)
|
71
71
|
self.to_h == other.to_h
|
72
72
|
end
|
73
|
+
|
74
|
+
# @return [Boolean] True if this message is a tombstone
|
75
|
+
def tombstone?
|
76
|
+
payload.nil?
|
77
|
+
end
|
73
78
|
end
|
74
79
|
end
|
@@ -0,0 +1,149 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'deimos/poll_info'
|
4
|
+
require 'sigurd/executor'
|
5
|
+
require 'sigurd/signal_handler'
|
6
|
+
|
7
|
+
module Deimos
|
8
|
+
module Utils
|
9
|
+
# Class which continually polls the database and sends Kafka messages.
|
10
|
+
class DbPoller
|
11
|
+
BATCH_SIZE = 1000
|
12
|
+
|
13
|
+
# Needed for Executor so it can identify the worker
|
14
|
+
attr_reader :id
|
15
|
+
|
16
|
+
# Begin the DB Poller process.
|
17
|
+
def self.start!
|
18
|
+
if Deimos.config.db_poller_objects.empty?
|
19
|
+
raise('No pollers configured!')
|
20
|
+
end
|
21
|
+
|
22
|
+
pollers = Deimos.config.db_poller_objects.map do |poller_config|
|
23
|
+
self.new(poller_config)
|
24
|
+
end
|
25
|
+
executor = Sigurd::Executor.new(pollers,
|
26
|
+
sleep_seconds: 5,
|
27
|
+
logger: Deimos.config.logger)
|
28
|
+
signal_handler = Sigurd::SignalHandler.new(executor)
|
29
|
+
signal_handler.run!
|
30
|
+
end
|
31
|
+
|
32
|
+
# @param config [Deimos::Configuration::ConfigStruct]
|
33
|
+
def initialize(config)
|
34
|
+
@config = config
|
35
|
+
@id = SecureRandom.hex
|
36
|
+
begin
|
37
|
+
@producer = @config.producer_class.constantize
|
38
|
+
rescue NameError
|
39
|
+
raise "Class #{@config.producer_class} not found!"
|
40
|
+
end
|
41
|
+
unless @producer < Deimos::ActiveRecordProducer
|
42
|
+
raise "Class #{@producer.class.name} is not an ActiveRecordProducer!"
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
# Start the poll:
|
47
|
+
# 1) Grab the current PollInfo from the database indicating the last
|
48
|
+
# time we ran
|
49
|
+
# 2) On a loop, process all the recent updates between the last time
|
50
|
+
# we ran and now.
|
51
|
+
def start
|
52
|
+
# Don't send asynchronously
|
53
|
+
if Deimos.config.producers.backend == :kafka_async
|
54
|
+
Deimos.config.producers.backend = :kafka
|
55
|
+
end
|
56
|
+
Deimos.config.logger.info('Starting...')
|
57
|
+
@signal_to_stop = false
|
58
|
+
retrieve_poll_info
|
59
|
+
loop do
|
60
|
+
if @signal_to_stop
|
61
|
+
Deimos.config.logger.info('Shutting down')
|
62
|
+
break
|
63
|
+
end
|
64
|
+
process_updates
|
65
|
+
sleep 0.1
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
# Grab the PollInfo or create if it doesn't exist.
|
70
|
+
def retrieve_poll_info
|
71
|
+
ActiveRecord::Base.connection.reconnect!
|
72
|
+
new_time = @config.start_from_beginning ? Time.new(0) : Time.zone.now
|
73
|
+
@info = Deimos::PollInfo.find_by_producer(@config.producer_class) ||
|
74
|
+
Deimos::PollInfo.create!(producer: @config.producer_class,
|
75
|
+
last_sent: new_time,
|
76
|
+
last_sent_id: 0)
|
77
|
+
end
|
78
|
+
|
79
|
+
# Stop the poll.
|
80
|
+
def stop
|
81
|
+
Deimos.config.logger.info('Received signal to stop')
|
82
|
+
@signal_to_stop = true
|
83
|
+
end
|
84
|
+
|
85
|
+
# Indicate whether this current loop should process updates. Most loops
|
86
|
+
# will busy-wait (sleeping 0.1 seconds) until it's ready.
|
87
|
+
# @return [Boolean]
|
88
|
+
def should_run?
|
89
|
+
Time.zone.now - @info.last_sent - @config.delay_time >= @config.run_every
|
90
|
+
end
|
91
|
+
|
92
|
+
# @param record [ActiveRecord::Base]
|
93
|
+
# @return [ActiveSupport::TimeWithZone]
|
94
|
+
def last_updated(record)
|
95
|
+
record.public_send(@config.timestamp_column)
|
96
|
+
end
|
97
|
+
|
98
|
+
# Send messages for updated data.
|
99
|
+
def process_updates
|
100
|
+
return unless should_run?
|
101
|
+
|
102
|
+
time_from = @config.full_table ? Time.new(0) : @info.last_sent.in_time_zone
|
103
|
+
time_to = Time.zone.now - @config.delay_time
|
104
|
+
Deimos.config.logger.info("Polling #{@producer.topic} from #{time_from} to #{time_to}")
|
105
|
+
message_count = 0
|
106
|
+
batch_count = 0
|
107
|
+
|
108
|
+
# poll_query gets all the relevant data from the database, as defined
|
109
|
+
# by the producer itself.
|
110
|
+
loop do
|
111
|
+
Deimos.config.logger.debug("Polling #{@producer.topic}, batch #{batch_count + 1}")
|
112
|
+
batch = fetch_results(time_from, time_to).to_a
|
113
|
+
break if batch.empty?
|
114
|
+
|
115
|
+
batch_count += 1
|
116
|
+
process_batch(batch)
|
117
|
+
message_count += batch.size
|
118
|
+
time_from = last_updated(batch.last)
|
119
|
+
end
|
120
|
+
Deimos.config.logger.info("Poll #{@producer.topic} complete at #{time_to} (#{message_count} messages, #{batch_count} batches}")
|
121
|
+
end
|
122
|
+
|
123
|
+
# @param time_from [ActiveSupport::TimeWithZone]
|
124
|
+
# @param time_to [ActiveSupport::TimeWithZone]
|
125
|
+
# @return [ActiveRecord::Relation]
|
126
|
+
def fetch_results(time_from, time_to)
|
127
|
+
id = @producer.config[:record_class].primary_key
|
128
|
+
quoted_timestamp = ActiveRecord::Base.connection.quote_column_name(@config.timestamp_column)
|
129
|
+
quoted_id = ActiveRecord::Base.connection.quote_column_name(id)
|
130
|
+
@producer.poll_query(time_from: time_from,
|
131
|
+
time_to: time_to,
|
132
|
+
column_name: @config.timestamp_column,
|
133
|
+
min_id: @info.last_sent_id).
|
134
|
+
limit(BATCH_SIZE).
|
135
|
+
order("#{quoted_timestamp}, #{quoted_id}")
|
136
|
+
end
|
137
|
+
|
138
|
+
# @param batch [Array<ActiveRecord::Base>]
|
139
|
+
def process_batch(batch)
|
140
|
+
record = batch.last
|
141
|
+
id_method = record.class.primary_key
|
142
|
+
last_id = record.public_send(id_method)
|
143
|
+
last_updated_at = last_updated(record)
|
144
|
+
@producer.send_events(batch)
|
145
|
+
@info.update_attributes!(last_sent: last_updated_at, last_sent_id: last_id)
|
146
|
+
end
|
147
|
+
end
|
148
|
+
end
|
149
|
+
end
|
@@ -2,7 +2,8 @@
|
|
2
2
|
|
3
3
|
module Deimos
|
4
4
|
module Utils
|
5
|
-
# Class which continually polls the
|
5
|
+
# Class which continually polls the kafka_messages table
|
6
|
+
# in the database and sends Kafka messages.
|
6
7
|
class DbProducer
|
7
8
|
include Phobos::Producer
|
8
9
|
attr_accessor :id, :current_topic
|
@@ -86,9 +87,9 @@ module Deimos
|
|
86
87
|
begin
|
87
88
|
produce_messages(compacted_messages.map(&:phobos_message))
|
88
89
|
rescue Kafka::BufferOverflow, Kafka::MessageSizeTooLarge, Kafka::RecordListTooLarge
|
89
|
-
Deimos::KafkaMessage.where(id: messages.map(&:id)).delete_all
|
90
90
|
@logger.error('Message batch too large, deleting...')
|
91
91
|
@logger.error(Deimos::KafkaMessage.decoded(messages))
|
92
|
+
Deimos::KafkaMessage.where(id: messages.map(&:id)).delete_all
|
92
93
|
raise
|
93
94
|
end
|
94
95
|
end
|
@@ -132,7 +133,11 @@ module Deimos
|
|
132
133
|
metrics.gauge('pending_db_messages_max_wait', 0)
|
133
134
|
end
|
134
135
|
messages.each do |record|
|
135
|
-
|
136
|
+
earliest = record.earliest
|
137
|
+
# SQLite gives a string here
|
138
|
+
earliest = Time.zone.parse(earliest) if earliest.is_a?(String)
|
139
|
+
|
140
|
+
time_diff = Time.zone.now - earliest
|
136
141
|
metrics.gauge('pending_db_messages_max_wait', time_diff,
|
137
142
|
tags: ["topic:#{record.topic}"])
|
138
143
|
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Deimos
|
4
|
+
module Utils
|
5
|
+
# Utility class to retry a given block if a a deadlock is encountered.
|
6
|
+
# Supports Postgres and MySQL deadlocks and lock wait timeouts.
|
7
|
+
class DeadlockRetry
|
8
|
+
class << self
|
9
|
+
# Maximum number of times to retry the block after encountering a deadlock
|
10
|
+
RETRY_COUNT = 2
|
11
|
+
|
12
|
+
# Need to match on error messages to support older Rails versions
|
13
|
+
DEADLOCK_MESSAGES = [
|
14
|
+
# MySQL
|
15
|
+
'Deadlock found when trying to get lock',
|
16
|
+
'Lock wait timeout exceeded',
|
17
|
+
|
18
|
+
# Postgres
|
19
|
+
'deadlock detected'
|
20
|
+
].freeze
|
21
|
+
|
22
|
+
# Retry the given block when encountering a deadlock. For any other
|
23
|
+
# exceptions, they are reraised. This is used to handle cases where
|
24
|
+
# the database may be busy but the transaction would succeed if
|
25
|
+
# retried later. Note that your block should be idempotent and it will
|
26
|
+
# be wrapped in a transaction.
|
27
|
+
# Sleeps for a random number of seconds to prevent multiple transactions
|
28
|
+
# from retrying at the same time.
|
29
|
+
# @param tags [Array] Tags to attach when logging and reporting metrics.
|
30
|
+
# @yield Yields to the block that may deadlock.
|
31
|
+
def wrap(tags=[])
|
32
|
+
count = RETRY_COUNT
|
33
|
+
|
34
|
+
begin
|
35
|
+
ActiveRecord::Base.transaction do
|
36
|
+
yield
|
37
|
+
end
|
38
|
+
rescue ActiveRecord::StatementInvalid => e
|
39
|
+
# Reraise if not a known deadlock
|
40
|
+
raise if DEADLOCK_MESSAGES.none? { |m| e.message.include?(m) }
|
41
|
+
|
42
|
+
# Reraise if all retries exhausted
|
43
|
+
raise if count <= 0
|
44
|
+
|
45
|
+
Deimos.config.logger.warn(
|
46
|
+
message: 'Deadlock encountered when trying to execute query. '\
|
47
|
+
"Retrying. #{count} attempt(s) remaining",
|
48
|
+
tags: tags
|
49
|
+
)
|
50
|
+
|
51
|
+
Deimos.config.metrics&.increment(
|
52
|
+
'deadlock',
|
53
|
+
tags: tags
|
54
|
+
)
|
55
|
+
|
56
|
+
count -= 1
|
57
|
+
|
58
|
+
# Sleep for a random amount so that if there are multiple
|
59
|
+
# transactions deadlocking, they don't all retry at the same time
|
60
|
+
sleep(Random.rand(5.0) + 0.5)
|
61
|
+
|
62
|
+
retry
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
@@ -29,30 +29,21 @@ module Deimos
|
|
29
29
|
self.topics[topic.to_s].report_lag(partition)
|
30
30
|
end
|
31
31
|
|
32
|
-
# @param topic [String]
|
33
|
-
# @param partition [Integer]
|
34
|
-
# @param lag [Integer]
|
35
|
-
def assign_lag(topic, partition, lag)
|
36
|
-
self.topics[topic.to_s] ||= Topic.new(topic, self)
|
37
|
-
self.topics[topic.to_s].assign_lag(partition, lag)
|
38
|
-
end
|
39
|
-
|
40
|
-
# Figure out the current lag by asking Kafka based on the current offset.
|
41
32
|
# @param topic [String]
|
42
33
|
# @param partition [Integer]
|
43
34
|
# @param offset [Integer]
|
44
|
-
def
|
35
|
+
def assign_current_offset(topic, partition, offset)
|
45
36
|
self.topics[topic.to_s] ||= Topic.new(topic, self)
|
46
|
-
self.topics[topic.to_s].
|
37
|
+
self.topics[topic.to_s].assign_current_offset(partition, offset)
|
47
38
|
end
|
48
39
|
end
|
49
40
|
|
50
|
-
# Topic which has a hash of partition => last known
|
41
|
+
# Topic which has a hash of partition => last known current offsets
|
51
42
|
class Topic
|
52
43
|
# @return [String]
|
53
44
|
attr_accessor :topic_name
|
54
45
|
# @return [Hash<Integer, Integer>]
|
55
|
-
attr_accessor :
|
46
|
+
attr_accessor :partition_current_offsets
|
56
47
|
# @return [ConsumerGroup]
|
57
48
|
attr_accessor :consumer_group
|
58
49
|
|
@@ -61,35 +52,33 @@ module Deimos
|
|
61
52
|
def initialize(topic_name, group)
|
62
53
|
self.topic_name = topic_name
|
63
54
|
self.consumer_group = group
|
64
|
-
self.
|
55
|
+
self.partition_current_offsets = {}
|
65
56
|
end
|
66
57
|
|
67
58
|
# @param partition [Integer]
|
68
|
-
|
69
|
-
|
70
|
-
self.partition_offset_lags[partition.to_i] = lag
|
59
|
+
def assign_current_offset(partition, offset)
|
60
|
+
self.partition_current_offsets[partition.to_i] = offset
|
71
61
|
end
|
72
62
|
|
73
63
|
# @param partition [Integer]
|
74
|
-
# @param offset [Integer]
|
75
64
|
def compute_lag(partition, offset)
|
76
|
-
return if self.partition_offset_lags[partition.to_i]
|
77
|
-
|
78
65
|
begin
|
79
66
|
client = Phobos.create_kafka_client
|
80
67
|
last_offset = client.last_offset_for(self.topic_name, partition)
|
81
|
-
|
68
|
+
lag = last_offset - offset
|
82
69
|
rescue StandardError # don't do anything, just wait
|
83
70
|
Deimos.config.logger.
|
84
71
|
debug("Error computing lag for #{self.topic_name}, will retry")
|
85
72
|
end
|
73
|
+
lag || 0
|
86
74
|
end
|
87
75
|
|
88
76
|
# @param partition [Integer]
|
89
77
|
def report_lag(partition)
|
90
|
-
|
91
|
-
return unless
|
78
|
+
current_offset = self.partition_current_offsets[partition.to_i]
|
79
|
+
return unless current_offset
|
92
80
|
|
81
|
+
lag = compute_lag(partition, current_offset)
|
93
82
|
group = self.consumer_group.id
|
94
83
|
Deimos.config.logger.
|
95
84
|
debug("Sending lag: #{group}/#{partition}: #{lag}")
|
@@ -109,16 +98,20 @@ module Deimos
|
|
109
98
|
@groups = {}
|
110
99
|
end
|
111
100
|
|
101
|
+
# offset_lag = event.payload.fetch(:offset_lag)
|
102
|
+
# group_id = event.payload.fetch(:group_id)
|
103
|
+
# topic = event.payload.fetch(:topic)
|
104
|
+
# partition = event.payload.fetch(:partition)
|
112
105
|
# @param payload [Hash]
|
113
106
|
def message_processed(payload)
|
114
|
-
|
107
|
+
offset = payload[:offset] || payload[:last_offset]
|
115
108
|
topic = payload[:topic]
|
116
109
|
group = payload[:group_id]
|
117
110
|
partition = payload[:partition]
|
118
111
|
|
119
112
|
synchronize do
|
120
113
|
@groups[group.to_s] ||= ConsumerGroup.new(group)
|
121
|
-
@groups[group.to_s].
|
114
|
+
@groups[group.to_s].assign_current_offset(topic, partition, offset)
|
122
115
|
end
|
123
116
|
end
|
124
117
|
|
@@ -131,7 +124,7 @@ module Deimos
|
|
131
124
|
|
132
125
|
synchronize do
|
133
126
|
@groups[group.to_s] ||= ConsumerGroup.new(group)
|
134
|
-
@groups[group.to_s].
|
127
|
+
@groups[group.to_s].assign_current_offset(topic, partition, offset)
|
135
128
|
end
|
136
129
|
end
|
137
130
|
|