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.
Files changed (120) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop_todo.yml +0 -17
  3. data/.tool-versions +1 -0
  4. data/CHANGELOG.md +5 -0
  5. data/README.md +287 -498
  6. data/deimos-ruby.gemspec +4 -4
  7. data/docs/CONFIGURATION.md +133 -226
  8. data/docs/UPGRADING.md +237 -0
  9. data/lib/deimos/active_record_consume/batch_consumption.rb +29 -28
  10. data/lib/deimos/active_record_consume/mass_updater.rb +59 -4
  11. data/lib/deimos/active_record_consume/message_consumption.rb +15 -21
  12. data/lib/deimos/active_record_consumer.rb +36 -21
  13. data/lib/deimos/active_record_producer.rb +28 -9
  14. data/lib/deimos/backends/base.rb +4 -35
  15. data/lib/deimos/backends/kafka.rb +6 -22
  16. data/lib/deimos/backends/kafka_async.rb +6 -22
  17. data/lib/deimos/backends/{db.rb → outbox.rb} +13 -9
  18. data/lib/deimos/config/configuration.rb +116 -379
  19. data/lib/deimos/consume/batch_consumption.rb +24 -124
  20. data/lib/deimos/consume/message_consumption.rb +36 -63
  21. data/lib/deimos/consumer.rb +16 -75
  22. data/lib/deimos/ext/consumer_route.rb +35 -0
  23. data/lib/deimos/ext/producer_middleware.rb +94 -0
  24. data/lib/deimos/ext/producer_route.rb +22 -0
  25. data/lib/deimos/ext/redraw.rb +29 -0
  26. data/lib/deimos/ext/routing_defaults.rb +72 -0
  27. data/lib/deimos/ext/schema_route.rb +70 -0
  28. data/lib/deimos/kafka_message.rb +2 -2
  29. data/lib/deimos/kafka_source.rb +2 -7
  30. data/lib/deimos/kafka_topic_info.rb +1 -1
  31. data/lib/deimos/logging.rb +71 -0
  32. data/lib/deimos/message.rb +2 -11
  33. data/lib/deimos/metrics/datadog.rb +40 -1
  34. data/lib/deimos/metrics/provider.rb +4 -4
  35. data/lib/deimos/producer.rb +39 -116
  36. data/lib/deimos/railtie.rb +6 -0
  37. data/lib/deimos/schema_backends/avro_base.rb +21 -21
  38. data/lib/deimos/schema_backends/avro_schema_registry.rb +1 -2
  39. data/lib/deimos/schema_backends/avro_validation.rb +2 -2
  40. data/lib/deimos/schema_backends/base.rb +19 -12
  41. data/lib/deimos/schema_backends/mock.rb +6 -1
  42. data/lib/deimos/schema_backends/plain.rb +47 -0
  43. data/lib/deimos/schema_class/base.rb +2 -2
  44. data/lib/deimos/schema_class/enum.rb +1 -1
  45. data/lib/deimos/schema_class/record.rb +2 -2
  46. data/lib/deimos/test_helpers.rb +95 -320
  47. data/lib/deimos/tracing/provider.rb +6 -6
  48. data/lib/deimos/transcoder.rb +88 -0
  49. data/lib/deimos/utils/db_poller/base.rb +16 -14
  50. data/lib/deimos/utils/db_poller/state_based.rb +3 -3
  51. data/lib/deimos/utils/db_poller/time_based.rb +4 -4
  52. data/lib/deimos/utils/db_poller.rb +1 -1
  53. data/lib/deimos/utils/deadlock_retry.rb +1 -1
  54. data/lib/deimos/utils/{db_producer.rb → outbox_producer.rb} +16 -47
  55. data/lib/deimos/utils/schema_class.rb +0 -7
  56. data/lib/deimos/version.rb +1 -1
  57. data/lib/deimos.rb +79 -26
  58. data/lib/generators/deimos/{db_backend_generator.rb → outbox_backend_generator.rb} +4 -4
  59. data/lib/generators/deimos/schema_class_generator.rb +0 -1
  60. data/lib/generators/deimos/v2/templates/karafka.rb.tt +149 -0
  61. data/lib/generators/deimos/v2_generator.rb +193 -0
  62. data/lib/tasks/deimos.rake +5 -7
  63. data/spec/active_record_batch_consumer_association_spec.rb +22 -13
  64. data/spec/active_record_batch_consumer_spec.rb +84 -65
  65. data/spec/active_record_consume/batch_consumption_spec.rb +10 -10
  66. data/spec/active_record_consume/batch_slicer_spec.rb +12 -12
  67. data/spec/active_record_consume/mass_updater_spec.rb +137 -0
  68. data/spec/active_record_consumer_spec.rb +29 -13
  69. data/spec/active_record_producer_spec.rb +36 -26
  70. data/spec/backends/base_spec.rb +0 -23
  71. data/spec/backends/kafka_async_spec.rb +1 -3
  72. data/spec/backends/kafka_spec.rb +1 -3
  73. data/spec/backends/{db_spec.rb → outbox_spec.rb} +14 -20
  74. data/spec/batch_consumer_spec.rb +66 -116
  75. data/spec/consumer_spec.rb +53 -147
  76. data/spec/deimos_spec.rb +10 -126
  77. data/spec/kafka_source_spec.rb +19 -52
  78. data/spec/karafka/karafka.rb +69 -0
  79. data/spec/karafka_config/karafka_spec.rb +97 -0
  80. data/spec/logging_spec.rb +25 -0
  81. data/spec/message_spec.rb +9 -9
  82. data/spec/producer_spec.rb +112 -254
  83. data/spec/rake_spec.rb +1 -3
  84. data/spec/schema_backends/avro_validation_spec.rb +1 -1
  85. data/spec/schemas/com/my-namespace/MySchemaWithTitle.avsc +22 -0
  86. data/spec/snapshots/consumers-no-nest.snap +49 -0
  87. data/spec/snapshots/consumers.snap +49 -0
  88. data/spec/snapshots/consumers_and_producers-no-nest.snap +49 -0
  89. data/spec/snapshots/consumers_and_producers.snap +49 -0
  90. data/spec/snapshots/consumers_circular-no-nest.snap +49 -0
  91. data/spec/snapshots/consumers_circular.snap +49 -0
  92. data/spec/snapshots/consumers_complex_types-no-nest.snap +49 -0
  93. data/spec/snapshots/consumers_complex_types.snap +49 -0
  94. data/spec/snapshots/consumers_nested-no-nest.snap +49 -0
  95. data/spec/snapshots/consumers_nested.snap +49 -0
  96. data/spec/snapshots/namespace_folders.snap +49 -0
  97. data/spec/snapshots/namespace_map.snap +49 -0
  98. data/spec/snapshots/producers_with_key-no-nest.snap +49 -0
  99. data/spec/snapshots/producers_with_key.snap +49 -0
  100. data/spec/spec_helper.rb +61 -29
  101. data/spec/utils/db_poller_spec.rb +49 -39
  102. data/spec/utils/{db_producer_spec.rb → outbox_producer_spec.rb} +17 -184
  103. metadata +58 -67
  104. data/lib/deimos/batch_consumer.rb +0 -7
  105. data/lib/deimos/config/phobos_config.rb +0 -163
  106. data/lib/deimos/instrumentation.rb +0 -95
  107. data/lib/deimos/monkey_patches/phobos_cli.rb +0 -35
  108. data/lib/deimos/utils/inline_consumer.rb +0 -158
  109. data/lib/deimos/utils/lag_reporter.rb +0 -186
  110. data/lib/deimos/utils/schema_controller_mixin.rb +0 -129
  111. data/spec/config/configuration_spec.rb +0 -321
  112. data/spec/kafka_listener_spec.rb +0 -55
  113. data/spec/phobos.bad_db.yml +0 -73
  114. data/spec/phobos.yml +0 -77
  115. data/spec/utils/inline_consumer_spec.rb +0 -31
  116. data/spec/utils/lag_reporter_spec.rb +0 -76
  117. data/spec/utils/platform_schema_validation_spec.rb +0 -0
  118. data/spec/utils/schema_controller_mixin_spec.rb +0 -84
  119. /data/lib/generators/deimos/{db_backend → outbox_backend}/templates/migration +0 -0
  120. /data/lib/generators/deimos/{db_backend → outbox_backend}/templates/rails3_migration +0 -0
@@ -0,0 +1,193 @@
1
+ # frozen_string_literal: true
2
+
3
+ # THINGS TO REMEMBER
4
+ # logger
5
+ # fatal_error
6
+ # bulk_import_id_generator
7
+
8
+ require 'rails/generators'
9
+ require 'rails/version'
10
+
11
+ # Generates a new consumer.
12
+ module Deimos
13
+ module Generators
14
+ # Generator for ActiveRecord model and migration.
15
+ class V2Generator < Rails::Generators::Base
16
+
17
+ class ProcString < String
18
+ def inspect
19
+ self.to_s
20
+ end
21
+ end
22
+
23
+ source_root File.expand_path('v2/templates', __dir__)
24
+
25
+ no_commands do
26
+ def deimos_config
27
+ Deimos.config
28
+ end
29
+
30
+ def deimos_configs
31
+ configs = {
32
+ producers: %i(topic_prefix disabled backend),
33
+ schema: %i(backend registry_url user password path generated_class_path use_schema_classes
34
+ nest_child_schemas use_full_namespace schema_namespace_map),
35
+ db_producer: %i(log_topics compact_topics),
36
+ }
37
+
38
+ response = {}
39
+ configs.each do |group, settings|
40
+ group_setting = deimos_config.send(group)
41
+ next if settings.all? { |s| group_setting.default_value?(s)}
42
+
43
+ response[group] = {}
44
+ settings.each do |field|
45
+ unless group_setting.default_value?(field.to_sym)
46
+ response[group][field.to_s] = group_setting.send(field.to_sym)
47
+ end
48
+ end
49
+ end
50
+ response
51
+ end
52
+
53
+ def setup_configs
54
+ configs = {}
55
+ configs[:client_id] = if deimos_config.kafka.client_id && deimos_config.kafka.client_id != 'phobos'
56
+ deimos_config.kafka.client_id
57
+ else
58
+ Rails::Application.subclasses.first&.name&.gsub('::Application', '')&.underscore
59
+ end
60
+ if deimos_config.consumer_objects.any? { |c| c.max_concurrency.present? }
61
+ configs[:concurrency] = deimos_config.consumer_objects.map(&:max_concurrency).compact.max
62
+ end
63
+ if deimos_config.consumer_objects.any? { |c| c.max_wait_time.present? }
64
+ configs[:max_wait_time] = deimos_config.consumer_objects.map(&:max_wait_time).compact.max
65
+ end
66
+ configs.compact
67
+ end
68
+
69
+ def default_kafka_configs
70
+ configs = {}
71
+ configs["bootstrap.servers"] = deimos_config.kafka.seed_brokers.join(',')
72
+ configs["socket.connection.setup.timeout.ms"] = deimos_config.kafka.connect_timeout * 1000
73
+ configs["socket.timeout.ms"] = deimos_config.kafka.socket_timeout * 1000
74
+ configs["security.protocol"] = if deimos_config.kafka.ssl.enabled
75
+ "ssl"
76
+ elsif deimos_config.kafka.sasl.enabled
77
+ if deimos_config.kafka.sasl.enforce_ssl
78
+ "sasl_ssl"
79
+ else
80
+ "sasl_plain"
81
+ end
82
+ end
83
+ configs["ssl.ca.pem"] = deimos_config.kafka.ssl.ca_cert
84
+ configs["ssl.certificate.pem"] = deimos_config.kafka.ssl.client_cert
85
+ configs["ssl.key.pem"] = deimos_config.kafka.ssl.client_cert_key
86
+ configs["ssl.endpoint.identification.algorithm"] = "https" if deimos_config.kafka.ssl.verify_hostname
87
+ configs["sasl.kerberos.principal"] = deimos_config.kafka.sasl.gssapi_principal
88
+ configs["sasl.kerberos.keytab"] = deimos_config.kafka.sasl.gssapi_keytab
89
+ configs["sasl.username"] = deimos_config.kafka.sasl.plain_username || deimos_config.kafka.sasl.scram_username
90
+ configs["sasl.password"] = deimos_config.kafka.sasl.plain_password || deimos_config.kafka.sasl.scram_password
91
+ configs["sasl.mechanisms"] = deimos_config.kafka.sasl.scram_mechanism
92
+ configs["request.required.acks"] = deimos_config.producers.required_acks
93
+ configs["message.send.max.retries"] = deimos_config.producers.max_retries
94
+ configs["retry.backoff.ms"] = deimos_config.producers.retry_backoff * 1000 if deimos_config.producers.retry_backoff
95
+ configs["compression.codec"] = deimos_config.producers.compression_codec
96
+ configs.compact
97
+ end
98
+
99
+ def default_configs
100
+ {
101
+ payload_log: deimos_config.payload_log,
102
+ reraise_errors: deimos_config.consumers.reraise_errors,
103
+ replace_associations: deimos_config.consumers.replace_associations,
104
+ namespace: deimos_config.producers.schema_namespace,
105
+ use_schema_classes: deimos_config.schema.use_schema_classes
106
+ }.compact
107
+ end
108
+
109
+ def consumer_configs
110
+ deimos_config.consumer_objects.group_by(&:group_id).map do |group_id, consumers|
111
+ [group_id, consumers.map do |consumer|
112
+ kafka_configs = {}
113
+ kafka_configs["auto.offset.reset"] = consumer.start_from_beginning ? 'earliest' : 'latest'
114
+ kafka_configs["session.timeout.ms"] = consumer.session_timeout * 1000 unless consumer.default_value?(:session_timeout)
115
+ kafka_configs["auto.commit.interval.ms"] = consumer.offset_commit_interval * 1000 unless consumer.default_value?(:offset_commit_interval)
116
+ kafka_configs["heartbeat.interval.ms"] = consumer.heartbeat_interval * 1000 unless consumer.default_value?(:heartbeat_interval)
117
+ configs = {
118
+ kafka: kafka_configs.compact,
119
+ topic: consumer.topic,
120
+ consumer: ProcString.new(consumer.class_name),
121
+ schema: consumer.schema,
122
+ namespace: consumer.namespace,
123
+ key_config: consumer.key_config,
124
+ }
125
+ configs[:use_schema_classes] = consumer.use_schema_classes unless consumer.default_value?(:use_schema_classes)
126
+ configs[:max_db_batch_size] = consumer.max_db_batch_size unless consumer.default_value?(:max_db_batch_size)
127
+ configs[:bulk_import_id_column] = consumer.bulk_import_id_column unless consumer.default_value?(:bulk_import_id_column)
128
+ configs[:replace_associations] = consumer.replace_associations unless consumer.default_value?(:replace_associations)
129
+ configs[:active] = false if consumer.disabled
130
+ configs[:each_message] = true unless consumer.delivery.to_s == 'inline_batch'
131
+ configs
132
+ end]
133
+ end.to_h
134
+ end
135
+
136
+ def producer_configs
137
+ deimos_config.producer_objects.map do |producer|
138
+ {
139
+ topic: producer.topic,
140
+ producer_class: ProcString.new(producer.class_name),
141
+ schema: producer.schema,
142
+ namespace: producer.namespace || deimos_config.producers.schema_namespace,
143
+ key_config: producer.key_config,
144
+ use_schema_classes: producer.use_schema_classes
145
+ }.compact
146
+ end
147
+ end
148
+
149
+ def rename_consumer_methods
150
+ deimos_config.consumer_objects.each do |consumer|
151
+ consumer.class_name.constantize
152
+ file = Object.const_source_location(consumer.class_name)[0]
153
+ if file.to_s.include?(Rails.root.to_s)
154
+ gsub_file(file, /([\t ]+)def consume\((\w+)(, *(\w+)?)\)/,
155
+ "\\1def consume_message(message)\n\\1 \\2 = message.payload\n\\1 \\4 = message.metadata")
156
+ gsub_file(file, /([\t ]+)def consume_batch\((\w+)(, *(\w+)?)\)/,
157
+ "\\1def consume_batch\n\\1 \\2 = messages.payloads\n\\1 \\4 = messages.metadata")
158
+ gsub_file(file, /def record_attributes\((\w+)\)/,
159
+ "def record_attributes(\\1, key)")
160
+ end
161
+ end
162
+ end
163
+
164
+ def fix_specs
165
+ Dir["*/**/*_spec.rb"].each do |file|
166
+ gsub_file(file, /,\s*call_original: true/, "")
167
+ gsub_file(file, 'Deimos::Backends::Test.sent_messages', "Deimos::TestHelpers.sent_messages")
168
+ end
169
+ end
170
+
171
+ def process_all_files
172
+ template('karafka.rb.tt', "karafka.rb", force: true)
173
+ rename_consumer_methods
174
+ fix_specs
175
+ end
176
+
177
+ end
178
+
179
+ desc 'Generate and update app files for version 2.0'
180
+ # @return [void]
181
+ def generate
182
+ process_all_files
183
+ say "Generation complete! You are safe to remove the existing initializer that configures Deimos.", :green
184
+ say "Note: The following settings cannot be determined by the generator:", :yellow
185
+ say "* logger / phobos_logger (dynamic object, cannot be printed out)", :yellow
186
+ say "* kafka.sasl.oauth_token_provider", :yellow
187
+ say "* producers.max_buffer_size", :yellow
188
+ say "* consumer.backoff (only handles minimum, not maximum)", :yellow
189
+ say "For more information, see https://github.com/flipp-oss/deimos/blob/master/docs/UPGRADING.md", :yellow
190
+ end
191
+ end
192
+ end
193
+ end
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'phobos'
4
- require 'phobos/cli'
5
3
  require 'generators/deimos/schema_class_generator'
