Skip to content

A reply routing cannot deliver goes to the waiter, not the floor — and a quiescing hub stops taking work it can never answer - #3302

Merged
rbuergi merged 4 commits into
mainfrom
fix/3261-quiescing-refuses-new-requests
Sep 4, 2026
Merged

rbuergi merged 4 commits into
mainfrom
fix/3261-quiescing-refuses-new-requests

Conversation

@rbuergi

@rbuergi rbuergi commented Sep 4, 2026

Copy link
Copy Markdown
Contributor

Why — this is currently reddening every open PR

MeshWeaver.Graph.Test.NackReachesTheWaiterDuringTeardownTest.OwnerDisposingUnderMeshTeardown_StillAnswersTheWaitingCaller is failing shard 4 on #3297, #3299 and #3300 simultaneously, and failed core CI runs 33861078925 and 33865385033 earlier today. It is not three problems and it is not a flake to retry — it is one broken edge, and the test's own log names it:

[fence] caller is armed on the late watch (ArmedCount=1)
[dispose] mesh disposal invoked — parent is past DisposeHostedHubs
Message delivery failed for PatchDataResponse (ID: l9ret6X…) in TestData/teardown-nack-node:
  Hub TestData/teardown-nack-node cannot route PatchDataResponse to cache/VLe3ZpQ…

The owner answered correctly. Its post was accepted — its own run level is still open, so PostImplGeneric does not refuse — and one turn later HierarchicalRouting finds the parent past DisposeHostedHubs and drops the reply: classified, logged, and told to nobody. The waiter is in the same process, with its registry entry armed and able to take the answer directly, but the routing layer had no way to reach it. So the caller burns its whole budget in silence.

What changes

1. An undeliverable correlated reply is handed over, not dropped. New IUndeliverableReplySink in MeshWeaver.Messaging.Hub; HierarchicalRouting offers a dropped reply to a local waiter on the two arms where it gives up (parent past DisposeHostedHubs, and the disposing no-route arm). LatePatchResponseRegistry implements it, registered as a factory over the same singleton — a second instance would dispatch into a registry nobody armed, which reads exactly like "nobody was waiting". A sink that throws cannot turn a classified drop into a routing fault.

🚨 Only where the delivery is being dropped. Serving an armed caller alongside a healthy post was tried twice at the patch-verdict seam and reverted both times — the sink dispatches synchronously on the owner's turn, ahead of the state the ack is about (ComboGateRollTest run 33863349195, ImportTypeBeforeInstanceTest run 33865385033). Here there is no post left to race, which is what makes it safe.

2. A quiescing hub refuses new work it can never answer. Quiescing exists so the replies a hub is still owed can come in, and the intake gate only refused from DisposeHostedHubs on. Between the two, a hub whose quiesce had finished still admitted brand-new work — and built hosted hubs to serve it. Measured on #3261's branch (DanglingNodeTypeUpdateTest):

[QUIESCE-START] TestData/dnt…: 0 pending callbacks at dispose entry: <none>
[QUIESCE-OK]    TestData/dnt…: drained 0 callback(s) in 0ms
Start processing SubscribeRequest (cache/… -> TestData/dnt…)          ← 40 ms later
Creating new hosted hub for address sync/4wzOafy… in host TestData/dnt…

Now refused at the door with the same transient ShuttingDown NACK the DisposeHostedHubs arm mints. Responses stay admitted — the RequestId is read off the envelope, so a cross-hub RawJson still counts — and so does lifecycle traffic.

🚨 Scoped to what is addressed here, by somebody else. ScheduleNotify is not an inbox: a hub's own outbound posts pass through it too. The first version refused those and SourcesWatcherStopsAtTeardownTest went red — the defect in miniature, since NackThroughParent declines to answer a hub's own delivery and the callback it had just armed then held Quiescing for the whole budget.

