deimos-ruby 1.24.2 → 2.0.0.pre.alpha1
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/.rubocop_todo.yml +0 -17
- data/.tool-versions +1 -0
- data/CHANGELOG.md +5 -0
- data/README.md +287 -498
- data/deimos-ruby.gemspec +4 -4
- data/docs/CONFIGURATION.md +133 -226
- data/docs/UPGRADING.md +237 -0
- data/lib/deimos/active_record_consume/batch_consumption.rb +29 -28
- data/lib/deimos/active_record_consume/mass_updater.rb +59 -4
- data/lib/deimos/active_record_consume/message_consumption.rb +15 -21
- data/lib/deimos/active_record_consumer.rb +36 -21
- data/lib/deimos/active_record_producer.rb +28 -9
- data/lib/deimos/backends/base.rb +4 -35
- data/lib/deimos/backends/kafka.rb +6 -22
- data/lib/deimos/backends/kafka_async.rb +6 -22
- data/lib/deimos/backends/{db.rb → outbox.rb} +13 -9
- data/lib/deimos/config/configuration.rb +116 -379
- data/lib/deimos/consume/batch_consumption.rb +24 -124
- data/lib/deimos/consume/message_consumption.rb +36 -63
- data/lib/deimos/consumer.rb +16 -75
- data/lib/deimos/ext/consumer_route.rb +35 -0
- data/lib/deimos/ext/producer_middleware.rb +94 -0
- data/lib/deimos/ext/producer_route.rb +22 -0
- data/lib/deimos/ext/redraw.rb +29 -0
- data/lib/deimos/ext/routing_defaults.rb +72 -0
- data/lib/deimos/ext/schema_route.rb +70 -0
- data/lib/deimos/kafka_message.rb +2 -2
- data/lib/deimos/kafka_source.rb +2 -7
- data/lib/deimos/kafka_topic_info.rb +1 -1
- data/lib/deimos/logging.rb +71 -0
- data/lib/deimos/message.rb +2 -11
- data/lib/deimos/metrics/datadog.rb +40 -1
- data/lib/deimos/metrics/provider.rb +4 -4
- data/lib/deimos/producer.rb +39 -116
- data/lib/deimos/railtie.rb +6 -0
- data/lib/deimos/schema_backends/avro_base.rb +21 -21
- data/lib/deimos/schema_backends/avro_schema_registry.rb +1 -2
- data/lib/deimos/schema_backends/avro_validation.rb +2 -2
- data/lib/deimos/schema_backends/base.rb +19 -12
- data/lib/deimos/schema_backends/mock.rb +6 -1
- data/lib/deimos/schema_backends/plain.rb +47 -0
- data/lib/deimos/schema_class/base.rb +2 -2
- data/lib/deimos/schema_class/enum.rb +1 -1
- data/lib/deimos/schema_class/record.rb +2 -2
- data/lib/deimos/test_helpers.rb +95 -320
- data/lib/deimos/tracing/provider.rb +6 -6
- data/lib/deimos/transcoder.rb +88 -0
- data/lib/deimos/utils/db_poller/base.rb +16 -14
- data/lib/deimos/utils/db_poller/state_based.rb +3 -3
- data/lib/deimos/utils/db_poller/time_based.rb +4 -4
- data/lib/deimos/utils/db_poller.rb +1 -1
- data/lib/deimos/utils/deadlock_retry.rb +1 -1
- data/lib/deimos/utils/{db_producer.rb → outbox_producer.rb} +16 -47
- data/lib/deimos/utils/schema_class.rb +0 -7
- data/lib/deimos/version.rb +1 -1
- data/lib/deimos.rb +79 -26
- data/lib/generators/deimos/{db_backend_generator.rb → outbox_backend_generator.rb} +4 -4
- data/lib/generators/deimos/schema_class_generator.rb +0 -1
- data/lib/generators/deimos/v2/templates/karafka.rb.tt +149 -0
- data/lib/generators/deimos/v2_generator.rb +193 -0
- data/lib/tasks/deimos.rake +5 -7
- data/spec/active_record_batch_consumer_association_spec.rb +22 -13
- data/spec/active_record_batch_consumer_spec.rb +84 -65
- data/spec/active_record_consume/batch_consumption_spec.rb +10 -10
- data/spec/active_record_consume/batch_slicer_spec.rb +12 -12
- data/spec/active_record_consume/mass_updater_spec.rb +137 -0
- data/spec/active_record_consumer_spec.rb +29 -13
- data/spec/active_record_producer_spec.rb +36 -26
- data/spec/backends/base_spec.rb +0 -23
- data/spec/backends/kafka_async_spec.rb +1 -3
- data/spec/backends/kafka_spec.rb +1 -3
- data/spec/backends/{db_spec.rb → outbox_spec.rb} +14 -20
- data/spec/batch_consumer_spec.rb +66 -116
- data/spec/consumer_spec.rb +53 -147
- data/spec/deimos_spec.rb +10 -126
- data/spec/kafka_source_spec.rb +19 -52
- data/spec/karafka/karafka.rb +69 -0
- data/spec/karafka_config/karafka_spec.rb +97 -0
- data/spec/logging_spec.rb +25 -0
- data/spec/message_spec.rb +9 -9
- data/spec/producer_spec.rb +112 -254
- data/spec/rake_spec.rb +1 -3
- data/spec/schema_backends/avro_validation_spec.rb +1 -1
- data/spec/schemas/com/my-namespace/MySchemaWithTitle.avsc +22 -0
- data/spec/snapshots/consumers-no-nest.snap +49 -0
- data/spec/snapshots/consumers.snap +49 -0
- data/spec/snapshots/consumers_and_producers-no-nest.snap +49 -0
- data/spec/snapshots/consumers_and_producers.snap +49 -0
- data/spec/snapshots/consumers_circular-no-nest.snap +49 -0
- data/spec/snapshots/consumers_circular.snap +49 -0
- data/spec/snapshots/consumers_complex_types-no-nest.snap +49 -0
- data/spec/snapshots/consumers_complex_types.snap +49 -0
- data/spec/snapshots/consumers_nested-no-nest.snap +49 -0
- data/spec/snapshots/consumers_nested.snap +49 -0
- data/spec/snapshots/namespace_folders.snap +49 -0
- data/spec/snapshots/namespace_map.snap +49 -0
- data/spec/snapshots/producers_with_key-no-nest.snap +49 -0
- data/spec/snapshots/producers_with_key.snap +49 -0
- data/spec/spec_helper.rb +61 -29
- data/spec/utils/db_poller_spec.rb +49 -39
- data/spec/utils/{db_producer_spec.rb → outbox_producer_spec.rb} +17 -184
- metadata +58 -67
- data/lib/deimos/batch_consumer.rb +0 -7
- data/lib/deimos/config/phobos_config.rb +0 -163
- data/lib/deimos/instrumentation.rb +0 -95
- data/lib/deimos/monkey_patches/phobos_cli.rb +0 -35
- data/lib/deimos/utils/inline_consumer.rb +0 -158
- data/lib/deimos/utils/lag_reporter.rb +0 -186
- data/lib/deimos/utils/schema_controller_mixin.rb +0 -129
- data/spec/config/configuration_spec.rb +0 -321
- data/spec/kafka_listener_spec.rb +0 -55
- data/spec/phobos.bad_db.yml +0 -73
- data/spec/phobos.yml +0 -77
- data/spec/utils/inline_consumer_spec.rb +0 -31
- data/spec/utils/lag_reporter_spec.rb +0 -76
- data/spec/utils/platform_schema_validation_spec.rb +0 -0
- data/spec/utils/schema_controller_mixin_spec.rb +0 -84
- /data/lib/generators/deimos/{db_backend → outbox_backend}/templates/migration +0 -0
- /data/lib/generators/deimos/{db_backend → outbox_backend}/templates/rails3_migration +0 -0
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
|
-
|
61
|
-
|
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 = :
|
20
|
+
config.producers.backend = :outbox
|
81
21
|
end
|
82
|
-
described_class.
|
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.
|
91
|
-
to raise_error('Publish backend is not set to :
|
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 = :
|
37
|
+
config.producers.backend = :outbox
|
98
38
|
end
|
99
|
-
expect { described_class.
|
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 = :
|
46
|
+
config.producers.backend = :outbox
|
107
47
|
end
|
108
|
-
expect { described_class.
|
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
|
data/spec/kafka_source_spec.rb
CHANGED
@@ -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 = :
|
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
|
341
|
+
it 'raises a MissingImplementationError exception' do
|
375
342
|
expect {
|
376
343
|
WidgetBuggy.create(widget_id: 1, name: 'Widget 1')
|
377
|
-
}.to raise_error(
|
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
|
@@ -0,0 +1,97 @@
|
|
1
|
+
RSpec.describe 'Karafka configs' do
|
2
|
+
before(:each) do
|
3
|
+
KarafkaApp.routes.clear
|
4
|
+
$found_stuff = nil
|
5
|
+
end
|
6
|
+
|
7
|
+
let(:consumer_class) do
|
8
|
+
Class.new(Deimos::Consumer) do
|
9
|
+
def consume_message(message)
|
10
|
+
$found_stuff = message.payload
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
let(:producer_class) do
|
16
|
+
Class.new(Deimos::Producer) do
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
describe 'producers' do
|
21
|
+
before(:each) do
|
22
|
+
stub_const('MyProducer', producer_class)
|
23
|
+
end
|
24
|
+
|
25
|
+
it 'should work with key none' do
|
26
|
+
KarafkaApp.routes.draw do
|
27
|
+
topic 'MyTopic' do
|
28
|
+
producer_class MyProducer
|
29
|
+
schema 'MySchema'
|
30
|
+
namespace 'com.my-namespace'
|
31
|
+
key_config(none: true)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
producer_class.publish({test_id: "id1", some_int: 5})
|
35
|
+
expect('MyTopic').to have_sent({test_id: "id1", some_int: 5})
|
36
|
+
end
|
37
|
+
|
38
|
+
it 'should work with key plain' do
|
39
|
+
KarafkaApp.routes.draw do
|
40
|
+
topic 'MyTopic' do
|
41
|
+
producer_class MyProducer
|
42
|
+
schema 'MySchema'
|
43
|
+
namespace 'com.my-namespace'
|
44
|
+
key_config({plain: true})
|
45
|
+
end
|
46
|
+
end
|
47
|
+
producer_class.publish({test_id: "id1", some_int: 5, payload_key: 'key'})
|
48
|
+
expect('MyTopic').to have_sent({test_id: "id1", some_int: 5}, 'key')
|
49
|
+
end
|
50
|
+
|
51
|
+
it 'should work with key field' do
|
52
|
+
KarafkaApp.routes.draw do
|
53
|
+
topic 'MyTopic' do
|
54
|
+
producer_class MyProducer
|
55
|
+
schema 'MySchema'
|
56
|
+
namespace 'com.my-namespace'
|
57
|
+
key_config({field: :test_id})
|
58
|
+
end
|
59
|
+
end
|
60
|
+
producer_class.publish({test_id: "id1", some_int: 5})
|
61
|
+
expect('MyTopic').to have_sent({test_id: "id1", some_int: 5}, 'id1')
|
62
|
+
end
|
63
|
+
|
64
|
+
it 'should work with key schema' do
|
65
|
+
KarafkaApp.routes.draw do
|
66
|
+
topic 'MyTopic' do
|
67
|
+
producer_class MyProducer
|
68
|
+
schema 'MySchema'
|
69
|
+
namespace 'com.my-namespace'
|
70
|
+
key_config({schema: 'MySchema_key'})
|
71
|
+
end
|
72
|
+
end
|
73
|
+
producer_class.publish({test_id: "id1", some_int: 5, payload_key: {test_id: 'id3'}})
|
74
|
+
expect('MyTopic').to have_sent({test_id: "id1", some_int: 5}, { test_id: 'id3'})
|
75
|
+
end
|
76
|
+
|
77
|
+
end
|
78
|
+
|
79
|
+
it 'should be able to pick up a consumer' do
|
80
|
+
stub_const('MyConsumer', consumer_class)
|
81
|
+
KarafkaApp.routes.draw do
|
82
|
+
topic 'MyTopic' do
|
83
|
+
consumer MyConsumer
|
84
|
+
schema 'MySchema'
|
85
|
+
namespace 'com.my-namespace'
|
86
|
+
key_config({field: :test_id})
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
test_consume_message('MyTopic', {test_id: "id1", some_int: 5}, key: "id1")
|
91
|
+
expect($found_stuff).to eq({'test_id' => "id1", 'some_int' => 5})
|
92
|
+
$found_stuff = nil
|
93
|
+
test_consume_message(MyConsumer, {test_id: "id1", some_int: 5}, key: "id1")
|
94
|
+
expect($found_stuff).to eq({'test_id' => "id1", 'some_int' => 5})
|
95
|
+
end
|
96
|
+
|
97
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
RSpec.describe Deimos::Logging do
|
2
|
+
include_context 'with publish_backend'
|
3
|
+
describe '#messages_log_text' do
|
4
|
+
it 'should return whole payload (default behavior)' do
|
5
|
+
log_message = described_class.messages_log_text(:payloads, messages)
|
6
|
+
expect(log_message[:payloads].count).to eq(3)
|
7
|
+
expect(log_message[:payloads].first[:payload]).to eq({ some_int: 1, test_id: 'foo1' })
|
8
|
+
expect(log_message[:payloads].first[:key]).to eq('foo1')
|
9
|
+
end
|
10
|
+
|
11
|
+
it 'should return only keys of messages' do
|
12
|
+
log_message = described_class.messages_log_text(:keys, messages)
|
13
|
+
expect(log_message[:payload_keys].count).to eq(3)
|
14
|
+
expect(log_message[:payload_keys]).to be_a(Array)
|
15
|
+
expect(log_message[:payload_keys].first).to eq('foo1')
|
16
|
+
end
|
17
|
+
|
18
|
+
it 'should return only messages count' do
|
19
|
+
log_message = described_class.messages_log_text(:count, messages)
|
20
|
+
expect(log_message[:payloads_count]).to be_a(Integer)
|
21
|
+
expect(log_message[:payloads_count]).to eq(3)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
data/spec/message_spec.rb
CHANGED
@@ -2,37 +2,37 @@
|
|
2
2
|
|
3
3
|
RSpec.describe(Deimos::Message) do
|
4
4
|
it 'should detect tombstones' do
|
5
|
-
expect(described_class.new(nil,
|
5
|
+
expect(described_class.new(nil, key: 'key1')).
|
6
6
|
to be_tombstone
|
7
|
-
expect(described_class.new({ v: 'val1' },
|
7
|
+
expect(described_class.new({ v: 'val1' }, key: 'key1')).
|
8
8
|
not_to be_tombstone
|
9
|
-
expect(described_class.new({ v: '' },
|
9
|
+
expect(described_class.new({ v: '' }, key: 'key1')).
|
10
10
|
not_to be_tombstone
|
11
|
-
expect(described_class.new({ v: 'val1' },
|
11
|
+
expect(described_class.new({ v: 'val1' }, key: nil)).
|
12
12
|
not_to be_tombstone
|
13
13
|
end
|
14
14
|
|
15
15
|
it 'can support complex keys/values' do
|
16
|
-
expect { described_class.new({ a: 1, b: 2 },
|
16
|
+
expect { described_class.new({ a: 1, b: 2 }, key: { c: 3, d: 4 }) }.
|
17
17
|
not_to raise_exception
|
18
18
|
end
|
19
19
|
|
20
20
|
describe 'headers' do
|
21
21
|
it 'returns nil when not set' do
|
22
|
-
expect(described_class.new({ v: 'val1' },
|
22
|
+
expect(described_class.new({ v: 'val1' }, key: 'key1')).
|
23
23
|
to have_attributes(headers: nil)
|
24
24
|
end
|
25
25
|
|
26
26
|
it 'can set and get headers' do
|
27
|
-
expect(described_class.new({ v: 'val1' },
|
27
|
+
expect(described_class.new({ v: 'val1' }, key: 'key1', headers: { a: 1 })).
|
28
28
|
to have_attributes(headers: { a: 1 })
|
29
29
|
end
|
30
30
|
|
31
31
|
it 'includes headers when converting to Hash' do
|
32
|
-
expect(described_class.new({ v: 'val1' },
|
32
|
+
expect(described_class.new({ v: 'val1' }, key: 'key1', headers: { a: 1 }).to_h).
|
33
33
|
to include(headers: { a: 1 })
|
34
34
|
|
35
|
-
expect(described_class.new({ v: 'val1' },
|
35
|
+
expect(described_class.new({ v: 'val1' }, key: 'key1', headers: { a: 1 }).encoded_hash).
|
36
36
|
to include(headers: { a: 1 })
|
37
37
|
end
|
38
38
|
end
|