6
4
  require 'optparse'
7
5
 
@@ -15,17 +13,17 @@ namespace :deimos do
15
13
  ENV['DEIMOS_TASK_NAME'] = 'consumer'
16
14
  STDOUT.sync = true
17
15
  Rails.logger.info('Running deimos:start rake task.')
18
- Phobos::CLI::Commands.start(%w(start --skip_config))
16
+ Karafka::Server.run
19
17
  end
20
18
 
21
19
  desc 'Starts the Deimos database producer'
22
- task db_producer: :environment do
20
+ task outbox: :environment do
23
21
  ENV['DEIMOS_RAKE_TASK'] = 'true'
24
- ENV['DEIMOS_TASK_NAME'] = 'db_producer'
22
+ ENV['DEIMOS_TASK_NAME'] = 'outbox'
25
23
  STDOUT.sync = true
26
- Rails.logger.info('Running deimos:db_producer rake task.')
24
+ Rails.logger.info('Running deimos:outbox rake task.')
27
25
  thread_count = ENV['THREAD_COUNT'].to_i.zero? ? 1 : ENV['THREAD_COUNT'].to_i
28
- Deimos.start_db_backend!(thread_count: thread_count)
26
+ Deimos.start_outbox_backend!(thread_count: thread_count)
29
27
  end
