Skip to content

GH-3519: restart a projection agent whose shard wedged out from under it - #3550

Merged
jeremydmiller merged 1 commit into
mainfrom
gh-3519-restart-wedged-agents
Jul 20, 2026
Merged

GH-3519: restart a projection agent whose shard wedged out from under it#3550
jeremydmiller merged 1 commit into
mainfrom
gh-3519-restart-wedged-agents

Conversation

@jeremydmiller

Copy link
Copy Markdown
Member

Closes the residual of #3519 (following #3535 start-failure isolation and #3520 rebuild/rewind resume).

The wedge

On multi-store, Wolverine-managed Marten hosts (the CritterWatch MultiStoreHost repro), a projection shard can start successfully and then die underneath its EventSubscriptionAgent wrapper — a lost first-assignment start race that wedged daemon-side, a daemon-side stop, or an execution-loop fault. The victim is randomized across boots and stays dead for the process lifetime, sitting in the observable 30s retry loop.

Two gaps kept it stuck:

  1. EventSubscriptionAgent.Status latched Running. Once started it never flipped back, so a shard that stopped underneath the wrapper kept reporting Running. NodeAgentController only restarts agents it can see are non-Running, so it saw nothing to fix (this is the "agents landing in RegisteredIdle aren't restarted by the controller" symptom from the issue).
  2. NodeAgentController.StartAgentAsync short-circuited on a bare Agents.ContainsKey. Any registered agent was treated as healthy forever, so the recurring Solo reevaluation never resurrected a registered-but-dead one.

The fix

  • EventSubscriptionAgent.Status now delegates to the live inner daemon agent once started (the daemon keeps that status current), and still reads Stopped before start / after an explicit StopAsync.
  • NodeAgentController.StartAgentAsync re-drives a registered agent whose shard has Stopped — stopping it first to release any lingering daemon-side shard state — while leaving genuinely Running and deliberately Paused (error backoff / blue-green gate) agents untouched (idempotent).

Tests

agent_restart_when_wedged in CoreTests — red-verified without the controller change:

  • restarts a registered agent whose shard stopped underneath it
  • does not restart a genuinely running agent (idempotent)
  • leaves a Paused agent alone

Full wolverine.slnx builds clean (0 warnings). Also bumps to 6.22.0-alpha.3.

🤖 Generated with Claude Code

Follow-up to GH-3535 (start-failure isolation) and GH-3520 (rebuild/rewind
resume). On multi-store, Wolverine-managed Marten hosts a projection shard can
start successfully and then die underneath its EventSubscriptionAgent wrapper --
a lost first-assignment start race that wedged daemon-side, a daemon-side stop,
or an execution-loop fault. Two gaps kept it dead for the process lifetime:

1. EventSubscriptionAgent.Status latched Running once started, so a shard that
   stopped underneath the wrapper kept reporting Running. NodeAgentController
   only restarts agents it can see are non-Running, so it saw nothing to fix.

2. NodeAgentController.StartAgentAsync short-circuited on a bare
   Agents.ContainsKey, treating any registered agent as healthy forever. The
   recurring Solo reevaluation therefore never resurrected a registered-but-dead
   agent -- it just sat in the observable 30s retry loop reporting a stale
   Running while its high-water climbed.

Fix both: Status now delegates to the live inner daemon agent once started (and
still reads Stopped before start / after an explicit stop), and StartAgentAsync
re-drives a registered agent whose shard has Stopped -- stopping it first to
release any lingering daemon-side shard state -- while leaving genuinely Running
and deliberately Paused (error backoff / blue-green gate) agents untouched.

Red-green regression test agent_restart_when_wedged in CoreTests. Also bumps to
6.22.0-alpha.3.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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.

1 participant