Skip to content

Worker classification

Verified by tests

ClaimWorkerTests, ClaimWorkerGateCadenceTests, HeartbeatWorkerAdaptiveCadenceTests, DeadLetterRecoveryWorkerTests, TransportDeadLetterDrainWorkerTests, MaintenanceWorkerTests, IntegrityCheckpointWorkerTests, IntegrityAuditWorkerTests, SubscriptionExpansionWorkerTests, BackupTickCoordinatorTests — library CI run #31657041675 (2026-08-13)

Every Whizbang background worker falls into one of six classes. The class tells operators "what wakes this worker?" and tells contributors "what should I touch when changing this worker's cadence?"

Legend

Class Meaning
A — NOTIFY-driven Subscribes to IWorkNotificationListener.OnSignal for its primary wake. Polling may still exist as a safety-net.
B — Channel-driven Wakes on a Channel<T> / BatchFlusher<T> write from an in-process producer. No DB polling.
C — Transport-driven Wakes on a broker (RabbitMQ / ASB) message arrival. Not a DB concern.
D — Polling, NOTIFY-eligible Has a Task.Delay / PeriodicTimer loop AND a NOTIFY signal would semantically apply if wired. Highest-value conversion candidates.
E — Necessarily timed Heartbeat / TTL sweep / scheduled maintenance — must be timer-driven by definition. NOTIFY doesn't apply.
F — One-shot / lifecycle Runs once at start / shutdown / migration. Not a polling concern.

As of v0.681, zero D-class workers remain — every previously-polling worker either converted to A (NOTIFY-driven) or stayed at the only-makes-sense-as-timed E class.

Per-worker table

Worker Class Cadence / driver Notes
ClaimWorker A OnSignal + safety-net poll — NotifyHealthyPollingIntervalMilliseconds (default 5 s) when NOTIFY is healthy, tight PollingIntervalMilliseconds (250 ms base, adaptive backoff) otherwise Outbox/Inbox/Perspective/OrphanRedistribute via _onSignal; EnableSafetyNetPoll=false gives pure NOTIFY-only wakes
PerspectiveWorker A WorkSignalCategory.Perspective + NotifyHealthyPollingIntervalMilliseconds safety-net (default 1 s, equal to the poll interval; raise to relax) v0.681 slice 7a wired the previously-unused signal
DeadLetterRecoveryWorker A WorkSignalCategory.DeadLetterReady + ScanIntervalMinutes backstop (10 min) v0.681 slice 7c added the AFTER INSERT trigger
TransportDeadLetterDrainWorker E (push planned) Timer poll — IntervalMinutes (default 10 min) v0.681 slice 7d added the ITransport.SubscribeToDeadLetterAsync push contract (default throws NotSupportedException), but neither this worker nor any shipped transport wires it yet — polling is the only active path today
OutboxPublishWorker B IWorkChannelWriter Drained when ClaimWorker dispatches
InboxHandlerWorker B BatchFlusher<HandlerCommitRequest>
InboxDispatchWorker B IInboxChannelWriter
InboxDrainWorker B IInboxDrainChannel
OutboxDrainWorker B IOutboxDrainChannel
OutboxCompletionFlushWorker B BatchFlusher<Guid>
PerspectiveCompletionFlushWorker B BatchFlusher
FailureFlushWorker B BatchFlusher
LeaseRenewalWorker B BatchFlusher<CategorizedLeaseRenewal>
TransportConsumerWorker C Transport subscription
ServiceBusConsumerWorker C ASB receiver loop
HeartbeatWorker E 30 s default; adaptive 60 s when alive-lock held (slice 7b) See instance liveness
MaintenanceWorker E 10 min default (IntervalMinutes) Full-table scan; not event-driven
RecentlyProcessedEventCacheSweepWorker E 60 s (SweepIntervalSeconds) In-memory TTL eviction
IntegrityCheckpointWorker E CheckpointIntervalSeconds (default 60 s) Stream-integrity Phase B origin-side checkpoint publisher; publishes even on empty windows
IntegrityAuditWorker E AuditIntervalMinutes (default 1440 — daily) Stream-integrity Phases A + L deep audit
BackupTickCoordinator E Idle-aware: ASLEEP until IdleThreshold (30 s), then ticks every PollingInterval (30 s; FastPollingInterval 5 s when NOTIFY broken) Runs registered backup ticks only when NOTIFY-driven activity isn't expected
OrphanInboxJanitor F StartAsync once (startup purge sweep; ExecuteAsync is a no-op)
SubscriptionExpansionWorker F Runs once after the schema gate Stream-integrity Phase S startup reconciler; baselines/backfills consumed-type expansions
WhizbangShutdownService F Shutdown only (StartAsync is a no-op)
PerspectiveMigrationWorker F On-demand rebuild

When to read this page

  • Adding a new worker → pick the right class and cite the existing examples.
  • Reviewing a worker's cadence → confirm the class is honoured (e.g. an A-class worker MUST have a NOTIFY subscription, not just a backstop poll).
  • Investigating a "why isn't this worker waking" → start with the column "Cadence / driver" and trace it to the source.