Repository navigation
RoutingGrain dispatch pool saturates on transient bursts; both gauges show legs stuck waiting for ThreadPool slots before subscribe #5432
Description
What is failing. The MeshWeaver.Hosting.Orleans.RoutingGrain crossing log fired at Critical twice within 50 ms on both memex portal pods, each time because 64 route dispatches were in flight simultaneously — the reporting threshold itself was the only reason a line appeared. This is the routine back-pressure report, not a new symptom.
Probable cause (moderate confidence). Reading the episode's own gauges per the line's built-in guidance: the second crossing shows 62 legs waiting for a pool slot with subscribing at 0 and oldest leg only 46 ms — a short burst of dispatches queued on the routing pool, all legs young, which the line's own discriminator classifies as load the silo is absorbing, not a leaked slot (a leak needs one leg minutes old, and none is). The first crossing is pure load: every gauge zero, oldest leg 18 ms. Cause is therefore most likely a brief dispatch burst (e.g. a hub fan-out or startup/activation wave) hitting the 64-slot cap; not evidence of a termination bug. If a later line on the same activation shows a higher episode or a clear, the episodes drained normally — the samples here do not carry a follow-up line, so drain is unconfirmed but likely given the ages.
Impact. Two occurrences, one instant, both portal pods, self-describing as absorbed load. No user-visible failure is evidenced. This log site is also the subject of an ongoing alert-noise problem: because the episode stamp [activation#episode started timestamp] carries per-episode identity inside the message, the fingerprint splits per crossing and dozens of near-identical Critical incidents are minted — see core issues #5009, #5018 and #5118 and feedback node rbuergi/Feedback/route-backpressure-fingerprint-splits-20260921-2236 (which proposes the fix: a normaliser rule masking the bracketed episode group, owned by the incident filer in the plugins repository, not by core). This ticket is likely a recurrence of that same condition and can be folded under the tracking issue.
Where to look. src/MeshWeaver.Hosting.Orleans/RoutingGrain.cs, method ReportSaturation, and the routing-pool sizing/subscription-prologue path; and, for the flood aspect, the log-watch incident filer's message normaliser (MeshWeaver.Plugins).
Evidence
| Fingerprint | 8d31853de4769503 |
| Category | MeshWeaver.Hosting.Orleans.RoutingGrain |
| Severity | Critical |
| Namespace | memex |
| Pods | memex-portal-deployment-5c99d79997-7qdb4, memex-portal-deployment-5c99d79997-xs87x |
| Occurrences | 2 |
| First seen | 2026-09-23 02:10:01Z |
| Last seen | 2026-09-23 02:10:01Z |
| Routing | not determined — no configured route matches the category MeshWeaver.Hosting.Orleans.RoutingGrain. This repository is the configured fallback, not a finding about who owns the fault; the category names the LOGGER, which may not be the subject. |
Recent log lines
2026-09-23 02:10:01Z memex-portal-deployment-5c99d79997-7qdb4 crit: MeshWeaver.Hosting.Orleans.RoutingGrain[0]
[ROUTE] Routing back-pressure [d0ab09d0#4 started 2026-09-23T02:10:01.0241587Z]: 64 route dispatches in flight (reporting threshold 64); ordered channels queued 0 over 0 stream destination(s), deepest per-channel queue 0, routing pool subscribing 0, waiting for a pool slot 0; oldest leg in flight 18 ms — dispatch → Store/Coupon (delivery sfupbRjHsEauXKm4Wb_u9A). Latest dispatch target Hosting/ModuleReportInbox — the address that happened to cross the threshold, NOT a diagnosis. A slot is held from dispatch until the leg terminates, INCLUDING the unbounded wait for a ThreadPool thread before the leg's own timeouts start, so a CPU-starved silo raises this with nothing stuck. A CHANNEL is (destination, stream), so a deepest queue of 1 or more means legs of the SAME stream are blocked behind one another (head-of-line within one channel); 0 means nothing is waiting on anything, so read it as load. Many channels over FEW destinations is a multiplexer hub draining in parallel, which is load too — before issue #5009 those legs shared one channel and read as head-of-line. 🚨 THE TWO POOL GAUGES ANSWER DIFFERENT QUESTIONS AND ONLY ONE SEES THE WAIT NAMED ABOVE — issue #5018. 'subscribing' counts legs inside their SUBSCRIBE PROLOGUE only, so a leg still waiting for a ThreadPool thread and a leg already past its subscribe BOTH read 0 there; 'waiting for a pool slot' is the gauge that sees the pre-subscribe wait. High while subscribing is below the pool's cap is a THREAD shortage; both near zero means the legs are past their subscribe and the wait is downstream I/O. 🚨 READ THE OLDEST LEG FIRST: it is the only load-vs-leak discriminator available from a SINGLE line — every leg young is load the silo is absorbing, one leg minutes old is a slot that never came back and the label names it — whereas the episode stamp below needs a SECOND line to say anything. 🚨 Deepest is sampled AT THE CROSSING, so like the in-flight count it …[truncated]
2026-09-23 02:10:01Z memex-portal-deployment-5c99d79997-xs87x crit: MeshWeaver.Hosting.Orleans.RoutingGrain[0]
[ROUTE] Routing back-pressure [b96b00b1#9 started 2026-09-23T02:10:01.0742163Z]: 64 route dispatches in flight (reporting threshold 64); ordered channels queued 63 over 1 stream destination(s), deepest per-channel queue 0, routing pool subscribing 0, waiting for a pool slot 62; oldest leg in flight 46 ms — stream-routed → cache/BTXLtn5Y8kKaySVjx3hPoQ (delivery 9p7IofykaEym3nzbioR9_w). Latest dispatch target cache/BTXLtn5Y8kKaySVjx3hPoQ — the address that happened to cross the threshold, NOT a diagnosis. A slot is held from dispatch until the leg terminates, INCLUDING the unbounded wait for a ThreadPool thread before the leg's own timeouts start, so a CPU-starved silo raises this with nothing stuck. A CHANNEL is (destination, stream), so a deepest queue of 1 or more means legs of the SAME stream are blocked behind one another (head-of-line within one channel); 0 means nothing is waiting on anything, so read it as load. Many channels over FEW destinations is a multiplexer hub draining in parallel, which is load too — before issue #5009 those legs shared one channel and read as head-of-line. 🚨 THE TWO POOL GAUGES ANSWER DIFFERENT QUESTIONS AND ONLY ONE SEES THE WAIT NAMED ABOVE — issue #5018. 'subscribing' counts legs inside their SUBSCRIBE PROLOGUE only, so a leg still waiting for a ThreadPool thread and a leg already past its subscribe BOTH read 0 there; 'waiting for a pool slot' is the gauge that sees the pre-subscribe wait. High while subscribing is below the pool's cap is a THREAD shortage; both near zero means the legs are past their subscribe and the wait is downstream I/O. 🚨 READ THE OLDEST LEG FIRST: it is the only load-vs-leak discriminator available from a SINGLE line — every leg young is load the silo is absorbing, one leg minutes old is a slot that never came back and the label names it — whereas the episode stamp below needs a SECOND line to say anything. 🚨 Deepest is sampled AT THE CROSSING, so li…[truncated]
Re-addressed by the current identity function: this incident inherited e4a97855ab595beb (Systemorph/MeshWeaver.Plugins#1796). Those nodes are superseded and will not fold, file or comment again.
Opened automatically from Admin/_LogIncident/8d31853de4769503. Recurrences are folded into this issue rather than opening new ones.
Activity
rbuergi commentedon Sep 23, 2026
Duplicate of #5118.
Build: pod(s)
memex-portal-deployment-5c99d79997-7qdb4,memex-portal-deployment-5c99d79997-xs87xin namespacememex. ReplicaSet5c99d79997runs3da3423c/3.0.0-ci.9193, which does not contain the compile-off-ThreadPool fix (#5327/#5375) (read fromOps/Status/memex). memex has not rolled yet, so this is the build that #5118's post-roll verification still has to replace.Window 2026-09-23 02:10:01Z → 2026-09-23 02:10:01Z, 2 occurrence(s), fingerprint
8d31853de4769503.d0ab09d0#4b96b00b1#9This issue's own discriminator: thread-shortage (T) and past-subscribe (L) samples interleaved on the same pods. The oldest leg is at most 46 ms, so no leg is minutes old and no slot leaked. It is the same log site and the same reading, sampled on the build without the fix. The cause, the fix, and the verification after memex rolls are recorded on #5118.