30
28
 
31
29
  task db_poller: :environment do
@@ -74,9 +74,21 @@ module ActiveRecordBatchConsumerTest # rubocop:disable Metrics/ModuleLength
74
74
  end
75
75
 
76
76
  prepend_before(:each) do
77
- consumer_class.config[:bulk_import_id_column] = :bulk_import_id
78
77
  stub_const('MyBatchConsumer', consumer_class)
79
78
  stub_const('ConsumerTest::MyBatchConsumer', consumer_class)
79
+ klass = consumer_class
80
+ col = bulk_import_id_column
81
+ rep = replace_associations
82
+ Karafka::App.routes.redraw do
83
+ topic 'my-topic' do
84
+ consumer klass
85
+ schema 'MySchemaWithTitle'
86
+ namespace 'com.my-namespace'
87
+ key_config plain: true
88
+ bulk_import_id_column col
89
+ replace_associations rep
90
+ end
91
+ end
80
92
  end
81
93
 
82
94
  # Helper to publish a list of messages and call the consumer
@@ -84,16 +96,16 @@ module ActiveRecordBatchConsumerTest # rubocop:disable Metrics/ModuleLength
84
96
  keys = messages.map { |m| m[:key] }
85
97
  payloads = messages.map { |m| m[:payload] }
86
98
 
