deimos-ruby 1.8.2 → 1.8.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +9 -0
- data/README.md +1 -0
- data/docs/INTEGRATION_TESTS.md +52 -0
- data/lib/deimos/backends/db.rb +10 -1
- data/lib/deimos/config/configurable.rb +5 -0
- data/lib/deimos/kafka_source.rb +3 -2
- data/lib/deimos/utils/db_producer.rb +5 -1
- data/lib/deimos/version.rb +1 -1
- data/spec/backends/db_spec.rb +6 -0
- data/spec/kafka_source_spec.rb +83 -0
- data/spec/utils/db_producer_spec.rb +27 -0
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2badd671866a8fbde743e03acbc2da623279ac08abd5ed983ae9a15a0bef415d
|
4
|
+
data.tar.gz: e4b3cd2e80b13fc00ff54ebc85b59ab2e1daad052ab170dafd9a75f57eb80ce3
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 98b41fa7354e624a4538ac5006a091e1a144a2caaa57e2ef77c214e350cd8dcbf2b126c01a3663365d28bbed19baa7525e3c52390cc60f6378537ad8fe6bee12
|
7
|
+
data.tar.gz: 208ba417a35c7042155383d5c0eb9d9e35be95c0459cf2e58cdc24168c8fdf30af16434fcc66c612d197aab38279d790907f146e19b6a36157cde008c5aa60a3
|
data/CHANGELOG.md
CHANGED
@@ -7,6 +7,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
7
7
|
|
8
8
|
## UNRELEASED
|
9
9
|
|
10
|
+
## 1.8.3 - 2020-11-18
|
11
|
+
|
12
|
+
### Fixes :wrench:
|
13
|
+
- Do not resend already sent messages when splitting up batches
|
14
|
+
(fixes [#24](https://github.com/flipp-oss/deimos/issues/24))
|
15
|
+
- KafkaSource crashing on bulk-imports if import hooks are disabled
|
16
|
+
(fixes [#73](https://github.com/flipp-oss/deimos/issues/73))
|
17
|
+
- #96 Use string-safe encoding for partition keys
|
18
|
+
|
10
19
|
## 1.8.2 - 2020-09-25
|
11
20
|
|
12
21
|
### Features :star:
|
data/README.md
CHANGED
@@ -42,6 +42,7 @@ Please see the following for further information not covered by this readme:
|
|
42
42
|
* [Configuration Reference](docs/CONFIGURATION.md)
|
43
43
|
* [Database Backend Feature](docs/DATABASE_BACKEND.md)
|
44
44
|
* [Upgrading Deimos](docs/UPGRADING.md)
|
45
|
+
* [Contributing to Integration Tests](docs/INTEGRATION_TESTS.md)
|
45
46
|
|
46
47
|
# Installation
|
47
48
|
|
@@ -0,0 +1,52 @@
|
|
1
|
+
# Running Integration Tests
|
2
|
+
|
3
|
+
This repo includes integration tests in the [spec/utils](spec/utils) directory.
|
4
|
+
Here, there are tests for more deimos features that include a database integration like
|
5
|
+
* [Database Poller](README.md#Database Poller)
|
6
|
+
* [Database Backend](docs/DATABASE_BACKEND.md)
|
7
|
+
* [Deadlock Retrying](lib/deimos/utils/deadlock_retry.rb)
|
8
|
+
|
9
|
+
You will need to set up the following databases to develop and create unit tests in these test suites.
|
10
|
+
* [SQLite](#SQLite)
|
11
|
+
* [MySQL](#MySQL)
|
12
|
+
* [PostgreSQL](#PostgreSQL)
|
13
|
+
|
14
|
+
### SQLite
|
15
|
+
This database is covered through the `sqlite3` gem.
|
16
|
+
|
17
|
+
## MySQL
|
18
|
+
### Setting up a local MySQL server (Mac)
|
19
|
+
```bash
|
20
|
+
# Download MySQL (Optionally, choose a version you are comfortable with)
|
21
|
+
brew install mysql
|
22
|
+
# Start automatically after rebooting your machine
|
23
|
+
brew services start mysql
|
24
|
+
|
25
|
+
# Cleanup once you are done with MySQL
|
26
|
+
brew services stop mysql
|
27
|
+
```
|
28
|
+
|
29
|
+
## PostgreSQL
|
30
|
+
### Setting up a local PostgreSQL server (Mac)
|
31
|
+
```bash
|
32
|
+
# Install postgres if it's not already installed
|
33
|
+
brew install postgres
|
34
|
+
|
35
|
+
# Initialize and Start up postgres db
|
36
|
+
brew services start postgres
|
37
|
+
initdb /usr/local/var/postgres
|
38
|
+
# Create the default database and user
|
39
|
+
# Use the password "root"
|
40
|
+
createuser -s --password postgres
|
41
|
+
|
42
|
+
# Cleanup once done with Postgres
|
43
|
+
killall postgres
|
44
|
+
brew services stop postgres
|
45
|
+
```
|
46
|
+
|
47
|
+
## Running Integration Tests
|
48
|
+
You must specify the tag "integration" when running these these test suites.
|
49
|
+
This can be done through the CLI with the `--tag integration` argument.
|
50
|
+
```bash
|
51
|
+
rspec spec/utils/ --tag integration
|
52
|
+
```
|
data/lib/deimos/backends/db.rb
CHANGED
@@ -14,7 +14,7 @@ module Deimos
|
|
14
14
|
message = Deimos::KafkaMessage.new(
|
15
15
|
message: m.encoded_payload ? m.encoded_payload.to_s.b : nil,
|
16
16
|
topic: m.topic,
|
17
|
-
partition_key: m
|
17
|
+
partition_key: partition_key_for(m)
|
18
18
|
)
|
19
19
|
message.key = m.encoded_key.to_s.b unless producer_class.config[:no_keys]
|
20
20
|
message
|
@@ -26,6 +26,15 @@ module Deimos
|
|
26
26
|
by: records.size
|
27
27
|
)
|
28
28
|
end
|
29
|
+
|
30
|
+
# @param message [Deimos::Message]
|
31
|
+
# @return [String] the partition key to use for this message
|
32
|
+
def partition_key_for(message)
|
33
|
+
return message.partition_key if message.partition_key.present?
|
34
|
+
return message.key unless message.key.is_a?(Hash)
|
35
|
+
|
36
|
+
message.key.to_yaml
|
37
|
+
end
|
29
38
|
end
|
30
39
|
end
|
31
40
|
end
|
data/lib/deimos/kafka_source.rb
CHANGED
@@ -88,8 +88,9 @@ module Deimos
|
|
88
88
|
array_of_attributes,
|
89
89
|
options={})
|
90
90
|
results = super
|
91
|
-
|
92
|
-
|
91
|
+
if !self.kafka_config[:import] || array_of_attributes.empty?
|
92
|
+
return results
|
93
|
+
end
|
93
94
|
|
94
95
|
# This will contain an array of hashes, where each hash is the actual
|
95
96
|
# attribute hash that created the object.
|
@@ -190,11 +190,14 @@ module Deimos
|
|
190
190
|
end
|
191
191
|
end
|
192
192
|
|
193
|
+
# Produce messages in batches, reducing the size 1/10 if the batch is too
|
194
|
+
# large. Does not retry batches of messages that have already been sent.
|
193
195
|
# @param batch [Array<Hash>]
|
194
196
|
def produce_messages(batch)
|
195
197
|
batch_size = batch.size
|
198
|
+
current_index = 0
|
196
199
|
begin
|
197
|
-
batch.in_groups_of(batch_size, false).each do |group|
|
200
|
+
batch[current_index..-1].in_groups_of(batch_size, false).each do |group|
|
198
201
|
@logger.debug("Publishing #{group.size} messages to #{@current_topic}")
|
199
202
|
producer.publish_list(group)
|
200
203
|
Deimos.config.metrics&.increment(
|
@@ -202,6 +205,7 @@ module Deimos
|
|
202
205
|
tags: %W(status:success topic:#{@current_topic}),
|
203
206
|
by: group.size
|
204
207
|
)
|
208
|
+
current_index += group.size
|
205
209
|
@logger.info("Sent #{group.size} messages to #{@current_topic}")
|
206
210
|
end
|
207
211
|
rescue Kafka::BufferOverflow, Kafka::MessageSizeTooLarge,
|
data/lib/deimos/version.rb
CHANGED
data/spec/backends/db_spec.rb
CHANGED
@@ -43,6 +43,12 @@ each_db_config(Deimos::Backends::Db) do
|
|
43
43
|
described_class.publish(producer_class: MyNoKeyProducer,
|
44
44
|
messages: [messages.first])
|
45
45
|
expect(Deimos::KafkaMessage.count).to eq(4)
|
46
|
+
end
|
46
47
|
|
48
|
+
it 'should add messages with Hash keys with JSON encoding' do
|
49
|
+
described_class.publish(producer_class: MyProducer,
|
50
|
+
messages: [build_message({ foo: 0 }, 'my-topic', { 'test_id' => 0 })])
|
51
|
+
expect(Deimos::KafkaMessage.count).to eq(1)
|
52
|
+
expect(Deimos::KafkaMessage.last.partition_key).to eq(%(---\ntest_id: 0\n))
|
47
53
|
end
|
48
54
|
end
|
data/spec/kafka_source_spec.rb
CHANGED
@@ -225,5 +225,88 @@ module KafkaSourceSpec
|
|
225
225
|
expect(Deimos::KafkaMessage.count).to eq(0)
|
226
226
|
end
|
227
227
|
end
|
228
|
+
|
229
|
+
context 'with import hooks disabled' do
|
230
|
+
before(:each) do
|
231
|
+
# Dummy class we can include the mixin in. Has a backing table created
|
232
|
+
# earlier and has the import hook disabled
|
233
|
+
class WidgetNoImportHook < ActiveRecord::Base
|
234
|
+
include Deimos::KafkaSource
|
235
|
+
self.table_name = 'widgets'
|
236
|
+
|
237
|
+
# :nodoc:
|
238
|
+
def self.kafka_config
|
239
|
+
{
|
240
|
+
update: true,
|
241
|
+
delete: true,
|
242
|
+
import: false,
|
243
|
+
create: true
|
244
|
+
}
|
245
|
+
end
|
246
|
+
|
247
|
+
# :nodoc:
|
248
|
+
def self.kafka_producers
|
249
|
+
[WidgetProducer]
|
250
|
+
end
|
251
|
+
end
|
252
|
+
WidgetNoImportHook.reset_column_information
|
253
|
+
end
|
254
|
+
|
255
|
+
it 'should not fail when bulk-importing with existing records' do
|
256
|
+
widget1 = WidgetNoImportHook.create(widget_id: 1, name: 'Widget 1')
|
257
|
+
widget2 = WidgetNoImportHook.create(widget_id: 2, name: 'Widget 2')
|
258
|
+
widget1.name = 'New Widget No Import Hook 1'
|
259
|
+
widget2.name = 'New Widget No Import Hook 2'
|
260
|
+
|
261
|
+
expect {
|
262
|
+
WidgetNoImportHook.import([widget1, widget2], on_duplicate_key_update: %i(widget_id name))
|
263
|
+
}.not_to raise_error
|
264
|
+
|
265
|
+
expect('my-topic').not_to have_sent({
|
266
|
+
widget_id: 1,
|
267
|
+
name: 'New Widget No Import Hook 1',
|
268
|
+
id: widget1.id,
|
269
|
+
created_at: anything,
|
270
|
+
updated_at: anything
|
271
|
+
}, widget1.id)
|
272
|
+
expect('my-topic').not_to have_sent({
|
273
|
+
widget_id: 2,
|
274
|
+
name: 'New Widget No Import Hook 2',
|
275
|
+
id: widget2.id,
|
276
|
+
created_at: anything,
|
277
|
+
updated_at: anything
|
278
|
+
}, widget2.id)
|
279
|
+
end
|
280
|
+
|
281
|
+
it 'should not fail when mixing existing and new records' do
|
282
|
+
widget1 = WidgetNoImportHook.create(widget_id: 1, name: 'Widget 1')
|
283
|
+
expect('my-topic').to have_sent({
|
284
|
+
widget_id: 1,
|
285
|
+
name: 'Widget 1',
|
286
|
+
id: widget1.id,
|
287
|
+
created_at: anything,
|
288
|
+
updated_at: anything
|
289
|
+
}, widget1.id)
|
290
|
+
|
291
|
+
widget2 = WidgetNoImportHook.new(widget_id: 2, name: 'Widget 2')
|
292
|
+
widget1.name = 'New Widget 1'
|
293
|
+
WidgetNoImportHook.import([widget1, widget2], on_duplicate_key_update: %i(widget_id))
|
294
|
+
widgets = WidgetNoImportHook.all
|
295
|
+
expect('my-topic').not_to have_sent({
|
296
|
+
widget_id: 1,
|
297
|
+
name: 'New Widget 1',
|
298
|
+
id: widgets[0].id,
|
299
|
+
created_at: anything,
|
300
|
+
updated_at: anything
|
301
|
+
}, widgets[0].id)
|
302
|
+
expect('my-topic').not_to have_sent({
|
303
|
+
widget_id: 2,
|
304
|
+
name: 'Widget 2',
|
305
|
+
id: widgets[1].id,
|
306
|
+
created_at: anything,
|
307
|
+
updated_at: anything
|
308
|
+
}, widgets[1].id)
|
309
|
+
end
|
310
|
+
end
|
228
311
|
end
|
229
312
|
end
|
@@ -96,7 +96,32 @@ each_db_config(Deimos::Utils::DbProducer) do
|
|
96
96
|
expect(phobos_producer).to have_received(:publish_list).with(['A'] * 100).once
|
97
97
|
expect(phobos_producer).to have_received(:publish_list).with(['A'] * 10).once
|
98
98
|
expect(phobos_producer).to have_received(:publish_list).with(['A']).once
|
99
|
+
end
|
100
|
+
|
101
|
+
it 'should not resend batches of sent messages' do
|
102
|
+
allow(phobos_producer).to receive(:publish_list) do |group|
|
103
|
+
raise Kafka::BufferOverflow if group.any?('A') && group.size >= 1000
|
104
|
+
raise Kafka::BufferOverflow if group.any?('BIG') && group.size >= 10
|
105
|
+
end
|
106
|
+
allow(Deimos.config.metrics).to receive(:increment)
|
107
|
+
batch = ['A'] * 450 + ['BIG'] * 550
|
108
|
+
producer.produce_messages(batch)
|
109
|
+
|
110
|
+
expect(phobos_producer).to have_received(:publish_list).with(batch)
|
111
|
+
expect(phobos_producer).to have_received(:publish_list).with(['A'] * 100).exactly(4).times
|
112
|
+
expect(phobos_producer).to have_received(:publish_list).with(['A'] * 50 + ['BIG'] * 50)
|
113
|
+
expect(phobos_producer).to have_received(:publish_list).with(['A'] * 10).exactly(5).times
|
114
|
+
expect(phobos_producer).to have_received(:publish_list).with(['BIG'] * 1).exactly(550).times
|
99
115
|
|
116
|
+
expect(Deimos.config.metrics).to have_received(:increment).with('publish',
|
117
|
+
tags: %w(status:success topic:),
|
118
|
+
by: 100).exactly(4).times
|
119
|
+
expect(Deimos.config.metrics).to have_received(:increment).with('publish',
|
120
|
+
tags: %w(status:success topic:),
|
121
|
+
by: 10).exactly(5).times
|
122
|
+
expect(Deimos.config.metrics).to have_received(:increment).with('publish',
|
123
|
+
tags: %w(status:success topic:),
|
124
|
+
by: 1).exactly(550).times
|
100
125
|
end
|
101
126
|
|
102
127
|
describe '#compact_messages' do
|
@@ -289,6 +314,8 @@ each_db_config(Deimos::Utils::DbProducer) do
|
|
289
314
|
message: "mess#{i}",
|
290
315
|
partition_key: "key#{i}"
|
291
316
|
)
|
317
|
+
end
|
318
|
+
(5..8).each do |i|
|
292
319
|
Deimos::KafkaMessage.create!(
|
293
320
|
id: i,
|
294
321
|
topic: 'my-topic2',
|
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.8.
|
4
|
+
version: 1.8.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Daniel Orner
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-
|
11
|
+
date: 2020-11-18 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: avro_turf
|
@@ -348,6 +348,7 @@ files:
|
|
348
348
|
- docs/ARCHITECTURE.md
|
349
349
|
- docs/CONFIGURATION.md
|
350
350
|
- docs/DATABASE_BACKEND.md
|
351
|
+
- docs/INTEGRATION_TESTS.md
|
351
352
|
- docs/PULL_REQUEST_TEMPLATE.md
|
352
353
|
- docs/UPGRADING.md
|
353
354
|
- lib/deimos.rb
|