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

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: 380e0a86786dc4b308858186e5f97a102d13e0d0ac72ca172e941d42b6dfb81a
4
+ data.tar.gz: ca99315cf3ee096160c24f15d194ce73d77d95b1e7c27b3454e2a5e4165ef156
5
5
  SHA512:
6
- metadata.gz: d920dc6b7b2020364a7095ab61a986a3bb16c5fac5367227ed8cbded422399e78887fc91ecce81d9857c66bce5739f6423335e835ca87612a1a2306c50cd93b0
7
- data.tar.gz: 78fd60871b0852b03e91e2223afbe5ac419dc929d6de6340799f8376f89efffd2775e4d93025ab8089cac5a7579d00bc33c64c3f9ec50cc3fb603f6381e3d82a
6
+ metadata.gz: 8aa602000e702a9ce3c271b0796f4ea864fdc0acaf0852fdf7bdc3bf51e0eaf2df3265420e2558f37e3c174a5a555d43920323bb3a1539753b8821ae00ad6f32
7
+ data.tar.gz: c505ef8b587d2a405f9773a3c0f5a178f27811a071a4579ecfbe46c9f75759f1375b773949a384cdd465638ca7e95c53e6e34d8a1b7dd27e5128e03a90c0c3b1
@@ -7,6 +7,13 @@ 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-beta3 - 2020-08-05
11
+
12
+ ### Fixes :wrench:
13
+ - Simplify decoding messages and handle producer not found
14
+ - Consolidate types in sub-records recursively
15
+ (fixes [#72](https://github.com/flipp-oss/deimos/issues/72))
16
+
10
17
  ## 1.8.1-beta2 - 2020-07-28
11
18
 
12
19
  ### Features :star:
@@ -41,6 +48,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
41
48
  - Added `ActiveRecordConsumer` batch mode
42
49
 
43
50
  ### Fixes :wrench:
51
+ - Fixes `send_produce_error` to decode `failed_messages` with built-in decoder.
44
52
  - Lag calculation can be incorrect if no messages are being consumed.
45
53
  - Fixed bug where printing messages on a MessageSizeTooLarge
46
54
  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.beta3)
5
5
  avro_turf (~> 0.11)
6
6
  phobos (~> 1.9)
7
7
  ruby-kafka (~> 0.7)
@@ -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,8 +71,10 @@ module Deimos
54
71
  else
55
72
  true
56
73
  end
57
- else
58
- val
74
+ when :union
75
+ coerce_union(type, val)
76
+ when :record
77
+ coerce_record(type, val)
59
78
  end
60
79
  end
61
80
 
@@ -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-beta3'
5
5
  end
@@ -0,0 +1,54 @@
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
+ Deimos.subscribe('produce_error') do |event|
34
+ expect(event.payload).to include(
35
+ producer: MyProducer,
36
+ topic: 'my-topic',
37
+ payloads: payloads
38
+ )
39
+ end
40
+ expect(Deimos.config.metrics).to receive(:increment).
41
+ with('publish_error', tags: %w(topic:my-topic), by: 2)
42
+ expect { MyProducer.publish_list(payloads) }.to raise_error(Kafka::DeliveryFailed)
43
+ end
44
+
45
+ it 'should not send any notifications when producer is not found' do
46
+ Deimos.subscribe('produce_error') do |_|
47
+ raise 'OH NOES'
48
+ end
49
+ allow(Deimos::Producer).to receive(:descendants).and_return([])
50
+ expect(Deimos.config.metrics).not_to receive(:increment).with('publish_error', anything)
51
+ expect { MyProducer.publish_list(payloads) }.to raise_error(Kafka::DeliveryFailed)
52
+ end
53
+ end
54
+ 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,32 @@ 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
+ 'some_nested_record' => {
250
+ 'some_int' => 123,
251
+ 'some_float' => BigDecimal('456.789'),
252
+ 'some_string' => '123',
253
+ 'some_optional_int' => nil
254
+ },
255
+ 'some_optional_record' => nil
256
+ )
257
+ expect(MyNestedSchemaProducer.topic).to have_sent(
258
+ 'test_id' => 'foo',
259
+ 'test_float' => 123.456,
260
+ 'some_nested_record' => {
261
+ 'some_int' => 123,
262
+ 'some_float' => 456.789,
263
+ 'some_string' => '123',
264
+ 'some_optional_int' => nil
265
+ },
266
+ 'some_optional_record' => nil
267
+ )
268
+ end
269
+
236
270
  it 'should error with nothing set' do
237
271
  expect {
238
272
  MyErrorProducer.publish_list(
@@ -0,0 +1,55 @@
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": "some_nested_record",
19
+ "doc": "some nested record",
20
+ "type": {
21
+ "name": "MyNestedRecord",
22
+ "type": "record",
23
+ "fields": [
24
+ {
25
+ "name": "some_int",
26
+ "type": "int",
27
+ "doc": "some int"
28
+ },
29
+ {
30
+ "name": "some_float",
31
+ "type": "float",
32
+ "doc": "some float"
33
+ },
34
+ {
35
+ "name": "some_string",
36
+ "type": "string",
37
+ "doc": "some string"
38
+ },
39
+ {
40
+ "name": "some_optional_int",
41
+ "type": [ "null", "int" ],
42
+ "doc": "some optional int",
43
+ "default": null
44
+ }
45
+ ]
46
+ }
47
+ },
48
+ {
49
+ "name": "some_optional_record",
50
+ "doc": "some optional record",
51
+ "type": [ "null", "MyNestedRecord" ],
52
+ "default": null
53
+ }
54
+ ]
55
+ }
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.beta3
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-05 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