deimos-ruby 1.24.3 → 2.0.0.pre.alpha1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (118) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop_todo.yml +0 -17
  3. data/.tool-versions +1 -0
  4. data/CHANGELOG.md +1 -1
  5. data/README.md +287 -498
  6. data/deimos-ruby.gemspec +4 -4
  7. data/docs/CONFIGURATION.md +133 -227
  8. data/docs/UPGRADING.md +237 -0
  9. data/lib/deimos/active_record_consume/batch_consumption.rb +28 -29
  10. data/lib/deimos/active_record_consume/message_consumption.rb +15 -21
  11. data/lib/deimos/active_record_consumer.rb +36 -26
  12. data/lib/deimos/active_record_producer.rb +28 -9
  13. data/lib/deimos/backends/base.rb +4 -35
  14. data/lib/deimos/backends/kafka.rb +6 -22
  15. data/lib/deimos/backends/kafka_async.rb +6 -22
  16. data/lib/deimos/backends/{db.rb → outbox.rb} +13 -9
  17. data/lib/deimos/config/configuration.rb +116 -385
  18. data/lib/deimos/consume/batch_consumption.rb +24 -124
  19. data/lib/deimos/consume/message_consumption.rb +36 -63
  20. data/lib/deimos/consumer.rb +16 -75
  21. data/lib/deimos/ext/consumer_route.rb +35 -0
  22. data/lib/deimos/ext/producer_middleware.rb +94 -0
  23. data/lib/deimos/ext/producer_route.rb +22 -0
  24. data/lib/deimos/ext/redraw.rb +29 -0
  25. data/lib/deimos/ext/routing_defaults.rb +72 -0
  26. data/lib/deimos/ext/schema_route.rb +70 -0
  27. data/lib/deimos/kafka_message.rb +2 -2
  28. data/lib/deimos/kafka_source.rb +2 -7
  29. data/lib/deimos/kafka_topic_info.rb +1 -1
  30. data/lib/deimos/logging.rb +71 -0
  31. data/lib/deimos/message.rb +2 -11
  32. data/lib/deimos/metrics/datadog.rb +40 -1
  33. data/lib/deimos/metrics/provider.rb +4 -4
  34. data/lib/deimos/producer.rb +39 -116
  35. data/lib/deimos/railtie.rb +6 -0
  36. data/lib/deimos/schema_backends/avro_base.rb +21 -21
  37. data/lib/deimos/schema_backends/avro_schema_registry.rb +1 -2
  38. data/lib/deimos/schema_backends/avro_validation.rb +2 -2
  39. data/lib/deimos/schema_backends/base.rb +19 -12
  40. data/lib/deimos/schema_backends/mock.rb +6 -1
  41. data/lib/deimos/schema_backends/plain.rb +47 -0
  42. data/lib/deimos/schema_class/base.rb +2 -2
  43. data/lib/deimos/schema_class/enum.rb +1 -1
  44. data/lib/deimos/schema_class/record.rb +2 -2
  45. data/lib/deimos/test_helpers.rb +95 -320
  46. data/lib/deimos/tracing/provider.rb +6 -6
  47. data/lib/deimos/transcoder.rb +88 -0
  48. data/lib/deimos/utils/db_poller/base.rb +16 -14
  49. data/lib/deimos/utils/db_poller/state_based.rb +3 -3
  50. data/lib/deimos/utils/db_poller/time_based.rb +4 -4
  51. data/lib/deimos/utils/db_poller.rb +1 -1
  52. data/lib/deimos/utils/deadlock_retry.rb +1 -1
  53. data/lib/deimos/utils/{db_producer.rb → outbox_producer.rb} +16 -47
  54. data/lib/deimos/utils/schema_class.rb +0 -7
  55. data/lib/deimos/version.rb +1 -1
  56. data/lib/deimos.rb +79 -26
  57. data/lib/generators/deimos/{db_backend_generator.rb → outbox_backend_generator.rb} +4 -4
  58. data/lib/generators/deimos/schema_class_generator.rb +0 -1
  59. data/lib/generators/deimos/v2/templates/karafka.rb.tt +149 -0
  60. data/lib/generators/deimos/v2_generator.rb +193 -0
  61. data/lib/tasks/deimos.rake +5 -7
  62. data/spec/active_record_batch_consumer_association_spec.rb +22 -13
  63. data/spec/active_record_batch_consumer_spec.rb +84 -65
  64. data/spec/active_record_consume/batch_consumption_spec.rb +10 -10
  65. data/spec/active_record_consume/batch_slicer_spec.rb +12 -12
  66. data/spec/active_record_consumer_spec.rb +29 -13
  67. data/spec/active_record_producer_spec.rb +36 -26
  68. data/spec/backends/base_spec.rb +0 -23
  69. data/spec/backends/kafka_async_spec.rb +1 -3
  70. data/spec/backends/kafka_spec.rb +1 -3
  71. data/spec/backends/{db_spec.rb → outbox_spec.rb} +14 -20
  72. data/spec/batch_consumer_spec.rb +66 -116
  73. data/spec/consumer_spec.rb +53 -147
  74. data/spec/deimos_spec.rb +10 -126
  75. data/spec/kafka_source_spec.rb +19 -52
  76. data/spec/karafka/karafka.rb +69 -0
  77. data/spec/karafka_config/karafka_spec.rb +97 -0
  78. data/spec/logging_spec.rb +25 -0
  79. data/spec/message_spec.rb +9 -9
  80. data/spec/producer_spec.rb +112 -254
  81. data/spec/rake_spec.rb +1 -3
  82. data/spec/schema_backends/avro_validation_spec.rb +1 -1
  83. data/spec/schemas/com/my-namespace/MySchemaWithTitle.avsc +22 -0
  84. data/spec/snapshots/consumers-no-nest.snap +49 -0
  85. data/spec/snapshots/consumers.snap +49 -0
  86. data/spec/snapshots/consumers_and_producers-no-nest.snap +49 -0
  87. data/spec/snapshots/consumers_and_producers.snap +49 -0
  88. data/spec/snapshots/consumers_circular-no-nest.snap +49 -0
  89. data/spec/snapshots/consumers_circular.snap +49 -0
  90. data/spec/snapshots/consumers_complex_types-no-nest.snap +49 -0
  91. data/spec/snapshots/consumers_complex_types.snap +49 -0
  92. data/spec/snapshots/consumers_nested-no-nest.snap +49 -0
  93. data/spec/snapshots/consumers_nested.snap +49 -0
  94. data/spec/snapshots/namespace_folders.snap +49 -0
  95. data/spec/snapshots/namespace_map.snap +49 -0
  96. data/spec/snapshots/producers_with_key-no-nest.snap +49 -0
  97. data/spec/snapshots/producers_with_key.snap +49 -0
  98. data/spec/spec_helper.rb +61 -29
  99. data/spec/utils/db_poller_spec.rb +49 -39
  100. data/spec/utils/{db_producer_spec.rb → outbox_producer_spec.rb} +17 -184
  101. metadata +58 -67
  102. data/lib/deimos/batch_consumer.rb +0 -7
  103. data/lib/deimos/config/phobos_config.rb +0 -164
  104. data/lib/deimos/instrumentation.rb +0 -95
  105. data/lib/deimos/monkey_patches/phobos_cli.rb +0 -35
  106. data/lib/deimos/utils/inline_consumer.rb +0 -158
  107. data/lib/deimos/utils/lag_reporter.rb +0 -186
  108. data/lib/deimos/utils/schema_controller_mixin.rb +0 -129
  109. data/spec/config/configuration_spec.rb +0 -329
  110. data/spec/kafka_listener_spec.rb +0 -55
  111. data/spec/phobos.bad_db.yml +0 -73
  112. data/spec/phobos.yml +0 -77
  113. data/spec/utils/inline_consumer_spec.rb +0 -31
  114. data/spec/utils/lag_reporter_spec.rb +0 -76
  115. data/spec/utils/platform_schema_validation_spec.rb +0 -0
  116. data/spec/utils/schema_controller_mixin_spec.rb +0 -84
  117. /data/lib/generators/deimos/{db_backend → outbox_backend}/templates/migration +0 -0
  118. /data/lib/generators/deimos/{db_backend → outbox_backend}/templates/rails3_migration +0 -0
