Repository navigation
GH-5003 Resume continuous execution after a projection rebuild, even a failed one - #5008
Merged
Merged
Conversation
…a failed one A rebuild hard-stops the projection's continuous agents and tears down its state before replaying. When it threw, nothing restarted the shard, and on the registered-projection path nothing restarted it even on success. - EventSubscriptionAgent.RebuildAsync resumes continuous execution when the rebuild throws (unless it was cancelled), then rethrows the rebuild's exception. A failed resume is logged and never hides the rebuild failure. - EventStoreAgents remembers the wrappers it builds. TryRebuildRegisteredProjectionAsync resumes this node's wrappers for the rebuilt projection, on the same daemon and tenant, whether the rebuild succeeded or threw. Wrappers stopped on purpose are left alone. - IEventSubscriptionAgent.IsRebuilding (default false) reads the JasperFx 2.87.0 IProjectionDaemon.IsRebuilding guard (#1009). NodeAgentController's sweep skips a shard while its projection is rebuilding, which closes the GH-4193 race where a shard that is mid-rebuild looked like a wedged one. Closes #5003 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #5003.
A projection rebuild hard-stops the projection's continuous agents and tears down its state before it replays. When the rebuild threw, nothing restarted the shard. On the registered-projection path (GH-3163, used by CritterWatch when no live agent hosts the shard), nothing restarted it even when the rebuild succeeded; it was left for the GH-4193 sweep to notice. The sweep only reports a stopped shard whose pre-rebuild agent carried an
Otherfailure, so that shard stayed stopped until the process restarted.This builds on the JasperFx 2.87.0 rebuild guard (JasperFx/jasperfx#1009, merged in #1010), which
mainalready pins.Changes
EventSubscriptionAgent.RebuildAsync(the live-agent path) resumes continuous execution when the rebuild throws, then rethrows the rebuild's exception. A cancelled rebuild is not resumed. A resume that fails is logged and never hides why the rebuild failed. The progress is already reset by then, so the resumed agent effectively rebuilds from the start in continuous mode.EventStoreAgents.TryRebuildRegisteredProjectionAsync(the registered-projection path) now resumes this node's agents for the rebuilt projection, whether the rebuild succeeded or threw.EventStoreAgentsremembers the wrappers it builds. It resumes only those on the same daemon, for the same projection and tenant, that weren't stopped on purpose. A wrapper stopped by an operator pause or a reassignment readsStoppedand is skipped.IEventSubscriptionAgent.IsRebuilding(defaultfalse, so existing implementations are unaffected) readsIProjectionDaemon.IsRebuilding.NodeAgentController's sweep skips a shard while its projection is rebuilding. During the replay the shard readsStoppedwith its old failure, which looked exactly like a wedged shard. The A locally-stopped event-subscription agent that keeps its assignment is never recovered: the health sweep defers to StartAgentAsync, which the leader never calls for it #4193 restart could then start a continuous agent on the same progression row as the rebuild (the race described in the Restart a shard the daemon paused on a transient failure #4986 review). The daemon now refuses that start anyway, but this avoids the stop/evict churn that came before the refusal.Tests
failed_rebuild_resumes_continuous_execution, live-agent path:IsRebuildingcomes from the daemonfailed_rebuild_resumes_continuous_execution, registered-projection path:paused_shard_failure_surfacing:OtherfailureWith the three production changes disabled, 6 of these fail, which is the expected set.
Verification
dotnet build wolverine.slnx -c Release -f net9.0: clean.CoreTests.Runtime.Agents(net9.0): 485 passed, 6 skipped, 0 failed.inline_projection_rebuild,store_scoped_transient_rebuild_3618,event_subscription_agent*) against local Postgres: 11 of 11 passed.🤖 Generated with Claude Code