deimos-ruby 1.8.3 → 1.8.4
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 +11 -0
- data/Gemfile.lock +1 -1
- data/README.md +9 -0
- data/docs/PULL_REQUEST_TEMPLATE.md +1 -0
- data/lib/deimos/active_record_consume/message_consumption.rb +9 -0
- data/lib/deimos/active_record_consumer.rb +8 -0
- data/lib/deimos/schema_backends/avro_schema_coercer.rb +5 -3
- data/lib/deimos/utils/inline_consumer.rb +8 -2
- data/lib/deimos/version.rb +1 -1
- data/spec/active_record_consumer_spec.rb +13 -0
- data/spec/schema_backends/avro_base_shared.rb +26 -1
- data/spec/utils/inline_consumer_spec.rb +31 -0
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 497160ac15346d0acb471818c0f02e10f745761ba3777a70039b1a762e8f4edb
|
4
|
+
data.tar.gz: d85717deea2ac68d6c995d1dce1c42187d4fe5585b577c4468cafff7f0f71097
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 441577e56812dbc9d0dbbf22b43cb76e390c792165f7d21febcdf17a309fe85ec57bd9ddaad0929ddd3d49dd602a94601fc2660b124419e5943460e2fab94aa5
|
7
|
+
data.tar.gz: 2fdd3a94d91130c2430d189165504bdf437907fa1a9a459841bd4e260a2ff906469fb0f6989a8faf6c140cc133d22639af91a89549c85a9aeef2145636aa7b58
|
data/CHANGELOG.md
CHANGED
@@ -7,6 +7,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
7
7
|
|
8
8
|
## UNRELEASED
|
9
9
|
|
10
|
+
## 1.8.4 - 2020-12-02
|
11
|
+
|
12
|
+
### Features :star:
|
13
|
+
- Add overridable "process_message?" method to ActiveRecordConsumer to allow for skipping of saving/updating records
|
14
|
+
|
15
|
+
### Fixes :wrench:
|
16
|
+
|
17
|
+
- Do not apply type coercion to `timestamp-millis` and `timestamp-micros` logical types (fixes [#97](https://github.com/flipp-oss/deimos/issues/97))
|
18
|
+
|
10
19
|
## 1.8.3 - 2020-11-18
|
11
20
|
|
12
21
|
### Fixes :wrench:
|
@@ -15,6 +24,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
15
24
|
- KafkaSource crashing on bulk-imports if import hooks are disabled
|
16
25
|
(fixes [#73](https://github.com/flipp-oss/deimos/issues/73))
|
17
26
|
- #96 Use string-safe encoding for partition keys
|
27
|
+
- Retry on offset seek failures in inline consumer
|
28
|
+
(fixes [#5](Inline consumer should use retries when seeking))
|
18
29
|
|
19
30
|
## 1.8.2 - 2020-09-25
|
20
31
|
|
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
@@ -626,6 +626,15 @@ class MyConsumer < Deimos::ActiveRecordConsumer
|
|
626
626
|
def record_key(payload)
|
627
627
|
super
|
628
628
|
end
|
629
|
+
|
630
|
+
# Optional override, returns true by default.
|
631
|
+
# When this method returns true, a record corresponding to the message
|
632
|
+
# is created/updated.
|
633
|
+
# When this method returns false, message processing is skipped and a
|
634
|
+
# corresponding record will NOT be created/updated.
|
635
|
+
def process_message?(payload)
|
636
|
+
super
|
637
|
+
end
|
629
638
|
end
|
630
639
|
```
|
631
640
|
|
@@ -28,6 +28,7 @@ Please describe the tests that you ran to verify your changes. Provide instructi
|
|
28
28
|
- [ ] I have performed a self-review of my own code
|
29
29
|
- [ ] I have commented my code, particularly in hard-to-understand areas
|
30
30
|
- [ ] I have made corresponding changes to the documentation
|
31
|
+
- [ ] I have added a line in the CHANGELOG describing this change, under the UNRELEASED heading
|
31
32
|
- [ ] My changes generate no new warnings
|
32
33
|
- [ ] I have added tests that prove my fix is effective or that my feature works
|
33
34
|
- [ ] New and existing unit tests pass locally with my changes
|
@@ -26,6 +26,15 @@ module Deimos
|
|
26
26
|
|
27
27
|
# :nodoc:
|
28
28
|
def consume(payload, metadata)
|
29
|
+
unless self.process_message?(payload)
|
30
|
+
Deimos.config.logger.debug(
|
31
|
+
message: 'Skipping processing of message',
|
32
|
+
payload: payload,
|
33
|
+
metadata: metadata
|
34
|
+
)
|
35
|
+
return
|
36
|
+
end
|
37
|
+
|
29
38
|
key = metadata.with_indifferent_access[:key]
|
30
39
|
klass = self.class.config[:record_class]
|
31
40
|
record = fetch_record(klass, (payload || {}).with_indifferent_access, key)
|
@@ -55,5 +55,13 @@ module Deimos
|
|
55
55
|
def record_attributes(payload, _key=nil)
|
56
56
|
@converter.convert(payload)
|
57
57
|
end
|
58
|
+
|
59
|
+
# Override this message to conditionally save records
|
60
|
+
# @param payload [Hash] The kafka message as a hash
|
61
|
+
# @return [Boolean] if true, record is created/update.
|
62
|
+
# If false, record processing is skipped but message offset is still committed.
|
63
|
+
def process_message?(_payload)
|
64
|
+
true
|
65
|
+
end
|
58
66
|
end
|
59
67
|
end
|
@@ -44,9 +44,11 @@ module Deimos
|
|
44
44
|
|
45
45
|
case field_type
|
46
46
|
when :int, :long
|
47
|
-
if
|
48
|
-
|
49
|
-
|
47
|
+
if %w(timestamp-millis timestamp-micros).include?(type.logical_type)
|
48
|
+
val
|
49
|
+
elsif val.is_a?(Integer) ||
|
50
|
+
_is_integer_string?(val) ||
|
51
|
+
int_classes.any? { |klass| val.is_a?(klass) }
|
50
52
|
val.to_i
|
51
53
|
else
|
52
54
|
val # this will fail
|
@@ -6,6 +6,7 @@ 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
|
+
MAX_SEEK_RETRIES = 3
|
9
10
|
attr_accessor :num_messages
|
10
11
|
|
11
12
|
# :nodoc:
|
@@ -13,8 +14,10 @@ module Deimos
|
|
13
14
|
@num_messages ||= 10
|
14
15
|
@consumer = create_kafka_consumer
|
15
16
|
@consumer.subscribe(topic, @subscribe_opts)
|
17
|
+
attempt = 0
|
16
18
|
|
17
19
|
begin
|
20
|
+
attempt += 1
|
18
21
|
last_offset = @kafka_client.last_offset_for(topic, 0)
|
19
22
|
offset = last_offset - num_messages
|
20
23
|
if offset.positive?
|
@@ -22,7 +25,11 @@ module Deimos
|
|
22
25
|
@consumer.seek(topic, 0, offset)
|
23
26
|
end
|
24
27
|
rescue StandardError => e
|
25
|
-
|
28
|
+
if attempt < MAX_SEEK_RETRIES
|
29
|
+
sleep(1.seconds * attempt)
|
30
|
+
retry
|
31
|
+
end
|
32
|
+
log_error("Could not seek to offset: #{e.message} after #{MAX_SEEK_RETRIES} retries", listener_metadata)
|
26
33
|
end
|
27
34
|
|
28
35
|
instrument('listener.start_handler', listener_metadata) do
|
@@ -50,7 +57,6 @@ module Deimos
|
|
50
57
|
|
51
58
|
# :nodoc:
|
52
59
|
def consume(payload, metadata)
|
53
|
-
puts "Got #{payload}"
|
54
60
|
self.class.total_messages << {
|
55
61
|
key: metadata[:key],
|
56
62
|
payload: payload
|
data/lib/deimos/version.rb
CHANGED
@@ -137,5 +137,18 @@ module ActiveRecordConsumerTest
|
|
137
137
|
expect(Widget.find_by_test_id('id1').some_int).to eq(3)
|
138
138
|
expect(Widget.find_by_test_id('id2').some_int).to eq(4)
|
139
139
|
end
|
140
|
+
|
141
|
+
it 'should not create record of process_message returns false' do
|
142
|
+
MyConsumer.any_instance.stub(:process_message?).and_return(false)
|
143
|
+
expect(Widget.count).to eq(0)
|
144
|
+
test_consume_message(MyConsumer, {
|
145
|
+
test_id: 'abc',
|
146
|
+
some_int: 3,
|
147
|
+
updated_at: 1.day.ago.to_i,
|
148
|
+
some_datetime_int: Time.zone.now.to_i,
|
149
|
+
timestamp: 2.minutes.ago.to_s
|
150
|
+
}, { call_original: true, key: 5 })
|
151
|
+
expect(Widget.count).to eq(0)
|
152
|
+
end
|
140
153
|
end
|
141
154
|
end
|
@@ -42,6 +42,20 @@ RSpec.shared_examples_for('an Avro backend') do
|
|
42
42
|
{
|
43
43
|
'name' => 'union-int-field',
|
44
44
|
'type' => %w(null int)
|
45
|
+
},
|
46
|
+
{
|
47
|
+
'name' => 'timestamp-millis-field',
|
48
|
+
'type' => {
|
49
|
+
'type' => 'long',
|
50
|
+
'logicalType' => 'timestamp-millis'
|
51
|
+
}
|
52
|
+
},
|
53
|
+
{
|
54
|
+
'name' => 'timestamp-micros-field',
|
55
|
+
'type' => {
|
56
|
+
'type' => 'long',
|
57
|
+
'logicalType' => 'timestamp-micros'
|
58
|
+
}
|
45
59
|
}
|
46
60
|
]
|
47
61
|
}
|
@@ -95,7 +109,9 @@ RSpec.shared_examples_for('an Avro backend') do
|
|
95
109
|
'string-field' => 'hi mom',
|
96
110
|
'boolean-field' => true,
|
97
111
|
'union-field' => nil,
|
98
|
-
'union-int-field' => nil
|
112
|
+
'union-int-field' => nil,
|
113
|
+
'timestamp-millis-field' => Time.utc(2020, 11, 12, 13, 14, 15, 909_090),
|
114
|
+
'timestamp-micros-field' => Time.utc(2020, 11, 12, 13, 14, 15, 909_090)
|
99
115
|
}
|
100
116
|
end
|
101
117
|
|
@@ -169,6 +185,15 @@ RSpec.shared_examples_for('an Avro backend') do
|
|
169
185
|
expect(result['union-field']).to eq('itsme')
|
170
186
|
end
|
171
187
|
|
188
|
+
it 'should not convert timestamp-millis' do
|
189
|
+
result = backend.coerce(payload)
|
190
|
+
expect(result['timestamp-millis-field']).to eq(Time.utc(2020, 11, 12, 13, 14, 15, 909_090))
|
191
|
+
end
|
192
|
+
|
193
|
+
it 'should not convert timestamp-micros' do
|
194
|
+
result = backend.coerce(payload)
|
195
|
+
expect(result['timestamp-micros-field']).to eq(Time.utc(2020, 11, 12, 13, 14, 15, 909_090))
|
196
|
+
end
|
172
197
|
end
|
173
198
|
|
174
199
|
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
describe Deimos::Utils::SeekListener do
|
4
|
+
|
5
|
+
describe '#start_listener' do
|
6
|
+
let(:consumer) { instance_double(Kafka::Consumer) }
|
7
|
+
let(:handler) { class_double(Deimos::Utils::MessageBankHandler) }
|
8
|
+
|
9
|
+
before(:each) do
|
10
|
+
allow(handler).to receive(:start)
|
11
|
+
allow(consumer).to receive(:subscribe)
|
12
|
+
allow_any_instance_of(Phobos::Listener).to receive(:create_kafka_consumer).and_return(consumer)
|
13
|
+
allow_any_instance_of(Kafka::Client).to receive(:last_offset_for).and_return(100)
|
14
|
+
stub_const('Deimos::Utils::SeekListener::MAX_SEEK_RETRIES', 2)
|
15
|
+
end
|
16
|
+
|
17
|
+
it 'should seek offset' do
|
18
|
+
allow(consumer).to receive(:seek)
|
19
|
+
expect(consumer).to receive(:seek).once
|
20
|
+
seek_listener = described_class.new({ handler: handler, group_id: 999, topic: 'test_topic' })
|
21
|
+
seek_listener.start_listener
|
22
|
+
end
|
23
|
+
|
24
|
+
it 'should retry on errors when seeking offset' do
|
25
|
+
allow(consumer).to receive(:seek).and_raise(StandardError)
|
26
|
+
expect(consumer).to receive(:seek).twice
|
27
|
+
seek_listener = described_class.new({ handler: handler, group_id: 999, topic: 'test_topic' })
|
28
|
+
seek_listener.start_listener
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
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.8.
|
4
|
+
version: 1.8.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Daniel Orner
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-
|
11
|
+
date: 2020-12-02 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: avro_turf
|
@@ -465,6 +465,7 @@ files:
|
|
465
465
|
- spec/utils/db_poller_spec.rb
|
466
466
|
- spec/utils/db_producer_spec.rb
|
467
467
|
- spec/utils/deadlock_retry_spec.rb
|
468
|
+
- spec/utils/inline_consumer_spec.rb
|
468
469
|
- spec/utils/lag_reporter_spec.rb
|
469
470
|
- spec/utils/platform_schema_validation_spec.rb
|
470
471
|
- spec/utils/schema_controller_mixin_spec.rb
|
@@ -548,6 +549,7 @@ test_files:
|
|
548
549
|
- spec/utils/db_poller_spec.rb
|
549
550
|
- spec/utils/db_producer_spec.rb
|
550
551
|
- spec/utils/deadlock_retry_spec.rb
|
552
|
+
- spec/utils/inline_consumer_spec.rb
|
551
553
|
- spec/utils/lag_reporter_spec.rb
|
552
554
|
- spec/utils/platform_schema_validation_spec.rb
|
553
555
|
- spec/utils/schema_controller_mixin_spec.rb
|