Remove zombie node logic and add out-of-sync fallback selection#85
Open
poopoothegorilla wants to merge 5 commits intomainfrom
Open
Remove zombie node logic and add out-of-sync fallback selection#85poopoothegorilla wants to merge 5 commits intomainfrom
poopoothegorilla wants to merge 5 commits intomainfrom
Conversation
|
👋 poopoothegorilla, thanks for creating this pull request! To help reviewers, please consider creating future PRs as drafts first. This allows you to self-review and make any final changes before notifying the team. Once you're ready, you can mark it as "Ready for review" to request feedback. Thanks! |
| tests.AssertLogEventually(t, observedLogs, fmt.Sprintf("RPC endpoint failed to respond to %d consecutive polls", pollFailureThreshold)) | ||
| assert.Equal(t, nodeStateAlive, node.State()) | ||
| }) | ||
| t.Run("with threshold poll failures, we are the last node alive, but is a proxy, transitions to unreachable", func(t *testing.T) { |
Contributor
There was a problem hiding this comment.
why remove this tests?
Collaborator
Author
There was a problem hiding this comment.
Addressed — restored both zombie behavior tests: 'forcibly keeps it alive' (with poolInfo mock returning 0 live nodes excluding caller) and 'but is a proxy, transitions to unreachable'. Updated mocks to use LatestChainInfo(mock.Anything).
dhaidashenko
approved these changes
Mar 18, 2026
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.
Description
Remove "zombie" node logic that incorrectly kept unhealthy nodes alive when they were the last node in the pool, which caused missed state transitions and silent metric gaps (PLEX-2538)
Add fallback node selection that uses out-of-sync nodes when no alive nodes are available, with automatic upgrade back to alive nodes when they recover
Problem
When two nodes (A and B) exist and node A is in FinalizedBlockOutOfSync state, LatestChainInfo() does not count it as alive (since State() returns nodeStateFinalizedBlockOutOfSync). If node B then fails health checks, the l < 2 guard in the alive loop mistakenly treats B as the "last alive node" and keeps it in a zombie state instead of transitioning it to Unreachable or OutOfSync. These zombie nodes are invisible to metrics, preventing proper alerting.
Changes
node_lifecycle.go: Remove all zombie node guards from aliveLoop (4 locations) and outOfSyncLoop (1 location). Nodes now always transition to their correct state regardless of pool size. Remove zombieNodeCheckInterval(), msgCannotDisable, and msgDegradedState.
multi_node.go: Add selectOutOfSyncNode() fallback that picks the best out-of-sync node by highest block number. Update awaitNodeSelection() to fall back to out-of-sync nodes when no alive nodes exist. Update selectNode() to upgrade from an out-of-sync active node to an alive one when available. Update checkLease() to handle nil selector results with out-of-sync fallback.
Tests: Update 5 zombie behavior tests to verify correct state transitions. Add 6 new tests for out-of-sync fallback selection, best-node picking, and alive-node upgrade.
Test plan
[x] All existing multinode tests pass (50/50)
[x] Zombie tests updated to verify nodes transition to correct unhealthy state
[x] New tests cover: fallback to OutOfSync node, fallback to FinalizedBlockOutOfSync node, best-node selection by block height, keeping out-of-sync active when no alive available, upgrading from out-of-sync to alive