@@ -4,33 +4,44 @@
4
4
  # rubocop:disable Metrics/ModuleLength
5
5
  module ConsumerTest
6
6
  describe Deimos::Consumer, 'Message Consumer' do
7
+ let(:use_schema_classes) { false }
8
+ let(:reraise_errors) { false }
7
9
  prepend_before(:each) do
8
10
  # :nodoc:
9
11
  consumer_class = Class.new(described_class) do
10
- schema 'MySchema'
11
- namespace 'com.my-namespace'
12
- key_config field: 'test_id'
13
12
 
14
13
  # :nodoc:
15
- def fatal_error?(_exception, payload, _metadata)
16
- payload.to_s == 'fatal'
14
+ def fatal_error?(_exception, messages)
15
+ messages.payloads.first&.dig(:test_id) == ['fatal']
17
16
  end
18
17
 
19
18
  # :nodoc:
20
- def consume(_payload, _metadata)
21
- raise 'This should not be called unless call_original is set'
19
+ def consume_message(message)
20
+ message.payload
22
21
  end
23
22
  end
24
23
  stub_const('ConsumerTest::MyConsumer', consumer_class)
24
+ route_usc = use_schema_classes
25
+ route_rre = reraise_errors
26
+ Karafka::App.routes.redraw do
27
+ topic 'my_consume_topic' do
28
+ schema 'MySchema'
29
+ namespace 'com.my-namespace'
30
+ key_config field: 'test_id'
31
+ consumer consumer_class
32
+ use_schema_classes route_usc
33
+ reraise_errors route_rre
34
+ end
35
+ end
25
36
  end