Tests

  • QuiescingRefusesNewRequestsTest: a request arriving after Quiescing began is refused at the door (the victim genuinely owes a withheld reply, so the phase is really waiting, and the late request's handler would answer — a pass can only come from the refusal), plus an anti-vacuity anchor that a reply the hub is owed is still admitted. Verified by reverting the production change: the first fails with "No exception was thrown", the anchor passes either way.
  • DisposalRaceNackTest door one now observes the acceptance it always assumed, waiting on the fate ledger for ENQUEUED@<victim> before disposing. It had been decided by whether the host's routing turn beat Dispose(); adding a class to the suite shifted the scheduling and it lost. (First attempt at that wait used o.WithMessageId(...), which Observe<TResponse> overrides — so it polled a key that never existed and timed out having proved nothing. The Observe(request, options, messageId) overload is the right one.)

Verification

Release, DOTNET_PROCESSOR_COUNT=2 (CI's shape): Graph 1324/1324 (including the failing test above), Hosting 350/350, Data 475/475, Memex.Portal.Shared 727/727, DisposalRaceNackTest + QuiescingRefusesNewRequestsTest 4/4.

What this does NOT do

It does not close #3261. I re-measured its eight reds on a tree carrying this change: still 7 of 7 failing (Graph 1, Compiler.Pipeline 2, Portal.Shared 4). The graph specimen's ledger verdict moved — from "a reply WAS posted … lost" to "no handler was ever entered" — so the reply-loss path it took is closed and a different leak now dominates. The remaining shapes, counted off the ledger: "a reply WAS posted … still pending" ×6, "no handler was ever entered" ×3 (a request that cannot be routed during disposal, classified and still unanswered — a third hole, on the request leg), "handler entered, nothing recorded since" ×2, "chain FAULTED, no reply" ×1. Those need a further change set; this one is scoped to the edge that is breaking CI today.

Pairs-with: none — one new public interface, no removals.

🤖 Generated with Claude Code

rbuergi and others added 2 commits September 4, 2026 15:07
…ting cannot deliver is handed to the waiter instead of dropped

Two halves of one defect: work that arrives at a hub too late to be served, and an answer that
cannot get back out. Both end as a caller holding a pending callback until its own budget expires,
which is what #3261's detector measures — 72 of 690 disposals at ≈2.0 s, silently.

**1. Quiescing refuses new requests (ScheduleNotify).** Quiescing exists so the replies a hub is
still OWED can come in, and the intake gate only refused from DisposeHostedHubs on. Between the
two, a hub whose quiesce had already finished still admitted brand-new work — and built hosted hubs
to serve it. Measured on #3261's branch (DanglingNodeTypeUpdateTest, 2026-09-04):

    [QUIESCE-START] TestData/dnt…: 0 pending callbacks at dispose entry: <none>
    [QUIESCE-OK]    TestData/dnt…: drained 0 callback(s) in 0ms
    Start processing SubscribeRequest (cache/… -> TestData/dnt…)          ← 40 ms later
    Creating new hosted hub for address sync/4wzOafy… in host TestData/dnt…

Three late SubscribeRequests, fresh sync/* children built for them, on a hub already being torn
down. One was answered; the others died with it. Now such a delivery is refused at the door with
the same transient ShuttingDown NACK the DisposeHostedHubs arm mints — an answer in milliseconds
instead of a wait. Responses are exempt (the RequestId is read off the ENVELOPE, so a cross-hub
RawJson still counts), and so is lifecycle traffic.

🚨 Scoped to what is addressed HERE by somebody else. ScheduleNotify is not an inbox: a hub's own
OUTBOUND posts pass through it too. The first version refused those, SourcesWatcherStopsAtTeardown
went red, and the reason is the defect itself in miniature — NackThroughParent declines to answer a
hub's own delivery, so the callback it had just armed held Quiescing for the whole budget.

**2. An undeliverable reply is handed over, not dropped (HierarchicalRouting).** The owner answers,
its post is accepted (its run level is still open), and one turn later routing finds the parent past
DisposeHostedHubs and drops it — classified, logged, told to nobody:

    Message delivery failed for PatchDataResponse (ID: l9ret6X…) in TestData/teardown-nack-node:
      Hub … cannot route PatchDataResponse to cache/VLe3ZpQ…

while the waiter sits in the SAME process with its registry entry armed. That is
NackReachesTheWaiterDuringTeardownTest, red on CI runs 33861078925 and 33865385033 and locally
under load. New IUndeliverableReplySink: routing offers a dropped CORRELATED reply to a local
waiter before discarding it; LatePatchResponseRegistry implements it, registered as a factory over
the same singleton (a second instance would dispatch into a registry nobody armed).

🚨 Only where the delivery is being DROPPED. Serving an armed caller alongside a healthy post was
tried twice at the patch-verdict seam and reverted both times — the sink dispatches synchronously
on the owner's turn, ahead of the state the ack is about (ComboGateRollTest,
ImportTypeBeforeInstanceTest). Here there is no post left to race.

**Tests.** QuiescingRefusesNewRequestsTest: a request arriving after Quiescing began is refused at
the door (the victim genuinely owes a withheld reply, so the phase is really waiting, and the late
request's handler WOULD answer — a pass can only come from the refusal); plus the anti-vacuity
anchor that a reply the hub is owed is still admitted. Verified by reverting the production change:
the first fails ("No exception was thrown"), the anchor passes either way.

DisposalRaceNackTest door one now OBSERVES the acceptance it always assumed — it waits on the fate
ledger for ENQUEUED@<victim> before disposing. `Observe` posts on the HOST, whose turn then routes
down to the victim, so whether the request got in was decided by which turn won; adding a class to
the suite shifted the scheduling and it lost, failing with the DisposeHostedHubs arm's own NACK.

Verified in Release at DOTNET_PROCESSOR_COUNT=2: Graph 1324/1324, Hosting 350/350, Data 475/475,
Memex.Portal.Shared 727/727, Messaging.Hub (in progress at commit time; reported on the PR).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…polls the real key

The first version of the wait set the correlation id via `o.WithMessageId(...)`, but
`Observe<TResponse>` mints its own — so the poll asked the ledger for a key that never existed
and timed out at 36 s having proved nothing. A wait that cannot succeed is the same defect class
as a gate that skips: it settles for reasons unrelated to its subject.

`Observe(request, options, messageId)` is the framework's overload for exactly this. Both doors
now pass in 618 ms, and the wait observes the acceptance the test always assumed.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Copilot AI lite review requested due to automatic review settings September 4, 2026 13:22
@rbuergi
rbuergi enabled auto-merge September 4, 2026 13:23
@github-actions

github-actions Bot commented Sep 4, 2026 •

Copy link
Copy Markdown
Contributor

Test Results (shard 1)

475 tests  ±0   475 ✅ ±0   43s ⏱️ +5s
  1 suites ±0     0 💤 ±0 
  1 files   ±0     0 ❌ ±0 

Results for commit 0d4043b. ± Comparison against base commit 90755f2.

♻️ This comment has been updated with latest results.

@github-actions

github-actions Bot commented Sep 4, 2026 •

Copy link
Copy Markdown
Contributor

Test Results (shard 0)

251 tests  ±0   251 ✅ ±0   2m 11s ⏱️ -14s
  1 suites ±0     0 💤 ±0 
  1 files   ±0     0 ❌ ±0 

Results for commit 0d4043b. ± Comparison against base commit 90755f2.

♻️ This comment has been updated with latest results.

@github-actions

github-actions Bot commented Sep 4, 2026 •

Copy link
Copy Markdown
Contributor

Test Results (shard 3)

368 tests  ±0   366 ✅ ±0   1m 3s ⏱️ +19s
  3 suites ±0     2 💤 ±0 
  3 files   ±0     0 ❌ ±0 

Results for commit 0d4043b. ± Comparison against base commit 90755f2.

♻️ This comment has been updated with latest results.

@github-actions

github-actions Bot commented Sep 4, 2026 •

Copy link
Copy Markdown
Contributor

Test Results (shard 5)

    4 files  ±0      4 suites  ±0   1m 46s ⏱️ -13s
1 478 tests ±0  1 478 ✅ ±0  0 💤 ±0  0 ❌ ±0 
1 479 runs  ±0  1 479 ✅ ±0  0 💤 ±0  0 ❌ ±0 

Results for commit 0d4043b. ± Comparison against base commit 90755f2.

♻️ This comment has been updated with latest results.

@github-actions

github-actions Bot commented Sep 4, 2026 •

Copy link
Copy Markdown
Contributor

Test Results (shard 2)

588 tests  ±0   396 ✅ ±0   2m 55s ⏱️ -10s
  3 suites ±0   192 💤 ±0 
  3 files   ±0     0 ❌ ±0 

Results for commit 0d4043b. ± Comparison against base commit 90755f2.

♻️ This comment has been updated with latest results.

@github-actions

github-actions Bot commented Sep 4, 2026 •

Copy link
Copy Markdown
Contributor

Test Results (shard 4)

2 275 tests  ±0   2 275 ✅ ±0   2m 38s ⏱️ -1s
    3 suites ±0       0 💤 ±0 
    3 files   ±0       0 ❌ ±0 

Results for commit 0d4043b. ± Comparison against base commit 90755f2.

♻️ This comment has been updated with latest results.

@github-actions

github-actions Bot commented Sep 4, 2026 •

Copy link
Copy Markdown
Contributor

Test Results

   15 files  ±0     15 suites  ±0   11m 18s ⏱️ -13s
5 435 tests ±0  5 241 ✅ ±0  194 💤 ±0  0 ❌ ±0 
5 436 runs  ±0  5 242 ✅ ±0  194 💤 ±0  0 ❌ ±0 

Results for commit 0d4043b. ± Comparison against base commit 90755f2.

♻️ This comment has been updated with latest results.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

HierarchicalRouting.TryHandOverUndeliverableReply resolves from DI during teardown and may throw/log warnings when scopes are already disposed; handling ObjectDisposedException benignly would avoid shutdown noise and make the new path safer.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Pull request overview

This PR addresses CI-failing teardown races in the messaging hub by (1) ensuring correlated replies that become unroutable during shutdown are still delivered to an in-process waiter when possible, and (2) refusing new requests once a hub has entered Quiescing, while still admitting replies and lifecycle traffic so teardown can complete.

Changes:

  • Add IUndeliverableReplySink and route undeliverable correlated replies to a local waiter (via LatePatchResponseRegistry) instead of dropping them during teardown.
  • Refuse newly addressed requests at MessageHubRunLevel.Quiescing with a transient shutdown NACK, while continuing to admit replies (by envelope RequestId) and lifecycle messages.
  • Add/adjust tests to pin both behaviors and remove race assumptions in DisposalRaceNackTest.
File summaries
File Description
test/MeshWeaver.Messaging.Hub.Test/QuiescingRefusesNewRequestsTest.cs New tests pinning “refuse new requests during Quiescing” plus an anti-vacuity anchor that replies are still admitted.
test/MeshWeaver.Messaging.Hub.Test/DisposalRaceNackTest.cs Makes “accepted/enqueued” observed via fate ledger before disposal; uses caller-supplied request id overload.
src/MeshWeaver.Messaging.Hub/MessageService.cs Adds Quiescing-time intake refusal for new addressed requests while still permitting replies and lifecycle traffic.
src/MeshWeaver.Messaging.Hub/IUndeliverableReplySink.cs Introduces the new public sink interface for last-resort local delivery of undeliverable correlated replies.
src/MeshWeaver.Messaging.Hub/HierarchicalRouting.cs Offers undeliverable correlated replies to the sink in the two “give up” routing arms instead of dropping.
src/MeshWeaver.Mesh.Contract/LatePatchWriteWatch.cs Implements IUndeliverableReplySink on LatePatchResponseRegistry to dispatch dropped replies to armed local waiters.
src/MeshWeaver.Hosting/Persistence/PersistenceExtensions.cs Registers the same LatePatchResponseRegistry instance as both ILatePatchVerdictSink and IUndeliverableReplySink.
Review details
  • Files reviewed: 7/7 changed files
  • Comments generated: 1
  • Review effort level: Lite

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment on lines +41 to +62
try
{
var sink = hub.ServiceProvider.GetService<IUndeliverableReplySink>();
if (sink is null || !sink.TryDeliver(delivery))
return false;
logger.LogDebug(
"Undeliverable {MessageType} (ID: {MessageId}) for request {RequestId} was handed to "
+ "a local waiter instead of being dropped in {Address}",
delivery.Message?.GetType().Name, delivery.Id,
delivery.Properties[PostOptions.RequestId], hub.Address);
return true;
}
catch (Exception ex)
{
// The hand-over is a last resort; a sink that throws must not turn a classified drop
// into an unhandled fault on the routing path.
logger.LogWarning(ex,
"The undeliverable-reply sink threw for {MessageType} (ID: {MessageId}) in {Address}; "
+ "the delivery is dropped as before",
delivery.Message?.GetType().Name, delivery.Id, hub.Address);
return false;
}
@rbuergi

rbuergi commented Sep 4, 2026

Copy link
Copy Markdown
Contributor Author

Dropping the quiescing intake gate from this PR — CI found a documented contract it contradicts, and the gate had no measured benefit anyway.

MeshWeaver.Layout.Test.SubscribeDuringRecycleTest.SubscribeRacingDisposal_IsRejectedAsTransient_NotWithANullReference went red on shard 5, and its own comments state the contract my gate broke:

Waits until the hub has demonstrably entered the disposal window (Quiescing or later) — creation frozen, message intake still open.

…proves the test exercised the STREAM-CREATION refusal and was not answered by MessageService's intake / deferred-queue NACKs, which carry a different banner and are a different, already-fixed defect.

So a quiescing hub can answer new work: it admits the SubscribeRequest, LayoutAreaHost fails to build its stream, and the handler NACKs with HubDisposingException — a real refusal path with a real diagnosis. My premise ("a hub past Quiescing can never answer a new request, so it must not accept one") is false, and the gate pre-empted that path with a generic door NACK.

Weighed against what the gate actually bought: nothing measurable. I re-ran #3261's eight reds on a tree carrying it — still 7 of 7 failing, same counts. The late-admission leak I traced is real, but it is not what those tests fail on, and answering it at the intake gate breaks a case that was working.

So this PR is now the routing hand-over alone — IUndeliverableReplySink, the two arms where HierarchicalRouting gives up, the registry implementation and its factory registration, plus the DisposalRaceNackTest determinism fix. That is the part with direct evidence: it cures NackReachesTheWaiterDuringTeardownTest, currently red on #3297, #3299 and #3300 simultaneously.

Re-verifying now over Layout (which I had not run locally — that is how this got past me), Graph, Messaging.Hub, Hosting and Data.

…sal window stays open, and it cleared nothing

SubscribeDuringRecycleTest.SubscribeRacingDisposal_IsRejectedAsTransient_NotWithANullReference went
red on shard 5, and its own comments state the contract the gate broke:

    Waits until <hub> has demonstrably entered the disposal window (Quiescing or later) —
    creation frozen, message intake still open.

    …proves the test exercised the STREAM-CREATION refusal and was not answered by MessageService's
    intake / deferred-queue NACKs, which carry a different banner and are a different,
    already-fixed defect.

So a quiescing hub CAN answer new work: it admits the SubscribeRequest, LayoutAreaHost fails to
build its stream, and the handler NACKs with HubDisposingException — a real refusal with a real
diagnosis. The gate's premise ("past Quiescing it can never answer, so it must not accept") is
false, and the gate replaced that diagnosis with a generic door NACK.

Against that cost, the measured benefit was zero: re-running #3261's eight reds on a tree carrying
the gate left 7 of 7 failing, same counts. The late-admission leak it was built from is real —
hubs do accept work at Quiescing and build children for it — but that is not what those tests fail
on, and answering it at the intake gate breaks a case that was working.

What remains is the half with direct evidence: the undeliverable-reply hand-over, which cures
NackReachesTheWaiterDuringTeardownTest — red on #3297, #3299 and #3300 simultaneously.

Verified in Release at DOTNET_PROCESSOR_COUNT=2 with the gate removed: MeshWeaver.Layout.Test
445/445 (the suite that caught this, and which I had not run before — the change touched
ScheduleNotify, i.e. every hub's intake, so "teardown-related suites" was the wrong selection rule).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@rbuergi

rbuergi commented Sep 4, 2026

Copy link
Copy Markdown
Contributor Author

Re-verified with the quiescing gate removed — the change is now the routing hand-over alone, and every suite I can run locally is green at DOTNET_PROCESSOR_COUNT=2 (CI's shape):

suite result
MeshWeaver.Layout.Test 445/445 — the suite whose SubscribeDuringRecycleTest caught the dropped gate
MeshWeaver.Graph.Test 1324/1324 — includes NackReachesTheWaiterDuringTeardownTest, the failure this PR exists for
MeshWeaver.Messaging.Hub.Test 367/367
MeshWeaver.Hosting.Test 350/350
MeshWeaver.Data.Test 475/475

2,861 tests, zero failures, including both suites that previously found defects in this change.

The diff is now: IUndeliverableReplySink (new), the two HierarchicalRouting arms that were dropping correlated replies, LatePatchResponseRegistry implementing it, the factory registration over the same singleton, and the DisposalRaceNackTest determinism fix. No MessageService intake change.

…lism assertion measured the runner

`IndependentNodesRunInParallel_BoundedByTheSlotCount` asserts

    peak > 1, "nodes with no edge between them run at the same time"

and got there by running six nodes on three slots with a `Thread.Sleep(80)` in each body, trusting
that two would land inside the sleep together. On a starved runner they need not: the scheduler can
admit, run and release one node before the next is dispatched, `peak` stays 1, and the assertion
fails having measured the RUNNER'S LOAD rather than the cascade's parallelism.

Measured on core CI run 33878671896 (shard 3) — while `Run tests (shard 3)` was green on three
sibling PRs and on main's previous three runs, and the class is 10/10 locally. That combination is
the signature of a load-sensitive assertion, not of a defect in the subject.

Now the overlap is OBSERVED: each body waits, bounded, for a second node to join it before it
leaves. If the cascade admits more than one — the property under test — both see it immediately and
neither waits, so the green path costs nothing. If it genuinely serialises, the wait expires and
the assertion fails for the reason it is written for, in a couple of seconds rather than hanging.
The decrement moves into a `finally` so a throwing body cannot strand the count and turn this into
a different, harder question.

No hand-woven gate: a volatile counter polled under a bounded `SpinWait.SpinUntil`, which is the
shape AGENTS.md sanctions for a release into a worker the test deliberately parks.

Verified: MeshWeaver.PluginTester.Test CascadeTest 10/10 in Release at DOTNET_PROCESSOR_COUNT=2
(2 s, versus 485 ms before — the cost of proving the overlap rather than assuming it).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@rbuergi

rbuergi commented Sep 4, 2026

Copy link
Copy Markdown
Contributor Author

Folding #3304 in here — the two flakes were cross-blocking each other's fixes.

Measured deadlock:

PR fixes red on
#3302 (this) NackReachesTheWaiterDuringTeardownTest CascadeTest (shard 3)
#3304 CascadeTest NackReachesTheWaiterDuringTeardownTest (shard 4)

Each one's CI trips over the flake the OTHER one fixes, so neither can go green alone while both are live — and the merge queue was ejecting everything for the same two reasons (steward comments on #3296 at 13:19 and 13:40, one per flake). Re-running either is a lottery, not a fix.

So #3304's commit is cherry-picked here unchanged and I will close it. This PR now carries both cures, which is the only combination whose CI can actually be green:

  • the routing hand-over — a correlated reply HierarchicalRouting must drop is offered to a waiter armed in the same process (IUndeliverableReplySink), instead of being dropped classified-and-silent;
  • the cascade overlap — the parallelism assertion OBSERVES a second node joining rather than sleeping 80 ms and trusting the scheduler, which on a loaded runner measures the runner.

Local verification of the combined branch, Release at DOTNET_PROCESSOR_COUNT=2: PluginTester CascadeTest 10/10, plus the earlier full runs — Layout 445/445, Graph 1324/1324 (including the test this PR exists for), Messaging.Hub 367/367, Hosting 350/350, Data 475/475.

@rbuergi

rbuergi commented Sep 4, 2026

Copy link
Copy Markdown
Contributor Author

Third CI-blocking failure covered by this PR, found on #3305 (opened after this one):

PR / shard failing test fixed here by
#3305 shard 4, #3297/#3299/#3300 shard 4, #3296 queue build NackReachesTheWaiterDuringTeardownTest the routing hand-over
#3296 queue build, #3302 shard 3 CascadeTest.IndependentNodesRunInParallel_BoundedByTheSlotCount observing the overlap
#3305 shard 2 DisposalRaceNackTest.FaultingRequest_IsNacked_WhenTheHandlerRacesDisposal observing the acceptance

That third one is door TWO of the same test whose door one I made deterministic here — both doors go through RunRace, and #3305 shows door two racing on main exactly the same way: Observe posts on the HOST, whose turn then routes down to the victim, and Dispose() runs on the test thread in between, so whether the request got in was decided by which turn won. The fate-ledger wait covers both.

So this PR is currently the fix for every distinct assertion blocking the core queue: three tests, one of them a real framework defect (a reply routing must drop, dropped classified-and-silent while the waiter sits armed) and two tests that inferred a concurrent condition from elapsed time instead of observing it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants