Skip to content

PerspectiveWorker NOTIFY wake

Verified by tests

PerspectiveWorkerStartupAndMaintenanceTests, PerspectiveWorkerDeepPathChannelTests, V502DefaultsTests — library CI run #31657041675 (2026-08-13)

WorkSignalCategory.Perspective already fires from the database on every wh_perspective_events insert — the producer trigger has been live for releases. Until v0.681 the signal had no consumer, so PerspectiveWorker spun on a 1 s default poll loop regardless of actual perspective_event arrival.

This page documents the consumer subscription added in slice 7a.

Wake mechanism

graph LR
  Producer[Producer commits<br/>wh_perspective_events row] -->|trigger fires| PG[(PostgreSQL<br/>NOTIFY 'perspective')]
  PG -->|LISTEN dispatch| Listener[IWorkNotificationListener]
  Listener -->|OnSignal(Perspective)| Worker[PerspectiveWorker._perspectiveWake.Release]
  Worker -->|Task.WhenAny wins| Drain[Scan + drain]

The worker's main Task.WhenAny now races the standard channel-readers, the safety-net Task.Delay, AND the wake semaphore. Whichever completes first triggers the drain.

Options

Property Default Used when
PollingIntervalMilliseconds 1000 NOTIFY listener is null / disabled — the worker falls back to this cadence so an outage doesn't introduce latency
NotifyHealthyPollingIntervalMilliseconds 1000 NOTIFY listener is wired — safety-net cadence (signal does the actual wake). Ships equal to the poll interval; raise it (e.g. 30000+) to relax the safety net on hosts with reliable LISTEN connections

The worker picks the max of the two when a listener is wired, so leaving NotifyHealthyPollingIntervalMilliseconds = PollingIntervalMilliseconds (the shipped default) means no relaxed cadence. The tight default is deliberate: new streams not yet present in wh_active_streams receive no per-instance NOTIFY on their first batch, so the safety net must catch them quickly.

Operator notes

  • When no IWorkNotificationListener is registered (legacy / no-direct-conn hosts), behaviour is bit-for-bit identical to pre-v0.681.
  • The signal handler filters by category == Perspective; Outbox/Inbox/OrphanRedistribute don't wake this worker.
  • StopAsync unsubscribes symmetrically; a host restart doesn't double-subscribe.

Verification

After deploy, pg_stat_statements filtered to your service's database should show the perspective-fetch-shaped query call count drop sharply during idle periods once the safety-net cadence is relaxed (e.g. ~4 calls/sec on 250 ms poll-only vs ~0.03/sec at a 30 s safety-net cadence). The whizbang.perspective.empty_batches counter (idle wake cycles that found no work) is the observability signal that the loop is no longer poll-spinning.