tobox 0.2.0 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +27 -0
- data/README.md +62 -0
- data/lib/tobox/configuration.rb +2 -0
- data/lib/tobox/fetcher.rb +21 -5
- data/lib/tobox/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 33a40c07b2d7f36933540d390287aeeb7d4e0aa39a7d19a439eaca6a2f8fc23f
|
4
|
+
data.tar.gz: 939699d801afdb03ed85c0048eee3b00c50a786a345b0731c6dba910fafb51ad
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5f5567a9f8e43fbc0f22e10465a0026a4984932183ae7d70c6fa95cdcb092995cbedd44422fa8a65b6c2d321be3ae15a0b72e905edd43c96427fbaac7cc73e34
|
7
|
+
data.tar.gz: f7823d88490ab6edc02ed6a8fd846f7e6cf40ce042fb046e502e3024c26f24ced5ee143de80669ffad49d5077dc97e338c363eda1e0ea1d3196479447c6aa7f6
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,32 @@
|
|
1
1
|
## [Unreleased]
|
2
2
|
|
3
|
+
## [0.3.0] - 2022-12-12
|
4
|
+
|
5
|
+
### Features
|
6
|
+
|
7
|
+
#### Inbox
|
8
|
+
|
9
|
+
Implementation of the "inbox pattern", which ensures that events are processed to completion only once.
|
10
|
+
|
11
|
+
```ruby
|
12
|
+
# create an inbox table and reference it
|
13
|
+
create_table(:inbox) do
|
14
|
+
column :id, :varchar, null: true, primary_key: true
|
15
|
+
# ...
|
16
|
+
create_table(:outbox) do
|
17
|
+
column :inbox_id, :varchar
|
18
|
+
foreign_key :inbox_id, :inbox
|
19
|
+
# ...
|
20
|
+
|
21
|
+
# tobox.rb
|
22
|
+
inbox_table :inbox
|
23
|
+
inbox_column :inbox_id
|
24
|
+
|
25
|
+
# event production
|
26
|
+
DB[:outbox].insert(event_type: "order_created", inbox_id: "order_created_#{order.id}", ....
|
27
|
+
DB[:outbox].insert(event_type: "billing_event_started", inbox_id: "billing_event_started_#{order.id}", ....
|
28
|
+
```
|
29
|
+
|
3
30
|
## [0.2.0] - 2022-12-05
|
4
31
|
|
5
32
|
### Features
|
data/README.md
CHANGED
@@ -15,6 +15,7 @@ Simple, data-first events processing framework based on the [transactional outbo
|
|
15
15
|
- [Event](#event)
|
16
16
|
- [Features](#features)
|
17
17
|
- [Ordered event processing](#ordered-event-processing)
|
18
|
+
- [Inbox](#inbox)
|
18
19
|
- [Plugins](#plugins)
|
19
20
|
- [Zeitwerk](#zeitwerk)
|
20
21
|
- [Sentry](#sentry)
|
@@ -299,6 +300,14 @@ Overrides the default log level ("info" when in "production" environment, "debug
|
|
299
300
|
|
300
301
|
Defines the column to be used for event grouping, when [ordered processing of events is a requirement](#ordered-event-processing).
|
301
302
|
|
303
|
+
### inbox table
|
304
|
+
|
305
|
+
Defines the name of the table to be used for inbox, when [inbox usage is a requirement](#inbox).
|
306
|
+
|
307
|
+
### inbox column
|
308
|
+
|
309
|
+
Defines the column in the outbox table which references the inbox table, when one is set.
|
310
|
+
|
302
311
|
<a id="markdown-event" name="event"></a>
|
303
312
|
## Event
|
304
313
|
|
@@ -361,6 +370,59 @@ end
|
|
361
370
|
# "order_created" will be processed first
|
362
371
|
# "billing_event_created" will only start processing once "order_created" finishes
|
363
372
|
```
|
373
|
+
<a id="inbox" name="inbox"></a>
|
374
|
+
### Inbox
|
375
|
+
|
376
|
+
`tobox` also supports the [inbox pattern](https://event-driven.io/en/outbox_inbox_patterns_and_delivery_guarantees_explained/), to ensure "exactly-once" processing of events. This is achieved by "tagging" events with a unique identifier, and registering them in the inbox before processing (and if they're there, ignoring it altogether).
|
377
|
+
|
378
|
+
In order to do so, you'll have to:
|
379
|
+
|
380
|
+
1. add an "inbox" table in the database
|
381
|
+
|
382
|
+
```ruby
|
383
|
+
create_table(:inbox) do
|
384
|
+
column :inbox_id, :varchar, null: true, primary_key: true # it can also be a uuid, you decide
|
385
|
+
column :created_at, "timestamp without time zone", null: false, default: Sequel::CURRENT_TIMESTAMP
|
386
|
+
end
|
387
|
+
```
|
388
|
+
|
389
|
+
2. add the unique id reference in the outbox table:
|
390
|
+
|
391
|
+
```ruby
|
392
|
+
create_table(:outbox) do
|
393
|
+
primary_key :id
|
394
|
+
column :type, :varchar, null: false
|
395
|
+
column :inbox_id, :varchar, null: true
|
396
|
+
# ...
|
397
|
+
foreign_key :inbox_id, :inbox
|
398
|
+
```
|
399
|
+
|
400
|
+
3. reference them in the configuration
|
401
|
+
|
402
|
+
```ruby
|
403
|
+
# tobox.rb
|
404
|
+
inbox_table :inbox
|
405
|
+
inbox_column :inbox_id
|
406
|
+
```
|
407
|
+
|
408
|
+
4. insert related outbox events with an inbox id
|
409
|
+
|
410
|
+
```ruby
|
411
|
+
order = Order.new(
|
412
|
+
item_id: item.id,
|
413
|
+
price: 20_20,
|
414
|
+
currency: "EUR"
|
415
|
+
)
|
416
|
+
DB.transaction do
|
417
|
+
order.save
|
418
|
+
DB[:outbox].insert(event_type: "order_created", inbox_id: "ord_crt_#{order.id}", data_after: order.to_hash)
|
419
|
+
DB[:outbox].insert(event_type: "billing_event_started", inbox_id: "bil_evt_std_#{order.id}", data_after: order.to_hash)
|
420
|
+
end
|
421
|
+
|
422
|
+
# assuming this bit above runs two times in two separate workers, each will be processed by tobox only once.
|
423
|
+
```
|
424
|
+
|
425
|
+
**NOTE**: make sure you keep cleaning the inbox periodically from older messages, once there's no more danger of receiving them again.
|
364
426
|
|
365
427
|
<a id="markdown-plugins" name="plugins"></a>
|
366
428
|
## Plugins
|
data/lib/tobox/configuration.rb
CHANGED
data/lib/tobox/fetcher.rb
CHANGED
@@ -24,6 +24,9 @@ module Tobox
|
|
24
24
|
|
25
25
|
max_attempts = configuration[:max_attempts]
|
26
26
|
|
27
|
+
@inbox_table = configuration[:inbox_table]
|
28
|
+
@inbox_column = configuration[:inbox_column]
|
29
|
+
|
27
30
|
@ds = @db[@table]
|
28
31
|
|
29
32
|
run_at_conds = [
|
@@ -80,14 +83,17 @@ module Tobox
|
|
80
83
|
if blk
|
81
84
|
num_events = events.size
|
82
85
|
|
83
|
-
events.
|
84
|
-
|
85
|
-
|
86
|
-
|
86
|
+
events.map! do |ev|
|
87
|
+
try_insert_inbox(ev) do
|
88
|
+
ev[:metadata] = try_json_parse(ev[:metadata])
|
89
|
+
handle_before_event(ev)
|
90
|
+
yield(to_message(ev))
|
91
|
+
ev
|
92
|
+
end
|
87
93
|
rescue StandardError => e
|
88
94
|
error = e
|
89
95
|
raise Sequel::Rollback
|
90
|
-
end
|
96
|
+
end.compact!
|
91
97
|
else
|
92
98
|
events.map!(&method(:to_message))
|
93
99
|
end
|
@@ -148,6 +154,16 @@ module Tobox
|
|
148
154
|
data
|
149
155
|
end
|
150
156
|
|
157
|
+
def try_insert_inbox(event)
|
158
|
+
return yield unless @inbox_table && @inbox_column
|
159
|
+
|
160
|
+
ret = @db[@inbox_table].insert_conflict.insert(@inbox_column => event[@inbox_column])
|
161
|
+
|
162
|
+
return unless ret
|
163
|
+
|
164
|
+
yield
|
165
|
+
end
|
166
|
+
|
151
167
|
def handle_before_event(event)
|
152
168
|
@logger.debug do
|
153
169
|
log_message("outbox event (type: \"#{event[:type]}\", attempts: #{event[:attempts]}) starting...")
|
data/lib/tobox/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: tobox
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- HoneyryderChuck
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-12-
|
11
|
+
date: 2022-12-21 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: sequel
|