Class: Wurk::Swarm::Backoff

Inherits:
Object
  • Object
show all
Defined in:
lib/wurk/swarm/backoff.rb

Overview

Per-key exponential backoff timer with survival-based reset. Pure bookkeeping — no sleeping, no I/O — so the supervise thread never blocks on it: it records a failure, then asks ready? each tick and acts only once the delay has elapsed.

Delay grows base → 2·base → 4·base … capped at cap. A key whose child survived at least reset_after seconds counts as a fresh first failure, so a slot that ran healthily for a while doesn't inherit an old crash streak. Keyed by slot index for crash-respawn and (in Swarm::Restart) for replacement-retry delays.

Constant Summary collapse

BASE =
1.0
CAP =
30.0
RESET_AFTER =
60.0

Instance Method Summary collapse

Constructor Details

#initialize(base: BASE, cap: CAP, reset_after: RESET_AFTER, clock: nil) ⇒ Backoff

Returns a new instance of Backoff.



20
21
22
23
24
25
26
27
# File 'lib/wurk/swarm/backoff.rb', line 20

def initialize(base: BASE, cap: CAP, reset_after: RESET_AFTER, clock: nil)
  @base = base
  @cap = cap
  @reset_after = reset_after
  @clock = clock || -> { ::Process.clock_gettime(::Process::CLOCK_MONOTONIC) }
  @streak = Hash.new(0)
  @due_at = {}
end

Instance Method Details

#clear(key) ⇒ Object

Forget the key entirely (slot retired or restart succeeded).



58
59
60
61
# File 'lib/wurk/swarm/backoff.rb', line 58

def clear(key)
  @streak.delete(key)
  @due_at.delete(key)
end

#consume(key) ⇒ Object

Mark the scheduled retry as issued without resetting the streak, so a child that crashes straight back escalates toward the cap.



53
54
55
# File 'lib/wurk/swarm/backoff.rb', line 53

def consume(key)
  @due_at.delete(key)
end

#fail(key, lifetime: 0.0) ⇒ Object

Record a failure and schedule the next attempt. lifetime is how long the failed child lived; >= reset_after resets the streak. Returns the delay applied.



32
33
34
35
36
37
38
# File 'lib/wurk/swarm/backoff.rb', line 32

def fail(key, lifetime: 0.0)
  @streak[key] = 0 if lifetime >= @reset_after
  @streak[key] += 1
  delay = [@base * (2**(@streak[key] - 1)), @cap].min
  @due_at[key] = now + delay
  delay
end

#pending?(key) ⇒ Boolean

A retry is scheduled and not yet issued.

Returns:

  • (Boolean)


41
42
43
# File 'lib/wurk/swarm/backoff.rb', line 41

def pending?(key)
  @due_at.key?(key)
end

#ready?(key) ⇒ Boolean

The scheduled delay has elapsed (or nothing is scheduled).

Returns:

  • (Boolean)


46
47
48
49
# File 'lib/wurk/swarm/backoff.rb', line 46

def ready?(key)
  at = @due_at[key]
  at.nil? || now >= at
end