26
37
 
27
38
  describe 'consume' do
28
39
  SCHEMA_CLASS_SETTINGS.each do |setting, use_schema_classes|
40
+ let(:use_schema_classes) { use_schema_classes }
29
41
  context "with Schema Class consumption #{setting}" do
30
42
 
31
43
  before(:each) do
32
44
  Deimos.configure do |config|
33
- config.schema.use_schema_classes = use_schema_classes
34
45
  config.schema.use_full_namespace = true
35
46
  end
36
47
  end
@@ -45,26 +56,9 @@ module ConsumerTest
45
56
  end
46
57
 
47
58
  it 'should consume a nil message' do
48
- test_consume_message(MyConsumer, nil) do |payload, _metadata|
49
- expect(payload).to be_nil
50
- end
51
- end
52
-
53
- it 'should consume a message idempotently' do
54
- # testing for a crash and re-consuming the same message/metadata
55
- key = { 'test_id' => 'foo' }
56
- test_metadata = { key: key }
57
- allow_any_instance_of(MyConsumer).to(receive(:decode_key)) do |_instance, k|
58
- k['test_id']
59
+ test_consume_message(MyConsumer, nil, key: 'foo') do
60
+ expect(messages).to be_empty
59
61
  end
60
- MyConsumer.new.around_consume({ 'test_id' => 'foo',
61
- 'some_int' => 123 }, test_metadata) do |_payload, metadata|
62
- expect(metadata[:key]).to eq('foo')
63
- end
64
- MyConsumer.new.around_consume({ 'test_id' => 'foo',
65
- 'some_int' => 123 }, test_metadata) do |_payload, metadata|
66
- expect(metadata[:key]).to eq('foo')
67
- end
68
62
  end
69
63
 
70
64
  it 'should consume a message on a topic' do
@@ -77,83 +71,82 @@ module ConsumerTest
77
71
  end
78
72
 
79
73
  it 'should fail on invalid message' do
80
- test_consume_invalid_message(MyConsumer, { 'invalid' => 'key' })
74
+ expect { test_consume_message(MyConsumer, { 'invalid' => 'key' }) }.
75
+ to raise_error(Avro::SchemaValidator::ValidationError)
81
76
  end
82
77
 
83
78
  it 'should fail if reraise is false but fatal_error is true' do
84
- Deimos.configure { |config| config.consumers.reraise_errors = false }
85
- test_consume_invalid_message(MyConsumer, 'fatal')
79
+ expect { test_consume_message(MyConsumer, {test_id: 'fatal'}) }.
80
+ to raise_error(Avro::SchemaValidator::ValidationError)
86
81
  end
87
82
 
88
83
  it 'should fail if fatal_error is true globally' do
89
- Deimos.configure do |config|
90
- config.consumers.fatal_error = proc { true }
91
- config.consumers.reraise_errors = false
92
- end
93
- test_consume_invalid_message(MyConsumer, { 'invalid' => 'key' })
84
+ set_karafka_config(:fatal_error, proc { true })
85
+ expect { test_consume_message(MyConsumer, { 'invalid' => 'key' }) }.
86
+ to raise_error(Avro::SchemaValidator::ValidationError)
94
87
  end
95
88
 
