Skip to content

audit of core deferral code - #39311

Merged
jbardin merged 6 commits into
mainfrom
jbardin/deferral-auditing
Oct 1, 2026
Merged

jbardin merged 6 commits into
mainfrom
jbardin/deferral-auditing

Conversation

@jbardin

@jbardin jbardin commented Sep 30, 2026 •

Copy link
Copy Markdown
Member

Stepping through the core deferral handling uncovered some more loose ends and repetition. Cleanup the code some more, and try to make it more understandable.

  • Fix action expansion deferral recording. Action expansion was being recorded as action deferral, instead of
    action expansion deferral. We don't actually have "action deferrals" right now though, so remove that too while we're at it.
  • Add missing calls to ReportModuleExpansionDeferred which were never wired into module expansion
  • Remove dead code in GetDeferredPartialExpandedResource
  • Make "deferrals off" behavior of Deferred structure consistent. The Report* methods still record even when deferrals are off, so the duplicate-report panics still catch bugs outside stacks
  • Create a shared helper putUnique for the "insert once, panic on duplicate" logic in Deferring
  • LoadPlannedDeferrals decides between a full and a partial deferral from the wildcard instance key in the address. It no longer relies on DeferredReasonInstanceCountUnknown.
  • PartialExpandedResources returns the partial expansions for a resource, so resource nodes can access them during apply.
  • decodeDeferredResources is the inverse of the plan's deferredResources. A missing schema or a decode failure is now a normal error diagnostic that stops apply before the walk begins, instead of an error inside a graph node.
  • checkForPartialExpansion asks ctx.Deferrals() for partial expansions instead of having them pushed in by the transformer. We can remove the special transformer and extra deferral-related nodes from the graph entirely.
  • unify where NodeAbstractResourceInstance defers the instance from multiple call sites.
  • Fix where deferred destroys could lose track of their actions invocations.
  • Fix panic from "checking whether X should be deferred when it was already deferred". Now we also check for the opposite error, that a resource can't be deferred during apply if it was not planned as such.
  • Further streamline the managed instance planning process to make it easier to follow, and coalesce duplicate calls.

Action expansion was being recorded as action deferral, instance of
action expansion deferral. We don't actually have "action deferrals"
right now though, so remove that too while we're at it.
These were never wired into module expansion
@jbardin jbardin added the no-changelog-needed Add this to your PR if the change does not require a changelog entry label Sep 30, 2026
@jbardin
jbardin force-pushed the jbardin/deferral-auditing branch from 7984b5d to f9aa50c Compare September 30, 2026 12:18
diags = diags.Append(n.reportPlan(ctx, deferred, planDeferred, importing, change, instanceRefreshState, instancePlanState, repData))

} else {
if n.skipPlanChanges {

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I hate unnecessary else blocks, especially in long sections where you can't easily see which condition's else you're under, or when the primary path is the else. This makes the refresh-only path the exception, and the normal planing path the main line.

@jbardin
jbardin marked this pull request as ready for review September 30, 2026 12:34
@jbardin
jbardin requested a review from a team as a code owner September 30, 2026 12:34
Remove dead code in GetDeferredPartialExpandedResource

Make "deferrals off" behavior of Deferred structure consistent. The
Report* methods still record even when deferrals are off, so the
duplicate-report panics still catch bugs outside stacks

Create a shared helper of the "insert once, panic on duplicate" logic

Remove duplicate code in nodeApplyableDeferredInstance
LoadPlannedDeferrals decides between a full and a partial deferral from
the wildcard instance key in the address. It no longer relies on
DeferredReasonInstanceCountUnknown.

PartialExpandedResources(addrs.ConfigResource) returns the partial
expansions for a resource.

decodeDeferredResources is the reverse of the plan's deferredResources.
A missing schema or a decode failure is now a normal error diagnostic
that stops apply before the walk begins. Before, it was an error inside
a graph node

checkForPartialExpansion asks ctx.Deferrals() for partial expansions
instead of having them pushed in by the transformer

We can remove the special transformer and extra deferral-related nodes
from the graph entirely.
unify where NodeAbstractResourceInstance defers the instance from
multiple call sites.

Fix where deferred destroys could lose actions. The actions are now
deferred.

Fix panic from "checking whether X should be deferred when it was
already deferred". Now we also check that a resource can't be deferred
during apply if it was not planned as such.
There was some confusing logic and repeated calls around resource
deferrals and actions, because each can defer the other.

planActionTriggers now returns (invocations, deferred, diags). It no
longer adds invocations to the plan or removes the resource's change, so
all bookkeeping is in the caller.

recordActionInvocations adds the invocations to the plan once nothing
needs to be undone.

finalizePlan saves the working-state object before writing the change and
planned object, then plans actions. If an action is deferred, it
restores that object as well as removing the change. Before, the planned
object was left in the working state. The evaluator always prefers the
deferred value, so nothing could see that leftover object. Actions are
planned right after the change is written, so on deferral only the
change needs undoing. Before, the orphan node had already removed the
object from the working state and never put it back.
@jbardin
jbardin force-pushed the jbardin/deferral-auditing branch from f9aa50c to 5a06dda Compare October 1, 2026 13:25

@austinvalle austinvalle left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Very nice! 🧹

@jbardin
jbardin merged commit 0abcc9a into main Oct 1, 2026
9 checks passed
@jbardin
jbardin deleted the jbardin/deferral-auditing branch October 1, 2026 16:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

no-changelog-needed Add this to your PR if the change does not require a changelog entry

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants