Skip to content

GetMeshNode timeout: probe the target hub too, not just the reader - #663

Merged
rbuergi merged 1 commit into
mainfrom
fix/getmeshnode-timeout-target-probe
Jul 27, 2026
Merged

rbuergi merged 1 commit into
mainfrom
fix/getmeshnode-timeout-target-probe

Conversation

@rbuergi

@rbuergi rbuergi commented Jul 27, 2026

Copy link
Copy Markdown
Contributor

Follow-up to #661, driven by what its first CI occurrence actually said.

What #661's diagnostic proved

Verbatim from the trx of run 30242205261 (ThreadAgentIntegrationTest.cs:280 — the ReadNode call itself, 60.49 s):

System.TimeoutException : GetMeshNode('ACME/ProductLaunch') timed out after 60.0s (budget 60s)
— the owning per-node hub never answered the GetDataRequest. This is NOT 'node not found'.
Hub mesh/ErNNufoXIke8fYHLFHYQpg RunLevel=Started Queue(buffer=0,deferred=0,exec=0)
PendingCallbacks=1[…=GetDataRequest@ACME/ProductLaunch(60000ms)]

The reader is exonerated, precisely: RunLevel=Started, all queues empty, and no Executing(...) clause at all — so it wasn't congested, blocked or starved, and its only outstanding work was our own read. The request left the reader, was routed, and the owner produced no reply in 60 s.

What the message can't yet distinguish is why: owner never activated versus owner answered and the reply was lost. Those need completely different fixes.

The change

The timeout branch now probes the target as well, via GetHostedHub(address, HostedHubCreation.Never) — a pure dictionary lookup that must never activate the hub as a side effect of diagnosing it (RoutingServiceBase already uses exactly this probe). The message becomes Reader: … Target: … with three outcomes:

  • Target: NO LOCAL HUB at 'X' — it never activated here (or is owned by another silo), so no reply was ever going to be produced.
  • Target: Hub X RunLevel=… Queue(…) Executing(…) PendingCallbacks=… → activated but stuck, and on what.
  • Target: this hub itself. → self-read, so no false "no local hub" claim.

Either of the first two closes the diagnosis on the next occurrence.

Two assertions added to GetMeshNodeTimeoutSurfacingTest (Reader: / Target:). Verified locally: Release -p:CIRun=true -warnaserror clean; MeshWeaver.AI.Test 889/894 (5 skipped, 0 failed); Hosting.Monolith.Test 398/400 (2 skipped, 0 failed).

Where this points

The timing profile in that run puts the money on cold activation, not a dropped reply: the two sibling tests in the same class were already 5–10× slower than a healthy run before the failing one executed (11.69 s and 3.72 s vs 2.09 s and 0.83 s). Every [Fact] builds a fresh mesh with a fresh assembly-store root, so the class activates ACME/ProductLaunch — an ACME/Project node requiring a Roslyn compile — three times over, on a 4-core runner. That also explains SwitchThread_IsolatesConversationState failing the same way on a different PR: same class, same path, whichever method draws the short straw pays the cost.

No What's New entry — diagnostics only, no user-visible behaviour change.

🤖 Generated with Claude Code

The first CI occurrence with the loud timeout proved the reader is innocent:
Reader RunLevel=Started, Queue(buffer=0,deferred=0,exec=0), no Executing clause
at all, its only outstanding work our own read. So the request left the reader,
was routed, and the owner produced no reply in 60s.

What the message could not distinguish is WHY: 'owner never activated' vs
'owner answered and the reply was lost'. This probes the target with
GetHostedHub(address, HostedHubCreation.Never) — a pure dictionary lookup that
must never activate the hub as a side effect of diagnosing it (RoutingServiceBase
uses the same probe) — and reports one of three outcomes: no local hub (never
activated here, or owned by another silo), the target's own RunLevel/queue/
executing/pending snapshot (activated but stuck, and on what), or 'this hub
itself' for a self-read.

