Skip to content

Commit 063757f

Browse files
authored
stacks: refactor plan, state, and removed tracking with tree structures for efficient lookups (#36850)
1 parent 7414a3f commit 063757f

26 files changed

+537
-317
lines changed

internal/rpcapi/resource_identity.go

+6-5
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,16 @@
44
package rpcapi
55

66
import (
7+
"github.com/zclconf/go-cty/cty"
8+
ctyjson "github.com/zclconf/go-cty/cty/json"
9+
"google.golang.org/grpc/codes"
10+
"google.golang.org/grpc/status"
11+
712
"github.com/hashicorp/terraform/internal/addrs"
813
"github.com/hashicorp/terraform/internal/plans"
914
"github.com/hashicorp/terraform/internal/providers"
1015
"github.com/hashicorp/terraform/internal/rpcapi/terraform1/stacks"
1116
"github.com/hashicorp/terraform/internal/stacks/stackstate"
12-
"github.com/zclconf/go-cty/cty"
13-
ctyjson "github.com/zclconf/go-cty/cty/json"
14-
"google.golang.org/grpc/codes"
15-
"google.golang.org/grpc/status"
1617
)
1718

1819
func listResourceIdentities(stackState *stackstate.State, identitySchemas map[addrs.Provider]map[string]providers.IdentitySchema) ([]*stacks.ListResourceIdentities_Resource, error) {
@@ -23,7 +24,7 @@ func listResourceIdentities(stackState *stackstate.State, identitySchemas map[ad
2324
return resourceIdentities, nil
2425
}
2526

26-
for ci := range stackState.AllComponentInstances().All() {
27+
for ci := range stackState.AllComponentInstances() {
2728
componentIdentities := stackState.IdentitiesForComponent(ci)
2829
for ri, src := range componentIdentities {
2930
// We skip resources without identity JSON

internal/stacks/stackaddrs/stack.go

+11
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,17 @@ func (s Stack) UniqueKey() collections.UniqueKey[Stack] {
6969
return stackUniqueKey(s.String())
7070
}
7171

72+
// ToStackCall converts the stack address into the absolute address of the stack
73+
// call that would create this stack.
74+
func (s Stack) ToStackCall() ConfigStackCall {
75+
return ConfigStackCall{
76+
Stack: s.Parent(),
77+
Item: StackCall{
78+
Name: s[len(s)-1].Name,
79+
},
80+
}
81+
}
82+
7283
type stackUniqueKey string
7384

7485
// IsUniqueKey implements collections.UniqueKey.

internal/stacks/stackconfig/declarations.go

+8-5
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ type Declarations struct {
5353

5454
// RemovedEmbeddedStacks is the list of embedded stacks that have been removed
5555
// from the configuration.
56-
RemovedEmbeddedStacks collections.Map[stackaddrs.Stack, []*Removed]
56+
RemovedEmbeddedStacks collections.Map[stackaddrs.ConfigStackCall, []*Removed]
5757
}
5858

5959
func makeDeclarations() Declarations {
@@ -65,7 +65,7 @@ func makeDeclarations() Declarations {
6565
OutputValues: make(map[string]*OutputValue),
6666
ProviderConfigs: make(map[addrs.LocalProviderConfig]*ProviderConfig),
6767
RemovedComponents: collections.NewMap[stackaddrs.ConfigComponent, []*Removed](),
68-
RemovedEmbeddedStacks: collections.NewMap[stackaddrs.Stack, []*Removed](),
68+
RemovedEmbeddedStacks: collections.NewMap[stackaddrs.ConfigStackCall, []*Removed](),
6969
}
7070
}
7171

@@ -142,8 +142,11 @@ func (d *Declarations) addEmbeddedStack(decl *EmbeddedStack) tfdiags.Diagnostics
142142
return diags
143143
}
144144

145-
if blocks, exists := d.RemovedEmbeddedStacks.GetOk(stackaddrs.Stack{
146-
stackaddrs.StackStep{Name: name},
145+
if blocks, exists := d.RemovedEmbeddedStacks.GetOk(stackaddrs.ConfigStackCall{
146+
Stack: nil,
147+
Item: stackaddrs.StackCall{
148+
Name: name,
149+
},
147150
}); exists {
148151
for _, removed := range blocks {
149152
if removed.From.Stack[0].Index == nil {
@@ -319,7 +322,7 @@ func (d *Declarations) addRemoved(decl *Removed) tfdiags.Diagnostics {
319322

320323
d.RemovedComponents.Put(addr, append(d.RemovedComponents.Get(addr), decl))
321324
} else {
322-
addr := decl.From.TargetStack()
325+
addr := decl.From.TargetStack().ToStackCall()
323326

324327
if len(decl.From.Stack) == 1 && decl.From.Stack[0].Index == nil {
325328
// Same logic as for components, we can just error a bit earlier

internal/stacks/stackplan/from_proto.go

+24-38
Original file line numberDiff line numberDiff line change
@@ -35,11 +35,11 @@ type Loader struct {
3535
// Constructs a new [Loader], with an initial empty plan.
3636
func NewLoader() *Loader {
3737
ret := &Plan{
38+
Root: newStackInstance(stackaddrs.RootStackInstance),
3839
RootInputValues: make(map[stackaddrs.InputVariable]cty.Value),
3940
ApplyTimeInputVariables: collections.NewSetCmp[stackaddrs.InputVariable](),
4041
DeletedInputVariables: collections.NewSet[stackaddrs.InputVariable](),
4142
DeletedOutputValues: collections.NewSet[stackaddrs.OutputValue](),
42-
Components: collections.NewMap[stackaddrs.AbsComponentInstance, *Component](),
4343
DeletedComponents: collections.NewSet[stackaddrs.AbsComponentInstance](),
4444
PrevRunStateRaw: make(map[string]*anypb.Any),
4545
}
@@ -220,27 +220,24 @@ func (l *Loader) AddRaw(rawMsg *anypb.Any) error {
220220
})
221221
}
222222

223-
if !l.ret.Components.HasKey(addr) {
224-
l.ret.Components.Put(addr, &Component{
225-
PlannedAction: plannedAction,
226-
Mode: mode,
227-
PlanApplyable: msg.PlanApplyable,
228-
PlanComplete: msg.PlanComplete,
229-
Dependencies: dependencies,
230-
Dependents: collections.NewSet[stackaddrs.AbsComponent](),
231-
PlannedInputValues: inputVals,
232-
PlannedInputValueMarks: inputValMarks,
233-
PlannedOutputValues: outputVals,
234-
PlannedChecks: checkResults,
235-
PlannedFunctionResults: functionResults,
236-
237-
ResourceInstancePlanned: addrs.MakeMap[addrs.AbsResourceInstanceObject, *plans.ResourceInstanceChangeSrc](),
238-
ResourceInstancePriorState: addrs.MakeMap[addrs.AbsResourceInstanceObject, *states.ResourceInstanceObjectSrc](),
239-
ResourceInstanceProviderConfig: addrs.MakeMap[addrs.AbsResourceInstanceObject, addrs.AbsProviderConfig](),
240-
DeferredResourceInstanceChanges: addrs.MakeMap[addrs.AbsResourceInstanceObject, *plans.DeferredResourceInstanceChangeSrc](),
241-
})
242-
}
243-
c := l.ret.Components.Get(addr)
223+
c := l.ret.GetOrCreate(addr, &Component{
224+
PlannedAction: plannedAction,
225+
Mode: mode,
226+
PlanApplyable: msg.PlanApplyable,
227+
PlanComplete: msg.PlanComplete,
228+
Dependencies: dependencies,
229+
Dependents: collections.NewSet[stackaddrs.AbsComponent](),
230+
PlannedInputValues: inputVals,
231+
PlannedInputValueMarks: inputValMarks,
232+
PlannedOutputValues: outputVals,
233+
PlannedChecks: checkResults,
234+
PlannedFunctionResults: functionResults,
235+
236+
ResourceInstancePlanned: addrs.MakeMap[addrs.AbsResourceInstanceObject, *plans.ResourceInstanceChangeSrc](),
237+
ResourceInstancePriorState: addrs.MakeMap[addrs.AbsResourceInstanceObject, *states.ResourceInstanceObjectSrc](),
238+
ResourceInstanceProviderConfig: addrs.MakeMap[addrs.AbsResourceInstanceObject, addrs.AbsProviderConfig](),
239+
DeferredResourceInstanceChanges: addrs.MakeMap[addrs.AbsResourceInstanceObject, *plans.DeferredResourceInstanceChangeSrc](),
240+
})
244241
err = c.PlanTimestamp.UnmarshalText([]byte(msg.PlanTimestamp))
245242
if err != nil {
246243
return fmt.Errorf("invalid plan timestamp %q for %s", msg.PlanTimestamp, addr)
@@ -329,26 +326,15 @@ func (l *Loader) Plan() (*Plan, error) {
329326

330327
// Before we return we'll calculate the reverse dependency information
331328
// based on the forward dependency information we loaded above.
332-
for dependentInstAddr, dependencyInst := range l.ret.Components.All() {
329+
for dependentInstAddr, dependencyInst := range l.ret.AllComponents() {
333330
dependentAddr := stackaddrs.AbsComponent{
334331
Stack: dependentInstAddr.Stack,
335332
Item: dependentInstAddr.Item.Component,
336333
}
337334

338335
for dependencyAddr := range dependencyInst.Dependencies.All() {
339-
// FIXME: This is very inefficient because the current data structure doesn't
340-
// allow looking up all of the component instances that have a particular
341-
// component. This'll be okay as long as the number of components is
342-
// small, but we'll need to improve this if we ever want to support stacks
343-
// with a large number of components.
344-
for maybeDependencyInstAddr, dependencyInst := range l.ret.Components.All() {
345-
maybeDependencyAddr := stackaddrs.AbsComponent{
346-
Stack: maybeDependencyInstAddr.Stack,
347-
Item: maybeDependencyInstAddr.Item.Component,
348-
}
349-
if dependencyAddr.UniqueKey() == maybeDependencyAddr.UniqueKey() {
350-
dependencyInst.Dependents.Add(dependentAddr)
351-
}
336+
for _, dependencyInst := range l.ret.ComponentInstances(dependencyAddr) {
337+
dependencyInst.Dependents.Add(dependentAddr)
352338
}
353339
}
354340
}
@@ -439,7 +425,7 @@ func LoadComponentForResourceInstance(plan *Plan, change *tfstackdata1.PlanResou
439425
DeposedKey: deposedKey,
440426
}
441427

442-
c, ok := plan.Components.GetOk(cAddr)
428+
c, ok := plan.Root.GetOk(cAddr)
443429
if !ok {
444430
return nil, addrs.AbsResourceInstanceObject{}, addrs.AbsProviderConfig{}, fmt.Errorf("resource instance change for unannounced component instance %s", cAddr)
445431
}
@@ -476,7 +462,7 @@ func LoadComponentForPartialResourceInstance(plan *Plan, change *tfstackdata1.Pl
476462
DeposedKey: deposedKey,
477463
}
478464

479-
c, ok := plan.Components.GetOk(cAddr)
465+
c, ok := plan.Root.GetOk(cAddr)
480466
if !ok {
481467
return nil, addrs.AbsResourceInstanceObject{}, addrs.AbsProviderConfig{}, fmt.Errorf("resource instance change for unannounced component instance %s", cAddr)
482468
}

internal/stacks/stackplan/from_proto_test.go

+3
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ func TestAddRaw(t *testing.T) {
2626
"empty": {
2727
Raw: nil,
2828
Want: &Plan{
29+
Root: newStackInstance(stackaddrs.RootStackInstance),
2930
PrevRunStateRaw: make(map[string]*anypb.Any),
3031
RootInputValues: make(map[stackaddrs.InputVariable]cty.Value),
3132
},
@@ -48,6 +49,7 @@ func TestAddRaw(t *testing.T) {
4849
}),
4950
},
5051
Want: &Plan{
52+
Root: newStackInstance(stackaddrs.RootStackInstance),
5153
PrevRunStateRaw: make(map[string]*anypb.Any),
5254
RootInputValues: map[stackaddrs.InputVariable]cty.Value{
5355
stackaddrs.InputVariable{Name: "foo"}: cty.StringVal("boop").Mark(marks.Sensitive),
@@ -67,6 +69,7 @@ func TestAddRaw(t *testing.T) {
6769
}),
6870
},
6971
Want: &Plan{
72+
Root: newStackInstance(stackaddrs.RootStackInstance),
7073
PrevRunStateRaw: make(map[string]*anypb.Any),
7174
RootInputValues: map[stackaddrs.InputVariable]cty.Value{
7275
stackaddrs.InputVariable{Name: "foo"}: cty.StringVal("boop"),

0 commit comments

Comments
 (0)