96
89
  it 'should fail on message with extra fields' do
97
- test_consume_invalid_message(MyConsumer,
90
+ allow_any_instance_of(Deimos::SchemaBackends::AvroValidation).
91
+ to receive(:coerce) { |_, m| m.with_indifferent_access }
92
+ expect { test_consume_message(MyConsumer,
98
93
  { 'test_id' => 'foo',
99
94
  'some_int' => 123,
100
- 'extra_field' => 'field name' })
95
+ 'extra_field' => 'field name' }) }.
96
+ to raise_error(Avro::SchemaValidator::ValidationError)
101
97
  end
102
98
 
103
99
  it 'should not fail when before_consume fails without reraising errors' do
104
- Deimos.configure { |config| config.consumers.reraise_errors = false }
100
+ set_karafka_config(:reraise_errors, false)
105
101
  expect {
106
102
  test_consume_message(
107
103
  MyConsumer,
108
104
  { 'test_id' => 'foo',
109
- 'some_int' => 123 },
110
- skip_expectation: true
111
- ) { raise 'OH NOES' }
105
+ 'some_int' => 123 }) { raise 'OH NOES' }
112
106
  }.not_to raise_error
113
107
  end
114
108
 
115
109
  it 'should not fail when consume fails without reraising errors' do
116
- Deimos.configure { |config| config.consumers.reraise_errors = false }
110
+ set_karafka_config(:reraise_errors, false)
111
+ allow(Deimos::ProducerMiddleware).to receive(:call) { |m| m[:payload] = m[:payload].to_json; m }
117
112
  expect {
118
113
  test_consume_message(
119
114
  MyConsumer,
120
- { 'invalid' => 'key' },
121
- skip_expectation: true
122
- )
115
+ { 'invalid' => 'key' })
123
116
  }.not_to raise_error
124
117
  end
125
-
126
- it 'should call original' do
127
- expect {
128
- test_consume_message(MyConsumer,
129
- { 'test_id' => 'foo', 'some_int' => 123 },
130
- call_original: true)
131
- }.to raise_error('This should not be called unless call_original is set')
132
- end
133
118
  end
134
119
  end
135
120
 
136
121
  context 'with overriden schema classes' do
137
122
 
138
123
  before(:each) do
124
+ set_karafka_config(:use_schema_classes, true)
139
125
  Deimos.configure do |config|
140
- config.schema.use_schema_classes = true
141
126
  config.schema.use_full_namespace = true
142
127
  end
143
128
  end
144
129
 
145
130
  prepend_before(:each) do
146
131
  consumer_class = Class.new(described_class) do
147
- schema 'MyUpdatedSchema'
148
- namespace 'com.my-namespace'
149
- key_config field: 'test_id'
150
-
151
132
  # :nodoc:
152
- def consume(_payload, _metadata)
153
- raise 'This should not be called unless call_original is set'
133
+ def consume_message(message)
134
+ message.payload
154
135
  end
155
136
  end
156
137
  stub_const('ConsumerTest::MyConsumer', consumer_class)
138
+ Deimos.config.schema.use_schema_classes = true
139
+ Karafka::App.routes.redraw do
140
+ topic 'my_consume_topic' do
141
+ schema 'MyUpdatedSchema'
142
+ namespace 'com.my-namespace'
143
+ key_config field: 'test_id'
144
+ consumer consumer_class
145
+ end
146
+ end
147
+ end
148
+ after(:each) do
149
+ Karafka::App.routes.clear
157
150
  end
158
151
 
159
152
  it 'should consume messages' do
@@ -169,93 +162,6 @@ module ConsumerTest
169
162
  end
170
163
  end
171
164
 
