deimos-ruby 1.7.0.pre.beta1 → 1.8.1.pre.beta3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +8 -4
- data/CHANGELOG.md +50 -0
- data/Gemfile.lock +109 -75
- data/README.md +147 -16
- data/deimos-ruby.gemspec +4 -2
- data/docs/ARCHITECTURE.md +144 -0
- data/docs/CONFIGURATION.md +4 -0
- data/lib/deimos.rb +8 -7
- data/lib/deimos/active_record_consume/batch_consumption.rb +159 -0
- data/lib/deimos/active_record_consume/batch_slicer.rb +27 -0
- data/lib/deimos/active_record_consume/message_consumption.rb +58 -0
- data/lib/deimos/active_record_consume/schema_model_converter.rb +52 -0
- data/lib/deimos/active_record_consumer.rb +33 -75
- data/lib/deimos/batch_consumer.rb +2 -142
- data/lib/deimos/config/configuration.rb +8 -10
- data/lib/deimos/consume/batch_consumption.rb +150 -0
- data/lib/deimos/consume/message_consumption.rb +94 -0
- data/lib/deimos/consumer.rb +79 -72
- data/lib/deimos/instrumentation.rb +10 -5
- data/lib/deimos/kafka_message.rb +1 -1
- data/lib/deimos/kafka_topic_info.rb +21 -2
- data/lib/deimos/message.rb +6 -1
- data/lib/deimos/schema_backends/avro_base.rb +33 -1
- data/lib/deimos/schema_backends/avro_schema_coercer.rb +30 -11
- data/lib/deimos/schema_backends/base.rb +21 -2
- data/lib/deimos/utils/db_poller.rb +6 -6
- data/lib/deimos/utils/db_producer.rb +57 -15
- data/lib/deimos/utils/deadlock_retry.rb +68 -0
- data/lib/deimos/utils/lag_reporter.rb +19 -26
- data/lib/deimos/utils/schema_controller_mixin.rb +111 -0
- data/lib/deimos/version.rb +1 -1
- data/lib/generators/deimos/active_record/templates/migration.rb.tt +28 -0
- data/lib/generators/deimos/active_record/templates/model.rb.tt +5 -0
- data/lib/generators/deimos/active_record_generator.rb +79 -0
- data/lib/generators/deimos/db_backend/templates/migration +1 -0
- data/lib/generators/deimos/db_backend/templates/rails3_migration +1 -0
- data/spec/active_record_batch_consumer_spec.rb +481 -0
- data/spec/active_record_consume/batch_slicer_spec.rb +42 -0
- data/spec/active_record_consume/schema_model_converter_spec.rb +105 -0
- data/spec/active_record_consumer_spec.rb +3 -11
- data/spec/batch_consumer_spec.rb +24 -7
- data/spec/config/configuration_spec.rb +4 -0
- data/spec/consumer_spec.rb +6 -6
- data/spec/deimos_spec.rb +57 -49
- data/spec/generators/active_record_generator_spec.rb +56 -0
- data/spec/handlers/my_batch_consumer.rb +6 -1
- data/spec/handlers/my_consumer.rb +6 -1
- data/spec/kafka_listener_spec.rb +54 -0
- data/spec/kafka_topic_info_spec.rb +39 -16
- data/spec/message_spec.rb +19 -0
- data/spec/producer_spec.rb +34 -0
- data/spec/schemas/com/my-namespace/Generated.avsc +71 -0
- data/spec/schemas/com/my-namespace/MyNestedSchema.avsc +55 -0
- data/spec/schemas/com/my-namespace/MySchemaCompound-key.avsc +18 -0
- data/spec/schemas/com/my-namespace/Wibble.avsc +43 -0
- data/spec/schemas/com/my-namespace/request/Index.avsc +11 -0
- data/spec/schemas/com/my-namespace/request/UpdateRequest.avsc +11 -0
- data/spec/schemas/com/my-namespace/response/Index.avsc +11 -0
- data/spec/schemas/com/my-namespace/response/UpdateResponse.avsc +11 -0
- data/spec/spec_helper.rb +24 -0
- data/spec/utils/db_poller_spec.rb +2 -2
- data/spec/utils/db_producer_spec.rb +84 -10
- data/spec/utils/deadlock_retry_spec.rb +74 -0
- data/spec/utils/lag_reporter_spec.rb +29 -22
- data/spec/utils/schema_controller_mixin_spec.rb +68 -0
- metadata +87 -30
- data/lib/deimos/base_consumer.rb +0 -100
- data/lib/deimos/utils/executor.rb +0 -124
- data/lib/deimos/utils/platform_schema_validation.rb +0 -0
- data/lib/deimos/utils/signal_handler.rb +0 -68
- data/spec/utils/executor_spec.rb +0 -53
- data/spec/utils/signal_handler_spec.rb +0 -16
data/spec/spec_helper.rb
CHANGED
@@ -2,6 +2,8 @@
|
|
2
2
|
|
3
3
|
$LOAD_PATH.unshift(File.expand_path('../lib', __dir__))
|
4
4
|
require 'active_record'
|
5
|
+
require 'action_controller/railtie'
|
6
|
+
require 'database_cleaner'
|
5
7
|
require 'deimos'
|
6
8
|
require 'deimos/metrics/mock'
|
7
9
|
require 'deimos/tracing/mock'
|
@@ -10,6 +12,11 @@ require 'active_support/testing/time_helpers'
|
|
10
12
|
require 'activerecord-import'
|
11
13
|
require 'handlers/my_batch_consumer'
|
12
14
|
require 'handlers/my_consumer'
|
15
|
+
require 'rspec/rails'
|
16
|
+
|
17
|
+
class DeimosApp < Rails::Application
|
18
|
+
end
|
19
|
+
DeimosApp.initialize!
|
13
20
|
|
14
21
|
# Helpers for Executor/DbProducer
|
15
22
|
module TestRunners
|
@@ -31,6 +38,7 @@ module TestRunners
|
|
31
38
|
# Test runner
|
32
39
|
class TestRunner
|
33
40
|
attr_accessor :id, :started, :stopped, :should_error
|
41
|
+
|
34
42
|
# :nodoc:
|
35
43
|
def initialize(id=nil)
|
36
44
|
@id = id
|
@@ -147,6 +155,9 @@ RSpec.configure do |config|
|
|
147
155
|
# true by default for RSpec 4.0
|
148
156
|
config.shared_context_metadata_behavior = :apply_to_host_groups
|
149
157
|
|
158
|
+
config.filter_run(focus: true)
|
159
|
+
config.run_all_when_everything_filtered = true
|
160
|
+
|
150
161
|
config.before(:all) do
|
151
162
|
Time.zone = 'Eastern Time (US & Canada)'
|
152
163
|
ActiveRecord::Base.logger = Logger.new('/dev/null')
|
@@ -159,6 +170,9 @@ RSpec.configure do |config|
|
|
159
170
|
config.include ActiveSupport::Testing::TimeHelpers
|
160
171
|
config.before(:suite) do
|
161
172
|
setup_db(DbConfigs::DB_OPTIONS.last)
|
173
|
+
|
174
|
+
DatabaseCleaner.strategy = :transaction
|
175
|
+
DatabaseCleaner.clean_with(:truncation)
|
162
176
|
end
|
163
177
|
|
164
178
|
config.mock_with(:rspec) do |mocks|
|
@@ -179,6 +193,16 @@ RSpec.configure do |config|
|
|
179
193
|
deimos_config.schema.backend = :avro_validation
|
180
194
|
end
|
181
195
|
end
|
196
|
+
|
197
|
+
config.around(:each) do |example|
|
198
|
+
use_cleaner = !example.metadata[:integration]
|
199
|
+
|
200
|
+
DatabaseCleaner.start if use_cleaner
|
201
|
+
|
202
|
+
example.run
|
203
|
+
|
204
|
+
DatabaseCleaner.clean if use_cleaner
|
205
|
+
end
|
182
206
|
end
|
183
207
|
|
184
208
|
RSpec.shared_context('with widgets') do
|
@@ -48,8 +48,8 @@ each_db_config(Deimos::Utils::DbPoller) do
|
|
48
48
|
end
|
49
49
|
|
50
50
|
allow(Deimos::Utils::DbPoller).to receive(:new)
|
51
|
-
signal_double = instance_double(
|
52
|
-
allow(
|
51
|
+
signal_double = instance_double(Sigurd::SignalHandler, run!: nil)
|
52
|
+
allow(Sigurd::SignalHandler).to receive(:new).and_return(signal_double)
|
53
53
|
described_class.start!
|
54
54
|
expect(Deimos::Utils::DbPoller).to have_received(:new).twice
|
55
55
|
expect(Deimos::Utils::DbPoller).to have_received(:new).
|
@@ -16,11 +16,13 @@ each_db_config(Deimos::Utils::DbProducer) do
|
|
16
16
|
|
17
17
|
before(:each) do
|
18
18
|
stub_const('Deimos::Utils::DbProducer::BATCH_SIZE', 2)
|
19
|
+
stub_const('Deimos::Utils::DbProducer::DELETE_BATCH_SIZE', 1)
|
19
20
|
end
|
20
21
|
|
21
22
|
specify '#process_next_messages' do
|
22
23
|
expect(producer).to receive(:retrieve_topics).and_return(%w(topic1 topic2))
|
23
24
|
expect(producer).to receive(:process_topic).twice
|
25
|
+
expect(Deimos::KafkaTopicInfo).to receive(:ping_empty_topics).with(%w(topic1 topic2))
|
24
26
|
expect(producer).to receive(:sleep).with(0.5)
|
25
27
|
producer.process_next_messages
|
26
28
|
end
|
@@ -40,6 +42,9 @@ each_db_config(Deimos::Utils::DbProducer) do
|
|
40
42
|
Deimos::KafkaMessage.create!(topic: 'topic1',
|
41
43
|
message: 'blah',
|
42
44
|
key: "key#{i}")
|
45
|
+
Deimos::KafkaMessage.create!(topic: 'topic2',
|
46
|
+
message: 'blah',
|
47
|
+
key: "key#{i}")
|
43
48
|
end
|
44
49
|
stub_const('Deimos::Utils::DbProducer::BATCH_SIZE', 5)
|
45
50
|
producer.current_topic = 'topic1'
|
@@ -280,6 +285,12 @@ each_db_config(Deimos::Utils::DbProducer) do
|
|
280
285
|
message: "mess#{i}",
|
281
286
|
partition_key: "key#{i}"
|
282
287
|
)
|
288
|
+
Deimos::KafkaMessage.create!(
|
289
|
+
id: i,
|
290
|
+
topic: 'my-topic2',
|
291
|
+
message: "mess#{i}",
|
292
|
+
partition_key: "key#{i}"
|
293
|
+
)
|
283
294
|
end
|
284
295
|
|
285
296
|
expect(Deimos::KafkaTopicInfo).to receive(:lock).
|
@@ -288,9 +299,60 @@ each_db_config(Deimos::Utils::DbProducer) do
|
|
288
299
|
expect(producer).to receive(:retrieve_messages).and_return(messages)
|
289
300
|
expect(Deimos::KafkaTopicInfo).to receive(:register_error)
|
290
301
|
|
302
|
+
expect(Deimos::KafkaMessage.count).to eq(8)
|
303
|
+
producer.process_topic('my-topic')
|
291
304
|
expect(Deimos::KafkaMessage.count).to eq(4)
|
305
|
+
end
|
306
|
+
|
307
|
+
it 'should retry deletes and not re-publish' do
|
308
|
+
messages = (1..4).map do |i|
|
309
|
+
Deimos::KafkaMessage.create!(
|
310
|
+
id: i,
|
311
|
+
topic: 'my-topic',
|
312
|
+
message: "mess#{i}",
|
313
|
+
partition_key: "key#{i}"
|
314
|
+
)
|
315
|
+
end
|
316
|
+
(5..8).each do |i|
|
317
|
+
Deimos::KafkaMessage.create!(
|
318
|
+
id: i,
|
319
|
+
topic: 'my-topic2',
|
320
|
+
message: "mess#{i}",
|
321
|
+
partition_key: "key#{i}"
|
322
|
+
)
|
323
|
+
end
|
324
|
+
|
325
|
+
raise_error = true
|
326
|
+
expect(Deimos::KafkaMessage).to receive(:where).exactly(5).times.and_wrap_original do |m, *args|
|
327
|
+
if raise_error
|
328
|
+
raise_error = false
|
329
|
+
raise 'Lock wait timeout'
|
330
|
+
end
|
331
|
+
m.call(*args)
|
332
|
+
end
|
333
|
+
|
334
|
+
expect(Deimos::KafkaTopicInfo).to receive(:lock).
|
335
|
+
with('my-topic', 'abc').and_return(true)
|
336
|
+
expect(producer).to receive(:retrieve_messages).ordered.and_return(messages)
|
337
|
+
expect(producer).to receive(:retrieve_messages).ordered.and_return([])
|
338
|
+
expect(phobos_producer).to receive(:publish_list).once.with(messages.map(&:phobos_message))
|
339
|
+
|
340
|
+
expect(Deimos::KafkaMessage.count).to eq(8)
|
292
341
|
producer.process_topic('my-topic')
|
293
|
-
expect(Deimos::KafkaMessage.count).to eq(
|
342
|
+
expect(Deimos::KafkaMessage.count).to eq(4)
|
343
|
+
end
|
344
|
+
|
345
|
+
it 'should re-raise misc errors on delete' do
|
346
|
+
messages = (1..3).map do |i|
|
347
|
+
Deimos::KafkaMessage.create!(
|
348
|
+
id: i,
|
349
|
+
topic: 'my-topic',
|
350
|
+
message: "mess#{i}",
|
351
|
+
partition_key: "key#{i}"
|
352
|
+
)
|
353
|
+
end
|
354
|
+
expect(Deimos::KafkaMessage).to receive(:where).once.and_raise('OH NOES')
|
355
|
+
expect { producer.delete_messages(messages) }.to raise_exception('OH NOES')
|
294
356
|
end
|
295
357
|
|
296
358
|
end
|
@@ -309,21 +371,34 @@ each_db_config(Deimos::Utils::DbProducer) do
|
|
309
371
|
Deimos::KafkaMessage.create!(topic: "topic#{i}", message: nil,
|
310
372
|
created_at: (1 + i).minute.ago)
|
311
373
|
end
|
374
|
+
Deimos::KafkaTopicInfo.create!(topic: 'topic1',
|
375
|
+
last_processed_at: 6.minutes.ago)
|
376
|
+
Deimos::KafkaTopicInfo.create!(topic: 'topic2',
|
377
|
+
last_processed_at: 3.minutes.ago)
|
378
|
+
Deimos::KafkaTopicInfo.create!(topic: 'topic3',
|
379
|
+
last_processed_at: 5.minutes.ago)
|
312
380
|
allow(Deimos.config.metrics).to receive(:gauge)
|
313
381
|
producer.send_pending_metrics
|
314
|
-
expect(Deimos.config.metrics).to have_received(:gauge).
|
382
|
+
expect(Deimos.config.metrics).to have_received(:gauge).exactly(6).times
|
383
|
+
# topic1 has the earliest message 4 minutes ago and last processed 6
|
384
|
+
# minutes ago, so the most amount of time we've seen nothing is 4 minutes
|
315
385
|
expect(Deimos.config.metrics).to have_received(:gauge).
|
316
386
|
with('pending_db_messages_max_wait', 4.minutes.to_i, tags: ['topic:topic1'])
|
387
|
+
# topic2 has earliest message 5 minutes ago and last processed 3 minutes
|
388
|
+
# ago, so we should give it 3 minutes
|
389
|
+
expect(Deimos.config.metrics).to have_received(:gauge).
|
390
|
+
with('pending_db_messages_max_wait', 3.minutes.to_i, tags: ['topic:topic2'])
|
391
|
+
# topic3 has no messages, so should get 0
|
317
392
|
expect(Deimos.config.metrics).to have_received(:gauge).
|
318
|
-
with('pending_db_messages_max_wait',
|
393
|
+
with('pending_db_messages_max_wait', 0, tags: ['topic:topic3'])
|
394
|
+
expect(Deimos.config.metrics).to have_received(:gauge).
|
395
|
+
with('pending_db_messages_count', 3, tags: ['topic:topic1'])
|
396
|
+
expect(Deimos.config.metrics).to have_received(:gauge).
|
397
|
+
with('pending_db_messages_count', 3, tags: ['topic:topic2'])
|
398
|
+
expect(Deimos.config.metrics).to have_received(:gauge).
|
399
|
+
with('pending_db_messages_count', 0, tags: ['topic:topic3'])
|
319
400
|
end
|
320
401
|
end
|
321
|
-
|
322
|
-
it 'should send 0 if no messages' do
|
323
|
-
expect(Deimos.config.metrics).to receive(:gauge).
|
324
|
-
with('pending_db_messages_max_wait', 0)
|
325
|
-
producer.send_pending_metrics
|
326
|
-
end
|
327
402
|
end
|
328
403
|
|
329
404
|
example 'Full integration test' do
|
@@ -403,5 +478,4 @@ each_db_config(Deimos::Utils::DbProducer) do
|
|
403
478
|
}
|
404
479
|
])
|
405
480
|
end
|
406
|
-
|
407
481
|
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
RSpec.describe Deimos::Utils::DeadlockRetry do
|
4
|
+
include_context 'with widgets'
|
5
|
+
|
6
|
+
before(:each) do
|
7
|
+
allow(described_class).to receive(:sleep)
|
8
|
+
end
|
9
|
+
|
10
|
+
describe 'deadlock handling' do
|
11
|
+
let(:batch) { [{ key: 1, payload: { test_id: 'abc', some_int: 3 } }] }
|
12
|
+
|
13
|
+
it 'should retry deadlocks 3 times' do
|
14
|
+
# Should receive original attempt + 2 retries
|
15
|
+
expect(Widget).
|
16
|
+
to receive(:create).
|
17
|
+
and_raise(ActiveRecord::Deadlocked.new('Lock wait timeout exceeded')).
|
18
|
+
exactly(3).times
|
19
|
+
|
20
|
+
# After 3 tries, should let it bubble up
|
21
|
+
expect {
|
22
|
+
described_class.wrap do
|
23
|
+
Widget.create(test_id: 'abc')
|
24
|
+
end
|
25
|
+
}.to raise_error(ActiveRecord::Deadlocked)
|
26
|
+
end
|
27
|
+
|
28
|
+
it 'should stop retrying deadlocks after success' do
|
29
|
+
allow(Widget).
|
30
|
+
to receive(:create).
|
31
|
+
with(hash_including(test_id: 'first')).
|
32
|
+
and_call_original
|
33
|
+
|
34
|
+
# Fail on first attempt, succeed on second
|
35
|
+
expect(Widget).
|
36
|
+
to receive(:create).
|
37
|
+
with(hash_including(test_id: 'second')).
|
38
|
+
and_raise(ActiveRecord::Deadlocked.new('Deadlock found when trying to get lock')).
|
39
|
+
once.
|
40
|
+
ordered
|
41
|
+
|
42
|
+
expect(Widget).
|
43
|
+
to receive(:create).
|
44
|
+
with(hash_including(test_id: 'second')).
|
45
|
+
once.
|
46
|
+
ordered.
|
47
|
+
and_call_original
|
48
|
+
|
49
|
+
# Should not raise anything
|
50
|
+
described_class.wrap do
|
51
|
+
Widget.create(test_id: 'first')
|
52
|
+
Widget.create(test_id: 'second')
|
53
|
+
end
|
54
|
+
|
55
|
+
expect(Widget.all).to match_array([
|
56
|
+
have_attributes(test_id: 'first'),
|
57
|
+
have_attributes(test_id: 'second')
|
58
|
+
])
|
59
|
+
end
|
60
|
+
|
61
|
+
it 'should not retry non-deadlock exceptions' do
|
62
|
+
expect(Widget).
|
63
|
+
to receive(:create).
|
64
|
+
and_raise(ActiveRecord::StatementInvalid.new('Oops!!')).
|
65
|
+
once
|
66
|
+
|
67
|
+
expect {
|
68
|
+
described_class.wrap do
|
69
|
+
Widget.create(test_id: 'abc')
|
70
|
+
end
|
71
|
+
}.to raise_error(ActiveRecord::StatementInvalid, 'Oops!!')
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
@@ -2,8 +2,11 @@
|
|
2
2
|
|
3
3
|
describe Deimos::Utils::LagReporter do
|
4
4
|
|
5
|
+
let(:kafka_client) { instance_double(Kafka::Client) }
|
6
|
+
let(:partition1_tags) { %w(consumer_group:group1 partition:1 topic:my-topic) }
|
7
|
+
let(:partition2_tags) { %w(consumer_group:group1 partition:2 topic:my-topic) }
|
8
|
+
|
5
9
|
before(:each) do
|
6
|
-
kafka_client = instance_double(Kafka::Client)
|
7
10
|
allow(kafka_client).to receive(:last_offset_for).and_return(100)
|
8
11
|
allow(Phobos).to receive(:create_kafka_client).and_return(kafka_client)
|
9
12
|
Deimos.configure { |c| c.consumers.report_lag = true }
|
@@ -20,38 +23,22 @@ describe Deimos::Utils::LagReporter do
|
|
20
23
|
'heartbeat.consumer.kafka',
|
21
24
|
group_id: 'group1', topic_partitions: { 'my-topic': [1] }
|
22
25
|
)
|
23
|
-
|
24
26
|
end
|
25
27
|
|
26
28
|
it 'should report lag' do
|
27
29
|
expect(Deimos.config.metrics).to receive(:gauge).ordered.twice.
|
28
|
-
with('consumer_lag', 95,
|
29
|
-
tags: %w(
|
30
|
-
consumer_group:group1
|
31
|
-
partition:1
|
32
|
-
topic:my-topic
|
33
|
-
))
|
30
|
+
with('consumer_lag', 95, tags: partition1_tags)
|
34
31
|
expect(Deimos.config.metrics).to receive(:gauge).ordered.once.
|
35
|
-
with('consumer_lag', 80,
|
36
|
-
tags: %w(
|
37
|
-
consumer_group:group1
|
38
|
-
partition:2
|
39
|
-
topic:my-topic
|
40
|
-
))
|
32
|
+
with('consumer_lag', 80, tags: partition2_tags)
|
41
33
|
expect(Deimos.config.metrics).to receive(:gauge).ordered.once.
|
42
|
-
with('consumer_lag', 0,
|
43
|
-
tags: %w(
|
44
|
-
consumer_group:group1
|
45
|
-
partition:2
|
46
|
-
topic:my-topic
|
47
|
-
))
|
34
|
+
with('consumer_lag', 0, tags: partition2_tags)
|
48
35
|
ActiveSupport::Notifications.instrument(
|
49
36
|
'seek.consumer.kafka',
|
50
37
|
offset: 5, topic: 'my-topic', group_id: 'group1', partition: 1
|
51
38
|
)
|
52
39
|
ActiveSupport::Notifications.instrument(
|
53
40
|
'start_process_message.consumer.kafka',
|
54
|
-
|
41
|
+
offset: 20, topic: 'my-topic', group_id: 'group1', partition: 2
|
55
42
|
)
|
56
43
|
ActiveSupport::Notifications.instrument(
|
57
44
|
'heartbeat.consumer.kafka',
|
@@ -59,8 +46,28 @@ describe Deimos::Utils::LagReporter do
|
|
59
46
|
)
|
60
47
|
ActiveSupport::Notifications.instrument(
|
61
48
|
'start_process_batch.consumer.kafka',
|
62
|
-
|
49
|
+
last_offset: 100, topic: 'my-topic', group_id: 'group1', partition: 2
|
50
|
+
)
|
51
|
+
ActiveSupport::Notifications.instrument(
|
52
|
+
'heartbeat.consumer.kafka',
|
53
|
+
group_id: 'group1', topic_partitions: { 'my-topic': [1, 2] }
|
54
|
+
)
|
55
|
+
end
|
56
|
+
|
57
|
+
it 'should update lag after heartbeat' do
|
58
|
+
expect(Deimos.config.metrics).to receive(:gauge).ordered.once.
|
59
|
+
with('consumer_lag', 94, tags: partition2_tags)
|
60
|
+
expect(Deimos.config.metrics).to receive(:gauge).ordered.once.
|
61
|
+
with('consumer_lag', 95, tags: partition2_tags)
|
62
|
+
ActiveSupport::Notifications.instrument(
|
63
|
+
'seek.consumer.kafka',
|
64
|
+
offset: 6, topic: 'my-topic', group_id: 'group1', partition: 2
|
65
|
+
)
|
66
|
+
ActiveSupport::Notifications.instrument(
|
67
|
+
'heartbeat.consumer.kafka',
|
68
|
+
group_id: 'group1', topic_partitions: { 'my-topic': [1, 2] }
|
63
69
|
)
|
70
|
+
allow(kafka_client).to receive(:last_offset_for).and_return(101)
|
64
71
|
ActiveSupport::Notifications.instrument(
|
65
72
|
'heartbeat.consumer.kafka',
|
66
73
|
group_id: 'group1', topic_partitions: { 'my-topic': [1, 2] }
|
@@ -0,0 +1,68 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'deimos/utils/schema_controller_mixin'
|
4
|
+
require 'deimos/schema_backends/avro_local'
|
5
|
+
|
6
|
+
RSpec.describe Deimos::Utils::SchemaControllerMixin, type: :controller do
|
7
|
+
|
8
|
+
before(:each) do
|
9
|
+
Deimos.configure do
|
10
|
+
schema.backend(:avro_local)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
controller(ActionController::Base) do
|
15
|
+
include Deimos::Utils::SchemaControllerMixin # rubocop:disable RSpec/DescribedClass
|
16
|
+
|
17
|
+
request_namespace 'com.my-namespace.request'
|
18
|
+
response_namespace 'com.my-namespace.response'
|
19
|
+
schemas :index, :show
|
20
|
+
schemas :update, request: 'UpdateRequest', response: 'UpdateResponse'
|
21
|
+
|
22
|
+
# :nodoc:
|
23
|
+
def index
|
24
|
+
render_schema({ 'response_id' => payload[:request_id] + ' mom' })
|
25
|
+
end
|
26
|
+
|
27
|
+
# :nodoc:
|
28
|
+
def show
|
29
|
+
render_schema({ 'response_id' => payload[:request_id] + ' dad' })
|
30
|
+
end
|
31
|
+
|
32
|
+
# :nodoc:
|
33
|
+
def update
|
34
|
+
render_schema({ 'update_response_id' => payload[:update_request_id] + ' sis' })
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
it 'should render the correct response for index' do
|
39
|
+
request_backend = Deimos.schema_backend(schema: 'Index',
|
40
|
+
namespace: 'com.my-namespace.request')
|
41
|
+
response_backend = Deimos.schema_backend(schema: 'Index',
|
42
|
+
namespace: 'com.my-namespace.response')
|
43
|
+
request.content_type = 'avro/binary'
|
44
|
+
get :index, body: request_backend.encode({ 'request_id' => 'hi' })
|
45
|
+
expect(response_backend.decode(response.body)).to eq({ 'response_id' => 'hi mom' })
|
46
|
+
end
|
47
|
+
|
48
|
+
it 'should render the correct response for show' do
|
49
|
+
request_backend = Deimos.schema_backend(schema: 'Index',
|
50
|
+
namespace: 'com.my-namespace.request')
|
51
|
+
response_backend = Deimos.schema_backend(schema: 'Index',
|
52
|
+
namespace: 'com.my-namespace.response')
|
53
|
+
request.content_type = 'avro/binary'
|
54
|
+
get :show, params: { id: 1 }, body: request_backend.encode({ 'request_id' => 'hi' })
|
55
|
+
expect(response_backend.decode(response.body)).to eq({ 'response_id' => 'hi dad' })
|
56
|
+
end
|
57
|
+
|
58
|
+
it 'should render the correct response for update' do
|
59
|
+
request_backend = Deimos.schema_backend(schema: 'UpdateRequest',
|
60
|
+
namespace: 'com.my-namespace.request')
|
61
|
+
response_backend = Deimos.schema_backend(schema: 'UpdateResponse',
|
62
|
+
namespace: 'com.my-namespace.response')
|
63
|
+
request.content_type = 'avro/binary'
|
64
|
+
post :update, params: { id: 1 }, body: request_backend.encode({ 'update_request_id' => 'hi' })
|
65
|
+
expect(response_backend.decode(response.body)).to eq({ 'update_response_id' => 'hi sis' })
|
66
|
+
end
|
67
|
+
|
68
|
+
end
|