deimos-ruby 2.6.0 → 2.6.1
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/CHANGELOG.md +7 -0
- data/lib/deimos/kafka_source.rb +6 -3
- data/lib/deimos/kafka_topic_info.rb +9 -5
- data/lib/deimos/message.rb +3 -3
- data/lib/deimos/version.rb +1 -1
- data/spec/active_record_batch_consumer_association_spec.rb +1 -2
- data/spec/active_record_consume/mass_updater_spec.rb +1 -2
- data/spec/active_record_consumer_spec.rb +1 -2
- data/spec/kafka_source_spec.rb +1 -2
- data/spec/kafka_topic_info_spec.rb +6 -0
- data/spec/karafka_config/karafka_spec.rb +1 -2
- data/spec/utils/outbox_producer_spec.rb +1 -2
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 132519f800025ce3300467213dfaa1136cbbb1ffe9c17074a09c84fd5292c151
|
|
4
|
+
data.tar.gz: cee617e162f21cee7e7d62c0c31a9b37451273a42be8d4007e94fb998b94ddb2
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 865fd30cd8674ab0b311664f1ce0692c043f46fd742e2d52cc5c72f62d91e9ccd908bed6c97a27eb0d81702f7c1b866ccd225ebde2f78ea2d73e8ec50f22fbad
|
|
7
|
+
data.tar.gz: 68c968a36f4eb6688d5877eb6514e4a8abe981336f2847b246aded7aeb3521ce2d92583bac1366b36865d7eb8e95f8c9133d79395f5e68f5ce56947788f47d65
|
data/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,13 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## Unreleased
|
|
9
|
+
|
|
10
|
+
## 2.6.1 - 2026-09-03
|
|
11
|
+
|
|
12
|
+
- Fix: `KafkaTopicInfo.lock` only attempts the `INSERT` when the topic row is missing, instead of relying on a unique-index failure. On MySQL a failed insert still consumes an auto-increment value, so the old behaviour burned an ID on every lock attempt and could exhaust the ID space.
|
|
13
|
+
- Fix: `KafkaSource#import_without_validations_or_callbacks` accepts a variable number of arguments, so it works with both `activerecord-import` 2.3+ (which prepends a `conn` argument) and earlier versions. Previously an app on 2.3+ raised `ArgumentError: wrong number of arguments` on any import with `kafka_config[:import]` enabled.
|
|
14
|
+
|
|
8
15
|
## 2.6.0 - 2026-08-13
|
|
9
16
|
|
|
10
17
|
- Breaking: a failed batch database write is now retried one record at a time, so one unpersistable record no longer loses the whole batch. `Deimos::BatchFallbackError` is raised naming the keys that still failed.
|
data/lib/deimos/kafka_source.rb
CHANGED
|
@@ -88,10 +88,13 @@ end
|
|
|
88
88
|
# Basically we want to first do the import, then reload the records
|
|
89
89
|
# and send them to Kafka.
|
|
90
90
|
# @!visibility private
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
91
|
+
#
|
|
92
|
+
# activerecord-import 2.3+ prepends a `conn` arg to this method - accept a splat and
|
|
93
|
+
# grab the last 3 args (column_names, array_of_attributes, options) either way.
|
|
94
|
+
def import_without_validations_or_callbacks(*args)
|
|
94
95
|
results = super
|
|
96
|
+
column_names, array_of_attributes, options = args.last(3)
|
|
97
|
+
options ||= {}
|
|
95
98
|
if !self.kafka_config[:import] || array_of_attributes.empty?
|
|
96
99
|
return results
|
|
97
100
|
end
|
|
@@ -16,11 +16,15 @@ module Deimos
|
|
|
16
16
|
# @param lock_id [String]
|
|
17
17
|
# @return [Boolean]
|
|
18
18
|
def lock(topic, lock_id) # rubocop:disable Naming/PredicateMethod
|
|
19
|
-
#
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
19
|
+
# Only attempt the insert if the row is missing. On MySQL a failed
|
|
20
|
+
# insert still consumes an auto-increment value, so retrying it on
|
|
21
|
+
# every lock attempt would eventually exhaust the ID space.
|
|
22
|
+
unless self.exists?(topic: topic)
|
|
23
|
+
begin
|
|
24
|
+
self.create!(topic: topic, last_processed_at: Time.zone.now)
|
|
25
|
+
rescue ActiveRecord::RecordNotUnique
|
|
26
|
+
# another process created it in the meantime - continue on
|
|
27
|
+
end
|
|
24
28
|
end
|
|
25
29
|
|
|
26
30
|
# Lock the record
|
data/lib/deimos/message.rb
CHANGED
|
@@ -12,11 +12,11 @@ module Deimos
|
|
|
12
12
|
# @return [Integer]
|
|
13
13
|
attr_accessor :partition_key
|
|
14
14
|
# @return [String]
|
|
15
|
-
attr_accessor
|
|
15
|
+
attr_accessor :encoded_key
|
|
16
16
|
# @return [String]
|
|
17
|
-
attr_accessor
|
|
17
|
+
attr_accessor :encoded_payload
|
|
18
18
|
# @return [String]
|
|
19
|
-
attr_accessor
|
|
19
|
+
attr_accessor :topic
|
|
20
20
|
|
|
21
21
|
# @param payload [Hash]
|
|
22
22
|
# @param topic [String]
|
data/lib/deimos/version.rb
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
# rubocop:disable Lint/ConstantDefinitionInBlock
|
|
3
|
+
# rubocop:disable-next Lint/ConstantDefinitionInBlock
|
|
4
4
|
module ActiveRecordBatchConsumerTest # rubocop:disable Metrics/ModuleLength
|
|
5
5
|
describe Deimos::ActiveRecordConsumer,
|
|
6
6
|
'Batch Consumer with MySQL handling associations',
|
|
@@ -321,4 +321,3 @@ module ActiveRecordBatchConsumerTest # rubocop:disable Metrics/ModuleLength
|
|
|
321
321
|
end
|
|
322
322
|
end
|
|
323
323
|
end
|
|
324
|
-
# rubocop:enable Lint/ConstantDefinitionInBlock
|
|
@@ -223,7 +223,7 @@ RSpec.describe Deimos::ActiveRecordConsume::MassUpdater do
|
|
|
223
223
|
WidgetFidget.reset_column_information
|
|
224
224
|
end
|
|
225
225
|
|
|
226
|
-
# rubocop:disable RSpec/ExampleLength
|
|
226
|
+
# rubocop:disable-next RSpec/ExampleLength
|
|
227
227
|
it 'should backfill the associations when upserting primary records' do
|
|
228
228
|
batch = Deimos::ActiveRecordConsume::BatchRecordList.new(
|
|
229
229
|
[
|
|
@@ -275,7 +275,6 @@ RSpec.describe Deimos::ActiveRecordConsume::MassUpdater do
|
|
|
275
275
|
expect(widget_fidget.note).to eq("Stuff #{ind + 1}")
|
|
276
276
|
end
|
|
277
277
|
end
|
|
278
|
-
# rubocop:enable RSpec/ExampleLength
|
|
279
278
|
|
|
280
279
|
end
|
|
281
280
|
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
require 'date'
|
|
4
4
|
|
|
5
5
|
# Wrapped in a module to prevent class leakage
|
|
6
|
-
# rubocop:disable Metrics/ModuleLength
|
|
6
|
+
# rubocop:disable-next Metrics/ModuleLength
|
|
7
7
|
module ActiveRecordConsumerTest
|
|
8
8
|
describe Deimos::ActiveRecordConsumer, 'Message Consumer' do
|
|
9
9
|
before(:all) do
|
|
@@ -299,4 +299,3 @@ module ActiveRecordConsumerTest
|
|
|
299
299
|
end
|
|
300
300
|
end
|
|
301
301
|
end
|
|
302
|
-
# rubocop:enable Metrics/ModuleLength
|
data/spec/kafka_source_spec.rb
CHANGED
|
@@ -5,7 +5,7 @@ require 'activerecord-import'
|
|
|
5
5
|
# Wrap in a module so our classes don't leak out afterwards
|
|
6
6
|
module KafkaSourceSpec
|
|
7
7
|
RSpec.describe Deimos::KafkaSource do
|
|
8
|
-
# rubocop:disable Lint/ConstantDefinitionInBlock
|
|
8
|
+
# rubocop:disable-next Lint/ConstantDefinitionInBlock
|
|
9
9
|
before(:all) do
|
|
10
10
|
ActiveRecord::Base.connection.create_table(:widgets, force: true) do |t|
|
|
11
11
|
t.integer(:widget_id)
|
|
@@ -42,7 +42,6 @@ module KafkaSourceSpec
|
|
|
42
42
|
end
|
|
43
43
|
Widget.reset_column_information
|
|
44
44
|
end
|
|
45
|
-
# rubocop:enable Lint/ConstantDefinitionInBlock
|
|
46
45
|
|
|
47
46
|
after(:all) do
|
|
48
47
|
ActiveRecord::Base.connection.drop_table(:widgets)
|
|
@@ -11,6 +11,12 @@ each_db_config(Deimos::KafkaTopicInfo) do
|
|
|
11
11
|
expect(described_class.last.locked_by).to eq('def')
|
|
12
12
|
end
|
|
13
13
|
|
|
14
|
+
it 'should not attempt to create the record if it already exists' do
|
|
15
|
+
described_class.create!(topic: 'my-topic', last_processed_at: Time.zone.now)
|
|
16
|
+
expect(described_class).not_to receive(:create!)
|
|
17
|
+
expect(described_class.lock('my-topic', 'abc')).to be_truthy
|
|
18
|
+
end
|
|
19
|
+
|
|
14
20
|
it 'should raise an error if the create fails for a non-unique reason' do
|
|
15
21
|
allow(described_class).to receive(:create!).and_raise(ActiveRecord::ActiveRecordError, 'some other error')
|
|
16
22
|
expect { described_class.lock('my-topic', 'abc') }.to raise_error(ActiveRecord::ActiveRecordError)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
# rubocop:disable Style/GlobalVars
|
|
3
|
+
# rubocop:disable-next Style/GlobalVars
|
|
4
4
|
RSpec.describe Karafka::Routing::Topic do
|
|
5
5
|
before(:each) do
|
|
6
6
|
KarafkaApp.routes.clear
|
|
@@ -150,4 +150,3 @@ RSpec.describe Karafka::Routing::Topic do
|
|
|
150
150
|
end
|
|
151
151
|
|
|
152
152
|
end
|
|
153
|
-
# rubocop:enable Style/GlobalVars
|
|
@@ -137,7 +137,7 @@ each_db_config(Deimos::Utils::OutboxProducer) do
|
|
|
137
137
|
end
|
|
138
138
|
|
|
139
139
|
# using expect(x).to have_received(:y).ordered doesn't work
|
|
140
|
-
# rubocop:disable RSpec/StubbedMock
|
|
140
|
+
# rubocop:disable-next RSpec/StubbedMock
|
|
141
141
|
it 'should complete successfully' do # rubocop:disable RSpec/ExampleLength
|
|
142
142
|
messages = (1..4).map do |i|
|
|
143
143
|
Deimos::KafkaMessage.new(
|
|
@@ -203,7 +203,6 @@ each_db_config(Deimos::Utils::OutboxProducer) do
|
|
|
203
203
|
with('my-topic', 'abc').once
|
|
204
204
|
producer.process_topic('my-topic')
|
|
205
205
|
end
|
|
206
|
-
# rubocop:enable RSpec/StubbedMock
|
|
207
206
|
|
|
208
207
|
it 'should register an error if it gets an error' do
|
|
209
208
|
allow(producer).to receive(:retrieve_messages).and_raise('OH NOES')
|