172
- describe 'decode_key' do
173
-
174
- it 'should use the key field in the value if set' do
175
- # actual decoding is disabled in test
176
- expect(MyConsumer.new.decode_key('test_id' => '123')).to eq('123')
177
- expect { MyConsumer.new.decode_key(123) }.to raise_error(NoMethodError)
178
- end
179
-
180
- it 'should use the key schema if set' do
181
- consumer_class = Class.new(described_class) do
182
- schema 'MySchema'
183
- namespace 'com.my-namespace'
184
- key_config schema: 'MySchema_key'
185
- end
186
- stub_const('ConsumerTest::MySchemaConsumer', consumer_class)
187
- expect(MyConsumer.new.decode_key('test_id' => '123')).to eq('123')
188
- expect { MyConsumer.new.decode_key(123) }.to raise_error(NoMethodError)
189
- end
190
-
191
- it 'should not decode if plain is set' do
192
- consumer_class = Class.new(described_class) do
193
- schema 'MySchema'
194
- namespace 'com.my-namespace'
195
- key_config plain: true
196
- end
197
- stub_const('ConsumerTest::MyNonEncodedConsumer', consumer_class)
198
- expect(MyNonEncodedConsumer.new.decode_key('123')).to eq('123')
199
- end
200
-
201
- it 'should error with nothing set' do
202
- consumer_class = Class.new(described_class) do
203
- schema 'MySchema'
204
- namespace 'com.my-namespace'
205
- end
206
- stub_const('ConsumerTest::MyErrorConsumer', consumer_class)
207
- expect { MyErrorConsumer.new.decode_key('123') }.
208
- to raise_error('No key config given - if you are not decoding keys, please use `key_config plain: true`')
209
- end
210
-
211
- end
212
-
213
- describe 'timestamps' do
214
- before(:each) do
215
- # :nodoc:
216
- consumer_class = Class.new(described_class) do
217
- schema 'MySchemaWithDateTimes'
218
- namespace 'com.my-namespace'
219
- key_config plain: true
220
-
221
- # :nodoc:
222
- def consume(_payload, _metadata)
223
- raise 'This should not be called unless call_original is set'
224
- end
225
- end
226
- stub_const('ConsumerTest::MyConsumer', consumer_class)
227
- end
228
-
229
- it 'should consume a message' do
230
- expect(Deimos.config.metrics).to receive(:histogram).twice
231
- test_consume_message('my_consume_topic',
232
- { 'test_id' => 'foo',
233
- 'some_int' => 123,
234
- 'updated_at' => Time.now.to_i,
235
- 'timestamp' => 2.minutes.ago.to_s }) do |payload, _metadata|
236
- expect(payload['test_id']).to eq('foo')
237
- end
238
- end
239
-
240
- it 'should fail nicely when timestamp wrong format' do
241
- expect(Deimos.config.metrics).to receive(:histogram).twice
242
- test_consume_message('my_consume_topic',
243
- { 'test_id' => 'foo',
244
- 'some_int' => 123,
245
- 'updated_at' => Time.now.to_i,
246
- 'timestamp' => 'dffdf' }) do |payload, _metadata|
247
- expect(payload['test_id']).to eq('foo')
248
- end
249
- test_consume_message('my_consume_topic',
250
- { 'test_id' => 'foo',
251
- 'some_int' => 123,
252
- 'updated_at' => Time.now.to_i,
253
- 'timestamp' => '' }) do |payload, _metadata|
254
- expect(payload['test_id']).to eq('foo')
255
- end
256
- end
257
-
258
- end
259
165
  end
260
166
  end
261
167
  # rubocop:enable Metrics/ModuleLength
data/spec/deimos_spec.rb CHANGED
@@ -2,72 +2,12 @@
2
2
 
3
3
  describe Deimos do
4
4
 
5
- let(:phobos_configuration) do
6
- { 'logger' =>
7
- { 'file' => 'log/phobos.log',
8
- 'stdout_json' => false,
9
- 'level' => 'debug',
10
- 'ruby_kafka' =>
11
- { 'level' => 'debug' } },
12
- 'kafka' =>
13
- { 'client_id' => 'phobos',
14
- 'connect_timeout' => 15,
15
- 'socket_timeout' => 15,
16
- 'seed_brokers' => 'my_seed_broker.com',
17
- 'ssl_ca_cert' => 'my_ssl_ca_cert',
18
- 'ssl_client_cert' => 'my_ssl_client_cert',
19
- 'ssl_client_cert_key' => 'my_ssl_client_cert_key' },
20
- 'producer' =>
21
- { 'ack_timeout' => 5,
22
- 'required_acks' => :all,
23
- 'max_retries' => 2,
24
- 'retry_backoff' => 1,
25
- 'max_buffer_size' => 10_000,
26
- 'max_buffer_bytesize' => 10_000_000,
27
- 'compression_codec' => nil,
28
- 'compression_threshold' => 1,
29
- 'max_queue_size' => 10_000,
30
- 'delivery_threshold' => 0,
31
- 'delivery_interval' => 0 },
32
- 'consumer' =>
33
- { 'session_timeout' => 300,
34
- 'offset_commit_interval' => 10,
35
- 'offset_commit_threshold' => 0,
36
- 'heartbeat_interval' => 10 },
37
- 'backoff' =>
38
- { 'min_ms' => 1000,
39
- 'max_ms' => 60_000 },
40
- 'listeners' => [
41
- { 'handler' => 'ConsumerTest::MyConsumer',
42
- 'topic' => 'my_consume_topic',
43
- 'group_id' => 'my_group_id',
44
- 'max_bytes_per_partition' => 524_288 },
45
- { 'handler' => 'ConsumerTest::MyBatchConsumer',
46
- 'topic' => 'my_batch_consume_topic',
47
- 'group_id' => 'my_batch_group_id',
48
- 'delivery' => 'inline_batch' }
49
- ],
50
- 'custom_logger' => nil,
51
- 'custom_kafka_logger' => nil }
52
- end
53
-
54
- let(:config_path) { File.join(File.dirname(__FILE__), 'phobos.yml') }
55
-
56
5
  it 'should have a version number' do
