Class: Pgbus::Process::WakePipe

Inherits:
Object
  • Object
show all
Defined in:
lib/pgbus/process/wake_pipe.rb

Overview

Fork-side receiver for supervisor-mediated wake-ups (issue #381, worker_notify_scope: :supervisor). The supervisor's NotifyHub holds the write end of a per-fork pipe; this class owns the inherited read end and a single watcher thread that translates the byte protocol into the fork's existing primitives:

W — a NOTIFY arrived for a queue this fork reads → wake_signal.notify!
H — the shared listener is healthy (connected + delivering) → the
  fork may sleep up to the NOTIFY poll ceiling
P — the shared listener is degraded (dead thread, mid-reconnect,
  pooler-deaf) → the fork falls back to fast polling

Status starts optimistic (mirrors NotifyListener's @delivering default) so a just-forked worker isn't pinned to fast polling before the hub's first broadcast. EOF on the pipe means the supervisor is gone: mark not-delivering and let the fork run on plain polling.

A nil reader (scope :fork, or the supervisor never armed the pipe) yields an inert instance: start is a no-op and delivering? is false, so wake_timeout math treats it exactly like an absent listener.

Constant Summary collapse

WAKE =
"W"
HEALTHY =
"H"
DEGRADED =
"P"

Instance Method Summary collapse

Constructor Details

#initialize(reader, wake_signal:, logger: Pgbus.logger) ⇒ WakePipe

Returns a new instance of WakePipe.



30
31
32
33
34
35
36
37
38
# File 'lib/pgbus/process/wake_pipe.rb', line 30

def initialize(reader, wake_signal:, logger: Pgbus.logger)
  @reader = reader
  @wake_signal = wake_signal
  @logger = logger
  @state_mutex = Mutex.new
  @running = false
  @thread = nil
  @delivering = !reader.nil?
end

Instance Method Details

#delivering?Boolean

Returns:

  • (Boolean)


40
41
42
# File 'lib/pgbus/process/wake_pipe.rb', line 40

def delivering?
  @state_mutex.synchronize { @delivering }
end

#running?Boolean

Returns:

  • (Boolean)


44
45
46
# File 'lib/pgbus/process/wake_pipe.rb', line 44

def running?
  @state_mutex.synchronize { @running }
end

#startObject



48
49
50
51
52
53
54
55
56
57
58
# File 'lib/pgbus/process/wake_pipe.rb', line 48

def start
  return self if @reader.nil?

  @state_mutex.synchronize do
    return self if @running

    @running = true
  end
  @thread = Thread.new { run_loop }
  self
end

#stopObject



60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
# File 'lib/pgbus/process/wake_pipe.rb', line 60

def stop
  @state_mutex.synchronize do
    return self unless @running

    @running = false
  end
  # Closing the pipe FD interrupts the watcher's blocking readpartial —
  # Ruby raises IOError in the blocked thread at the interpreter level.
  # (Safe for a plain IO, unlike PG::Connection#close, which is PQfinish
  # under a concurrent libpq call — issue #375. That constraint is about
  # libpq, not IO.)
  close_reader_quietly
  @thread&.join(2)
  @thread = nil
  self
end