deimos-kafka 1.0.0.pre.beta15

Sign up to get free protection for your applications and to get access to all the features.
Files changed (100) hide show
  1. checksums.yaml +7 -0
  2. data/.circleci/config.yml +74 -0
  3. data/.gitignore +41 -0
  4. data/.gitmodules +0 -0
  5. data/.rspec +1 -0
  6. data/.rubocop.yml +321 -0
  7. data/.ruby-gemset +1 -0
  8. data/.ruby-version +1 -0
  9. data/CHANGELOG.md +9 -0
  10. data/CODE_OF_CONDUCT.md +77 -0
  11. data/Dockerfile +23 -0
  12. data/Gemfile +6 -0
  13. data/Gemfile.lock +165 -0
  14. data/Guardfile +22 -0
  15. data/LICENSE.md +195 -0
  16. data/README.md +742 -0
  17. data/Rakefile +13 -0
  18. data/bin/deimos +4 -0
  19. data/deimos-kafka.gemspec +42 -0
  20. data/docker-compose.yml +71 -0
  21. data/docs/DATABASE_BACKEND.md +147 -0
  22. data/docs/PULL_REQUEST_TEMPLATE.md +34 -0
  23. data/lib/deimos.rb +134 -0
  24. data/lib/deimos/active_record_consumer.rb +81 -0
  25. data/lib/deimos/active_record_producer.rb +64 -0
  26. data/lib/deimos/avro_data_coder.rb +89 -0
  27. data/lib/deimos/avro_data_decoder.rb +36 -0
  28. data/lib/deimos/avro_data_encoder.rb +51 -0
  29. data/lib/deimos/backends/db.rb +27 -0
  30. data/lib/deimos/backends/kafka.rb +27 -0
  31. data/lib/deimos/backends/kafka_async.rb +27 -0
  32. data/lib/deimos/configuration.rb +88 -0
  33. data/lib/deimos/consumer.rb +164 -0
  34. data/lib/deimos/instrumentation.rb +71 -0
  35. data/lib/deimos/kafka_message.rb +27 -0
  36. data/lib/deimos/kafka_source.rb +126 -0
  37. data/lib/deimos/kafka_topic_info.rb +79 -0
  38. data/lib/deimos/message.rb +74 -0
  39. data/lib/deimos/metrics/datadog.rb +47 -0
  40. data/lib/deimos/metrics/mock.rb +39 -0
  41. data/lib/deimos/metrics/provider.rb +38 -0
  42. data/lib/deimos/monkey_patches/phobos_cli.rb +35 -0
  43. data/lib/deimos/monkey_patches/phobos_producer.rb +51 -0
  44. data/lib/deimos/monkey_patches/ruby_kafka_heartbeat.rb +85 -0
  45. data/lib/deimos/monkey_patches/schema_store.rb +19 -0
  46. data/lib/deimos/producer.rb +218 -0
  47. data/lib/deimos/publish_backend.rb +30 -0
  48. data/lib/deimos/railtie.rb +8 -0
  49. data/lib/deimos/schema_coercer.rb +108 -0
  50. data/lib/deimos/shared_config.rb +59 -0
  51. data/lib/deimos/test_helpers.rb +356 -0
  52. data/lib/deimos/tracing/datadog.rb +35 -0
  53. data/lib/deimos/tracing/mock.rb +40 -0
  54. data/lib/deimos/tracing/provider.rb +31 -0
  55. data/lib/deimos/utils/db_producer.rb +95 -0
  56. data/lib/deimos/utils/executor.rb +117 -0
  57. data/lib/deimos/utils/inline_consumer.rb +144 -0
  58. data/lib/deimos/utils/lag_reporter.rb +182 -0
  59. data/lib/deimos/utils/platform_schema_validation.rb +0 -0
  60. data/lib/deimos/utils/signal_handler.rb +68 -0
  61. data/lib/deimos/version.rb +5 -0
  62. data/lib/generators/deimos/db_backend/templates/migration +24 -0
  63. data/lib/generators/deimos/db_backend/templates/rails3_migration +30 -0
  64. data/lib/generators/deimos/db_backend_generator.rb +48 -0
  65. data/lib/tasks/deimos.rake +17 -0
  66. data/spec/active_record_consumer_spec.rb +81 -0
  67. data/spec/active_record_producer_spec.rb +107 -0
  68. data/spec/avro_data_decoder_spec.rb +18 -0
  69. data/spec/avro_data_encoder_spec.rb +37 -0
  70. data/spec/backends/db_spec.rb +35 -0
  71. data/spec/backends/kafka_async_spec.rb +11 -0
  72. data/spec/backends/kafka_spec.rb +11 -0
  73. data/spec/consumer_spec.rb +169 -0
  74. data/spec/deimos_spec.rb +117 -0
  75. data/spec/kafka_source_spec.rb +168 -0
  76. data/spec/kafka_topic_info_spec.rb +88 -0
  77. data/spec/phobos.bad_db.yml +73 -0
  78. data/spec/phobos.yml +73 -0
  79. data/spec/producer_spec.rb +397 -0
  80. data/spec/publish_backend_spec.rb +10 -0
  81. data/spec/schemas/com/my-namespace/MySchema-key.avsc +13 -0
  82. data/spec/schemas/com/my-namespace/MySchema.avsc +18 -0
  83. data/spec/schemas/com/my-namespace/MySchemaWithBooleans.avsc +18 -0
  84. data/spec/schemas/com/my-namespace/MySchemaWithDateTimes.avsc +33 -0
  85. data/spec/schemas/com/my-namespace/MySchemaWithId.avsc +28 -0
  86. data/spec/schemas/com/my-namespace/MySchemaWithUniqueId.avsc +32 -0
  87. data/spec/schemas/com/my-namespace/Widget.avsc +27 -0
  88. data/spec/schemas/com/my-namespace/WidgetTheSecond.avsc +27 -0
  89. data/spec/spec_helper.rb +207 -0
  90. data/spec/updateable_schema_store_spec.rb +36 -0
  91. data/spec/utils/db_producer_spec.rb +208 -0
  92. data/spec/utils/executor_spec.rb +42 -0
  93. data/spec/utils/lag_reporter_spec.rb +69 -0
  94. data/spec/utils/platform_schema_validation_spec.rb +0 -0
  95. data/spec/utils/signal_handler_spec.rb +16 -0
  96. data/support/deimos-solo.png +0 -0
  97. data/support/deimos-with-name-next.png +0 -0
  98. data/support/deimos-with-name.png +0 -0
  99. data/support/flipp-logo.png +0 -0
  100. 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
@@ -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