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

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