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

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 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