deimos-ruby 1.8.1.pre.beta2 → 1.8.1.pre.beta7

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e6f44746ef34713f973b24f4cf80954915545edec5e0f006e4d1a6c751478afa
4
- data.tar.gz: 17adb23fdc3e735cf8eab51f4c1df2bbcf1140c9a5696c0f8e17dc38bd728ba9
3
+ metadata.gz: 6ed6bdbc70b4e29dfd893853d2df53ae6779fe3a95adc85a099b270674908135
4
+ data.tar.gz: ca1da1180acac7a76cb3abd4ce9c4ab16712b16e94f88b79ae5c952d98a74500
5
5
  SHA512:
6
- metadata.gz: d920dc6b7b2020364a7095ab61a986a3bb16c5fac5367227ed8cbded422399e78887fc91ecce81d9857c66bce5739f6423335e835ca87612a1a2306c50cd93b0
7
- data.tar.gz: 78fd60871b0852b03e91e2223afbe5ac419dc929d6de6340799f8376f89efffd2775e4d93025ab8089cac5a7579d00bc33c64c3f9ec50cc3fb603f6381e3d82a
6
+ metadata.gz: fd3b5728ce13e60b35ffc0b7dd025e402fa4bbd760edc000ca8cd587bca0c7f39d136eba951ebd631a711cf79171716021f04bda38c8262318ab344d8452be34
7
+ data.tar.gz: '0305200088b9d463c92562be1d9bf27d2727a06c10a147fdb6bace097adc795b2cfca6d5942eb4f06ee0da316e5a41db26b1fd04698d63987e2b45fdf7aea4c7'
@@ -7,6 +7,37 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## UNRELEASED
9
9
 
