deimos-kafka 1.0.0.pre.beta15
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 +7 -0
- data/.circleci/config.yml +74 -0
- data/.gitignore +41 -0
- data/.gitmodules +0 -0
- data/.rspec +1 -0
- data/.rubocop.yml +321 -0
- data/.ruby-gemset +1 -0
- data/.ruby-version +1 -0
- data/CHANGELOG.md +9 -0
- data/CODE_OF_CONDUCT.md +77 -0
- data/Dockerfile +23 -0
- data/Gemfile +6 -0
- data/Gemfile.lock +165 -0
- data/Guardfile +22 -0
- data/LICENSE.md +195 -0
- data/README.md +742 -0
- data/Rakefile +13 -0
- data/bin/deimos +4 -0
- data/deimos-kafka.gemspec +42 -0
- data/docker-compose.yml +71 -0
- data/docs/DATABASE_BACKEND.md +147 -0
- data/docs/PULL_REQUEST_TEMPLATE.md +34 -0
- data/lib/deimos.rb +134 -0
- data/lib/deimos/active_record_consumer.rb +81 -0
- data/lib/deimos/active_record_producer.rb +64 -0
- data/lib/deimos/avro_data_coder.rb +89 -0
- data/lib/deimos/avro_data_decoder.rb +36 -0
- data/lib/deimos/avro_data_encoder.rb +51 -0
- data/lib/deimos/backends/db.rb +27 -0
- data/lib/deimos/backends/kafka.rb +27 -0
- data/lib/deimos/backends/kafka_async.rb +27 -0
- data/lib/deimos/configuration.rb +88 -0
- data/lib/deimos/consumer.rb +164 -0
- data/lib/deimos/instrumentation.rb +71 -0
- data/lib/deimos/kafka_message.rb +27 -0
- data/lib/deimos/kafka_source.rb +126 -0
- data/lib/deimos/kafka_topic_info.rb +79 -0
- data/lib/deimos/message.rb +74 -0
- data/lib/deimos/metrics/datadog.rb +47 -0
- data/lib/deimos/metrics/mock.rb +39 -0
- data/lib/deimos/metrics/provider.rb +38 -0
- data/lib/deimos/monkey_patches/phobos_cli.rb +35 -0
- data/lib/deimos/monkey_patches/phobos_producer.rb +51 -0
- data/lib/deimos/monkey_patches/ruby_kafka_heartbeat.rb +85 -0
- data/lib/deimos/monkey_patches/schema_store.rb +19 -0
- data/lib/deimos/producer.rb +218 -0
- data/lib/deimos/publish_backend.rb +30 -0
- data/lib/deimos/railtie.rb +8 -0
- data/lib/deimos/schema_coercer.rb +108 -0
- data/lib/deimos/shared_config.rb +59 -0
- data/lib/deimos/test_helpers.rb +356 -0
- data/lib/deimos/tracing/datadog.rb +35 -0
- data/lib/deimos/tracing/mock.rb +40 -0
- data/lib/deimos/tracing/provider.rb +31 -0
- data/lib/deimos/utils/db_producer.rb +95 -0
- data/lib/deimos/utils/executor.rb +117 -0
- data/lib/deimos/utils/inline_consumer.rb +144 -0
- data/lib/deimos/utils/lag_reporter.rb +182 -0
- data/lib/deimos/utils/platform_schema_validation.rb +0 -0
- data/lib/deimos/utils/signal_handler.rb +68 -0
- data/lib/deimos/version.rb +5 -0
- data/lib/generators/deimos/db_backend/templates/migration +24 -0
- data/lib/generators/deimos/db_backend/templates/rails3_migration +30 -0
- data/lib/generators/deimos/db_backend_generator.rb +48 -0
- data/lib/tasks/deimos.rake +17 -0
- data/spec/active_record_consumer_spec.rb +81 -0
- data/spec/active_record_producer_spec.rb +107 -0
- data/spec/avro_data_decoder_spec.rb +18 -0
- data/spec/avro_data_encoder_spec.rb +37 -0
- data/spec/backends/db_spec.rb +35 -0
- data/spec/backends/kafka_async_spec.rb +11 -0
- data/spec/backends/kafka_spec.rb +11 -0
- data/spec/consumer_spec.rb +169 -0
- data/spec/deimos_spec.rb +117 -0
- data/spec/kafka_source_spec.rb +168 -0
- data/spec/kafka_topic_info_spec.rb +88 -0
- data/spec/phobos.bad_db.yml +73 -0
- data/spec/phobos.yml +73 -0
- data/spec/producer_spec.rb +397 -0
- data/spec/publish_backend_spec.rb +10 -0
- data/spec/schemas/com/my-namespace/MySchema-key.avsc +13 -0
- data/spec/schemas/com/my-namespace/MySchema.avsc +18 -0
- data/spec/schemas/com/my-namespace/MySchemaWithBooleans.avsc +18 -0
- data/spec/schemas/com/my-namespace/MySchemaWithDateTimes.avsc +33 -0
- data/spec/schemas/com/my-namespace/MySchemaWithId.avsc +28 -0
- data/spec/schemas/com/my-namespace/MySchemaWithUniqueId.avsc +32 -0
- data/spec/schemas/com/my-namespace/Widget.avsc +27 -0
- data/spec/schemas/com/my-namespace/WidgetTheSecond.avsc +27 -0
- data/spec/spec_helper.rb +207 -0
- data/spec/updateable_schema_store_spec.rb +36 -0
- data/spec/utils/db_producer_spec.rb +208 -0
- data/spec/utils/executor_spec.rb +42 -0
- data/spec/utils/lag_reporter_spec.rb +69 -0
- data/spec/utils/platform_schema_validation_spec.rb +0 -0
- data/spec/utils/signal_handler_spec.rb +16 -0
- data/support/deimos-solo.png +0 -0
- data/support/deimos-with-name-next.png +0 -0
- data/support/deimos-with-name.png +0 -0
- data/support/flipp-logo.png +0 -0
- metadata +452 -0
@@ -0,0 +1,107 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# :nodoc:
|
4
|
+
module ActiveRecordProducerTest
|
5
|
+
describe Deimos::ActiveRecordProducer do
|
6
|
+
|
7
|
+
before(:all) do
|
8
|
+
ActiveRecord::Base.connection.create_table(:widgets) do |t|
|
9
|
+
t.string(:test_id)
|
10
|
+
t.integer(:some_int)
|
11
|
+
t.boolean(:some_bool)
|
12
|
+
t.timestamps
|
13
|
+
end
|
14
|
+
|
15
|
+
# :nodoc:
|
16
|
+
class Widget < ActiveRecord::Base
|
17
|
+
# @return [String]
|
18
|
+
def generated_id
|
19
|
+
'generated_id'
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
after(:all) do
|
25
|
+
ActiveRecord::Base.connection.drop_table(:widgets)
|
26
|
+
end
|
27
|
+
|
28
|
+
prepend_before(:each) do
|
29
|
+
|
30
|
+
producer_class = Class.new(Deimos::ActiveRecordProducer) do
|
31
|
+
schema 'MySchema'
|
32
|
+
namespace 'com.my-namespace'
|
33
|
+
topic 'my-topic'
|
34
|
+
key_config none: true
|
35
|
+
end
|
36
|
+
stub_const('MyProducer', producer_class)
|
37
|
+
|
38
|
+
producer_class = Class.new(Deimos::ActiveRecordProducer) do
|
39
|
+
schema 'MySchemaWithBooleans'
|
40
|
+
namespace 'com.my-namespace'
|
41
|
+
topic 'my-topic-with-boolean'
|
42
|
+
key_config none: true
|
43
|
+
end
|
44
|
+
stub_const('MyBooleanProducer', producer_class)
|
45
|
+
|
46
|
+
producer_class = Class.new(Deimos::ActiveRecordProducer) do
|
47
|
+
schema 'MySchemaWithId'
|
48
|
+
namespace 'com.my-namespace'
|
49
|
+
topic 'my-topic-with-id'
|
50
|
+
key_config none: true
|
51
|
+
record_class Widget
|
52
|
+
|
53
|
+
# :nodoc:
|
54
|
+
def self.generate_payload(attrs, widget)
|
55
|
+
super.merge(message_id: widget.generated_id)
|
56
|
+
end
|
57
|
+
|
58
|
+
end
|
59
|
+
stub_const('MyProducerWithID', producer_class)
|
60
|
+
|
61
|
+
producer_class = Class.new(Deimos::ActiveRecordProducer) do
|
62
|
+
schema 'MySchemaWithUniqueId'
|
63
|
+
namespace 'com.my-namespace'
|
64
|
+
topic 'my-topic-with-unique-id'
|
65
|
+
key_config field: :id
|
66
|
+
record_class Widget
|
67
|
+
end
|
68
|
+
stub_const('MyProducerWithUniqueID', producer_class)
|
69
|
+
end
|
70
|
+
|
71
|
+
it 'should send events correctly' do
|
72
|
+
MyProducer.send_event(Widget.new(test_id: 'abc', some_int: 3))
|
73
|
+
expect('my-topic').to have_sent(test_id: 'abc', some_int: 3)
|
74
|
+
end
|
75
|
+
|
76
|
+
it 'should coerce values' do
|
77
|
+
MyProducer.send_event(Widget.new(test_id: 'abc', some_int: '3'))
|
78
|
+
MyProducer.send_event(Widget.new(test_id: 'abc', some_int: 4.5))
|
79
|
+
expect('my-topic').to have_sent(test_id: 'abc', some_int: 3)
|
80
|
+
expect('my-topic').to have_sent(test_id: 'abc', some_int: 4)
|
81
|
+
expect {
|
82
|
+
MyProducer.send_event(Widget.new(test_id: 'abc', some_int: nil))
|
83
|
+
}.to raise_error(Avro::SchemaValidator::ValidationError)
|
84
|
+
|
85
|
+
MyBooleanProducer.send_event(Widget.new(test_id: 'abc', some_bool: nil))
|
86
|
+
MyBooleanProducer.send_event(Widget.new(test_id: 'abc', some_bool: true))
|
87
|
+
expect('my-topic-with-boolean').to have_sent(test_id: 'abc', some_bool: false)
|
88
|
+
expect('my-topic-with-boolean').to have_sent(test_id: 'abc', some_bool: true)
|
89
|
+
end
|
90
|
+
|
91
|
+
it 'should be able to call the record' do
|
92
|
+
widget = Widget.create!(test_id: 'abc2', some_int: 3)
|
93
|
+
MyProducerWithID.send_event(id: widget.id, test_id: 'abc2', some_int: 3)
|
94
|
+
expect('my-topic-with-id').to have_sent(
|
95
|
+
test_id: 'abc2',
|
96
|
+
some_int: 3,
|
97
|
+
message_id: 'generated_id',
|
98
|
+
timestamp: anything
|
99
|
+
)
|
100
|
+
end
|
101
|
+
|
102
|
+
specify '#watched_attributes' do
|
103
|
+
expect(MyProducer.watched_attributes).to eq(%w(test_id some_int))
|
104
|
+
end
|
105
|
+
|
106
|
+
end
|
107
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
describe Deimos::AvroDataDecoder do
|
4
|
+
|
5
|
+
let(:decoder) do
|
6
|
+
decoder = described_class.new(schema: 'MySchema',
|
7
|
+
namespace: 'com.my-namespace')
|
8
|
+
allow(decoder).to(receive(:decode)) { |payload| payload }
|
9
|
+
decoder
|
10
|
+
end
|
11
|
+
|
12
|
+
it 'should decode a key' do
|
13
|
+
# reset stub from TestHelpers
|
14
|
+
allow(described_class).to receive(:new).and_call_original
|
15
|
+
expect(decoder.decode_key({ 'test_id' => '123' }, 'test_id')).to eq('123')
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'avro_turf/messaging'
|
4
|
+
|
5
|
+
describe Deimos::AvroDataEncoder do
|
6
|
+
|
7
|
+
let(:encoder) do
|
8
|
+
encoder = described_class.new(schema: 'MySchema',
|
9
|
+
namespace: 'com.my-namespace')
|
10
|
+
allow(encoder).to(receive(:encode)) { |payload| payload }
|
11
|
+
encoder
|
12
|
+
end
|
13
|
+
|
14
|
+
specify 'generate_key_schema' do
|
15
|
+
expect_any_instance_of(AvroTurf::SchemaStore).
|
16
|
+
to receive(:add_schema).with(
|
17
|
+
'type' => 'record',
|
18
|
+
'name' => 'MySchema_key',
|
19
|
+
'namespace' => 'com.my-namespace',
|
20
|
+
'doc' => 'Key for com.my-namespace.MySchema',
|
21
|
+
'fields' => [
|
22
|
+
{
|
23
|
+
'name' => 'test_id',
|
24
|
+
'type' => 'string'
|
25
|
+
}
|
26
|
+
]
|
27
|
+
)
|
28
|
+
encoder.send(:_generate_key_schema, 'test_id')
|
29
|
+
end
|
30
|
+
|
31
|
+
it 'should encode a key' do
|
32
|
+
# reset stub from TestHelpers
|
33
|
+
allow(described_class).to receive(:new).and_call_original
|
34
|
+
expect(encoder.encode_key('test_id', '123')).to eq('test_id' => '123')
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
each_db_config(Deimos::Backends::Db) do
|
4
|
+
include_context 'with publish_backend'
|
5
|
+
|
6
|
+
it 'should save to the database' do
|
7
|
+
described_class.publish(producer_class: MyProducer, messages: messages)
|
8
|
+
records = Deimos::KafkaMessage.all
|
9
|
+
expect(records.size).to eq(3)
|
10
|
+
expect(records[0].attributes.to_h).to include(
|
11
|
+
'message' => '{"foo"=>1}',
|
12
|
+
'topic' => 'my-topic',
|
13
|
+
'key' => 'foo1'
|
14
|
+
)
|
15
|
+
expect(records[1].attributes.to_h).to include(
|
16
|
+
'message' => '{"foo"=>2}',
|
17
|
+
'topic' => 'my-topic',
|
18
|
+
'key' => 'foo2'
|
19
|
+
)
|
20
|
+
expect(records[2].attributes.to_h).to include(
|
21
|
+
'message' => '{"foo"=>3}',
|
22
|
+
'topic' => 'my-topic',
|
23
|
+
'key' => 'foo3'
|
24
|
+
)
|
25
|
+
end
|
26
|
+
it 'should add to non-keyed messages' do
|
27
|
+
described_class.publish(producer_class: MyNoKeyProducer,
|
28
|
+
messages: messages)
|
29
|
+
expect(Deimos::KafkaMessage.count).to eq(3)
|
30
|
+
described_class.publish(producer_class: MyNoKeyProducer,
|
31
|
+
messages: [messages.first])
|
32
|
+
expect(Deimos::KafkaMessage.count).to eq(4)
|
33
|
+
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
RSpec.describe Deimos::Backends::KafkaAsync do
|
4
|
+
include_context 'with publish_backend'
|
5
|
+
it 'should publish to Kafka asynchronously' do
|
6
|
+
producer = instance_double(Phobos::Producer::ClassMethods::PublicAPI)
|
7
|
+
expect(producer).to receive(:async_publish_list).with(messages.map(&:encoded_hash))
|
8
|
+
expect(described_class).to receive(:producer).and_return(producer)
|
9
|
+
described_class.publish(producer_class: MyProducer, messages: messages)
|
10
|
+
end
|
11
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
RSpec.describe Deimos::Backends::Kafka do
|
4
|
+
include_context 'with publish_backend'
|
5
|
+
it 'should publish to Kafka synchronously' do
|
6
|
+
producer = instance_double(Phobos::Producer::ClassMethods::PublicAPI)
|
7
|
+
expect(producer).to receive(:publish_list).with(messages.map(&:encoded_hash))
|
8
|
+
expect(described_class).to receive(:producer).and_return(producer)
|
9
|
+
described_class.publish(producer_class: MyProducer, messages: messages)
|
10
|
+
end
|
11
|
+
end
|
@@ -0,0 +1,169 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# :nodoc:
|
4
|
+
module ConsumerTest
|
5
|
+
describe Deimos::Consumer do
|
6
|
+
|
7
|
+
prepend_before(:each) do
|
8
|
+
# :nodoc:
|
9
|
+
consumer_class = Class.new(Deimos::Consumer) do
|
10
|
+
schema 'MySchema'
|
11
|
+
namespace 'com.my-namespace'
|
12
|
+
key_config field: 'test_id'
|
13
|
+
|
14
|
+
# :nodoc:
|
15
|
+
def consume(_payload, _metadata)
|
16
|
+
raise 'This should not be called unless call_original is set'
|
17
|
+
end
|
18
|
+
end
|
19
|
+
stub_const('ConsumerTest::MyConsumer', consumer_class)
|
20
|
+
end
|
21
|
+
|
22
|
+
it 'should consume a message' do
|
23
|
+
test_consume_message(MyConsumer,
|
24
|
+
'test_id' => 'foo',
|
25
|
+
'some_int' => 123) do |payload, _metadata|
|
26
|
+
expect(payload['test_id']).to eq('foo')
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
it 'should consume a message on a topic' do
|
31
|
+
test_consume_message('my_consume_topic',
|
32
|
+
'test_id' => 'foo',
|
33
|
+
'some_int' => 123) do |payload, _metadata|
|
34
|
+
expect(payload['test_id']).to eq('foo')
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
it 'should fail on invalid message' do
|
39
|
+
test_consume_invalid_message(MyConsumer, 'invalid' => 'key')
|
40
|
+
end
|
41
|
+
|
42
|
+
it 'should fail on message with extra fields' do
|
43
|
+
test_consume_invalid_message(MyConsumer,
|
44
|
+
'test_id' => 'foo',
|
45
|
+
'some_int' => 123,
|
46
|
+
'extra_field' => 'field name')
|
47
|
+
end
|
48
|
+
|
49
|
+
it 'should not fail when before_consume fails without reraising errors' do
|
50
|
+
Deimos.configure { |config| config.reraise_consumer_errors = false }
|
51
|
+
expect {
|
52
|
+
test_consume_message(
|
53
|
+
MyConsumer,
|
54
|
+
{ 'test_id' => 'foo',
|
55
|
+
'some_int' => 123 },
|
56
|
+
{ skip_expectation: true }
|
57
|
+
) { raise 'OH NOES' }
|
58
|
+
} .not_to raise_error
|
59
|
+
end
|
60
|
+
|
61
|
+
it 'should not fail when consume fails without reraising errors' do
|
62
|
+
Deimos.configure { |config| config.reraise_consumer_errors = false }
|
63
|
+
expect {
|
64
|
+
test_consume_message(
|
65
|
+
MyConsumer,
|
66
|
+
{ 'invalid' => 'key' },
|
67
|
+
{ skip_expectation: true }
|
68
|
+
)
|
69
|
+
} .not_to raise_error
|
70
|
+
end
|
71
|
+
|
72
|
+
it 'should call original' do
|
73
|
+
expect {
|
74
|
+
test_consume_message(MyConsumer,
|
75
|
+
{ 'test_id' => 'foo', 'some_int' => 123 },
|
76
|
+
{ call_original: true })
|
77
|
+
}.to raise_error('This should not be called unless call_original is set')
|
78
|
+
end
|
79
|
+
|
80
|
+
describe 'decode_key' do
|
81
|
+
|
82
|
+
it 'should use the key field in the value if set' do
|
83
|
+
# actual decoding is disabled in test
|
84
|
+
expect(MyConsumer.new.decode_key('test_id' => '123')).to eq('123')
|
85
|
+
expect { MyConsumer.new.decode_key(123) }.to raise_error(NoMethodError)
|
86
|
+
end
|
87
|
+
|
88
|
+
it 'should use the key schema if set' do
|
89
|
+
consumer_class = Class.new(Deimos::Consumer) do
|
90
|
+
schema 'MySchema'
|
91
|
+
namespace 'com.my-namespace'
|
92
|
+
key_config schema: 'MySchema_key'
|
93
|
+
end
|
94
|
+
stub_const('ConsumerTest::MySchemaConsumer', consumer_class)
|
95
|
+
expect(MyConsumer.new.decode_key('test_id' => '123')).to eq('123')
|
96
|
+
expect { MyConsumer.new.decode_key(123) }.to raise_error(NoMethodError)
|
97
|
+
end
|
98
|
+
|
99
|
+
it 'should not encode if plain is set' do
|
100
|
+
consumer_class = Class.new(Deimos::Consumer) do
|
101
|
+
schema 'MySchema'
|
102
|
+
namespace 'com.my-namespace'
|
103
|
+
key_config plain: true
|
104
|
+
end
|
105
|
+
stub_const('ConsumerTest::MyNonEncodedConsumer', consumer_class)
|
106
|
+
expect(MyNonEncodedConsumer.new.decode_key('123')).to eq('123')
|
107
|
+
end
|
108
|
+
|
109
|
+
it 'should error with nothing set' do
|
110
|
+
consumer_class = Class.new(Deimos::Consumer) do
|
111
|
+
schema 'MySchema'
|
112
|
+
namespace 'com.my-namespace'
|
113
|
+
end
|
114
|
+
stub_const('ConsumerTest::MyErrorConsumer', consumer_class)
|
115
|
+
expect { MyErrorConsumer.new.decode_key('123') }.
|
116
|
+
to raise_error('No key config given - if you are not decoding keys, please use `key_config plain: true`')
|
117
|
+
end
|
118
|
+
|
119
|
+
end
|
120
|
+
|
121
|
+
describe 'timestamps' do
|
122
|
+
before(:each) do
|
123
|
+
# :nodoc:
|
124
|
+
consumer_class = Class.new(Deimos::Consumer) do
|
125
|
+
schema 'MySchemaWithDateTimes'
|
126
|
+
namespace 'com.my-namespace'
|
127
|
+
key_config plain: true
|
128
|
+
|
129
|
+
# :nodoc:
|
130
|
+
def consume(_payload, _metadata)
|
131
|
+
raise 'This should not be called unless call_original is set'
|
132
|
+
end
|
133
|
+
end
|
134
|
+
stub_const('ConsumerTest::MyConsumer', consumer_class)
|
135
|
+
stub_consumer(consumer_class)
|
136
|
+
end
|
137
|
+
|
138
|
+
it 'should consume a message' do
|
139
|
+
expect(Deimos.config.metrics).to receive(:histogram).twice
|
140
|
+
test_consume_message('my_consume_topic',
|
141
|
+
'test_id' => 'foo',
|
142
|
+
'some_int' => 123,
|
143
|
+
'updated_at' => Time.now.to_i,
|
144
|
+
'timestamp' => 2.minutes.ago.to_s) do |payload, _metadata|
|
145
|
+
expect(payload['test_id']).to eq('foo')
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
it 'should fail nicely when timestamp wrong format' do
|
150
|
+
expect(Deimos.config.metrics).to receive(:histogram).twice
|
151
|
+
test_consume_message('my_consume_topic',
|
152
|
+
'test_id' => 'foo',
|
153
|
+
'some_int' => 123,
|
154
|
+
'updated_at' => Time.now.to_i,
|
155
|
+
'timestamp' => 'dffdf') do |payload, _metadata|
|
156
|
+
expect(payload['test_id']).to eq('foo')
|
157
|
+
end
|
158
|
+
test_consume_message('my_consume_topic',
|
159
|
+
'test_id' => 'foo',
|
160
|
+
'some_int' => 123,
|
161
|
+
'updated_at' => Time.now.to_i,
|
162
|
+
'timestamp' => '') do |payload, _metadata|
|
163
|
+
expect(payload['test_id']).to eq('foo')
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
end
|
168
|
+
end
|
169
|
+
end
|
data/spec/deimos_spec.rb
ADDED
@@ -0,0 +1,117 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
describe Deimos do
|
4
|
+
|
5
|
+
it 'should have a version number' do
|
6
|
+
expect(Deimos::VERSION).not_to be_nil
|
7
|
+
end
|
8
|
+
|
9
|
+
specify 'configure' do
|
10
|
+
phobos_configuration = { 'logger' =>
|
11
|
+
{ 'file' => 'log/phobos.log',
|
12
|
+
'stdout_json' => false,
|
13
|
+
'level' => 'debug',
|
14
|
+
'ruby_kafka' =>
|
15
|
+
{ 'level' => 'debug' } },
|
16
|
+
'kafka' =>
|
17
|
+
{ 'client_id' => 'phobos',
|
18
|
+
'connect_timeout' => 15,
|
19
|
+
'socket_timeout' => 15,
|
20
|
+
'seed_brokers' => 'my_seed_broker.com',
|
21
|
+
'ssl_ca_cert' => 'my_ssl_ca_cert',
|
22
|
+
'ssl_client_cert' => 'my_ssl_client_cert',
|
23
|
+
'ssl_client_cert_key' => 'my_ssl_client_cert_key' },
|
24
|
+
'producer' =>
|
25
|
+
{ 'ack_timeout' => 5,
|
26
|
+
'required_acks' => :all,
|
27
|
+
'max_retries' => 2,
|
28
|
+
'retry_backoff' => 1,
|
29
|
+
'max_buffer_size' => 10_000,
|
30
|
+
'max_buffer_bytesize' => 10_000_000,
|
31
|
+
'compression_codec' => nil,
|
32
|
+
'compression_threshold' => 1,
|
33
|
+
'max_queue_size' => 10_000,
|
34
|
+
'delivery_threshold' => 0,
|
35
|
+
'delivery_interval' => 0 },
|
36
|
+
'consumer' =>
|
37
|
+
{ 'session_timeout' => 300,
|
38
|
+
'offset_commit_interval' => 10,
|
39
|
+
'offset_commit_threshold' => 0,
|
40
|
+
'heartbeat_interval' => 10 },
|
41
|
+
'backoff' =>
|
42
|
+
{ 'min_ms' => 1000,
|
43
|
+
'max_ms' => 60_000 },
|
44
|
+
'listeners' => [
|
45
|
+
{ 'handler' => 'ConsumerTest::MyConsumer',
|
46
|
+
'topic' => 'my_consume_topic',
|
47
|
+
'group_id' => 'my_group_id',
|
48
|
+
'max_bytes_per_partition' => 524_288 }
|
49
|
+
],
|
50
|
+
'custom_logger' => nil,
|
51
|
+
'custom_kafka_logger' => nil }
|
52
|
+
|
53
|
+
expect(Phobos).to receive(:configure).with(phobos_configuration)
|
54
|
+
allow(described_class).to receive(:ssl_var_contents) { |key| key }
|
55
|
+
described_class.configure do |config|
|
56
|
+
config.phobos_config_file = File.join(File.dirname(__FILE__), 'phobos.yml')
|
57
|
+
config.seed_broker = 'my_seed_broker.com'
|
58
|
+
config.ssl_enabled = true
|
59
|
+
config.ssl_ca_cert = 'my_ssl_ca_cert'
|
60
|
+
config.ssl_client_cert = 'my_ssl_client_cert'
|
61
|
+
config.ssl_client_cert_key = 'my_ssl_client_cert_key'
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
it 'should error if required_acks is not all' do
|
66
|
+
expect {
|
67
|
+
described_class.configure do |config|
|
68
|
+
config.publish_backend = :db
|
69
|
+
config.phobos_config_file = File.join(File.dirname(__FILE__), 'phobos.bad_db.yml')
|
70
|
+
end
|
71
|
+
}.to raise_error('Cannot set publish_backend to :db unless required_acks is set to ":all" in phobos.yml!')
|
72
|
+
end
|
73
|
+
|
74
|
+
describe '#start_db_backend!' do
|
75
|
+
before(:each) do
|
76
|
+
allow(described_class).to receive(:run_db_backend_in_thread)
|
77
|
+
end
|
78
|
+
|
79
|
+
it 'should start if backend is db and num_producer_threads is > 0' do
|
80
|
+
signal_handler = instance_double(Deimos::Utils::SignalHandler)
|
81
|
+
allow(signal_handler).to receive(:run!)
|
82
|
+
expect(Deimos::Utils::SignalHandler).to receive(:new) do |executor|
|
83
|
+
expect(executor.runners.size).to eq(2)
|
84
|
+
signal_handler
|
85
|
+
end
|
86
|
+
described_class.configure do |config|
|
87
|
+
config.publish_backend = :db
|
88
|
+
end
|
89
|
+
described_class.start_db_backend!(thread_count: 2)
|
90
|
+
end
|
91
|
+
|
92
|
+
it 'should not start if backend is not db' do
|
93
|
+
expect(Deimos::Utils::SignalHandler).not_to receive(:new)
|
94
|
+
described_class.configure do |config|
|
95
|
+
config.publish_backend = :kafka
|
96
|
+
end
|
97
|
+
described_class.start_db_backend!(thread_count: 2)
|
98
|
+
end
|
99
|
+
|
100
|
+
it 'should not start if num_producer_threads is nil' do
|
101
|
+
expect(Deimos::Utils::SignalHandler).not_to receive(:new)
|
102
|
+
described_class.configure do |config|
|
103
|
+
config.publish_backend = :db
|
104
|
+
end
|
105
|
+
described_class.start_db_backend!(thread_count: nil)
|
106
|
+
end
|
107
|
+
|
108
|
+
it 'should not start if num_producer_threads is 0' do
|
109
|
+
expect(Deimos::Utils::SignalHandler).not_to receive(:new)
|
110
|
+
described_class.configure do |config|
|
111
|
+
config.publish_backend = :db
|
112
|
+
end
|
113
|
+
described_class.start_db_backend!(thread_count: 0)
|
114
|
+
end
|
115
|
+
|
116
|
+
end
|
117
|
+
end
|