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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 47065d5eaa801ccb1f5c23d66eb399e6a0a3c7d287685a43ffadd61abbfe7c1e
4
- data.tar.gz: 353df199b5e7d340116251f7d5e1bbe6920a908d7302d32b21e6440cc4867934
3
+ metadata.gz: f966662968b429ecea71fdb79f88c08b50beaa34be9bc8a5e506d81c74a312e3
4
+ data.tar.gz: cf424b2a28504aa3ba63d67bccc8562c2be5d827ef751e3ce0291d9b23e28ed6
5
5
  SHA512:
6
- metadata.gz: 88cac69ce1fa353ebcf0393543df3da455c97cf109be7e91c853a1dad018e1dcd00870d40b4df8faeceafa89134e213b75b2b90da73e26cc41b888aa74f9602c
7
- data.tar.gz: 2aede2ac9f3c008ce592b665f3c84c3d653aa79bfd27765f7d80dc2289c8295c42df9e3b4bf5d68b3f3458926e94b69802fbe5c12ec7f193dc85105cfa3a6d2b
6
+ metadata.gz: c80c2ab142b77dd45228e160363afc298b88920e33813f20836b27f9212a4df9fbcfb683827d697cdbec55cf88f77df3f4d208fc60d11a188feb16a0373bea94
7
+ data.tar.gz: 78a05d3f803223c1d58d00a7616b2ab6e89cc3c2c84b9bbfc1a7437621ac884ce3be20ad4027c29b5a8f36b4c343cf5bdda4c91401e8341031f38b0b67e1c371
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
- # The underlying SQL is the same as `try_lock`, but keyed on our
60
- # existing `(name, owner)` pair so it refreshes rather than blocks.
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 honker_lock_acquire(?, ?, ?)",
63
+ "SELECT honker_lock_renew(?, ?, ?)",
64
64
  [@name, @owner, ttl_s],
65
65
  )[0] == 1
66
66
  end
@@ -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. Cron change recomputes next_fire_at from now.
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(?, ?)",
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Honker
4
- VERSION = "0.3.0"
4
+ VERSION = "0.3.2"
5
5
  end
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["honker_watcher_open"],
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(db_path.to_s, backend.to_s, err, err.bytesize)
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
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: honker
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.3.2
5
5
  platform: x86_64-linux
6
6
  authors:
7
7
  - Russell Romney