87
- test_consume_batch(MyBatchConsumer, payloads, keys: keys, call_original: true)
99
+ test_consume_batch(MyBatchConsumer, payloads, keys: keys)
88
100
  end
89
101
 
102
+ let(:bulk_import_id_column) { :bulk_import_id }
103
+ let(:replace_associations) { true }
104
+
90
105
  let(:consumer_class) do
91
106
  klass = Class.new(described_class) do
92
107
  cattr_accessor :record_attributes_proc
93
108
  cattr_accessor :should_consume_proc
94
- schema 'MySchema'
95
- namespace 'com.my-namespace'
96
- key_config plain: true
97
109
  record_class Widget
98
110
 
99
111
  def should_consume?(record, associations)
@@ -154,7 +166,6 @@ module ActiveRecordBatchConsumerTest # rubocop:disable Metrics/ModuleLength
154
166
 
155
167
  context 'when association configured in consumer without model changes' do
156
168
  before(:each) do
157
- consumer_class.config[:bulk_import_id_column] = :bulk_import_id
158
169
  ActiveRecord::Base.connection.remove_column(:widgets, :bulk_import_id)
159
170
  Widget.reset_column_information
160
171
  end
@@ -164,6 +175,7 @@ module ActiveRecordBatchConsumerTest # rubocop:disable Metrics/ModuleLength
164
175
  end
165
176
 
166
177
  it 'should raise error when bulk_import_id is not found' do
