Class: Wurk::Fetcher::OrphanGrace

Inherits:
Object
  • Object
show all
Defined in:
lib/wurk/fetcher/orphan_grace.rb

Overview

The Reaper's wait between judging a list orphaned by its heartbeat and draining it.

A live owner can read as having no heartbeat hash: its first beat failed on a Redis blip, CPU-bound jobs held the beat back, or Redis restarted from an older snapshot. Its next beat lands within the wait, and that spares it.

The clock is this process's own and it is shared by nothing else, so it costs the same on every server and at every fleet size. That is the difference from what it replaces, OBJECT IDLETIME: any read of the list (a monitor's LLEN) reset that clock, some servers do not have the command, and a clock kept per sweep only advanced in a process that won the sweep lock twice. The loop thread therefore finishes what any sweep here started, with or without the lock (Reaper#drain_settled).

Defined Under Namespace

Classes: Entry

Constant Summary collapse

MEMORY =

Lists remembered at once. Past it the memory restarts, and every clock restarting is the conservative direction: the next sweep judges them anew.

10_000

Instance Method Summary collapse

Constructor Details

#initialize(grace) ⇒ OrphanGrace

Returns a new instance of OrphanGrace.



26
27
28
29
30
# File 'lib/wurk/fetcher/orphan_grace.rb', line 26

def initialize(grace)
  @grace = grace
  @lock = ::Mutex.new
  @entries = {}
end

Instance Method Details

#forget(key) ⇒ Object

The owner came back: its next judgement starts a new wait.



49
50
51
# File 'lib/wurk/fetcher/orphan_grace.rb', line 49

def forget(key)
  @lock.synchronize { @entries.delete(key) }
end

#next_due_in ⇒ Object

Seconds until the next wait ends, nil with nothing waiting.



64
65
66
67
68
69
70
# File 'lib/wurk/fetcher/orphan_grace.rb', line 64

def next_due_in
  now = monotonic
  @lock.synchronize do
    oldest = @entries.each_value.map(&:since).min
    oldest && [(oldest + @grace) - now, 0].max
  end
end

#recent?(key, owner) ⇒ Boolean

true while the list must be left alone. owner is what #take_settled hands back when the wait is over.

Returns:

  • (Boolean)


34
35
36
37
38
39
40
41
42
43
44
45
46
# File 'lib/wurk/fetcher/orphan_grace.rb', line 34

def recent?(key, owner)
  return false unless @grace.positive?

  now = monotonic
  @lock.synchronize do
    @entries.clear if @entries.size >= MEMORY && !@entries.key?(key)
    entry = (@entries[key] ||= Entry.new(now, owner))
    next true if now - entry.since < @grace

    @entries.delete(key)
    false
  end
end

#size ⇒ Object



72
73
74
# File 'lib/wurk/fetcher/orphan_grace.rb', line 72

def size
  @lock.synchronize { @entries.size }
end

#take_settled ⇒ Object

[[key, owner], ...] whose wait is over, removed from memory.



54
55
56
57
58
59
60
61
# File 'lib/wurk/fetcher/orphan_grace.rb', line 54

def take_settled
  now = monotonic
  @lock.synchronize do
    settled = @entries.select { |_, entry| now - entry.since >= @grace }
    settled.each_key { |key| @entries.delete(key) }
    settled.map { |key, entry| [key, entry.owner] }
  end
end