don't store an entire Resource in each Instance

The AbstractResourceInstance type was storing the entire Resource from
the state, when it only needs the actual instance state. This would
cause resources to consume memory on the order of n^2, where n in the
number of instances of the resource.

Rather than attaching the entire resource state, which includes copying
each individual instance, only attach the ResourceInstance state, and
extract out the provider address from the Resource.
This commit is contained in:
James Bardin 2020-07-10 10:16:45 -04:00
parent 1c7a8c3e43
commit ee8cc627a0
3 changed files with 26 additions and 26 deletions

View File

@ -101,10 +101,13 @@ type NodeAbstractResourceInstance struct {
NodeAbstractResource NodeAbstractResource
Addr addrs.AbsResourceInstance Addr addrs.AbsResourceInstance
// The fields below will be automatically set using the Attach // These are set via the AttachState method.
// interfaces if you're running those transforms, but also be explicitly instanceState *states.ResourceInstance
// set if you already have that information. // storedProviderConfig is the provider address retrieved from the
ResourceState *states.Resource // state, but since it is only stored in the whole Resource rather than the
// ResourceInstance, we extract it out here.
storedProviderConfig addrs.AbsProviderConfig
Dependencies []addrs.ConfigResource Dependencies []addrs.ConfigResource
} }
@ -287,13 +290,11 @@ func dottedInstanceAddr(tr addrs.ResourceInstance) string {
// StateDependencies returns the dependencies saved in the state. // StateDependencies returns the dependencies saved in the state.
func (n *NodeAbstractResourceInstance) StateDependencies() []addrs.ConfigResource { func (n *NodeAbstractResourceInstance) StateDependencies() []addrs.ConfigResource {
if rs := n.ResourceState; rs != nil { if s := n.instanceState; s != nil {
if s := rs.Instance(n.Addr.Resource.Key); s != nil {
if s.Current != nil { if s.Current != nil {
return s.Current.Dependencies return s.Current.Dependencies
} }
} }
}
return nil return nil
} }
@ -339,12 +340,12 @@ func (n *NodeAbstractResourceInstance) ProvidedBy() (addrs.ProviderConfig, bool)
}, false }, false
} }
// If we have state, then we will use the provider from there // See if we have a valid provider config from the state.
if n.ResourceState != nil { if n.storedProviderConfig.Provider.Type != "" {
// An address from the state must match exactly, since we must ensure // An address from the state must match exactly, since we must ensure
// we refresh/destroy a resource with the same provider configuration // we refresh/destroy a resource with the same provider configuration
// that created it. // that created it.
return n.ResourceState.ProviderConfig, true return n.storedProviderConfig, true
} }
// No provider configuration found; return a default address // No provider configuration found; return a default address
@ -410,7 +411,12 @@ func (n *NodeAbstractResource) AttachResourceDependencies(deps []addrs.ConfigRes
// GraphNodeAttachResourceState // GraphNodeAttachResourceState
func (n *NodeAbstractResourceInstance) AttachResourceState(s *states.Resource) { func (n *NodeAbstractResourceInstance) AttachResourceState(s *states.Resource) {
n.ResourceState = s if s == nil {
log.Printf("[WARN] attaching nil state to %s", n.Addr)
return
}
n.instanceState = s.Instance(n.Addr.Resource.Key)
n.storedProviderConfig = s.ProviderConfig
} }
// GraphNodeAttachResourceConfig // GraphNodeAttachResourceConfig

View File

@ -63,13 +63,11 @@ func (n *NodeDestroyResourceInstance) CreateBeforeDestroy() bool {
} }
// Otherwise check the state for a stored destroy order // Otherwise check the state for a stored destroy order
if rs := n.ResourceState; rs != nil { if s := n.instanceState; s != nil {
if s := rs.Instance(n.Addr.Resource.Key); s != nil {
if s.Current != nil { if s.Current != nil {
return s.Current.CreateBeforeDestroy return s.Current.CreateBeforeDestroy
} }
} }
}
return false return false
} }
@ -134,11 +132,7 @@ func (n *NodeDestroyResourceInstance) EvalTree() EvalNode {
addr := n.ResourceInstanceAddr() addr := n.ResourceInstanceAddr()
// Get our state // Get our state
rs := n.ResourceState is := n.instanceState
var is *states.ResourceInstance
if rs != nil {
is = rs.Instance(n.Addr.Resource.Key)
}
if is == nil { if is == nil {
log.Printf("[WARN] NodeDestroyResourceInstance for %s with no state", addr) log.Printf("[WARN] NodeDestroyResourceInstance for %s with no state", addr)
} }

View File

@ -215,7 +215,7 @@ func (n *NodeRefreshableManagedResourceInstance) EvalTree() EvalNode {
// Eval info is different depending on what kind of resource this is // Eval info is different depending on what kind of resource this is
switch addr.Resource.Resource.Mode { switch addr.Resource.Resource.Mode {
case addrs.ManagedResourceMode: case addrs.ManagedResourceMode:
if n.ResourceState == nil { if n.instanceState == nil {
log.Printf("[TRACE] NodeRefreshableManagedResourceInstance: %s has no existing state to refresh", addr) log.Printf("[TRACE] NodeRefreshableManagedResourceInstance: %s has no existing state to refresh", addr)
return n.evalTreeManagedResourceNoState() return n.evalTreeManagedResourceNoState()
} }
@ -253,7 +253,7 @@ func (n *NodeRefreshableManagedResourceInstance) evalTreeManagedResource() EvalN
// This happened during initial development. All known cases were // This happened during initial development. All known cases were
// fixed and tested but as a sanity check let's assert here. // fixed and tested but as a sanity check let's assert here.
if n.ResourceState == nil { if n.instanceState == nil {
err := fmt.Errorf( err := fmt.Errorf(
"No resource state attached for addr: %s\n\n"+ "No resource state attached for addr: %s\n\n"+
"This is a bug. Please report this to Terraform with your configuration\n"+ "This is a bug. Please report this to Terraform with your configuration\n"+