178
+ set_karafka_config(:reraise_errors, true)
167
179
  expect {
168
180
  publish_batch([{ key: 2,
169
181
  payload: { test_id: 'xyz', some_int: 5, title: 'Widget Title' } }])
@@ -173,10 +185,8 @@ module ActiveRecordBatchConsumerTest # rubocop:disable Metrics/ModuleLength
173
185
  end
174
186
 
175
187
  context 'with one-to-one relation in association and custom bulk_import_id' do
176
- before(:each) do
177
- consumer_class.config[:bulk_import_id_column] = :custom_id
178
- consumer_class.config[:replace_associations] = false
179
- end
188
+ let(:bulk_import_id_column) { :custom_id }
189
+ let(:replace_associations) { false }
180
190
 
181
191
  before(:all) do
182
192
  ActiveRecord::Base.connection.add_column(:widgets, :custom_id, :string, if_not_exists: true)
@@ -193,8 +203,8 @@ module ActiveRecordBatchConsumerTest # rubocop:disable Metrics/ModuleLength
193
203
  end
194
204
 
195
205
  context 'with one-to-many relationship in association and default bulk_import_id' do
206
+ let(:replace_associations) { false }
196
207
  before(:each) do
197
- consumer_class.config[:replace_associations] = false
198
208
  consumer_class.record_attributes_proc = proc do |payload|
199
209
  {
200
210
  test_id: payload['test_id'],
@@ -214,7 +224,6 @@ module ActiveRecordBatchConsumerTest # rubocop:disable Metrics/ModuleLength
214
224
  end
215
225
 
216
226
  it 'should save item to widget and associated details' do
217
- consumer_class.config[:replace_associations] = false
218
227
  publish_batch([{ key: 2,
219
228
  payload: { test_id: 'xyz', some_int: 5, title: 'Widget Title' } }])
220
229
  expect(Widget.count).to eq(2)
@@ -233,8 +242,8 @@ module ActiveRecordBatchConsumerTest # rubocop:disable Metrics/ModuleLength
233
242
  end
234
243
 
235
244
  context 'with replace_associations on' do
245
+ let(:replace_associations) { true }
236
246
  before(:each) do
237
- consumer_class.config[:replace_associations] = true
238
247
  consumer_class.record_attributes_proc = proc do |payload|
239
248
  {
240
249
  test_id: payload['test_id'],
@@ -34,7 +34,11 @@ module ActiveRecordBatchConsumerTest
34
34
  prepend_before(:each) do
35
35
  stub_const('MyBatchConsumer', consumer_class)
36
36
  stub_const('ConsumerTest::MyBatchConsumer', consumer_class)
37
- consumer_class.config[:bulk_import_id_column] = :bulk_import_id # default
37
+ register_consumer(MyBatchConsumer,
38
+ 'MySchema',
39
+ key_config: {plain: true},
40
+ configs: {bulk_import_id_column: :bulk_import_id})
41
+ Widget.delete_all
38
42
  end
39
43
 
40
44
  around(:each) do |ex|
@@ -50,9 +54,6 @@ module ActiveRecordBatchConsumerTest
50
54
  # Basic uncompacted consumer
51
55
  let(:consumer_class) do
52
56
  Class.new(described_class) do
53
- schema 'MySchema'
54
- namespace 'com.my-namespace'
55
- key_config plain: true
56
57
  record_class Widget
57
58
  compacted false
58
59
  end
@@ -68,7 +69,7 @@ module ActiveRecordBatchConsumerTest
68
69
  keys = messages.map { |m| m[:key] }
69
70
  payloads = messages.map { |m| m[:payload] }
70
71
 
71
- test_consume_batch(MyBatchConsumer, payloads, keys: keys, call_original: true)
72
+ test_consume_batch(MyBatchConsumer, payloads, keys: keys)
72
73
  end
73
74
 
74
75
  describe 'consume_batch' do
@@ -82,10 +83,6 @@ module ActiveRecordBatchConsumerTest
82
83
  end
83
84
  end
84
85
 
85
- it 'should handle an empty batch' do
86
- expect { publish_batch([]) }.not_to raise_error
87
- end
88
-
89
86
  it 'should create records from a batch' do
90
87
  publish_batch(
91
88
  [
@@ -257,12 +254,16 @@ module ActiveRecordBatchConsumerTest
257
254
  end
258
255
 
259
256
  describe 'compacted mode' do
257
+ before(:each) do
258
+ register_consumer(consumer_class,
259
+ 'MySchema',
260
+ key_config: {plain: true})
261
+
262
+ end
263
+
260
264
  # Create a compacted consumer
261
265
  let(:consumer_class) do
262
266
  Class.new(described_class) do
263
- schema 'MySchema'
264
- namespace 'com.my-namespace'
265
- key_config plain: true
266
267
  record_class Widget
267
268
 
268
269
  # :no-doc:
@@ -302,11 +303,14 @@ module ActiveRecordBatchConsumerTest
302
303
  end
303
304
 
304
305
  describe 'compound keys' do
306
+ before(:each) do
307
+ register_consumer(consumer_class,
308
+ 'MySchema',
309
+ key_config: {schema: 'MySchemaCompound_key'})
310
+ end
311
+
305
312
  let(:consumer_class) do
306
313
  Class.new(described_class) do
307
- schema 'MySchema'
308
- namespace 'com.my-namespace'
309
- key_config schema: 'MySchemaCompound_key'
310
314
  record_class Widget
311
315
  compacted false
312
316
 
@@ -353,13 +357,10 @@ module ActiveRecordBatchConsumerTest
353
357
  end
354
358
 
355
359
  describe 'no keys' do
356
- let(:consumer_class) do
357
- Class.new(described_class) do
358
- schema 'MySchema'
359
- namespace 'com.my-namespace'
360
- key_config none: true
361
- record_class Widget
362
- end
360
+ before(:each) do
361
+ register_consumer(consumer_class,
362
+ 'MySchema',
363
+ key_config: {none: true})
363
364
  end
364
365
 
365
366
  it 'should handle unkeyed topics' do
@@ -385,11 +386,13 @@ module ActiveRecordBatchConsumerTest
385
386
  end
386
387
 
387
388
  describe 'soft deletion' do
389
+ before(:each) do
390
+ register_consumer(consumer_class,
391
+ 'MySchema',
392
+ key_config: {plain: true})
393
+ end
388
394
  let(:consumer_class) do
389
395
  Class.new(described_class) do
390
- schema 'MySchema'
391
- namespace 'com.my-namespace'
392
- key_config plain: true
393
396
  record_class Widget
394
397
  compacted false
395
398
 
@@ -451,11 +454,13 @@ module ActiveRecordBatchConsumerTest
451
454
  end
452
455
 
453
456
  describe 'skipping records' do
457
+ before(:each) do
458
+ register_consumer(consumer_class,
459
+ 'MySchema',
460
+ key_config: {plain: true})
461
+ end
454
462
  let(:consumer_class) do
455
463
  Class.new(described_class) do
456
- schema 'MySchema'
457
- namespace 'com.my-namespace'
458
- key_config plain: true
459
464
  record_class Widget
460
465
 
461
466
  # Sample customization: Skipping records
@@ -471,9 +476,9 @@ module ActiveRecordBatchConsumerTest
471
476
  publish_batch(
472
477
  [
473
478
  { key: 1, # Record that consumer can decide to skip
474
- payload: { test_id: 'skipme' } },
479
+ payload: { test_id: 'skipme', some_int: 3 } },
475
480
  { key: 2,
476
- payload: { test_id: 'abc123' } }
481
+ payload: { test_id: 'abc123', some_int: 3 } }
477
482
  ]
478
483
  )
479
484
 
@@ -484,11 +489,13 @@ module ActiveRecordBatchConsumerTest
484
489
 
485
490
  describe 'pre processing' do
486
491
  context 'with uncompacted messages' do
492
+ before(:each) do
493
+ register_consumer(consumer_class,
494
+ 'MySchema',
495
+ key_config: {plain: true})
496
+ end
487
497
  let(:consumer_class) do
488
498
  Class.new(described_class) do
489
- schema 'MySchema'
490
- namespace 'com.my-namespace'
491
- key_config plain: true
492
499
  record_class Widget
493
500
  compacted false
494
501
 
@@ -527,9 +534,11 @@ module ActiveRecordBatchConsumerTest
527
534
  context 'with a global bulk_import_id_generator' do
528
535
 
529
536
  before(:each) do
530
- Deimos.configure do
531
- consumers.bulk_import_id_generator(proc { 'global' })
532
- end
537
+ register_consumer(consumer_class,
538
+ 'MySchema',
539
+ key_config: {plain: true},
540
+ configs: {bulk_import_id_generator: proc { 'global' }}
541
+ )
533
542
  end
534
543
 
535
544
  it 'should call the default bulk_import_id_generator proc' do
@@ -559,10 +568,17 @@ module ActiveRecordBatchConsumerTest
559
568
  context 'with a class defined bulk_import_id_generator' do
560
569
 
561
570
  before(:each) do
562
- Deimos.configure do
563
- consumers.bulk_import_id_generator(proc { 'global' })
571
+ Karafka::App.routes.clear
572
+ Karafka::App.routes.draw do
573
+ defaults do
574
+ bulk_import_id_generator(proc { 'global'})
575
+ end
564
576
  end
565
- consumer_class.config[:bulk_import_id_generator] = proc { 'custom' }
577
+ register_consumer(consumer_class,
578
+ 'MySchema',
579
+ key_config: {plain: true},
580
+ configs: {bulk_import_id_generator: proc { 'custom' }}
581
+ )
566
582
  end
567
583
 
568
584
  it 'should call the default bulk_import_id_generator proc' do
@@ -593,11 +609,13 @@ module ActiveRecordBatchConsumerTest
593
609
 
594
610
  describe 'should_consume?' do
595
611
 
612
+ before(:each) do
613
+ register_consumer(consumer_class,
614
+ 'MySchema',
615
+ key_config: {plain: true})
616
+ end
596
617
  let(:consumer_class) do
597
618
  Class.new(described_class) do
598
- schema 'MySchema'
599
- namespace 'com.my-namespace'
600
- key_config plain: true
601
619
  record_class Widget
602
620
  compacted false
603
621
 
@@ -609,9 +627,8 @@ module ActiveRecordBatchConsumerTest
609
627
  nil
610
628
  end
611
629
 
612
- ActiveSupport::Notifications.subscribe('batch_consumption.invalid_records') do |*args|
613
- payload = ActiveSupport::Notifications::Event.new(*args).payload
614
- payload[:consumer].process_invalid_records(payload[:records])
630
+ Karafka.monitor.subscribe('deimos.batch_consumption.invalid_records') do |event|
631
+ event[:consumer].process_invalid_records(event[:records])
615
632
  end
616
633
 
617
634
  end
@@ -649,11 +666,13 @@ module ActiveRecordBatchConsumerTest
649
666
  describe 'post processing' do
650
667
 
651
668
  context 'with uncompacted messages' do
669
+ before(:each) do
670
+ register_consumer(consumer_class,
671
+ 'MySchema',
672
+ key_config: {plain: true})
673
+ end
652
674
  let(:consumer_class) do
653
675
  Class.new(described_class) do
654
- schema 'MySchema'
655
- namespace 'com.my-namespace'
656
- key_config plain: true
657
676
  record_class Widget
658
677
  compacted false
659
678
 
@@ -673,13 +692,11 @@ module ActiveRecordBatchConsumerTest
673
692
  Widget.find_by(id: attrs['id'], test_id: attrs['test_id']).update!(some_int: attrs['some_int'])
674
693
  end
675
694
 
676
- ActiveSupport::Notifications.subscribe('batch_consumption.invalid_records') do |*args|
677
- payload = ActiveSupport::Notifications::Event.new(*args).payload
695
+ Karafka.monitor.subscribe('deimos.batch_consumption.invalid_records') do |payload|
678
696
  payload[:consumer].process_invalid_records(payload[:records])
679
697
  end
680
698
 
681
- ActiveSupport::Notifications.subscribe('batch_consumption.valid_records') do |*args|
682
- payload = ActiveSupport::Notifications::Event.new(*args).payload
699
+ Karafka.monitor.subscribe('deimos.batch_consumption.valid_records') do |payload|
683
700
  payload[:consumer].process_valid_records(payload[:records])
684
701
  end
685
702
 
@@ -707,11 +724,13 @@ module ActiveRecordBatchConsumerTest
707
724
  end
708
725
 
709
726
  context 'with compacted messages' do
727
+ before(:each) do
728
+ register_consumer(consumer_class,
729
+ 'MySchema',
730
+ key_config: {plain: true})
731
+ end
710
732
  let(:consumer_class) do
711
733
  Class.new(described_class) do
712
- schema 'MySchema'
713
- namespace 'com.my-namespace'
714
- key_config plain: true
715
734
  record_class Widget
716
735
  compacted true
717
736
 
@@ -731,13 +750,11 @@ module ActiveRecordBatchConsumerTest
731
750
  Widget.find_by(id: attrs['id'], test_id: attrs['test_id']).update!(some_int: attrs['some_int'])
732
751
  end
733
752
 
734
- ActiveSupport::Notifications.subscribe('batch_consumption.invalid_records') do |*args|
735
- payload = ActiveSupport::Notifications::Event.new(*args).payload
753
+ Karafka.monitor.subscribe('deimos.batch_consumption.invalid_records') do |payload|
736
754
  payload[:consumer].process_invalid_records(payload[:records])
737
755
  end
738
756
 
739
- ActiveSupport::Notifications.subscribe('batch_consumption.valid_records') do |*args|
740
- payload = ActiveSupport::Notifications::Event.new(*args).payload
757
+ Karafka.monitor.subscribe('deimos.batch_consumption.valid_records') do |payload|
741
758
  payload[:consumer].process_valid_records(payload[:records])
742
759
  end
743
760
 
@@ -765,11 +782,13 @@ module ActiveRecordBatchConsumerTest
765
782
  end
766
783
 
767
784
  context 'with post processing errors' do
785
+ before(:each) do
786
+ register_consumer(consumer_class,
787
+ 'MySchema',
788
+ key_config: {plain: true})
789
+ end
768
790
  let(:consumer_class) do
769
791
  Class.new(described_class) do
770
- schema 'MySchema'
771
- namespace 'com.my-namespace'
772
- key_config plain: true
773
792
  record_class Widget
774
793
  compacted false
775
794
 
@@ -777,15 +796,15 @@ module ActiveRecordBatchConsumerTest
777
796
  raise StandardError, 'Something went wrong'
778
797
  end
779
798
 
780
- ActiveSupport::Notifications.subscribe('batch_consumption.valid_records') do |*args|
781
- payload = ActiveSupport::Notifications::Event.new(*args).payload
782
- payload[:consumer].process_valid_records(payload[:records])
799
+ Karafka.monitor.subscribe('deimos.batch_consumption.valid_records') do |event|
800
+ event[:consumer].process_valid_records(event[:records])
783
801
  end
784
802
 
785
803
  end
786
804
  end
787
805
 
788
806
  it 'should save records if an exception occurs in post processing' do
807
+ set_karafka_config(:reraise_errors, true)
789
808
  Widget.create!(id: 1, test_id: 'abc', some_int: 1)
790
809
  Widget.create!(id: 2, test_id: 'def', some_int: 2)
791
810
 
@@ -9,11 +9,11 @@ RSpec.describe Deimos::ActiveRecordConsume::BatchConsumption do
9
9
  describe 'upsert_records' do
10
10
  let(:records) do
11
11
  [
12
- Deimos::Message.new({ v: 1 }, nil, key: 1),
13
- Deimos::Message.new({ v: 2 }, nil, key: 2),
14
- Deimos::Message.new({ v: 3 }, nil, key: 3),
15
- Deimos::Message.new({ v: 4 }, nil, key: 4),
16
- Deimos::Message.new({ v: 5 }, nil, key: 5)
12
+ Deimos::Message.new({ v: 1 }, key: 1),
13
+ Deimos::Message.new({ v: 2 }, key: 2),
14
+ Deimos::Message.new({ v: 3 }, key: 3),
15
+ Deimos::Message.new({ v: 4 }, key: 4),
16
+ Deimos::Message.new({ v: 5 }, key: 5)
17
17
  ]
18
18
  end
19
19
 
@@ -56,11 +56,11 @@ RSpec.describe Deimos::ActiveRecordConsume::BatchConsumption do
56
56
  describe 'remove_records' do
57
57
  let(:records) do
58
58
  [
59
- Deimos::Message.new(nil, nil, key: 1),
60
- Deimos::Message.new(nil, nil, key: 2),
61
- Deimos::Message.new(nil, nil, key: 3),
62
- Deimos::Message.new(nil, nil, key: 4),
63
- Deimos::Message.new(nil, nil, key: 5)
59
+ Deimos::Message.new(nil, key: 1),
60
+ Deimos::Message.new(nil, key: 2),
61
+ Deimos::Message.new(nil, key: 3),
62
+ Deimos::Message.new(nil, key: 4),
63
+ Deimos::Message.new(nil, key: 5)
64
64
  ]
65
65
  end
66
66