Skip to content

feat: single-patch reconcile model#90

Merged
bdchatham merged 3 commits intomainfrom
feat/single-patch-reconcile
Apr 16, 2026
Merged

feat: single-patch reconcile model#90
bdchatham merged 3 commits intomainfrom
feat/single-patch-reconcile

Conversation

@bdchatham
Copy link
Copy Markdown
Collaborator

Summary

Both controllers now accumulate all status mutations in-memory and flush with a single Status().Patch() at the end of Reconcile. This eliminates the class of optimistic lock conflicts between tasks and the executor, and establishes a clean ownership model.

  • Executor: Removes Client field entirely — pure in-memory mutation engine. Synchronous tasks advance in a loop without intermediate patches. failTask is void.
  • Node controller: Single statusBase captured after finalizer. reconcilePeers and observeCurrentImage return dirty flags. One flush at the end.
  • NodeDeployment controller: completePlan/failPlan are now void in-memory mutations. No more re-read after executor patches (because there are none).

Ownership model

Layer Writes to
Tasks Owned resources (StatefulSets, Services, PVCs, sidecar)
Executor In-memory plan state on owning resource
Reconciler Single Status().Patch() flush at end

What this unblocks

Tasks can now freely mutate the owning resource's status fields (e.g., status.currentImage, conditions) because everything is in-memory until the flush. This is the foundation for NodeUpdate plans where tasks need to set conditions and stamp image state.

Test plan

  • make build — compiles cleanly
  • make test — all tests pass (84.6% node controller, 43.5% planner, 32.9% nodedeployment)
  • make lint — zero issues

🤖 Generated with Claude Code

bdchatham and others added 2 commits April 16, 2026 08:18
Refactors both controllers to accumulate all status mutations in-memory
and flush with a single Status().Patch() at the end of Reconcile.

Executor changes:
- Removes Client field — executor is now a pure in-memory mutation engine
- Task loop: synchronous tasks advance in sequence without intermediate patches
- failTask is now a void function (in-memory mutations only)

Node controller changes:
- Single statusBase captured after ensureNodeFinalizer
- reconcilePeers returns (bool, error) dirty flag, mutates in-memory
- observeCurrentImage returns (bool, error), mutates in-memory
- Single Status().Patch() at end, gated on statusDirty
- Flushes before returning exec errors (preserves partial progress)

NodeDeployment controller changes:
- reconcilePlan no longer takes statusBase (in-memory mutations)
- completePlan/failPlan are now void (in-memory, no re-read)
- startPlan is now void (in-memory, caller flushes)

Tests need updating to match the new in-memory executor behavior.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Both controllers now accumulate all status mutations in-memory and flush
with a single Status().Patch() at the end of Reconcile. This eliminates
optimistic lock conflicts between tasks and the executor, and establishes
a clean ownership model: tasks mutate owned resources, the executor
mutates plan state in-memory, the reconciler flushes once.

Executor changes:
- Removes Client field — pure in-memory mutation engine
- Task loop: synchronous tasks advance in sequence without patches
- advanceTask returns ctrl.Result (no error — failures are in-memory)
- failTask is void (in-memory mutations only)

Node controller changes:
- Single statusBase captured after ensureNodeFinalizer
- reconcilePeers returns (bool, error) dirty flag, mutates in-memory
- observeCurrentImage returns (bool, error), mutates in-memory
- Single Status().Patch() at end, gated on statusDirty
- Flushes before returning exec errors (preserves partial progress)

NodeDeployment controller changes:
- reconcilePlan no longer takes statusBase
- completePlan/failPlan are void (in-memory, no re-read needed)
- startPlan is void (caller flushes)

Test updates:
- Executor unit tests assert against in-memory object (no API re-fetch)
- Integration tests use Reconcile (full pipeline with flush)
- peers_test, reconciler_test assert in-memory mutations
- nodedeployment plan_test asserts in-memory group status

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Comment thread internal/controller/node/controller.go Outdated
Comment thread internal/controller/node/controller.go
Comment thread internal/controller/node/peers_test.go
Comment thread internal/planner/executor.go Outdated
Comment thread internal/planner/executor.go Outdated
- Trim verbose strategy comment in Reconcile, add concise method doc
- Log exec error when status flush also fails (don't swallow silently)
- Remove "Task completed synchronously" comment from executor loop
- Use log.FromContext(ctx) instead of log.Log in failTask

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@bdchatham bdchatham merged commit 86fc035 into main Apr 16, 2026
2 checks passed
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