57
6
  expect(Deimos::VERSION).not_to be_nil
58
7
  end
59
8
 
60
- it 'should error if required_acks is not all' do
61
- expect {
62
- described_class.configure do |config|
63
- config.producers.backend = :db
64
- config.phobos_config_file = File.join(File.dirname(__FILE__), 'phobos.bad_db.yml')
65
- end
66
- }.to raise_error('Cannot set producers.backend to :db unless producers.required_acks is set to ":all"!')
67
- end
68
-
69
- describe '#start_db_backend!' do
70
- it 'should start if backend is db and thread_count is > 0' do
9
+ describe '#start_outbox_backend!' do
10
+ it 'should start if backend is outbox and thread_count is > 0' do
71
11
  signal_handler = instance_double(Sigurd::SignalHandler)
72
12
  allow(signal_handler).to receive(:run!)
73
13
  expect(Sigurd::Executor).to receive(:new).
@@ -77,9 +17,9 @@ describe Deimos do
77
17
  signal_handler
78
18
  end
79
19
  described_class.configure do |config|
80
- config.producers.backend = :db
20
+ config.producers.backend = :outbox
81
21
  end
82
- described_class.start_db_backend!(thread_count: 2)
22
+ described_class.start_outbox_backend!(thread_count: 2)
83
23
  end
84
24
 
85
25
  it 'should not start if backend is not db' do
@@ -87,83 +27,27 @@ describe Deimos do
87
27
  described_class.configure do |config|
88
28
  config.producers.backend = :kafka
89
29
  end
90
- expect { described_class.start_db_backend!(thread_count: 2) }.
91
- to raise_error('Publish backend is not set to :db, exiting')
30
+ expect { described_class.start_outbox_backend!(thread_count: 2) }.
31
+ to raise_error('Publish backend is not set to :outbox, exiting')
92
32
  end
93
33
 
94
34
  it 'should not start if thread_count is nil' do
95
35
  expect(Sigurd::SignalHandler).not_to receive(:new)
96
36
  described_class.configure do |config|
97
- config.producers.backend = :db
37
+ config.producers.backend = :outbox
98
38
  end
99
- expect { described_class.start_db_backend!(thread_count: nil) }.
39
+ expect { described_class.start_outbox_backend!(thread_count: nil) }.
100
40
  to raise_error('Thread count is not given or set to zero, exiting')
101
41
  end
102
42
 
103
43
  it 'should not start if thread_count is 0' do
104
44
  expect(Sigurd::SignalHandler).not_to receive(:new)
105
45
  described_class.configure do |config|
106
- config.producers.backend = :db
46
+ config.producers.backend = :outbox
107
47
  end
108
- expect { described_class.start_db_backend!(thread_count: 0) }.
48
+ expect { described_class.start_outbox_backend!(thread_count: 0) }.
109
49
  to raise_error('Thread count is not given or set to zero, exiting')
110
50
  end
111
51
  end
112
52
 
