deimos-ruby 1.22.2 → 1.22.4

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 5aa472d5bdd8c86fe0502451f7554598d189fa0d5004603d8970561feed38d9f
4
- data.tar.gz: 669d564cb23fccc41a9b5a3dd37c49a17787adf7fcecd486746f9d91031d8367
3
+ metadata.gz: 8204d945b6997589a67a4c8df92ad9d6b5a6d626d16ac7425e640e0f0b30e8f4
4
+ data.tar.gz: 364be699486b78896319d4a79cb61288b25cf0ac35078fa263b75547db19d68e
5
5
  SHA512:
6
- metadata.gz: 3f49565845478349949d7dd5909b7d276590f91201d0848123c42a0b433114ce2a31cda8a0d43058f6f3f76114d3dfc1d32b8ee53a2a291588865ed5aaa8a8ec
7
- data.tar.gz: 295802ba93bbf1e8a9c6ca95ac1944b85d3676881c3cb37854f883d42630f68f11a8971527611f1bf1c4681e5b8fab4aa202c1bcd2b1863296236b9a175bd253
6
+ metadata.gz: ff2f512454da6f4ac478e793ff228e35fac87e7a3c1b58431b6e137672d57014f1ae5dc7cb2d46515481137e9a6ac0a4549950925d802023a224e74b4a1b4d82
7
+ data.tar.gz: 73551186d75d2192c0e13dee3647516ae4315fabc42ab73cd27c2c550d7e7fc759ca8897b8e988e5f608d1915097c3c27f69092a002e8b93cf967bf374c2b4ba
data/CHANGELOG.md CHANGED
@@ -7,6 +7,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## UNRELEASED
9
9
 
10
+ # 1.22.4 - 2023-07-05
11
+ - Feature: Add support for message headers.
12
+
13
+ # 1.22.3 - 2023-06-13
14
+
15
+ - Fix: Don't update last_sent to current time on every poll.
16
+ - Feature: Allow for infinite retries in DB poller.
17
+
10
18
  # 1.22.2 - 2023-05-10
11
19
  - Feature: Add `DEIMOS_TASK_NAME` env variable when running a task (consumer, DB poller, DB producer).
12
20
 
data/README.md CHANGED
@@ -123,6 +123,7 @@ class MyProducer < Deimos::Producer
123
123
  }
124
124
  # You can also publish an array with self.publish_list(payloads)
125
125
  # You may specify the topic here with self.publish(payload, topic: 'my-topic')
126
+ # You may also specify the headers here with self.publish(payload, headers: { 'foo' => 'bar' })
126
127
  self.publish(payload)
127
128
  end
128
129
 
@@ -1171,13 +1172,14 @@ end
1171
1172
 
1172
1173
  # A matcher which allows you to test that a message was sent on the given
1173
1174
  # topic, without having to know which class produced it.
1174
- expect(topic_name).to have_sent(payload, key=nil)
1175
+ expect(topic_name).to have_sent(payload, key=nil, partition_key=nil, headers=nil)
1175
1176
 
1176
1177
  # Inspect sent messages
1177
1178
  message = Deimos::Backends::Test.sent_messages[0]
1178
1179
  expect(message).to eq({
1179
1180
  message: {'some-key' => 'some-value'},
1180
1181
  topic: 'my-topic',
1182
+ headers: { 'foo' => 'bar' },
1181
1183
  key: 'my-id'
1182
1184
  })
