discover-peers: refresh Sources on retry + cross-watch peer SeiNodes
Nobody has claimed this yet.
Assessment
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Newbie friendliness
- 48/100
- Issue type
- Bug
- Clarity
- Mostly clear
- Activity status
- Quiet
- Tech stack
- go, kubernetes
- Domain
- devops, infrastructure
Research direction
Start with internal/planner/planner.go:594-619 and internal/planner/executor.go:189-196 to trace how DiscoverPeersTask.Sources is built and retried, then inspect internal/controller/node/controller.go:175-184 and peers.go:13-66. Add focused tests for refreshed retry sources and peer-triggered reconciliation, including predicate filtering. Done means retries use current ResolvedPeers and peer creation, deletion, label changes, and PhaseReady transitions reconcile dependents without steady-state storms.
Written by the indexing model from the issue text.
Description
Problem
Two related correctness gaps in the SeiNode controller's peer-discovery flow:
1. DiscoverPeersTask.Sources is frozen at plan-build time
internal/planner/planner.go:594-619 (discoverPeersTask) snapshots node.Status.ResolvedPeers into the planned task's params at the moment the plan is built. The executor's retry path at internal/planner/executor.go:189-196 resets RetryCount/Status/SubmittedAt but does not refresh t.Params. So if node.Status.ResolvedPeers was incomplete (e.g., resolved when only 1 of N peer SeiNodes existed) at plan-build time, every retry submits the same stale endpoint list.
In the harbor nightly co-applied validator + RPC fleet, this is masked by the timing-friendly case: the resolved single endpoint (validator-0) eventually becomes reachable, retry succeeds. But in any race where the snapshotted peer is deleted, scaled away, or remains unreachable (DNS blackhole, network policy), retries are useless and the plan terminal-fails after the budget exhausts.
2. SetupWithManager doesn't watch peer SeiNodes
internal/controller/node/controller.go:175-184 registers For(SeiNode{}) with GenerationChangedPredicate and Owns() for owned children (StatefulSet/Service/PVC/Job). There is no Watches() on other SeiNodes. Status changes on validator SeiNodes (becoming Ready, being deleted, IP changes) do not trigger reconciliation of the RPC SeiNode that depends on them as peers.
Practical impact: reconcilePeers only re-resolves Status.ResolvedPeers on the RPC SeiNode's own reconcile cycle. Combined with #1 above, this means the RPC SeiNode is "blind" to validator state changes for the duration of an in-flight discover-peers task.
Impact
#209 (discoverPeersMaxRetries=20) papers over the harbor symptom by giving the snapshotted endpoint enough time to become reachable. But the underlying behavior is incorrect — the controller is not picking up new/updated peer SeiNodes. Future flakes will surface when:
- A validator pod is replaced (DNS records change → frozen Sources outdated).
- The peer-source label selector matches a SeiNode that doesn't exist yet at plan-build time.
- A validator SeiNode is deleted between plan-build and discover-peers retry.
Relevant experts
kubernetes-specialist— owns controller-runtime watch/event-source patterns, EnqueueRequestsFromMapFunc.
Proposed approach
Fix #1 — refresh Sources on retry
Two options:
(a) Repopulate task params on each reconcile. Before submitting (or on retry), the executor or a planner hook reads node.Status.ResolvedPeers and updates the in-flight task's Params to reflect current state. Smaller diff, but couples executor to a task-specific shape.
(b) Move endpoint resolution into the sidecar. The task params carry a PeerSource (e.g. label selector) instead of resolved endpoints; the sidecar resolves at task-execution time via the K8s API. Better separation of concerns. Requires sidecar to have list-peer-SeiNodes RBAC.
Recommend (a) for MVP — smaller blast radius, easier to revert if it surfaces a different problem.
Fix #2 — cross-watch peer SeiNodes
Add to SetupWithManager:
.Watches(&seiv1alpha1.SeiNode{},
handler.EnqueueRequestsFromMapFunc(r.enqueuePeerDependents),
builder.WithPredicates(predicate.Or(
predicate.GenerationChangedPredicate{},
statusResolvedPeersChangedPredicate{},
)),
)
Where enqueuePeerDependents lists all SeiNodes whose spec.peers[].label.selector matches the changed SeiNode's labels and enqueues each. Predicate filters out updates that don't change peer-relevant fields (avoid reconcile storms on every status patch).
Architectural constraints
- Watch must filter aggressively to avoid reconcile storms. Peer-relevant changes are: SeiNode creation, deletion, label changes (which affect what selectors match), and status transitions to/from
PhaseReady(peer becoming reachable / unreachable). Sources-refresh on retry must not race with the executor's status patch. Probably needs to be done in a single transaction by the reconciler before submitting the next task attempt.- Backwards compatible — existing SNDs with
EC2TagsorStaticpeer sources don't go throughStatus.ResolvedPeersand don't change.
Acceptance criteria
-
DiscoverPeersTask.Sourcesreflects currentStatus.ResolvedPeerson each retry attempt (test: simulate ResolvedPeers change between submission and retry, assert sidecar sees updated list). - Reconciliation of an RPC SeiNode is triggered when a peer SeiNode's labels change, the peer is created/deleted, or the peer transitions to/from
PhaseReady. - No reconcile storm under steady-state (validator status is patched frequently; predicate must filter).
-
MaxRetries=20on discover-peers (from #209) remains as a safety net, not as the primary correctness mechanism.
Out of scope
- Replacing the entire peer-discovery model (e.g. with a service-mesh approach). Stay within the current
PeerSourceshape. - Multi-cluster peer discovery (peers in another K8s cluster). Out of scope; current label-resolver is intra-namespace.
References
- Frozen-Sources site:
internal/planner/planner.go:594-619(discoverPeersTask). - Reconciler watch site:
internal/controller/node/controller.go:175-184(SetupWithManager). - Resolver:
internal/controller/node/peers.go:13-66(reconcilePeers+resolveLabelPeers). - Stop-gap retry budget: #209 (this work depends on it landing first).
- Operational evidence: harbor nightly release-test 2026-05-08 — discover-peers terminal-failed on first attempt, masked by #209's retry-budget bump.
- Dominant language
- Go
- Stars
- 1
- Forks
- 2
- Avg merge
- 2h 29m
- Merged PRs (30d)
- 56
Contributor guide
No contributing guide indexed for this repository
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from sei-protocol/sei-k8s-controller
-
Difficulty 5/5 Over a week Newbie friendliness 32/100
-
Difficulty 5/5 Over a week Newbie friendliness 32/100
sei-protocol/sei-k8s-controller#457 · 2 comments ·
-
Difficulty 3/5 1-2 days Newbie friendliness 68/100
-
Difficulty 4/5 3-5 days Newbie friendliness 45/100
-
Difficulty 3/5 1-2 days Newbie friendliness 65/100
All issues in sei-protocol/sei-k8s-controller
Similar issues
-
optimization optimization:agents-md-curator
Difficulty 2/5 1-3 hours Newbie friendliness 86/100
githubnext/gh-aw-cao#13143 ·
-
Difficulty 2/5 1-3 hours Newbie friendliness 84/100
blinklabs-io/bursa#904 ·
-
Difficulty 2/5 1-3 hours Newbie friendliness 84/100
yanet-platform/ipfw-go#129 ·
-
bug confmap/provider/googlesecretmanagerprovider needs triage
Difficulty 2/5 1-3 hours Newbie friendliness 72/100
open-telemetry/opentelemetry-collector-contrib#51273 · 2 comments ·
-
bug: AI Gateway client filter lists "Unknown" twice when NULL and literal Unknown clients coexist Openbug
Difficulty 2/5 1-3 hours Newbie friendliness 90/100