10
+ ## 1.8.1-beta7 - 2020-08-25
11
+
12
+ ### Fixes :wrench:
13
+ - Fix for crash when sending pending metrics with DB producer.
14
+ - Fix for compacting messages if all the keys are already unique
15
+ (fixes [#75](https://github.com/flipp-oss/deimos/issues/75))
16
+
17
+ ## 1.8.1-beta6 - 2020-08-13
18
+
19
+ ### Fixes :wrench:
20
+
21
+ - Fix for consuming nil payloads with Ruby 2.3.
22
+
23
+ ## 1.8.1-beta5 - 2020-08-13
24
+
25
+ ### Fixes :wrench:
26
+ - Fix regression bug which introduces backwards incompatibility
27
+ with ActiveRecordProducer's `record_attributes` method.
28
+
29
+ ## 1.8.1-beta4 - 2020-08-12
30
+
31
+ ### Fixes :wrench:
32
+ - Fix regression bug where arrays were not being encoded
33
+
34
+ ## 1.8.1-beta3 - 2020-08-05
35
+
36
+ ### Fixes :wrench:
37
+ - Simplify decoding messages and handle producer not found
38
+ - Consolidate types in sub-records recursively
39
+ (fixes [#72](https://github.com/flipp-oss/deimos/issues/72))
40
+
10
41
  ## 1.8.1-beta2 - 2020-07-28
11
42
 
12
43
  ### Features :star:
@@ -41,6 +72,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
41
72
  - Added `ActiveRecordConsumer` batch mode
42
73
 
43
74
  ### Fixes :wrench:
75
+ - Fixes `send_produce_error` to decode `failed_messages` with built-in decoder.
44
76
  - Lag calculation can be incorrect if no messages are being consumed.
45
77
  - Fixed bug where printing messages on a MessageSizeTooLarge
46
78
  error didn't work.
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- deimos-ruby (1.8.1.pre.beta2)
4
+ deimos-ruby (1.8.1.pre.beta7)
5
5
  avro_turf (~> 0.11)
6
6
  phobos (~> 1.9)
7
7
  ruby-kafka (~> 0.7)
data/README.md CHANGED
@@ -599,7 +599,7 @@ class MyConsumer < Deimos::ActiveRecordConsumer
599
599
 
600
600
  # Optional override to change the attributes of the record before they
601
601
  # are saved.
602
- def record_attributes(payload)
602
+ def record_attributes(payload, key)
603
603
  super.merge(:some_field => 'some_value')
604
604
  end
605
605
 
@@ -680,7 +680,7 @@ class MyConsumer < Deimos::ActiveRecordConsumer
680
680
 
681
681
  # Optional override to change the attributes of the record before they
682
682
  # are saved.
683
- def record_attributes(payload)
683
+ def record_attributes(payload, key)
684
684
  super.merge(:some_field => 'some_value')
685
685
  end
686
686
  end
@@ -88,8 +88,13 @@ module Deimos
88
88
 
89
89
  # Create payloads with payload + key attributes
90
90
  upserts = messages.map do |m|
91
- record_attributes(m.payload, m.key)&.
92
- merge(record_key(m.key))
91
+ attrs = if self.method(:record_attributes).parameters.size == 2
92
+ record_attributes(m.payload, m.key)
93
+ else
94
+ record_attributes(m.payload)
95
+ end
96
+
97
+ attrs&.merge(record_key(m.key))
93
98
  end
94
99
 
95
100
  # If overridden record_attributes indicated no record, skip
@@ -37,7 +37,14 @@ module Deimos
37
37
  record = klass.new
38
38
  assign_key(record, payload, key)
39
39
  end
40
- attrs = record_attributes(payload.with_indifferent_access, key)
40
+
41
+ # for backwards compatibility
42
+ # TODO next major release we should deprecate this
43
+ attrs = if self.method(:record_attributes).parameters.size == 2
44
+ record_attributes(payload.with_indifferent_access, key)
45
+ else
46
+ record_attributes(payload.with_indifferent_access)
47
+ end
41
48
  # don't use attributes= - bypass Rails < 5 attr_protected
42
49
  attrs.each do |k, v|
43
50
  record.send("#{k}=", v)
@@ -10,7 +10,7 @@ module Deimos
10
10
 
11
11
  # :nodoc:
12
12
  def around_consume(payload, metadata)
13
- decoded_payload = payload.dup
13
+ decoded_payload = payload.nil? ? nil : payload.dup
14
14
  new_metadata = metadata.dup
15
15
  benchmark = Benchmark.measure do
16
16
  _with_span do
@@ -46,13 +46,18 @@ module Deimos
46
46
 
47
47
  messages = exception.failed_messages
48
48
  messages.group_by(&:topic).each do |topic, batch|
49
- next if batch.empty?
49
+ producer = Deimos::Producer.descendants.find { |c| c.topic == topic }
50
+ next if batch.empty? || !producer
50
51
 
51
- producer = batch.first.metadata[:producer_name]
52
- payloads = batch.map { |m| m.metadata[:decoded_payload] }
52
+ decoder = Deimos.schema_backend(schema: producer.config[:schema],
53
+ namespace: producer.config[:namespace])
54
+ payloads = batch.map { |m| decoder.decode(m.value) }
53
55
 
54
- Deimos.config.metrics&.count('publish_error', payloads.size,
55
- tags: %W(topic:#{topic}))
56
+ Deimos.config.metrics&.increment(
57
+ 'publish_error',
58
+ tags: %W(topic:#{topic}),
59
+ by: payloads.size
60
+ )
56
61
  Deimos.instrument(
57
62
  'produce_error',
58
63
  producer: producer,
@@ -10,18 +10,37 @@ module Deimos
10
10
  @schema = schema
11
11
  end
12
12
 
13
- # @param type [Symbol]
13
+ # Coerce sub-records in a payload to match the schema.
14
+ # @param type [Avro::Schema::UnionSchema]
15
+ # @param val [Object]
16
+ # @return [Object]
17
+ def coerce_union(type, val)
18
+ union_types = type.schemas.map { |s| s.type.to_sym }
19
+ return nil if val.nil? && union_types.include?(:null)
20
+
21
+ schema_type = type.schemas.find { |s| s.type.to_sym != :null }
22
+ coerce_type(schema_type, val)
23
+ end
24
+
25
+ # Coerce sub-records in a payload to match the schema.
26
+ # @param type [Avro::Schema::RecordSchema]
27
+ # @param val [Object]
28
+ # @return [Object]
29
+ def coerce_record(type, val)
30
+ record = val.map do |name, value|
31
+ field = type.fields.find { |f| f.name == name }
32
+ coerce_type(field.type, value)
33
+ end
34
+ val.keys.zip(record).to_h
35
+ end
36
+
37
+ # Coerce values in a payload to match the schema.
38
+ # @param type [Avro::Schema]
14
39
  # @param val [Object]
15
40
  # @return [Object]
16
41
  def coerce_type(type, val)
17
42
  int_classes = [Time, ActiveSupport::TimeWithZone]
18
43
  field_type = type.type.to_sym
19
- if field_type == :union
20
- union_types = type.schemas.map { |s| s.type.to_sym }
21
- return nil if val.nil? && union_types.include?(:null)
22
-
23
- field_type = union_types.find { |t| t != :null }
24
- end
25
44
 
26
45
  case field_type
27
46
  when :int, :long
@@ -32,14 +51,12 @@ module Deimos
32
51
  else
33
52
  val # this will fail
34
53
  end
35
-
36
54
  when :float, :double
37
55
  if val.is_a?(Numeric) || _is_float_string?(val)
38
56
  val.to_f
39
57
  else
40
58
  val # this will fail
41
59
  end
42
-
43
60
  when :string
44
61
  if val.respond_to?(:to_str)
45
62
  val.to_s
@@ -54,6 +71,10 @@ module Deimos
54
71
  else
55
72
  true
56
73
  end
74
+ when :union
75
+ coerce_union(type, val)
76
+ when :record
77
+ coerce_record(type, val)
57
78
  else
58
79
  val
59
80
  end
@@ -161,7 +161,7 @@ module Deimos
161
161
  # the oldest message, or the last time we processed, whichever comes
162
162
  # last.
163
163
  if message_record
164
- record_earliest = record.earliest
164
+ record_earliest = message_record.earliest
165
165
  # SQLite gives a string here
166
166
  if record_earliest.is_a?(String)
167
167
  record_earliest = Time.zone.parse(record_earliest)
@@ -231,7 +231,7 @@ module Deimos
231
231
  return batch if config.compact_topics != :all &&
232
232
  !config.compact_topics.include?(topic)
233
233
 
234
- batch.reverse.uniq!(&:key).reverse!
234
+ batch.reverse.uniq(&:key).reverse!
235
235
  end
236
236
  end
237
237
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Deimos
4
- VERSION = '1.8.1-beta2'
4
+ VERSION = '1.8.1-beta7'
5
5
  end
@@ -32,6 +32,12 @@ module ConsumerTest
32
32
  end
33
33
  end
34
34
 
35
+ it 'should consume a nil message' do
36
+ test_consume_message(MyConsumer, nil) do |payload, _metadata|
37
+ expect(payload).to be_nil
38
+ end
39
+ end
40
+
35
41
  it 'should consume a message idempotently' do
36
42
  # testing for a crash and re-consuming the same message/metadata
37
43
  key = { 'test_id' => 'foo' }
@@ -0,0 +1,55 @@
1
+ # frozen_string_literal: true
2
+
3
+ describe Deimos::KafkaListener do
4
+ include_context 'with widgets'
5
+
6
+ prepend_before(:each) do
7
+ producer_class = Class.new(Deimos::Producer) do
8
+ schema 'MySchema'
9
+ namespace 'com.my-namespace'
10
+ topic 'my-topic'
11
+ key_config none: true
12
+ end
13
+ stub_const('MyProducer', producer_class)
14
+ end
15
+
16
+ before(:each) do
17
+ Deimos.configure do |c|
18
+ c.producers.backend = :kafka
19
+ c.schema.backend = :avro_local
20
+ end
21
+ allow_any_instance_of(Kafka::Cluster).to receive(:add_target_topics)
22
+ allow_any_instance_of(Kafka::Cluster).to receive(:partitions_for).
23
+ and_raise(Kafka::Error)
24
+ end
25
+
26
+ describe '.send_produce_error' do
27
+ let(:payloads) do
28
+ [{ 'test_id' => 'foo', 'some_int' => 123 },
29
+ { 'test_id' => 'bar', 'some_int' => 124 }]
30
+ end
31
+
32
+ it 'should listen to publishing errors and republish as Deimos events' do
33
+ allow(Deimos::Producer).to receive(:descendants).and_return([MyProducer])
34
+ Deimos.subscribe('produce_error') do |event|
35
+ expect(event.payload).to include(
36
+ producer: MyProducer,
37
+ topic: 'my-topic',
38
+ payloads: payloads
39
+ )
40
+ end
41
+ expect(Deimos.config.metrics).to receive(:increment).
42
+ with('publish_error', tags: %w(topic:my-topic), by: 2)
43
+ expect { MyProducer.publish_list(payloads) }.to raise_error(Kafka::DeliveryFailed)
44
+ end
45
+
46
+ it 'should not send any notifications when producer is not found' do
47
+ Deimos.subscribe('produce_error') do |_|
48
+ raise 'OH NOES'
49
+ end
50
+ allow(Deimos::Producer).to receive(:descendants).and_return([])
51
+ expect(Deimos.config.metrics).not_to receive(:increment).with('publish_error', anything)
52
+ expect { MyProducer.publish_list(payloads) }.to raise_error(Kafka::DeliveryFailed)
53
+ end
54
+ end
55
+ end
@@ -41,6 +41,14 @@ module ProducerTest
41
41
  end
42
42
  stub_const('MyNoKeyProducer', producer_class)
43
43
 
44
+ producer_class = Class.new(Deimos::Producer) do
45
+ schema 'MyNestedSchema'
46
+ namespace 'com.my-namespace'
47
+ topic 'my-topic'
48
+ key_config field: 'test_id'
49
+ end
50
+ stub_const('MyNestedSchemaProducer', producer_class)
51
+
44
52
  producer_class = Class.new(Deimos::Producer) do
45
53
  schema 'MySchema'
46
54
  namespace 'com.my-namespace'
@@ -233,6 +241,34 @@ module ProducerTest
233
241
  )
234
242
  end
235
243
 
244
+ it 'should properly encode and coerce values with a nested record' do
245
+ expect(MyNestedSchemaProducer.encoder).to receive(:encode_key).with('test_id', 'foo', topic: 'my-topic-key')
246
+ MyNestedSchemaProducer.publish(
247
+ 'test_id' => 'foo',
248
+ 'test_float' => BigDecimal('123.456'),
249
+ 'test_array' => ['1'],
250
+ 'some_nested_record' => {
251
+ 'some_int' => 123,
252
+ 'some_float' => BigDecimal('456.789'),
253
+ 'some_string' => '123',
254
+ 'some_optional_int' => nil
255
+ },
256
+ 'some_optional_record' => nil
257
+ )
258
+ expect(MyNestedSchemaProducer.topic).to have_sent(
259
+ 'test_id' => 'foo',
260
+ 'test_float' => 123.456,
261
+ 'test_array' => ['1'],
262
+ 'some_nested_record' => {
263
+ 'some_int' => 123,
264
+ 'some_float' => 456.789,
265
+ 'some_string' => '123',
266
+ 'some_optional_int' => nil
267
+ },
268
+ 'some_optional_record' => nil
269
+ )
270
+ end
271
+
236
272
  it 'should error with nothing set' do
237
273
  expect {
238
274
  MyErrorProducer.publish_list(
@@ -0,0 +1,62 @@
1
+ {
2
+ "namespace": "com.my-namespace",
3
+ "name": "MyNestedSchema",
4
+ "type": "record",
5
+ "doc": "Test schema",
6
+ "fields": [
7
+ {
8
+ "name": "test_id",
9
+ "type": "string",
10
+ "doc": "test string"
11
+ },
12
+ {
13
+ "name": "test_float",
14
+ "type": "float",
15
+ "doc": "test float"
16
+ },
17
+ {
18
+ "name": "test_array",
19
+ "type": {
20
+ "type": "array",
21
+ "items": "string"
22
+ }
23
+ },
24
+ {
25
+ "name": "some_nested_record",
26
+ "doc": "some nested record",
27
+ "type": {
28
+ "name": "MyNestedRecord",
29
+ "type": "record",
30
+ "fields": [
31
+ {
32
+ "name": "some_int",
33
+ "type": "int",
34
+ "doc": "some int"
35
+ },
36
+ {
37
+ "name": "some_float",
38
+ "type": "float",
39
+ "doc": "some float"
40
+ },
41
+ {
42
+ "name": "some_string",
43
+ "type": "string",
44
+ "doc": "some string"
45
+ },
46
+ {
47
+ "name": "some_optional_int",
48
+ "type": [ "null", "int" ],
49
+ "doc": "some optional int",
50
+ "default": null
51
+ }
52
+ ]
53
+ }
54
+ },
55
+ {
56
+ "name": "some_optional_record",
57
+ "doc": "some optional record",
58
+ "type": [ "null", "MyNestedRecord" ],
59
+ "default": null
60
+ }
61
+ ]
62
+ }
@@ -155,6 +155,10 @@ each_db_config(Deimos::Utils::DbProducer) do
155
155
  Deimos.configure { |c| c.db_producer.compact_topics = [] }
156
156
  end
157
157
 
158
+ it 'should compact messages when all messages are unique' do
159
+ Deimos.configure { |c| c.db_producer.compact_topics = %w(my-topic my-topic2) }
160
+ expect(producer.compact_messages(deduped_batch)).to eq(deduped_batch)
161
+ end
158
162
  end
159
163
  end
160
164
 
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.1.pre.beta2
4
+ version: 1.8.1.pre.beta7
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-07-28 00:00:00.000000000 Z
11
+ date: 2020-08-25 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: avro_turf
@@ -428,6 +428,7 @@ files:
428
428
  - spec/generators/active_record_generator_spec.rb
429
429
  - spec/handlers/my_batch_consumer.rb
430
430
  - spec/handlers/my_consumer.rb
431
+ - spec/kafka_listener_spec.rb
431
432
  - spec/kafka_source_spec.rb
432
433
  - spec/kafka_topic_info_spec.rb
433
434
  - spec/message_spec.rb
@@ -441,6 +442,7 @@ files:
441
442
  - spec/schema_backends/avro_validation_spec.rb
442
443
  - spec/schema_backends/base_spec.rb
443
444
  - spec/schemas/com/my-namespace/Generated.avsc
445
+ - spec/schemas/com/my-namespace/MyNestedSchema.avsc
444
446
  - spec/schemas/com/my-namespace/MySchema-key.avsc
445
447
  - spec/schemas/com/my-namespace/MySchema.avsc
446
448
  - spec/schemas/com/my-namespace/MySchemaCompound-key.avsc
@@ -507,6 +509,7 @@ test_files:
507
509
  - spec/generators/active_record_generator_spec.rb
508
510
  - spec/handlers/my_batch_consumer.rb
509
511
  - spec/handlers/my_consumer.rb
512
+ - spec/kafka_listener_spec.rb
510
513
  - spec/kafka_source_spec.rb
511
514
  - spec/kafka_topic_info_spec.rb
512
515
  - spec/message_spec.rb
@@ -520,6 +523,7 @@ test_files:
520
523
  - spec/schema_backends/avro_validation_spec.rb
521
524
  - spec/schema_backends/base_spec.rb
522
525
  - spec/schemas/com/my-namespace/Generated.avsc
526
+ - spec/schemas/com/my-namespace/MyNestedSchema.avsc
523
527
  - spec/schemas/com/my-namespace/MySchema-key.avsc
524
528
  - spec/schemas/com/my-namespace/MySchema.avsc
525
529
  - spec/schemas/com/my-namespace/MySchemaCompound-key.avsc