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 +4 -4
- data/CHANGELOG.md +8 -0
- data/Gemfile.lock +1 -1
- data/lib/deimos/instrumentation.rb +10 -5
- data/lib/deimos/schema_backends/avro_schema_coercer.rb +30 -11
- data/lib/deimos/version.rb +1 -1
- data/spec/kafka_listener_spec.rb +54 -0
- data/spec/producer_spec.rb +34 -0
- data/spec/schemas/com/my-namespace/MyNestedSchema.avsc +55 -0
- metadata +6 -2
    
        checksums.yaml
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            ---
         | 
| 2 2 | 
             
            SHA256:
         | 
| 3 | 
            -
              metadata.gz:  | 
| 4 | 
            -
              data.tar.gz:  | 
| 3 | 
            +
              metadata.gz: 380e0a86786dc4b308858186e5f97a102d13e0d0ac72ca172e941d42b6dfb81a
         | 
| 4 | 
            +
              data.tar.gz: ca99315cf3ee096160c24f15d194ce73d77d95b1e7c27b3454e2a5e4165ef156
         | 
| 5 5 | 
             
            SHA512:
         | 
| 6 | 
            -
              metadata.gz:  | 
| 7 | 
            -
              data.tar.gz:  | 
| 6 | 
            +
              metadata.gz: 8aa602000e702a9ce3c271b0796f4ea864fdc0acaf0852fdf7bdc3bf51e0eaf2df3265420e2558f37e3c174a5a555d43920323bb3a1539753b8821ae00ad6f32
         | 
| 7 | 
            +
              data.tar.gz: c505ef8b587d2a405f9773a3c0f5a178f27811a071a4579ecfbe46c9f75759f1375b773949a384cdd465638ca7e95c53e6e34d8a1b7dd27e5128e03a90c0c3b1
         | 
    
        data/CHANGELOG.md
    CHANGED
    
    | @@ -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.
         | 
    
        data/Gemfile.lock
    CHANGED
    
    
| @@ -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 | 
            -
                     | 
| 49 | 
            +
                    producer = Deimos::Producer.descendants.find { |c| c.topic == topic }
         | 
| 50 | 
            +
                    next if batch.empty? || !producer
         | 
| 50 51 |  | 
| 51 | 
            -
                     | 
| 52 | 
            -
             | 
| 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&. | 
| 55 | 
            -
             | 
| 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 | 
            -
                #  | 
| 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 | 
            -
                   | 
| 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 |  | 
    
        data/lib/deimos/version.rb
    CHANGED
    
    
| @@ -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
         | 
    
        data/spec/producer_spec.rb
    CHANGED
    
    | @@ -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. | 
| 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- | 
| 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
         |