Diagram — Listener Loops¶
Referenced from docs/technical/ARCHITECTURE.md. Shows the four
concurrently-watched tasks fleet-dispatch serve starts, and what tears the process down.
flowchart TB
subgraph serve["fleet-dispatch serve"]
direction TB
subgraph http["1. HTTP accept loop — DispatchServer.RunAsync"]
H1[HttpListener bound to LAN/Tailscale addr + 127.0.0.1] --> H2[GetContextAsync]
H2 --> H3[HandleSafeAsync per request, Task.Run]
H3 --> H2
end
subgraph queue["2. Queue-drain loop — RunQueueDrainAsync"]
Q1[Sleep poll interval, default 30s] --> Q2[QueueService.ListenOnceAsync]
Q2 --> Q1
Q2 -. "--no-queue disables this task entirely" .-> Q1
end
subgraph avail["3. Availability presence loop — AvailabilityDetector.RunAsync"]
A1[Sleep ~4s] --> A2[Tick: read live signals, apply hysteresis]
A2 --> A3[Publish WorkerAvailability for /health]
A3 --> A1
A2 -. "bad tick swallowed, never stops the loop" .-> A1
end
subgraph repo["4. Repo-scan loop — RunRepoScanLoopAsync"]
R1[Immediate boot scan] --> R2[Sleep --repo-scan-poll-hours, default 24h]
R2 --> R3[RepoRegistryScanner.ScanAndWriteAsync]
R3 --> R2
R3 -. "--no-repo-scan disables this task entirely" .-> R2
R3 -. "failed scan logs + retries next tick" .-> R2
end
end
WhenAny{{Task.WhenAny watches all 4}} --> Fault[Any task faults or Ctrl-C cancels]
Fault --> Shutdown[Process exits — a half-dead listener is never left running]
http --> WhenAny
queue --> WhenAny
avail --> WhenAny
repo --> WhenAny
Key points this diagram encodes:
- The availability presence loop (3) is always on, independent of whether jobs are currently being refused by the hard gate — it is how the box ever detects its own re-availability.
- The queue-drain loop (2) and the repo-scan loop (4) can each be individually disabled
(
--no-queue,--no-repo-scan); the HTTP loop (1) and the availability loop (3) cannot be. Task.WhenAny, notTask.WhenAll, watches the set — this is deliberate (FLT-71): aWhenAllcannot observe one faulted task while an infinite loop among the others keeps running, which previously left a dead HTTP listener next to a live queue-drainer with no way to notice.