tobox 0.5.0 → 0.5.1
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/CHANGELOG.md +8 -0
- data/README.md +5 -0
- data/lib/tobox/cli.rb +7 -1
- data/lib/tobox/configuration.rb +1 -0
- data/lib/tobox/fetcher.rb +26 -37
- data/lib/tobox/plugins/event_grouping.rb +2 -2
- data/lib/tobox/plugins/progress.rb +1 -1
- data/lib/tobox/pool/fiber_pool.rb +50 -10
- data/lib/tobox/pool/threaded_pool.rb +20 -11
- 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: 15d95687a102c98fcc33859b3a369dc9026f04fa58f7f785bcad1d9ccb40010f
|
4
|
+
data.tar.gz: 656b296f9d72d67877945317d4a6d5505de63044f74e08c506970b2c841599f4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 172b54fd340f865dbc26a30266f815fe556424cd150c8205c872da02d3725def786648710097b0880de41cc16940a39bd34c8592049d22a907d527ff84b26a3a
|
7
|
+
data.tar.gz: 1dafbf50e8d18c4eb7f4dfd31539322230bd1d11d403f250e4612a0579e878c366fe6b2d44eb24ae17f32476bd25994457563d3fd5ef1a46d7499dc92a93ee1d
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,13 @@
|
|
1
1
|
## [Unreleased]
|
2
2
|
|
3
|
+
## [0.5.1] - 2024-09-26
|
4
|
+
|
5
|
+
### Improvements
|
6
|
+
|
7
|
+
* Refactoring of management of event id which replaces `SELECT id IN (?)` resulting queries with `SELECT id = ?`.
|
8
|
+
* Process shutdown is now more predictable, also in the grace period.
|
9
|
+
* `grace_shutdown_timeout` is a new configuration, by default 5 (seconds).
|
10
|
+
|
3
11
|
## [0.5.0] - 2024-09-16
|
4
12
|
|
5
13
|
### Features
|
data/README.md
CHANGED
@@ -241,6 +241,10 @@ Time (in seconds) to wait before checking again for events in the outbox.
|
|
241
241
|
|
242
242
|
Time (in seconds) to wait for events to finishing processing, before hard-killing the process.
|
243
243
|
|
244
|
+
### `grace_shutdown_timeout`
|
245
|
+
|
246
|
+
Grace period (in seconds) to wait after, hard-killing the work in progress, and before exiting the process.
|
247
|
+
|
244
248
|
### `on(event_type) { |before, after| }`
|
245
249
|
|
246
250
|
callback executed when processing an event of the given type. By default, it'll yield the state of data before and after the event (unless `message_to_arguments` is set).
|
@@ -583,6 +587,7 @@ on_stats(5) do |stats_collector| # every 5 seconds
|
|
583
587
|
# now you can send them to your statsd collector
|
584
588
|
#
|
585
589
|
StatsD.gauge('outbox_pending_backlog', stats[:pending_count])
|
590
|
+
StatsD.gauge('outbox_oldest_message_age', stats[:oldest_event_age_in_seconds])
|
586
591
|
end
|
587
592
|
```
|
588
593
|
|
data/lib/tobox/cli.rb
CHANGED
@@ -108,12 +108,18 @@ module Tobox
|
|
108
108
|
opts[:tag] = arg
|
109
109
|
end
|
110
110
|
|
111
|
-
o.on "-t", "--shutdown-timeout NUM",
|
111
|
+
o.on "-t", "--shutdown-timeout NUM", Float, "Shutdown timeout (in seconds)" do |arg|
|
112
112
|
raise ArgumentError, "must be positive" unless arg.positive?
|
113
113
|
|
114
114
|
opts[:shutdown_timeout] = arg
|
115
115
|
end
|
116
116
|
|
117
|
+
o.on "-g", "--shutdown-grace-timeout NUM", Float, "Shutdown grace timeout (in seconds)" do |arg|
|
118
|
+
raise ArgumentError, "must be positive" unless arg.positive?
|
119
|
+
|
120
|
+
opts[:grace_shutdown_timeout] = arg
|
121
|
+
end
|
122
|
+
|
117
123
|
o.on "--verbose", "Print more verbose output" do |arg|
|
118
124
|
opts[:verbose] = arg
|
119
125
|
end
|
data/lib/tobox/configuration.rb
CHANGED
data/lib/tobox/fetcher.rb
CHANGED
@@ -38,27 +38,20 @@ module Tobox
|
|
38
38
|
def fetch_events(&blk)
|
39
39
|
num_events = 0
|
40
40
|
events_tr do
|
41
|
-
|
41
|
+
event_id = nil
|
42
42
|
|
43
|
-
|
44
|
-
|
45
|
-
mark_as_fetched(
|
43
|
+
event_id_tr do
|
44
|
+
event_id = fetch_event_id
|
45
|
+
mark_as_fetched(event_id) if event_id
|
46
46
|
end
|
47
47
|
|
48
|
-
|
48
|
+
if event_id
|
49
|
+
with_event(event_id) do |event|
|
50
|
+
num_events = 1
|
49
51
|
|
50
|
-
|
51
|
-
with_events(event_ids) do |events|
|
52
|
-
evts = events
|
53
|
-
num_events = events.count
|
54
|
-
|
55
|
-
evts = events.filter_map do |ev|
|
56
|
-
prepare_event(ev, &blk)
|
57
|
-
end
|
52
|
+
prepare_event(event, &blk)
|
58
53
|
end
|
59
54
|
end
|
60
|
-
|
61
|
-
return 0 if evts.nil?
|
62
55
|
end
|
63
56
|
|
64
57
|
num_events
|
@@ -70,56 +63,52 @@ module Tobox
|
|
70
63
|
event[:metadata] = try_json_parse(event[:metadata])
|
71
64
|
handle_before_event(event)
|
72
65
|
yield(to_message(event))
|
73
|
-
event
|
74
66
|
end
|
75
67
|
|
76
|
-
def
|
68
|
+
def fetch_event_id
|
77
69
|
@pick_next_sql.for_update
|
78
70
|
.skip_locked
|
79
|
-
.limit(1).select_map(:id) # lock starts here
|
71
|
+
.limit(1).select_map(:id).first # lock starts here
|
80
72
|
end
|
81
73
|
|
82
|
-
def mark_as_fetched(
|
83
|
-
@ds.where(id:
|
74
|
+
def mark_as_fetched(event_id)
|
75
|
+
@ds.where(id: event_id).update(@mark_as_fetched_params)
|
84
76
|
end
|
85
77
|
|
86
78
|
def events_tr(&block)
|
87
79
|
@db.transaction(savepoint: false, &block)
|
88
80
|
end
|
89
81
|
|
90
|
-
def
|
82
|
+
def event_id_tr
|
91
83
|
yield
|
92
84
|
end
|
93
85
|
|
94
|
-
def
|
95
|
-
|
86
|
+
def with_event(event_id, &blk)
|
87
|
+
event, error = yield_event(event_id, &blk)
|
96
88
|
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
handle_after_event(event)
|
103
|
-
end
|
89
|
+
if error
|
90
|
+
event.merge!(mark_as_error(event, error))
|
91
|
+
handle_error_event(event, error)
|
92
|
+
else
|
93
|
+
handle_after_event(event)
|
104
94
|
end
|
105
95
|
end
|
106
96
|
|
107
|
-
def
|
108
|
-
events_ds = @ds.where(id:
|
109
|
-
|
110
|
-
error = nil
|
97
|
+
def yield_event(event_id)
|
98
|
+
events_ds = @ds.where(id: event_id)
|
99
|
+
event = error = nil
|
111
100
|
|
112
101
|
begin
|
113
|
-
|
102
|
+
event = events_ds.first
|
114
103
|
|
115
|
-
yield
|
104
|
+
yield event
|
116
105
|
|
117
106
|
events_ds.delete
|
118
107
|
rescue StandardError => e
|
119
108
|
error = e
|
120
109
|
end
|
121
110
|
|
122
|
-
[
|
111
|
+
[event, error]
|
123
112
|
end
|
124
113
|
|
125
114
|
def log_message(msg)
|
@@ -16,7 +16,7 @@ module Tobox
|
|
16
16
|
|
17
17
|
private
|
18
18
|
|
19
|
-
def
|
19
|
+
def fetch_event_id
|
20
20
|
group = @pick_next_sql.for_update
|
21
21
|
.skip_locked
|
22
22
|
.limit(1)
|
@@ -37,7 +37,7 @@ module Tobox
|
|
37
37
|
end
|
38
38
|
|
39
39
|
# lock all, process 1
|
40
|
-
event_ids
|
40
|
+
event_ids.first
|
41
41
|
end
|
42
42
|
end
|
43
43
|
end
|
@@ -5,34 +5,74 @@ require "fiber_scheduler"
|
|
5
5
|
|
6
6
|
module Tobox
|
7
7
|
class FiberPool < Pool
|
8
|
-
def initialize(
|
8
|
+
def initialize(_)
|
9
9
|
Sequel.extension(:fiber_concurrency)
|
10
10
|
super
|
11
|
+
@fibers = []
|
12
|
+
|
13
|
+
@fiber_mtx = Mutex.new
|
14
|
+
@fiber_cond = ConditionVariable.new
|
15
|
+
@fiber_thread = nil
|
11
16
|
end
|
12
17
|
|
13
18
|
def start
|
14
19
|
@fiber_thread = Thread.start do
|
15
20
|
Thread.current.name = "tobox-fibers-thread"
|
16
21
|
|
17
|
-
|
18
|
-
|
19
|
-
|
22
|
+
begin
|
23
|
+
FiberScheduler do
|
24
|
+
@fiber_mtx.synchronize do
|
25
|
+
@workers.each do |worker|
|
26
|
+
@fibers << start_fiber_worker(worker)
|
27
|
+
end
|
28
|
+
@fiber_cond.signal
|
29
|
+
end
|
20
30
|
end
|
31
|
+
rescue KillError
|
32
|
+
@fibers.each { |f| f.raise(KillError) }
|
21
33
|
end
|
22
34
|
end
|
35
|
+
@fiber_mtx.synchronize do
|
36
|
+
@fiber_cond.wait(@fiber_mtx)
|
37
|
+
end
|
23
38
|
end
|
24
39
|
|
25
40
|
def stop
|
26
41
|
shutdown_timeout = @configuration[:shutdown_timeout]
|
42
|
+
grace_shutdown_timeout = @configuration[:grace_shutdown_timeout]
|
27
43
|
|
28
44
|
super
|
29
45
|
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
46
|
+
@fiber_thread.join(shutdown_timeout)
|
47
|
+
|
48
|
+
return unless @fiber_thread.alive?
|
49
|
+
|
50
|
+
@fiber_thread.raise(KillError)
|
51
|
+
@fiber_thread.join(grace_shutdown_timeout)
|
52
|
+
@fiber_thread.kill
|
53
|
+
@fiber_thread.join(1)
|
54
|
+
end
|
55
|
+
|
56
|
+
private
|
57
|
+
|
58
|
+
def start_fiber_worker(worker)
|
59
|
+
Fiber.schedule do
|
60
|
+
do_work(worker)
|
61
|
+
|
62
|
+
@fiber_mtx.synchronize do
|
63
|
+
@fibers.delete(Fiber.current)
|
64
|
+
|
65
|
+
if worker.finished? && @running
|
66
|
+
idx = @workers.index(worker)
|
67
|
+
|
68
|
+
raise Error, "worker not found" unless idx
|
69
|
+
|
70
|
+
subst_worker = Worker.new(worker.label, @configuration)
|
71
|
+
@workers[idx] = subst_worker
|
72
|
+
subst_fiber = start_fiber_worker(subst_worker)
|
73
|
+
@fiber_mtx.synchronize { @fibers << subst_fiber }
|
74
|
+
end
|
75
|
+
end
|
36
76
|
end
|
37
77
|
end
|
38
78
|
end
|
@@ -22,24 +22,35 @@ module Tobox
|
|
22
22
|
|
23
23
|
def stop
|
24
24
|
shutdown_timeout = @configuration[:shutdown_timeout]
|
25
|
-
|
26
|
-
deadline = Process.clock_gettime(::Process::CLOCK_MONOTONIC)
|
25
|
+
grace_shutdown_timeout = @configuration[:grace_shutdown_timeout]
|
27
26
|
|
28
27
|
super
|
29
28
|
Thread.pass # let workers finish
|
30
29
|
|
31
30
|
# soft exit
|
32
|
-
|
33
|
-
|
31
|
+
join = lambda do |timeout|
|
32
|
+
start = Process.clock_gettime(::Process::CLOCK_MONOTONIC)
|
33
|
+
|
34
|
+
loop do
|
35
|
+
terminating_th = @threads.synchronize { @threads.first }
|
36
|
+
|
37
|
+
return unless terminating_th
|
38
|
+
|
39
|
+
elapsed = Process.clock_gettime(Process::CLOCK_MONOTONIC) - start
|
34
40
|
|
35
|
-
|
41
|
+
break if elapsed > timeout
|
42
|
+
|
43
|
+
terminating_th.join(timeout - elapsed)
|
44
|
+
end
|
36
45
|
end
|
37
46
|
|
47
|
+
join.call(shutdown_timeout)
|
48
|
+
|
38
49
|
# hard exit
|
39
|
-
@threads.each { |th| th.raise(KillError) }
|
40
|
-
|
41
|
-
|
42
|
-
|
50
|
+
@threads.synchronize { @threads.each { |th| th.raise(KillError) } }
|
51
|
+
join.call(grace_shutdown_timeout)
|
52
|
+
@threads.synchronize { @threads.each(&:kill) }
|
53
|
+
join.call(1)
|
43
54
|
end
|
44
55
|
|
45
56
|
private
|
@@ -63,8 +74,6 @@ module Tobox
|
|
63
74
|
subst_thread = start_thread_worker(subst_worker)
|
64
75
|
@threads << subst_thread
|
65
76
|
end
|
66
|
-
# all workers went down abruply, we need to kill the process.
|
67
|
-
# @parent_thread.raise(Interrupt) if wk.finished? && @threads.empty? && @running
|
68
77
|
end
|
69
78
|
end
|
70
79
|
end
|
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.5.
|
4
|
+
version: 0.5.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- HoneyryderChuck
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-09-
|
11
|
+
date: 2024-09-26 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: sequel
|