honker 0.3.0-x86_64-linux → 0.3.2-x86_64-linux
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/lib/honker/libhonker_ext.so +0 -0
- data/lib/honker/lock.rb +3 -3
- data/lib/honker/scheduler.rb +26 -18
- data/lib/honker/version.rb +1 -1
- data/lib/honker.rb +15 -5
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: f966662968b429ecea71fdb79f88c08b50beaa34be9bc8a5e506d81c74a312e3
|
|
4
|
+
data.tar.gz: cf424b2a28504aa3ba63d67bccc8562c2be5d827ef751e3ce0291d9b23e28ed6
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: c80c2ab142b77dd45228e160363afc298b88920e33813f20836b27f9212a4df9fbcfb683827d697cdbec55cf88f77df3f4d208fc60d11a188feb16a0373bea94
|
|
7
|
+
data.tar.gz: 78a05d3f803223c1d58d00a7616b2ab6e89cc3c2c84b9bbfc1a7437621ac884ce3be20ad4027c29b5a8f36b4c343cf5bdda4c91401e8341031f38b0b67e1c371
|
data/lib/honker/libhonker_ext.so
CHANGED
|
Binary file
|
data/lib/honker/lock.rb
CHANGED
|
@@ -56,11 +56,11 @@ module Honker
|
|
|
56
56
|
|
|
57
57
|
# Extend the TTL. Returns true if we still hold the lock; false if
|
|
58
58
|
# it was stolen (the TTL elapsed and another owner acquired it).
|
|
59
|
-
#
|
|
60
|
-
# existing
|
|
59
|
+
# Uses honker_lock_renew — honker_lock_acquire does not refresh
|
|
60
|
+
# expires_at for an existing (name, owner) row.
|
|
61
61
|
def heartbeat(ttl_s:)
|
|
62
62
|
@db.db.get_first_row(
|
|
63
|
-
"SELECT
|
|
63
|
+
"SELECT honker_lock_renew(?, ?, ?)",
|
|
64
64
|
[@name, @owner, ttl_s],
|
|
65
65
|
)[0] == 1
|
|
66
66
|
end
|
data/lib/honker/scheduler.rb
CHANGED
|
@@ -43,13 +43,13 @@ module Honker
|
|
|
43
43
|
#
|
|
44
44
|
# Idempotent by `name`; registering the same name twice replaces
|
|
45
45
|
# the previous row.
|
|
46
|
-
def add(name:, queue:, cron: nil, schedule: nil, payload:, priority: 0, expires_s: nil)
|
|
46
|
+
def add(name:, queue:, cron: nil, schedule: nil, payload:, priority: 0, expires_s: nil, max_attempts: 3)
|
|
47
47
|
expr = schedule || cron
|
|
48
48
|
raise ArgumentError, "must provide cron: or schedule:" if expr.nil? || expr.empty?
|
|
49
49
|
|
|
50
50
|
@db.db.get_first_row(
|
|
51
|
-
"SELECT honker_scheduler_register(?, ?, ?, ?, ?, ?)",
|
|
52
|
-
[name, queue, expr, JSON.dump(payload), priority, expires_s],
|
|
51
|
+
"SELECT honker_scheduler_register(?, ?, ?, ?, ?, ?, ?)",
|
|
52
|
+
[name, queue, expr, JSON.dump(payload), priority, expires_s, max_attempts],
|
|
53
53
|
)
|
|
54
54
|
@db.mark_updated
|
|
55
55
|
nil
|
|
@@ -105,7 +105,7 @@ module Honker
|
|
|
105
105
|
|
|
106
106
|
# Return every registered schedule with current state. Each entry
|
|
107
107
|
# is a Hash with: name, queue, cron_expr, payload (JSON string),
|
|
108
|
-
# priority, expires_s, next_fire_at, enabled.
|
|
108
|
+
# priority, expires_s, next_fire_at, enabled, max_attempts.
|
|
109
109
|
def list
|
|
110
110
|
raw = @db.db.get_first_row("SELECT honker_scheduler_list()")[0]
|
|
111
111
|
return [] if raw.nil? || raw.empty?
|
|
@@ -115,9 +115,10 @@ module Honker
|
|
|
115
115
|
|
|
116
116
|
# Mutate fields in place. Pass only the kwargs you want changed
|
|
117
117
|
# (omitting a kwarg leaves the field alone). `payload: nil`
|
|
118
|
-
# writes JSON null
|
|
118
|
+
# writes JSON null; `max_attempts: nil` resets to default 3.
|
|
119
|
+
# Cron change recomputes next_fire_at from now.
|
|
119
120
|
# Returns true iff a row was updated.
|
|
120
|
-
def update(name, schedule: UNSET, cron: UNSET, payload: UNSET, priority: UNSET, expires_s: UNSET)
|
|
121
|
+
def update(name, schedule: UNSET, cron: UNSET, payload: UNSET, priority: UNSET, expires_s: UNSET, max_attempts: UNSET)
|
|
121
122
|
expr = nil
|
|
122
123
|
expr = schedule if schedule != UNSET
|
|
123
124
|
expr = cron if expr.nil? && cron != UNSET
|
|
@@ -126,13 +127,16 @@ module Honker
|
|
|
126
127
|
priority_arg = (priority == UNSET) ? nil : priority
|
|
127
128
|
touch_expires = (expires_s == UNSET) ? 0 : 1
|
|
128
129
|
expires_arg = (expires_s == UNSET) ? nil : expires_s
|
|
130
|
+
touch_max_attempts = (max_attempts == UNSET) ? 0 : 1
|
|
131
|
+
max_attempts_arg = (max_attempts == UNSET) ? nil : max_attempts
|
|
129
132
|
|
|
130
|
-
any_field = !expr.nil? || payload != UNSET || priority != UNSET || expires_s != UNSET
|
|
133
|
+
any_field = !expr.nil? || payload != UNSET || priority != UNSET || expires_s != UNSET || max_attempts != UNSET
|
|
131
134
|
return false unless any_field
|
|
132
135
|
|
|
133
136
|
n = @db.db.get_first_row(
|
|
134
|
-
"SELECT honker_scheduler_update(?, ?, ?, ?, ?, ?)",
|
|
135
|
-
[name, expr, payload_arg, priority_arg, expires_arg, touch_expires
|
|
137
|
+
"SELECT honker_scheduler_update(?, ?, ?, ?, ?, ?, ?, ?)",
|
|
138
|
+
[name, expr, payload_arg, priority_arg, expires_arg, touch_expires,
|
|
139
|
+
max_attempts_arg, touch_max_attempts],
|
|
136
140
|
)[0]
|
|
137
141
|
@db.mark_updated if n.positive?
|
|
138
142
|
n.positive?
|
|
@@ -187,18 +191,15 @@ module Honker
|
|
|
187
191
|
def leader_loop(owner, stop_fn)
|
|
188
192
|
last_heartbeat = monotonic_now
|
|
189
193
|
until stop_fn.call
|
|
194
|
+
still_ours = lock_renew(LEADER_LOCK, owner, LOCK_TTL_S)
|
|
195
|
+
# IMPORTANT: if refresh failed, a new leader has the lock.
|
|
196
|
+
# Break out before ticking so we don't double-fire.
|
|
197
|
+
return unless still_ours
|
|
198
|
+
|
|
199
|
+
last_heartbeat = monotonic_now
|
|
190
200
|
# tick errors escape up to `run`, which releases the lock in
|
|
191
201
|
# its `ensure` before re-raising.
|
|
192
202
|
tick
|
|
193
|
-
if monotonic_now - last_heartbeat >= HEARTBEAT_S
|
|
194
|
-
still_ours = lock_try_acquire(LEADER_LOCK, owner, LOCK_TTL_S)
|
|
195
|
-
# IMPORTANT: if refresh failed, a new leader has the lock.
|
|
196
|
-
# Break out of the leader loop so we don't double-fire. This
|
|
197
|
-
# is the bug the Rust binding fixed; don't reintroduce it.
|
|
198
|
-
return unless still_ours
|
|
199
|
-
|
|
200
|
-
last_heartbeat = monotonic_now
|
|
201
|
-
end
|
|
202
203
|
|
|
203
204
|
wait_s = HEARTBEAT_S - (monotonic_now - last_heartbeat)
|
|
204
205
|
wait_s = 0 if wait_s.negative?
|
|
@@ -239,6 +240,13 @@ module Honker
|
|
|
239
240
|
)[0] == 1
|
|
240
241
|
end
|
|
241
242
|
|
|
243
|
+
def lock_renew(name, owner, ttl_s)
|
|
244
|
+
@db.db.get_first_row(
|
|
245
|
+
"SELECT honker_lock_renew(?, ?, ?)",
|
|
246
|
+
[name, owner, ttl_s],
|
|
247
|
+
)[0] == 1
|
|
248
|
+
end
|
|
249
|
+
|
|
242
250
|
def lock_release(name, owner)
|
|
243
251
|
@db.db.get_first_row(
|
|
244
252
|
"SELECT honker_lock_release(?, ?)",
|
data/lib/honker/version.rb
CHANGED
data/lib/honker.rb
CHANGED
|
@@ -113,11 +113,11 @@ module Honker
|
|
|
113
113
|
end
|
|
114
114
|
|
|
115
115
|
class CoreWatcher
|
|
116
|
-
def initialize(db_path, extension_path, backend)
|
|
116
|
+
def initialize(db_path, extension_path, backend, watcher_poll_interval_ms)
|
|
117
117
|
@lib = Fiddle.dlopen(extension_path)
|
|
118
118
|
@open = Fiddle::Function.new(
|
|
119
|
-
@lib["
|
|
120
|
-
[Fiddle::TYPE_VOIDP, Fiddle::TYPE_VOIDP, Fiddle::TYPE_VOIDP, Fiddle::TYPE_SIZE_T],
|
|
119
|
+
@lib["honker_watcher_open_v2"],
|
|
120
|
+
[Fiddle::TYPE_VOIDP, Fiddle::TYPE_VOIDP, Fiddle::TYPE_LONG_LONG, Fiddle::TYPE_VOIDP, Fiddle::TYPE_SIZE_T],
|
|
121
121
|
Fiddle::TYPE_VOIDP,
|
|
122
122
|
)
|
|
123
123
|
@wait = Fiddle::Function.new(
|
|
@@ -131,7 +131,13 @@ module Honker
|
|
|
131
131
|
Fiddle::TYPE_VOID,
|
|
132
132
|
)
|
|
133
133
|
err = "\0" * 1024
|
|
134
|
-
@handle = @open.call(
|
|
134
|
+
@handle = @open.call(
|
|
135
|
+
db_path.to_s,
|
|
136
|
+
backend.to_s,
|
|
137
|
+
watcher_poll_interval_ms || 1,
|
|
138
|
+
err,
|
|
139
|
+
err.bytesize,
|
|
140
|
+
)
|
|
135
141
|
return unless @handle.to_i.zero?
|
|
136
142
|
|
|
137
143
|
raise ArgumentError, err.delete_suffix("\0").split("\0", 2).first
|
|
@@ -171,10 +177,14 @@ module Honker
|
|
|
171
177
|
attr_reader :db
|
|
172
178
|
|
|
173
179
|
def initialize(path, extension_path: nil, watcher_backend: nil,
|
|
180
|
+
watcher_poll_interval_ms: nil,
|
|
174
181
|
extension_resolver: ExtensionResolver.new)
|
|
175
182
|
unless watcher_backend.nil? || watcher_backend.is_a?(String)
|
|
176
183
|
raise ArgumentError, "unknown watcher backend"
|
|
177
184
|
end
|
|
185
|
+
unless watcher_poll_interval_ms.nil? || watcher_poll_interval_ms.to_i.positive?
|
|
186
|
+
raise ArgumentError, "watcher_poll_interval_ms must be positive"
|
|
187
|
+
end
|
|
178
188
|
|
|
179
189
|
resolved_extension = extension_resolver.resolve(extension_path)
|
|
180
190
|
@db = SQLite3::Database.new(path)
|
|
@@ -186,7 +196,7 @@ module Honker
|
|
|
186
196
|
@db.enable_load_extension(false)
|
|
187
197
|
@db.execute_batch(DEFAULT_PRAGMAS)
|
|
188
198
|
@db.execute("SELECT honker_bootstrap()")
|
|
189
|
-
@watcher = CoreWatcher.new(path, resolved_extension, watcher_backend)
|
|
199
|
+
@watcher = CoreWatcher.new(path, resolved_extension, watcher_backend, watcher_poll_interval_ms)
|
|
190
200
|
end
|
|
191
201
|
|
|
192
202
|
def close
|