1183
1185
  ```
@@ -37,6 +37,10 @@ module Deimos
37
37
  log_message.merge!(
38
38
  payloads_count: messages.count
39
39
  )
40
+ when :headers
41
+ log_message.merge!(
42
+ payload_headers: messages.map(&:headers)
43
+ )
40
44
  else
41
45
  log_message.merge!(
42
46
  payloads: messages.map do |message|
@@ -474,7 +474,8 @@ module Deimos # rubocop:disable Metrics/ModuleLength
474
474
  # time, it will run again immediately and the timeout
475
475
  # will be pushed to the next e.g. 1 minute.
476
476
  setting :run_every, 60
477
- # The number of times to retry production when encountering a *non-Kafka* error.
477
+ # The number of times to retry production when encountering a *non-Kafka* error. Set to nil
478
+ # for infinite retries.
478
479
  setting :retries, 1
479
480
  # Amount of time, in seconds, to wait before catching updates, to allow transactions
480
481
  # to complete but still pick up the right records. Should only be set for time-based mode.
@@ -7,6 +7,8 @@ module Deimos
7
7
  attr_accessor :payload
8
8
  # @return [Hash, String, Integer]
9
9
  attr_accessor :key
10
+ # @return [Hash]
11
+ attr_accessor :headers
10
12
  # @return [Integer]
11
13
  attr_accessor :partition_key
12
14
  # @return [String]
@@ -23,11 +25,12 @@ module Deimos
23
25
  # @param topic [String]
24
26
  # @param key [String, Integer, Hash]
25
27
  # @param partition_key [Integer]
26
- def initialize(payload, producer, topic: nil, key: nil, partition_key: nil)
28
+ def initialize(payload, producer, topic: nil, key: nil, headers: nil, partition_key: nil)
27
29
  @payload = payload&.with_indifferent_access
28
30
  @producer_name = producer&.name
29
31
  @topic = topic
30
32
  @key = key
33
+ @headers = headers&.with_indifferent_access
31
34
  @partition_key = partition_key
32
35
  end
33
36
 
@@ -59,13 +62,14 @@ module Deimos
59
62
  {
60
63
  topic: @topic,
61
64
  key: @encoded_key,
65
+ headers: @headers,
62
66
  partition_key: @partition_key || @encoded_key,
63
67
  payload: @encoded_payload,
64
68
  metadata: {
65
69
  decoded_payload: @payload,
66
70
  producer_name: @producer_name
67
71
  }
68
- }
72
+ }.delete_if { |k, v| k == :headers && v.nil? }
69
73
  end
70
74
 
71
75
  # @return [Hash]
@@ -73,13 +77,14 @@ module Deimos
73
77
  {
74
78
  topic: @topic,
75
79
  key: @key,
80
+ headers: @headers,
76
81
  partition_key: @partition_key || @key,
77
82
  payload: @payload,
78
83
  metadata: {
79
84
  decoded_payload: @payload,
80
85
  producer_name: @producer_name
81
86
  }
82
- }
87
+ }.delete_if { |k, v| k == :headers && v.nil? }
83
88
  end
84
89
 
85
90
  # @param other [Message]
@@ -95,9 +95,10 @@ module Deimos
95
95
  # Publish the payload to the topic.
96
96
  # @param payload [Hash, SchemaClass::Record] with an optional payload_key hash key.
97
97
  # @param topic [String] if specifying the topic
98
+ # @param headers [Hash] if specifying headers
98
99
  # @return [void]
99
- def publish(payload, topic: self.topic)
100
- publish_list([payload], topic: topic)
100
+ def publish(payload, topic: self.topic, headers: nil)
101
+ publish_list([payload], topic: topic, headers: headers)
101
102
  end
102
103
 
103
104
  # Publish a list of messages.
@@ -107,8 +108,9 @@ module Deimos
107
108
  # @param force_send [Boolean] if true, ignore the configured backend
108
109
  # and send immediately to Kafka.
109
110
  # @param topic [String] if specifying the topic
111
+ # @param headers [Hash] if specifying headers
110
112
  # @return [void]
111
- def publish_list(payloads, sync: nil, force_send: false, topic: self.topic)
113
+ def publish_list(payloads, sync: nil, force_send: false, topic: self.topic, headers: nil)
112
114
  return if Deimos.config.kafka.seed_brokers.blank? ||
113
115
  Deimos.config.producers.disabled ||
114
116
  Deimos.producers_disabled?(self)
@@ -122,7 +124,7 @@ module Deimos
122
124
  topic: topic,
123
125
  payloads: payloads
124
126
  ) do
125
- messages = Array(payloads).map { |p| Deimos::Message.new(p.to_h, self) }
127
+ messages = Array(payloads).map { |p| Deimos::Message.new(p.to_h, self, headers: headers) }
126
128
  messages.each { |m| _process_message(m, topic) }
127
129
  messages.in_groups_of(MAX_BATCH_SIZE, false) do |batch|
128
130
  self.produce_batch(backend_class, batch)
@@ -133,7 +133,7 @@ module Deimos
133
133
  str + "\nAll Messages received:\n#{message_string}"
134
134
  end
135
135
 
136
- RSpec::Matchers.define :have_sent do |msg, key=nil, partition_key=nil|
136
+ RSpec::Matchers.define :have_sent do |msg, key=nil, partition_key=nil, headers=nil|
137
137
  message = if msg.respond_to?(:with_indifferent_access)
138
138
  msg.with_indifferent_access
139
139
  else
@@ -147,7 +147,14 @@ module Deimos
147
147
  m[:payload]&.with_indifferent_access) &&
148
148
  topic == m[:topic] &&
149
149
  (key.present? ? key == m[:key] : true) &&
150
- (partition_key.present? ? partition_key == m[:partition_key] : true)
150
+ (partition_key.present? ? partition_key == m[:partition_key] : true) &&
151
+ if headers.present?
152
+ hash_matcher.send(:match,
153
+ headers&.with_indifferent_access,
154
+ m[:headers]&.with_indifferent_access)
155
+ else
156
+ true
157
+ end
151
158
  end
152
159
  end
153
160
 
@@ -124,7 +124,7 @@ module Deimos
124
124
  retry
125
125
  rescue StandardError => e
126
126
  Deimos.config.logger.error("Error publishing through DB poller: #{e.message}}")
127
- if retries < @config.retries
127
+ if @config.retries.nil? || retries < @config.retries
128
128
  retries += 1
129
129
  sleep(0.5)
130
130
  retry
@@ -12,20 +12,23 @@ module Deimos
12
12
  def process_updates
13
13
  Deimos.config.logger.info("Polling #{log_identifier}")
14
14
  status = PollStatus.new(0, 0, 0)
15
+ first_batch = true
15
16
 
16
17
  # poll_query gets all the relevant data from the database, as defined
17
18
  # by the producer itself.
18
19
  loop do
19
20
  Deimos.config.logger.debug("Polling #{log_identifier}, batch #{status.current_batch}")
20
21
  batch = fetch_results.to_a
21
- if batch.empty?
22
- @info.touch(:last_sent)
23
- break
24
- end
22
+ break if batch.empty?
25
23
 
24
+ first_batch = false
26
25
  success = process_batch_with_span(batch, status)
27
26
  finalize_batch(batch, success)
28
27
  end
28
+
29
+ # If there were no results at all, we update last_sent so that we still get a wait
30
+ # before the next poll.
31
+ @info.touch(:last_sent) if first_batch
29
32
  Deimos.config.logger.info("Poll #{log_identifier} complete (#{status.report}")
30
33
  end
31
34
 
@@ -30,20 +30,23 @@ module Deimos
30
30
  time_to = Time.zone.now - @config.delay_time
31
31
  Deimos.config.logger.info("Polling #{log_identifier} from #{time_from} to #{time_to}")
32
32
  status = PollStatus.new(0, 0, 0)
33
+ first_batch = true
33
34
 
34
35
  # poll_query gets all the relevant data from the database, as defined
35
36
  # by the producer itself.
36
37
  loop do
37
38
  Deimos.config.logger.debug("Polling #{log_identifier}, batch #{status.current_batch}")
38
39
  batch = fetch_results(time_from, time_to).to_a
39
- if batch.empty?
40
- @info.touch(:last_sent)
41
- break
42
- end
40
+ break if batch.empty?
43
41
 
42
+ first_batch = false
44
43
  process_and_touch_info(batch, status)
45
44
  time_from = last_updated(batch.last)
46
45
  end
46
+
47
+ # If there were no results at all, we update last_sent so that we still get a wait
48
+ # before the next poll.
49
+ @info.touch(:last_sent) if first_batch
47
50
  Deimos.config.logger.info("Poll #{log_identifier} complete at #{time_to} (#{status.report})")
48
51
  end
49
52
 
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Deimos
4
- VERSION = '1.22.2'
4
+ VERSION = '1.22.4'
5
5
  end
data/lib/deimos.rb CHANGED
@@ -23,7 +23,6 @@ require 'deimos/utils/schema_class'
23
23
  require 'deimos/schema_class/enum'
24
24
  require 'deimos/schema_class/record'
25
25
 
26
- require 'deimos/monkey_patches/phobos_producer'
27
26
  require 'deimos/monkey_patches/phobos_cli'
28
27
 
29
28
  require 'deimos/railtie' if defined?(Rails)
data/spec/message_spec.rb CHANGED
@@ -16,4 +16,24 @@ RSpec.describe(Deimos::Message) do
16
16
  expect { described_class.new({ a: 1, b: 2 }, nil, key: { c: 3, d: 4 }) }.
17
17
  not_to raise_exception
18
18
  end
19
+
20
+ describe 'headers' do
21
+ it 'returns nil when not set' do
22
+ expect(described_class.new({ v: 'val1' }, nil, key: 'key1')).
23
+ to have_attributes(headers: nil)
24
+ end
25
+
26
+ it 'can set and get headers' do
27
+ expect(described_class.new({ v: 'val1' }, nil, key: 'key1', headers: { a: 1 })).
28
+ to have_attributes(headers: { a: 1 })
29
+ end
30
+
31
+ it 'includes headers when converting to Hash' do
32
+ expect(described_class.new({ v: 'val1' }, nil, key: 'key1', headers: { a: 1 }).to_h).
33
+ to include(headers: { a: 1 })
34
+
35
+ expect(described_class.new({ v: 'val1' }, nil, key: 'key1', headers: { a: 1 }).encoded_hash).
36
+ to include(headers: { a: 1 })
37
+ end
38
+ end
19
39
  end
@@ -110,18 +110,20 @@ module ProducerTest
110
110
  expect('my-topic').not_to have_sent('test_id' => 'foo2', 'some_int' => 123)
111
111
  end
112
112
 
113
- it 'should allow setting the topic from publish_list' do
113
+ it 'should allow setting the topic and headers from publish_list' do
114
114
  expect(described_class).to receive(:produce_batch).once.with(
115
115
  Deimos::Backends::Test,
116
116
  [
117
117
  Deimos::Message.new({ 'test_id' => 'foo', 'some_int' => 123 },
118
118
  MyProducer,
119
119
  topic: 'a-new-topic',
120
+ headers: { 'foo' => 'bar' },
120
121
  partition_key: 'foo',
121
122
  key: 'foo'),
122
123
  Deimos::Message.new({ 'test_id' => 'bar', 'some_int' => 124 },
123
124
  MyProducer,
124
125
  topic: 'a-new-topic',
126
+ headers: { 'foo' => 'bar' },
125
127
  partition_key: 'bar',
126
128
  key: 'bar')
127
129
  ]
@@ -130,9 +132,10 @@ module ProducerTest
130
132
  MyProducer.publish_list(
131
133
  [{ 'test_id' => 'foo', 'some_int' => 123 },
132
134
  { 'test_id' => 'bar', 'some_int' => 124 }],
133
- topic: 'a-new-topic'
135
+ topic: 'a-new-topic',
136
+ headers: { 'foo' => 'bar' }
134
137
  )
135
- expect('a-new-topic').to have_sent('test_id' => 'foo', 'some_int' => 123)
138
+ expect('a-new-topic').to have_sent({ 'test_id' => 'foo', 'some_int' => 123 }, nil, nil, { 'foo' => 'bar' })
136
139
  expect('my-topic').not_to have_sent('test_id' => 'foo', 'some_int' => 123)
137
140
  expect('my-topic').not_to have_sent('test_id' => 'foo2', 'some_int' => 123)
138
141
  end
@@ -324,13 +324,18 @@ each_db_config(Deimos::Utils::DbPoller::Base) do
324
324
  poller.process_updates
325
325
  poller.process_updates
326
326
 
327
- expect(MyProducer).to have_received(:poll_query).twice.
327
+ expect(MyProducer).to have_received(:poll_query).
328
328
  with(time_from: time_value(secs: -1),
329
329
  time_to: time_value(secs: 120), # plus 122 seconds minus 2 seconds
330
330
  column_name: :updated_at,
331
331
  min_id: last_widget.id)
332
+ expect(MyProducer).to have_received(:poll_query).
333
+ with(time_from: time_value(secs: 122),
334
+ time_to: time_value(secs: 120), # yes this is weird but it's because of travel_to
335
+ column_name: :updated_at,
336
+ min_id: last_widget.id)
332
337
  expect(Deimos.config.logger).to have_received(:info).
333
- with('Poll my-topic-with-id complete at 2015-05-05 00:59:58 -0400 (3 batches, 0 errored batches, 7 processed messages)')
338
+ with('Poll MyProducer: ["my-topic-with-id"] complete at 2015-05-05 00:59:58 -0400 (3 batches, 0 errored batches, 7 processed messages)')
334
339
  end
335
340
 
336
341
  it 'should update PollInfo timestamp after processing' do
@@ -382,7 +387,7 @@ each_db_config(Deimos::Utils::DbPoller::Base) do
382
387
  expect(info.last_sent.in_time_zone).to eq(time_value(mins: -61, secs: 30))
383
388
  expect(info.last_sent_id).to eq(widgets[6].id)
384
389
  expect(Deimos.config.logger).to have_received(:info).
385
- with('Poll my-topic-with-id complete at 2015-05-05 00:59:58 -0400 (2 batches, 1 errored batches, 7 processed messages)')
390
+ with('Poll MyProducer: ["my-topic-with-id"] complete at 2015-05-05 00:59:58 -0400 (2 batches, 1 errored batches, 7 processed messages)')
386
391
  end
387
392
  end
388
393
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: deimos-ruby
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.22.2
4
+ version: 1.22.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Daniel Orner
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-05-10 00:00:00.000000000 Z
11
+ date: 2023-07-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: avro_turf
@@ -459,7 +459,6 @@ files:
459
459
  - lib/deimos/metrics/mock.rb
460
460
  - lib/deimos/metrics/provider.rb
461
461
  - lib/deimos/monkey_patches/phobos_cli.rb
462
- - lib/deimos/monkey_patches/phobos_producer.rb
463
462
  - lib/deimos/poll_info.rb
464
463
  - lib/deimos/producer.rb
465
464
  - lib/deimos/railtie.rb
@@ -1,52 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'phobos/producer'
4
-
5
- #@!visibility private
6
- module Phobos
7
- module Producer
8
- # :nodoc:
9
- class PublicAPI
10
- # :nodoc:
11
- def publish(topic, payload, key=nil, partition_key=nil)
12
- class_producer.publish(topic, payload, key, partition_key)
13
- end
14
-
15
- # :nodoc:
16
- def async_publish(topic, payload, key=nil, partition_key=nil)
17
- class_producer.async_publish(topic, payload, key, partition_key)
18
- end
19
- end
20
-
21
- # :nodoc:
22
- module ClassMethods
23
- # :nodoc:
24
- class PublicAPI
25
- # :nodoc:
26
- def publish(topic, payload, key=nil, partition_key=nil)
27
- publish_list([{ topic: topic, payload: payload, key: key,
28
- partition_key: partition_key }])
29
- end
30
-
31
- # :nodoc:
32
- def async_publish(topic, payload, key=nil, partition_key=nil)
33
- async_publish_list([{ topic: topic, payload: payload, key: key,
34
- partition_key: partition_key }])
35
- end
36
-
37
- private
38
-
39
- # :nodoc:
40
- def produce_messages(producer, messages)
41
- messages.each do |message|
42
- partition_key = message[:partition_key] || message[:key]
43
- producer.produce(message[:payload],
44
- topic: message[:topic],
45
- key: message[:key],
46
- partition_key: partition_key)
47
- end
48
- end
49
- end
50
- end
51
- end
52
- end