rimless 2.9.0 → 3.0.0
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/Appraisals +2 -2
- data/CHANGELOG.md +66 -0
- data/Gemfile +0 -1
- data/README.md +64 -62
- data/Rakefile +13 -4
- data/UPGRADING.md +491 -0
- data/doc/upgrade-guide-sources/README.md +221 -0
- data/doc/upgrade-guide-sources/dep-avro_turf-1.20.md +23 -0
- data/doc/upgrade-guide-sources/dep-karafka-2.0.md +117 -0
- data/doc/upgrade-guide-sources/dep-waterdrop-2.8.md +30 -0
- data/gemfiles/rails_8.0.gemfile +1 -1
- data/gemfiles/rails_8.1.gemfile +1 -1
- data/lib/rimless/compatibility/.gitkeep +0 -0
- data/lib/rimless/configuration.rb +80 -6
- data/lib/rimless/consumer/app.rb +182 -0
- data/lib/rimless/{karafka → consumer}/avro_deserializer.rb +8 -6
- data/lib/rimless/consumer/base.rb +118 -0
- data/lib/rimless/consumer/job.rb +35 -0
- data/lib/rimless/consumer/job_bridge.rb +113 -0
- data/lib/rimless/extensions/avro_helpers.rb +83 -0
- data/lib/rimless/extensions/configuration_handling.rb +77 -0
- data/lib/rimless/extensions/consumer.rb +20 -0
- data/lib/rimless/extensions/dependencies.rb +84 -0
- data/lib/rimless/extensions/kafka_helpers.rb +46 -0
- data/lib/rimless/extensions/producer.rb +103 -0
- data/lib/rimless/initializers/compatibility.rb +3 -4
- data/lib/rimless/railtie.rb +7 -7
- data/lib/rimless/rspec/helpers.rb +53 -13
- data/lib/rimless/rspec/matchers.rb +14 -11
- data/lib/rimless/rspec.rb +13 -29
- data/lib/rimless/tasks/consumer.rake +18 -6
- data/lib/rimless/tasks/templates/application_consumer.rb +1 -1
- data/lib/rimless/tasks/templates/custom_consumer.rb +1 -1
- data/lib/rimless/tasks/templates/custom_consumer_spec.rb +5 -4
- data/lib/rimless/tasks/templates/karafka.rb +5 -4
- data/lib/rimless/version.rb +3 -1
- data/lib/rimless.rb +12 -14
- data/rimless.gemspec +7 -9
- metadata +38 -67
- data/lib/rimless/avro_helpers.rb +0 -81
- data/lib/rimless/base_consumer.rb +0 -30
- data/lib/rimless/compatibility/karafka_1_4.rb +0 -52
- data/lib/rimless/configuration_handling.rb +0 -82
- data/lib/rimless/consumer.rb +0 -209
- data/lib/rimless/consumer_job.rb +0 -10
- data/lib/rimless/dependencies.rb +0 -69
- data/lib/rimless/kafka_helpers.rb +0 -104
- data/lib/rimless/karafka/base64_interchanger.rb +0 -32
- data/lib/rimless/karafka/passthrough_mapper.rb +0 -29
- data/lib/rimless/tasks/stats.rake +0 -22
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: a84c6d640596ee56269220afae67746c855c1fc06c91819312c6a0a9626ed422
|
|
4
|
+
data.tar.gz: 7179ae84177669855f9e38b2d044ccf35ccb3405f42b94aa3275ce75907b0cea
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 11a1fdcf0a778e581c36055cf4986880940d0fe5b41ec30b569598ad301ce51aae4cf9e2278983d33c77c0e93209b0224021d3b3ba138ac288ce83eb87a30707
|
|
7
|
+
data.tar.gz: c5d12b9ca331ea5b1000576c06291ecb1f8d336f2cd25f13ade6c2b509b11a83e50f1f23be3077399c70ec2356623ccfe45ee6edba0177ddeef6378e6805a6d2
|
data/Appraisals
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
appraise 'rails-8.0' do
|
|
4
|
+
gem 'activejob', '~> 8.0.0'
|
|
4
5
|
gem 'activesupport', '~> 8.0.0'
|
|
5
|
-
gem 'railties', '~> 8.0.0'
|
|
6
6
|
end
|
|
7
7
|
|
|
8
8
|
appraise 'rails-8.1' do
|
|
9
|
+
gem 'activejob', '~> 8.1.0'
|
|
9
10
|
gem 'activesupport', '~> 8.1.0'
|
|
10
|
-
gem 'railties', '~> 8.1.0'
|
|
11
11
|
end
|
data/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,72 @@
|
|
|
2
2
|
|
|
3
3
|
* TODO: Replace this bullet point with an actual description of a change.
|
|
4
4
|
|
|
5
|
+
### 3.0.0 (1 April 2026)
|
|
6
|
+
|
|
7
|
+
Breaking changes: ([#73](https://github.com/hausgold/rimless/pull/73))
|
|
8
|
+
|
|
9
|
+
* Upgraded `karafka` from `~> 1.4` to `~> 2.5` — now powered by librdkafka
|
|
10
|
+
instead of ruby-kafka. Consumer groups are now shared (one for all topics
|
|
11
|
+
instead of one per topic). Batch fetching is always on; tune
|
|
12
|
+
`max_wait_time`/`max_messages` for latency vs throughput.
|
|
13
|
+
See `UPGRADING.md` for details.
|
|
14
|
+
|
|
15
|
+
* Upgraded `waterdrop` from `~> 1.4` to `~> 2.8` — all time-related config
|
|
16
|
+
values are now in milliseconds. The Rimless producer helpers (`Rimless.message`
|
|
17
|
+
etc.) remain unchanged.
|
|
18
|
+
|
|
19
|
+
* Upgraded `avro_turf` from `~> 0.11.0` to `~> 1.20`
|
|
20
|
+
|
|
21
|
+
* Switched from Sidekiq to ActiveJob for consumer job processing. The
|
|
22
|
+
`karafka-sidekiq-backend` gem is no longer used.
|
|
23
|
+
|
|
24
|
+
* Renamed constants (old → new):
|
|
25
|
+
- `Rimless::BaseConsumer` → `Rimless::Consumer::Base`
|
|
26
|
+
- `Rimless::ConsumerApp` → `Rimless::Consumer::App`
|
|
27
|
+
- `Rimless::ConsumerJob` → `Rimless::Consumer::Job`
|
|
28
|
+
- `Rimless::Karafka::AvroDeserializer` → `Rimless::Consumer::AvroDeserializer`
|
|
29
|
+
|
|
30
|
+
* Removed constants:
|
|
31
|
+
- `Rimless::Karafka::Base64Interchanger` (no longer needed)
|
|
32
|
+
- `Rimless::Karafka::PassthroughMapper` (mappers removed in Karafka 2)
|
|
33
|
+
|
|
34
|
+
* Anonymous consumer classes are no longer supported. Because Karafka is no
|
|
35
|
+
longer loaded within the ActiveJob worker process, consumer class names must
|
|
36
|
+
be serializable (via `constantize`). Rewrite anonymous consumers as named
|
|
37
|
+
classes. See `UPGRADING.md` for details.
|
|
38
|
+
|
|
39
|
+
* Each Kafka message in a batch is now individually marked as consumed after
|
|
40
|
+
being enqueued to ActiveJob (`mark_as_consumed`). This may differ from
|
|
41
|
+
previous `karafka-sidekiq-backend` behavior depending on your Karafka
|
|
42
|
+
offset management configuration. See `UPGRADING.md` for details.
|
|
43
|
+
|
|
44
|
+
* `KAFKA_BROKERS` format changed — protocol prefix (`kafka://`) is no longer
|
|
45
|
+
required. Use plain `host:port` CSV (e.g. `your.domain:9092,host:port`).
|
|
46
|
+
The old `kafka://` format is still accepted for backwards compatibility.
|
|
47
|
+
|
|
48
|
+
* Consumer boot flow changed:
|
|
49
|
+
- Remove `.boot!` from the end of `Rimless.consumer.topics(...)` in
|
|
50
|
+
`karafka.rb`
|
|
51
|
+
- Remove `WaterDrop.setup` and `WaterDrop::Instrumentation::LoggerListener`
|
|
52
|
+
subscriptions from `karafka.rb`
|
|
53
|
+
|
|
54
|
+
New configuration options:
|
|
55
|
+
|
|
56
|
+
* `config.job_bridge_class` — custom job bridge for Kafka → ActiveJob
|
|
57
|
+
* `config.consumer_job_class` — custom consumer job class
|
|
58
|
+
* `config.consumer_logger_listener` — customize Karafka logging listener
|
|
59
|
+
* `config.avro_deserializer_class` — custom Apache Avro deserializer
|
|
60
|
+
* `config.avro_configure` — fully customize the `AvroTurf::Messaging` instance
|
|
61
|
+
* `config.producer_configure` — fully customize the `WaterDrop::Producer` instance
|
|
62
|
+
* `config.consumer_configure` — fully customize the `Karafka::App` instance
|
|
63
|
+
|
|
64
|
+
Testing changes:
|
|
65
|
+
|
|
66
|
+
* Replace `#karafka_consumer_for` with `#kafka_consumer_for` in specs
|
|
67
|
+
* Replace `#publish_for_karafka` with `karafka.produce` in specs
|
|
68
|
+
|
|
69
|
+
See [`UPGRADING.md`](./UPGRADING.md) for a complete migration guide.
|
|
70
|
+
|
|
5
71
|
### 2.9.0 (18 February 2026)
|
|
6
72
|
|
|
7
73
|
* Dropped 3rd-level gem dependencies which are not directly used
|
data/Gemfile
CHANGED
data/README.md
CHANGED
|
@@ -6,13 +6,15 @@
|
|
|
6
6
|
[](https://knowledge.hausgold.de/coverage)
|
|
7
7
|
[](https://www.rubydoc.info/gems/rimless)
|
|
8
8
|
|
|
9
|
-
This project is dedicated to
|
|
9
|
+
This project is dedicated to shipping a ready-to-use [Apache
|
|
10
10
|
Kafka](https://kafka.apache.org/) / [Confluent Schema
|
|
11
11
|
Registry](https://docs.confluent.io/current/schema-registry/index.html) /
|
|
12
|
-
[Apache Avro](https://avro.apache.org/) message
|
|
13
|
-
of the [WaterDrop](https://rubygems.org/gems/waterdrop)
|
|
12
|
+
[Apache Avro](https://avro.apache.org/) message streaming toolset by making use
|
|
13
|
+
of the [WaterDrop](https://rubygems.org/gems/waterdrop),
|
|
14
|
+
[Karafka](https://rubygems.org/gems/karafka) and
|
|
14
15
|
[AvroTurf](https://rubygems.org/gems/avro_turf) gems. It comes as an
|
|
15
|
-
opinionated framework
|
|
16
|
+
opinionated framework that sets up solid conventions for producing and
|
|
17
|
+
consuming messages.
|
|
16
18
|
|
|
17
19
|
- [Installation](#installation)
|
|
18
20
|
- [Usage](#usage)
|
|
@@ -60,7 +62,7 @@ $ gem install rimless
|
|
|
60
62
|
|
|
61
63
|
### Configuration
|
|
62
64
|
|
|
63
|
-
You can configure the rimless gem via
|
|
65
|
+
You can configure the rimless gem via a Rails initializer, by environment
|
|
64
66
|
variables or on demand. Here we show a common Rails initializer example:
|
|
65
67
|
|
|
66
68
|
```ruby
|
|
@@ -82,33 +84,42 @@ Rimless.configure do |conf|
|
|
|
82
84
|
|
|
83
85
|
# The list of Apache Kafka brokers for cluster discovery,
|
|
84
86
|
# set to HAUSGOLD defaults when not set
|
|
85
|
-
conf.kafka_brokers = 'kafka://your.domain:9092,kafka..'
|
|
87
|
+
conf.kafka_brokers = 'kafka://your.domain:9092,kafka..' # old format
|
|
88
|
+
conf.kafka_brokers = 'your.domain:9092,host:port..' # new format
|
|
86
89
|
|
|
87
90
|
# The Confluent Schema Registry API URL,
|
|
88
91
|
# set to HAUSGOLD defaults when not set
|
|
89
92
|
conf.schema_registry_url = 'http://your.schema-registry.local'
|
|
90
93
|
|
|
91
|
-
# The
|
|
94
|
+
# The ActiveJob queue to use for consuming jobs
|
|
92
95
|
config.consumer_job_queue = 'default'
|
|
93
96
|
end
|
|
94
97
|
```
|
|
95
98
|
|
|
96
|
-
The rimless gem comes with
|
|
97
|
-
extra configuration is not needed.
|
|
99
|
+
The rimless gem comes with sensible defaults as you can see. For most users an
|
|
100
|
+
extra configuration is not needed. For more details and all available
|
|
101
|
+
configurations, check the
|
|
102
|
+
[`lib/rimless/configuration.rb`](lib/rimless/configuration.rb) file.
|
|
98
103
|
|
|
99
104
|
#### Available environment variables
|
|
100
105
|
|
|
101
|
-
The rimless gem can be configured
|
|
106
|
+
The rimless gem can also be configured with its configuration code block as
|
|
102
107
|
shown before. Respecting the [twelve-factor app](https://12factor.net/)
|
|
103
108
|
concerns, the gem allows you to set almost all configurations (just the
|
|
104
109
|
relevant ones for runtime) via environment variables. Here comes a list of
|
|
105
110
|
available configuration options:
|
|
106
111
|
|
|
107
|
-
* **KAFKA_ENV**: The application environment. Falls back to `Rails.env` when
|
|
108
|
-
|
|
109
|
-
* **
|
|
110
|
-
|
|
111
|
-
* **
|
|
112
|
+
* **KAFKA_ENV**: The application environment. Falls back to `Rails.env` when
|
|
113
|
+
available.
|
|
114
|
+
* **KAFKA_CLIENT_ID**: The Apache Kafka client identifier, falls back to the
|
|
115
|
+
local application name.
|
|
116
|
+
* **KAFKA_BROKERS**: A comma-separated list of Apache Kafka brokers for cluster
|
|
117
|
+
discovery (Plaintext, no-auth/no-SSL by default) (eg.
|
|
118
|
+
`your.domain:9092,host:port..`)
|
|
119
|
+
* **KAFKA_SCHEMA_REGISTRY_URL**: The Confluent Schema Registry API URL to use
|
|
120
|
+
for schema registrations.
|
|
121
|
+
* **KAFKA_JOB_QUEUE**: The ActiveJob queue to use for consuming jobs.
|
|
122
|
+
Falls back to `default`. (falls back to `KAFKA_SIDEKIQ_JOB_QUEUE`)
|
|
112
123
|
|
|
113
124
|
### Conventions
|
|
114
125
|
|
|
@@ -158,13 +169,13 @@ templates](https://ruby-doc.org/stdlib-2.6.2/libdoc/erb/rdoc/ERB.html) and
|
|
|
158
169
|
painless JSON validation of them.
|
|
159
170
|
|
|
160
171
|
First things first, by convention the rimless gem looks for Apache Avro schema
|
|
161
|
-
ERB templates
|
|
172
|
+
ERB templates in the `$(pwd)/config/avro_schemas` directory. Nothing special
|
|
162
173
|
from the Rails perspective. You can also reconfigure the file locations, just
|
|
163
174
|
[see the configuration
|
|
164
175
|
block](https://github.com/hausgold/rimless/blob/master/lib/rimless/configuration.rb#L36).
|
|
165
176
|
|
|
166
177
|
Each schema template MUST end with the `.avsc.erb` extension to be picked up,
|
|
167
|
-
even in recursive directory structures.
|
|
178
|
+
even in recursive directory structures. You can make use of the ERB templating
|
|
168
179
|
or not, but rimless just looks for these templates. When it comes to
|
|
169
180
|
structuring the Avro Schemas it is important that the file path reflects the
|
|
170
181
|
embedded schema namespace correctly. So when `$(pwd)/config/avro_schemas` is our
|
|
@@ -178,7 +189,7 @@ The corresponding Avro Schema template is located at
|
|
|
178
189
|
be fancy. The automatic schema compiler picks up the dynamically/runtime set
|
|
179
190
|
namespace from the schema definition and converts it to its respective
|
|
180
191
|
directory structure. So when you boot your application container/instance
|
|
181
|
-
inside your *canary*
|
|
192
|
+
inside your *canary* environment, the schemas/messages should reflect this so
|
|
182
193
|
they do not mix with other environments.
|
|
183
194
|
|
|
184
195
|
Example time. **$(pwd)/config/avro_schemas/identity_api/user_v1.avsc.erb**:
|
|
@@ -237,7 +248,7 @@ The compiled Avro Schemas are written to the
|
|
|
237
248
|
`$(pwd)/config/avro_schemas/compiled/` directory by default. You can
|
|
238
249
|
[reconfigure the
|
|
239
250
|
location](https://github.com/hausgold/rimless/blob/master/lib/rimless/configuration.rb#L44)
|
|
240
|
-
if needed. For VCS systems like Git it is useful to create
|
|
251
|
+
if needed. For VCS systems like Git it is useful to create a relative ignore
|
|
241
252
|
list at `$(pwd)/config/avro_schemas/.gitignore` with the following contents:
|
|
242
253
|
|
|
243
254
|
```gitignore
|
|
@@ -248,8 +259,8 @@ compiled/
|
|
|
248
259
|
|
|
249
260
|
Under the hood the rimless gem makes use of the [WaterDrop
|
|
250
261
|
gem](https://rubygems.org/gems/waterdrop) to send messages to the Apache Kafka
|
|
251
|
-
cluster
|
|
252
|
-
single call. Here
|
|
262
|
+
cluster, but with the added ability to send Apache Avro encoded messages with a
|
|
263
|
+
single call. Here are some examples of how to use it:
|
|
253
264
|
|
|
254
265
|
```ruby
|
|
255
266
|
metadata = { hobbies: %w(dancing singing sports) }
|
|
@@ -279,7 +290,7 @@ Rimless.raw_message(data: encoded, topic: :users)
|
|
|
279
290
|
|
|
280
291
|
# In case you want to send messages to a non-local application topic you can
|
|
281
292
|
# specify the application, too. This allows you to send a message to the
|
|
282
|
-
# +<ENV>.address-api.addresses+ from
|
|
293
|
+
# +<ENV>.address-api.addresses+ from your local identity-api.
|
|
283
294
|
Rimless.raw_message(data: encoded, topic: { name: :users, app: 'address-api' })
|
|
284
295
|
# Also works with the Apache Avro encoding variant
|
|
285
296
|
Rimless.message(data: user, schema: :user_v1,
|
|
@@ -295,8 +306,8 @@ Rimless.async_raw_message(data: encoded, topic: :users)
|
|
|
295
306
|
The rimless gem makes it super easy to build consumer logic right into your
|
|
296
307
|
(Rails, standalone) application by utilizing the [Karafka
|
|
297
308
|
framework](https://github.com/karafka/karafka) under the hood. When you have
|
|
298
|
-
the rimless gem already installed you are ready to
|
|
299
|
-
|
|
309
|
+
the rimless gem already installed, you are ready to set up your application to
|
|
310
|
+
consume Apache Kafka messages. Just run the `$ rake
|
|
300
311
|
rimless:install` and all the consuming setup is done for you.
|
|
301
312
|
|
|
302
313
|
Afterwards you find the `karafka.rb` file at the root of your project together
|
|
@@ -312,7 +323,7 @@ architecture looks like this:
|
|
|
312
323
|
v
|
|
313
324
|
+-----------------------------+
|
|
314
325
|
| Karafka/Rimless Consumer | +--------------------------------------+
|
|
315
|
-
| Shares a single consumer |--->|
|
|
326
|
+
| Shares a single consumer |--->| ActiveJob |
|
|
316
327
|
| group, multiple processes | | Runs the consumer class (children |
|
|
317
328
|
+-----------------------------+ | of Rimless::BaseConsumer) for each |
|
|
318
329
|
| message (Rimless::ConsumerJob), |
|
|
@@ -321,15 +332,15 @@ architecture looks like this:
|
|
|
321
332
|
```
|
|
322
333
|
|
|
323
334
|
This architecture allows the consumer process to run mostly non-blocking and
|
|
324
|
-
the messages can be processed concurrently via
|
|
335
|
+
the messages can be processed concurrently via ActiveJob. (including the error
|
|
325
336
|
handling and retrying)
|
|
326
337
|
|
|
327
338
|
#### Routing messages to consumers
|
|
328
339
|
|
|
329
340
|
The `karafka.rb` file at the root of your project is dedicated to configure the
|
|
330
341
|
consumer process, including the routing table. The routing is as easy as it
|
|
331
|
-
gets by following this pattern: `topic => consumer`. Here
|
|
332
|
-
|
|
342
|
+
gets by following this pattern: `topic => consumer`. Here is the full
|
|
343
|
+
example:
|
|
333
344
|
|
|
334
345
|
```ruby
|
|
335
346
|
# Setup the topic-consumer routing table and boot the consumer application
|
|
@@ -338,7 +349,7 @@ Rimless.consumer.topics(
|
|
|
338
349
|
).boot!
|
|
339
350
|
```
|
|
340
351
|
|
|
341
|
-
The key side of the hash is anything
|
|
352
|
+
The key side of the hash is anything that is understood by the `Rimless.topic`
|
|
342
353
|
method. With one addition: you can change `:name` to `:names` and pass an array
|
|
343
354
|
of strings or symbols to listen to multiple application topics with a single
|
|
344
355
|
configuration line.
|
|
@@ -360,9 +371,9 @@ Rimless.consumer.topics(
|
|
|
360
371
|
#### Consuming event messages
|
|
361
372
|
|
|
362
373
|
By convention it makes sense to produce messages with various event types on a
|
|
363
|
-
single Apache Kafka topic. This is fine
|
|
364
|
-
|
|
365
|
-
schema with a dedicated name. This
|
|
374
|
+
single Apache Kafka topic. This is fine; they just must follow a single
|
|
375
|
+
constraint: each message must contain an `event`-named field in the Apache Avro
|
|
376
|
+
schema with a dedicated name. This allows structuring data in Kafka like this:
|
|
366
377
|
|
|
367
378
|
```
|
|
368
379
|
Topic: production.users-api.users
|
|
@@ -370,7 +381,7 @@ Events: user_created, user_updated, user_deleted
|
|
|
370
381
|
```
|
|
371
382
|
|
|
372
383
|
While respecting this convention your consumer classes will be super clean. See
|
|
373
|
-
the following example: (we keep the
|
|
384
|
+
the following example: (we keep the Users API example)
|
|
374
385
|
|
|
375
386
|
```ruby
|
|
376
387
|
class UserApiConsumer < ApplicationConsumer
|
|
@@ -385,7 +396,7 @@ schema fields of it, except the event field. The messages will be automatically
|
|
|
385
396
|
decoded with the help of the schema registry. All hashes/arrays ship deeply
|
|
386
397
|
symbolized keys for easy access.
|
|
387
398
|
|
|
388
|
-
**Heads up!** All messages with events
|
|
399
|
+
**Heads up!** All messages with events that are not reflected by a method will
|
|
389
400
|
just be ignored.
|
|
390
401
|
|
|
391
402
|
See the automatically generated spec (`spec/consumers/custom_consumer_spec.rb`)
|
|
@@ -408,20 +419,11 @@ $ rake rimless:routes
|
|
|
408
419
|
#### Starting the consumer process(es)
|
|
409
420
|
|
|
410
421
|
From system integration perspective you just need to start the consumer
|
|
411
|
-
processes and
|
|
422
|
+
processes and ActiveJob to get the thing going. Rimless allows you to start the
|
|
412
423
|
consumer with `$ rake rimless:consumer` or you can just use the [Karafka
|
|
413
424
|
binary](https://github.com/karafka/karafka/wiki/Fetching-messages) to start the
|
|
414
425
|
consumer (`$ bundle exec karafka server`). Both work identically.
|
|
415
426
|
|
|
416
|
-
When running inside a Rails application the consumer application initialization
|
|
417
|
-
is automatically done for Sidekiq. Otherwise you need to initialize the
|
|
418
|
-
consumer application manually with:
|
|
419
|
-
|
|
420
|
-
```ruby
|
|
421
|
-
# Manual consumer application initialization
|
|
422
|
-
Sidekiq.configure_server { Rimless.consumer.initialize! }
|
|
423
|
-
```
|
|
424
|
-
|
|
425
427
|
### Encoding/Decoding messages
|
|
426
428
|
|
|
427
429
|
By convention we focus on the [Apache Avro](https://avro.apache.org/) data
|
|
@@ -430,14 +432,14 @@ gem and the rimless gem adds some neat helpers on top of it. Here are a few
|
|
|
430
432
|
examples to show how rimless can be used to encode/decode Apache Avro data:
|
|
431
433
|
|
|
432
434
|
```ruby
|
|
433
|
-
# Encode a data structure (
|
|
435
|
+
# Encode a data structure (regardless of symbolized or stringified keys, or
|
|
434
436
|
# non-simple types) to Apache Avro format
|
|
435
437
|
encoded = Rimless.encode(user, schema: 'user_v1')
|
|
436
438
|
|
|
437
439
|
# Works the same for symbolized schema names
|
|
438
440
|
encoded = Rimless.encode(user, schema: :user_v1)
|
|
439
441
|
|
|
440
|
-
# Also supports the resolution of deep relative
|
|
442
|
+
# Also supports the resolution of deep relative schemas
|
|
441
443
|
# (+.user.address+ becomes +<ENV>.<APP>.user.address+)
|
|
442
444
|
encoded = Rimless.encode(user.address, schema: '.user.address')
|
|
443
445
|
|
|
@@ -448,15 +450,15 @@ decoded = Rimless.decode('your-avro-binary-data-here')
|
|
|
448
450
|
|
|
449
451
|
#### Handling of schemaless deep blobs
|
|
450
452
|
|
|
451
|
-
Apache Avro is by design a strict, type
|
|
453
|
+
Apache Avro is by design a strict, type-cast format that does not allow
|
|
452
454
|
undefined mix and matching of deep structures. This is fine because it forces
|
|
453
455
|
the producer to think twice about the schema definition. But sometimes there is
|
|
454
456
|
unstructured data inside of entities. Think of a metadata hash on a user entity
|
|
455
|
-
|
|
456
|
-
for later processing.
|
|
457
|
-
|
|
457
|
+
where the user (e.g. a frontend client) can just add whatever comes to mind
|
|
458
|
+
for later processing. It's not searchable, it's never touched by the backend,
|
|
459
|
+
but it's present.
|
|
458
460
|
|
|
459
|
-
That's a case we'
|
|
461
|
+
That's a case we've experienced and kind of solved in the rimless gem. You can
|
|
460
462
|
make use of the `Rimless.avro_schemaless_h` method to [sparsify the data
|
|
461
463
|
recursively](https://github.com/simplymeasured/sparsify). Say you have the
|
|
462
464
|
following metadata hash:
|
|
@@ -475,7 +477,7 @@ metadata = {
|
|
|
475
477
|
```
|
|
476
478
|
|
|
477
479
|
It's messy, by design. From the Apache Avro perspective you just can define a
|
|
478
|
-
map. The map keys are assumed to be strings
|
|
480
|
+
map. The map keys are assumed to be strings — and the most fitting value data
|
|
479
481
|
type is a string, too. That's where hash sparsification comes in. The resulting
|
|
480
482
|
metadata hash looks like this and can be encoded by Apache Avro:
|
|
481
483
|
|
|
@@ -492,18 +494,18 @@ Rimless.avro_schemaless_h(metadata)
|
|
|
492
494
|
```
|
|
493
495
|
|
|
494
496
|
With the help of the [sparsify gem](https://rubygems.org/gems/sparsify) you can
|
|
495
|
-
also revert this to its original form
|
|
496
|
-
correctness. Another approach can be used for these
|
|
497
|
+
also revert this to its original form, but with the loss of data type
|
|
498
|
+
correctness. Another approach can be used for these kinds of scenarios: encoding
|
|
497
499
|
the schemaless data with JSON and just set the metadata field on the Apache
|
|
498
500
|
Avro schema to be a string. Choice is yours.
|
|
499
501
|
|
|
500
502
|
### Writing tests for your messages
|
|
501
503
|
|
|
502
|
-
Producing messages is a
|
|
504
|
+
Producing messages is a breeze with the rimless gem, but producing code needs to
|
|
503
505
|
be tested as well. That's why the gem ships some RSpec helpers and matchers for
|
|
504
506
|
this purpose. A common situation is also handled by the RSpec extension: on the
|
|
505
|
-
test environment (
|
|
506
|
-
|
|
507
|
+
test environment (e.g. a continuous integration service) it's not likely to have
|
|
508
|
+
an Apache Kafka/Confluent Schema Registry cluster available. That's why actual
|
|
507
509
|
calls to Kafka/Schema Registry are mocked away.
|
|
508
510
|
|
|
509
511
|
First of all, just add `require 'rimless/rspec'` to your `spec_helper.rb` or
|
|
@@ -525,8 +527,8 @@ end
|
|
|
525
527
|
|
|
526
528
|
Nothing special, not really fancy. A more complex situation occurs when you
|
|
527
529
|
separate your Kafka message producing logic inside an asynchronous job (eg.
|
|
528
|
-
|
|
529
|
-
|
|
530
|
+
ActiveJob). The `have_sent_kafka_message` matcher is available for
|
|
531
|
+
this purpose. Example time:
|
|
530
532
|
|
|
531
533
|
```ruby
|
|
532
534
|
describe 'message producer job' do
|
|
@@ -559,7 +561,7 @@ describe 'message producer job' do
|
|
|
559
561
|
expect { action }.to \
|
|
560
562
|
have_sent_kafka_message.with(key: String, topic: anything)
|
|
561
563
|
# mind the order --^
|
|
562
|
-
#
|
|
564
|
+
# it's an argument list validation, all keys must be named
|
|
563
565
|
end
|
|
564
566
|
|
|
565
567
|
it 'sends the correct user data' do
|
|
@@ -571,14 +573,14 @@ describe 'message producer job' do
|
|
|
571
573
|
expect { nil }.not_to have_sent_kafka_message
|
|
572
574
|
end
|
|
573
575
|
|
|
574
|
-
it 'allows complex
|
|
576
|
+
it 'allows complex expectations' do
|
|
575
577
|
expect { action; action }.to \
|
|
576
578
|
have_sent_kafka_message('test.identity_api.user_v1')
|
|
577
579
|
.with(key: user.id, topic: 'test.identity-api.users').twice
|
|
578
580
|
.with_data(firstname: 'John', lastname: 'Doe').twice
|
|
579
581
|
end
|
|
580
582
|
|
|
581
|
-
it 'allows
|
|
583
|
+
it 'allows capturing messages to check them in detail' do
|
|
582
584
|
(capture_kafka_messages { action }).tap do |messages|
|
|
583
585
|
expect(messages.first[:data]).to \
|
|
584
586
|
match(a_hash_including('entity' => a_hash_including('items' => items)))
|
data/Rakefile
CHANGED
|
@@ -15,10 +15,19 @@ Countless.configure do |config|
|
|
|
15
15
|
pattern: %r{/lib(/rimless)?/[^/]+\.rb$} },
|
|
16
16
|
{ name: 'Top-levels specs', test: true, dir: 'spec',
|
|
17
17
|
pattern: %r{/spec(/rimless)?/[^/]+_spec\.rb$} },
|
|
18
|
-
{ name: '
|
|
19
|
-
{ name: '
|
|
18
|
+
{ name: 'Initializers', pattern: 'lib/rimless/initializers/**/*.rb' },
|
|
19
|
+
{ name: 'Compatibilities', pattern: 'lib/rimless/compatibility/**/*.rb' },
|
|
20
|
+
{ name: 'Compatibilities specs', test: true,
|
|
21
|
+
pattern: 'spec/rimless/compatibility/**/*_spec.rb' },
|
|
22
|
+
{ name: 'Consumer', pattern: 'lib/rimless/consumer/**/*.rb' },
|
|
23
|
+
{ name: 'Consumer specs', test: true,
|
|
24
|
+
pattern: 'spec/rimless/consumer/**/*_spec.rb' },
|
|
25
|
+
{ name: 'Extensions', pattern: 'lib/rimless/extensions/**/*.rb' },
|
|
26
|
+
{ name: 'Extensions specs', test: true,
|
|
27
|
+
pattern: 'spec/rimless/extensions/**/*_spec.rb' },
|
|
28
|
+
{ name: 'RSpec extensions', pattern: 'lib/rimless/rspec/**/*.rb' },
|
|
29
|
+
{ name: 'RSpec extensions specs', test: true,
|
|
20
30
|
pattern: 'spec/rimless/rspec/**/*_spec.rb' },
|
|
21
|
-
{ name: 'Rake Tasks', pattern: 'lib/rimless/tasks/**/*' }
|
|
22
|
-
{ name: 'Karafka Extensions', pattern: 'lib/rimless/karafka/**/*.rb' }
|
|
31
|
+
{ name: 'Rake Tasks', pattern: 'lib/rimless/tasks/**/*' }
|
|
23
32
|
]
|
|
24
33
|
end
|