Either of the first two closes the diagnosis on the next occurrence.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings July 27, 2026 07:22

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.

Pull request overview

Improves GetMeshNode timeout diagnostics so a stalled read can better distinguish “reader is idle” from “target hub never activated / is stuck,” helping narrow down activation vs. reply-loss scenarios in CI and production.

Changes:

  • Extend the GetMeshNode timeout message to include both reader-hub and target-hub diagnostic snapshots.
  • Add test assertions ensuring the surfaced timeout message includes Reader: and Target: labels.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

File Description
test/MeshWeaver.Hosting.Monolith.Test/GetMeshNodeTimeoutSurfacingTest.cs Adds assertions that timeout messages label reader vs. target diagnostics.
src/MeshWeaver.Mesh.Contract/MeshNodeStreamExtensions.cs Adds target-hub probing to the timeout message alongside existing reader diagnostics.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +1431 to +1444
string targetState;
try
{
targetState = string.Equals(hub.Address.ToString(), path, StringComparison.Ordinal)
? "Target: this hub itself."
: hub.GetHostedHub(new Address(path), HostedHubCreation.Never) is { } owner
? $"Target: {owner.GetPendingRequestDiagnostics()}"
: $"Target: NO LOCAL HUB at '{path}' — it never activated here (or is owned "
+ "by another silo), so no reply was ever going to be produced.";
}
catch (Exception targetEx)
{
targetState = $"<target diagnostics unavailable: {targetEx.GetType().Name}>";
}
@github-actions

Copy link
Copy Markdown
Contributor

Test Results (shard 0)

934 tests  ±0   928 ✅ ±0   4m 51s ⏱️ -12s
  5 suites ±0     6 💤 ±0 
  5 files   ±0     0 ❌ ±0 

Results for commit 0956091. ± Comparison against base commit 7026a3a.

@github-actions

Copy link
Copy Markdown
Contributor

Test Results (shard 2)

1 129 tests   1 026 ✅  5m 33s ⏱️
   10 suites    103 💤
   10 files        0 ❌

Results for commit 0956091.

@github-actions

Copy link
Copy Markdown
Contributor

Test Results (shard 3)

   12 files  ±0     12 suites  ±0   4m 38s ⏱️ +18s
  970 tests ±0    966 ✅ ±0  4 💤 ±0  0 ❌ ±0 
1 033 runs  ±0  1 029 ✅ ±0  4 💤 ±0  0 ❌ ±0 

Results for commit 0956091. ± Comparison against base commit 7026a3a.

@github-actions

Copy link
Copy Markdown
Contributor

Test Results (shard 5)

1 138 tests   1 137 ✅  6m 4s ⏱️
   12 suites      1 💤
   12 files        0 ❌

Results for commit 0956091.

@github-actions

Copy link
Copy Markdown
Contributor

Test Results (shard 4)

1 968 tests  ±0   1 780 ✅ ±0   5m 24s ⏱️ +7s
   12 suites ±0     188 💤 ±0 
   12 files   ±0       0 ❌ ±0 

Results for commit 0956091. ± Comparison against base commit 7026a3a.

@github-actions

Copy link
Copy Markdown
Contributor

Test Results (shard 1)

1 180 tests  ±0   1 179 ✅ ±0   6m 50s ⏱️ +3s
   10 suites ±0       1 💤 ±0 
   10 files   ±0       0 ❌ ±0 

Results for commit 0956091. ± Comparison against base commit 7026a3a.

@github-actions

Copy link
Copy Markdown
Contributor

Test Results

   61 files  ±0     61 suites  ±0   33m 22s ⏱️ +30s
7 319 tests ±0  7 016 ✅ ±0  303 💤 ±0  0 ❌ ±0 
7 382 runs  ±0  7 079 ✅ ±0  303 💤 ±0  0 ❌ ±0 

Results for commit 0956091. ± Comparison against base commit 7026a3a.

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