113
- describe 'delivery configuration' do
114
- before(:each) do
115
- allow(YAML).to receive(:load).and_return(phobos_configuration)
116
- end
117
-
118
- it 'should not raise an error with properly configured handlers' do
119
- expect {
120
- described_class.configure do
121
- consumer do
122
- class_name 'ConsumerTest::MyConsumer'
123
- delivery :message
124
- end
125
- consumer do
126
- class_name 'ConsumerTest::MyConsumer'
127
- delivery :batch
128
- end
129
- consumer do
130
- class_name 'ConsumerTest::MyBatchConsumer'
131
- delivery :inline_batch
132
- end
133
- end
134
- }.not_to raise_error
135
- end
136
-
137
- it 'should raise an error if inline_batch listeners do not implement consume_batch' do
138
- expect {
139
- described_class.configure do
140
- consumer do
141
- class_name 'ConsumerTest::MyConsumer'
142
- delivery :inline_batch
143
- end
144
- end
145
- }.to raise_error('BatchConsumer ConsumerTest::MyConsumer does not implement `consume_batch`')
146
- end
147
-
148
- it 'should raise an error if Consumers do not have message or batch delivery' do
149
- expect {
150
- described_class.configure do
151
- consumer do
152
- class_name 'ConsumerTest::MyBatchConsumer'
153
- delivery :message
154
- end
155
- end
156
- }.to raise_error('Non-batch Consumer ConsumerTest::MyBatchConsumer does not implement `consume`')
157
- end
158
-
159
- it 'should treat nil as `batch`' do
160
- expect {
161
- described_class.configure do
162
- consumer do
163
- class_name 'ConsumerTest::MyConsumer'
164
- end
165
- end
166
- }.not_to raise_error
167
- end
168
- end
169
53
  end
@@ -17,18 +17,10 @@ module KafkaSourceSpec
17
17
 
18
18
  # Dummy producer which mimicks the behavior of a real producer
19
19
  class WidgetProducer < Deimos::ActiveRecordProducer
20
- topic 'my-topic'
21
- namespace 'com.my-namespace'
22
- schema 'Widget'
23
- key_config field: :id
24
20
  end
25
21
 
26
22
  # Dummy producer which mimicks the behavior of a real producer
27
23
  class WidgetProducerTheSecond < Deimos::ActiveRecordProducer
28
- topic 'my-topic-the-second'
29
- namespace 'com.my-namespace'
30
- schema 'WidgetTheSecond'
31
- key_config field: :id
32
24
  end
33
25
 
34
26
  # Dummy class we can include the mixin in. Has a backing table created
@@ -51,6 +43,22 @@ module KafkaSourceSpec
51
43
 
52
44
  before(:each) do
53
45
  Widget.delete_all
46
+ Karafka::App.routes.redraw do
47
+ topic 'my-topic' do
48
+ namespace 'com.my-namespace'
49
+ schema 'Widget'
50
+ key_config field: :id
51
+ producer_class WidgetProducer
52
+ end
53
+
54
+ topic 'my-topic-the-second' do
55
+ namespace 'com.my-namespace'
56
+ schema 'WidgetTheSecond'
57
+ key_config field: :id
58
+ producer_class WidgetProducerTheSecond
59
+ end
60
+
61
+ end
54
62
  end
55
63
 
56
64
  it 'should send events on creation, update, and deletion' do
@@ -206,10 +214,9 @@ module KafkaSourceSpec
206
214
  context 'with DB backend' do
207
215
  before(:each) do
208
216
  Deimos.configure do |config|
209
- config.producers.backend = :db
217
+ config.producers.backend = :outbox
210
218
  end
211
219
  setup_db(DB_OPTIONS.last) # sqlite
212
- allow(Deimos::Producer).to receive(:produce_batch).and_call_original
213
220
  end
214
221
 
215
222
  it 'should save to the DB' do
@@ -309,46 +316,6 @@ module KafkaSourceSpec
309
316
  end
310
317
  end
311
318
 
312
- context 'with AR models that implement the kafka_producer interface' do
313
- before(:each) do
314
- # Dummy class we can include the mixin in. Has a backing table created
315
- # earlier and has the import hook disabled
316
- deprecated_class = Class.new(ActiveRecord::Base) do
317
- include Deimos::KafkaSource
318
- self.table_name = 'widgets'
319
-
320
- # :nodoc:
321
- def self.kafka_config
322
- {
323
- update: true,
324
- delete: true,
325
- import: false,
326
- create: true
327
- }
328
- end
329
-
330
- # :nodoc:
331
- def self.kafka_producer
332
- WidgetProducer
333
- end
334
- end
335
- stub_const('WidgetDeprecated', deprecated_class)
336
- WidgetDeprecated.reset_column_information
337
- end
338
-
339
- it 'logs a warning and sends the message as usual' do
340
- expect(Deimos.config.logger).to receive(:warn).with({ message: WidgetDeprecated::DEPRECATION_WARNING })
341
- widget = WidgetDeprecated.create(widget_id: 1, name: 'Widget 1')
342
- expect('my-topic').to have_sent({
343
- widget_id: 1,
344
- name: 'Widget 1',
345
- id: widget.id,
346
- created_at: anything,
347
- updated_at: anything
348
- }, widget.id)
349
- end
350
- end
351
-
352
319
  context 'with AR models that do not implement any producer interface' do
353
320
  before(:each) do
354
321
  # Dummy class we can include the mixin in. Has a backing table created
@@ -371,10 +338,10 @@ module KafkaSourceSpec
371
338
  WidgetBuggy.reset_column_information
372
339
  end
373
340
 
374
- it 'raises a NotImplementedError exception' do
341
+ it 'raises a MissingImplementationError exception' do
375
342
  expect {
376
343
  WidgetBuggy.create(widget_id: 1, name: 'Widget 1')
377
- }.to raise_error(NotImplementedError)
344
+ }.to raise_error(Deimos::MissingImplementationError)
378
345
  end
379
346
  end
380
347
  end
@@ -0,0 +1,69 @@
1
+ # frozen_string_literal: true
2
+ class KarafkaApp < Karafka::App
3
+ setup do |config|
4
+ config.kafka = { 'bootstrap.servers': '127.0.0.1:9092' }
5
+ config.client_id = 'example_app'
6
+ # Recreate consumers with each batch. This will allow Rails code reload to work in the
7
+ # development mode. Otherwise Karafka process would not be aware of code changes
8
+ config.consumer_persistence = !Rails.env.development?
9
+ end
10
+
11
+ # Comment out this part if you are not using instrumentation and/or you are not
12
+ # interested in logging events for certain environments. Since instrumentation
13
+ # notifications add extra boilerplate, if you want to achieve max performance,
14
+ # listen to only what you really need for given environment.
15
+ Karafka.monitor.subscribe(Karafka::Instrumentation::LoggerListener.new)
16
+ # Karafka.monitor.subscribe(Karafka::Instrumentation::ProctitleListener.new)
17
+
18
+ # This logger prints the producer development info using the Karafka logger.
19
+ # It is similar to the consumer logger listener but producer oriented.
20
+ Karafka.producer.monitor.subscribe(
21
+ WaterDrop::Instrumentation::LoggerListener.new(
22
+ # Log producer operations using the Karafka logger
23
+ Karafka.logger,
24
+ # If you set this to true, logs will contain each message details
25
+ # Please note, that this can be extensive
26
+ log_messages: false
27
+ )
28
+ )
29
+
30
+ # You can subscribe to all consumer related errors and record/track then that way
31
+ #
32
+ # Karafka.monitor.subscribe 'error.occurred' do |event|
33
+ # type = event[:type]
34
+ # error = event[:error]
35
+ # details = (error.backtrace || []).join("\n")
36
+ # ErrorTracker.send_error(error, type, details)
37
+ # end
38
+
39
+ # You can subscribe to all producer related errors and record/track then that way
40
+ # Please note, that producer and consumer have their own notifications pipeline so you need to
41
+ # setup error tracking independently for each of them
42
+ #
43
+ # Karafka.producer.monitor.subscribe('error.occurred') do |event|
44
+ # type = event[:type]
45
+ # error = event[:error]
46
+ # details = (error.backtrace || []).join("\n")
47
+ # ErrorTracker.send_error(error, type, details)
48
+ # end
49
+
50
+ routes.draw do
51
+ # Uncomment this if you use Karafka with ActiveJob
52
+ # You need to define the topic per each queue name you use
53
+ # active_job_topic :default
54
+ # topic :example do
55
+ # Uncomment this if you want Karafka to manage your topics configuration
56
+ # Managing topics configuration via routing will allow you to ensure config consistency
57
+ # across multiple environments
58
+ #
59
+ # config(partitions: 2, 'cleanup.policy': 'compact')
60
+ # consumer ExampleConsumer
61
+ # end
62
+ end
63
+ end
64
+
65
+ # Karafka now features a Web UI!
66
+ # Visit the setup documentation to get started and enhance your experience.
67
+ #
68
+ # https://karafka.io/docs/Web-UI-Getting-Started
69
+ Deimos.setup_karafka