diff --git a/.changes/v1.13/ENHANCEMENTS-20250522-093102.yaml b/.changes/v1.13/ENHANCEMENTS-20250522-093102.yaml new file mode 100644 index 000000000000..5363103a4311 --- /dev/null +++ b/.changes/v1.13/ENHANCEMENTS-20250522-093102.yaml @@ -0,0 +1,5 @@ +kind: ENHANCEMENTS +body: Performance fix for evaluating high cardinality resources +time: 2025-05-22T09:31:02.761383-04:00 +custom: + Issue: "26355" diff --git a/internal/terraform/evaluate.go b/internal/terraform/evaluate.go index 095830db79ff..e3151ea654d1 100644 --- a/internal/terraform/evaluate.go +++ b/internal/terraform/evaluate.go @@ -658,6 +658,16 @@ func (d *evaluationStateData) GetResource(addr addrs.Resource, rng tfdiags.Sourc instances[key] = value } + // Proactively read out all the resource changes before iteration. Not only + // does GetResourceInstanceChange have to iterate over all instances + // internally causing an n^2 lookup, but Changes is also a major point of + // lock contention. + resChanges := d.Evaluator.Changes.GetChangesForConfigResource(addr.InModule(moduleConfig.Path)) + instChanges := addrs.MakeMap[addrs.AbsResourceInstance, *plans.ResourceInstanceChange]() + for _, ch := range resChanges { + instChanges.Put(ch.Addr, ch) + } + // Decode all instances in the current state pendingDestroy := d.Operation == walkDestroy for key, is := range rs.Instances { @@ -675,7 +685,7 @@ func (d *evaluationStateData) GetResource(addr addrs.Resource, rng tfdiags.Sourc } instAddr := addr.Instance(key).Absolute(d.ModulePath) - change := d.Evaluator.Changes.GetResourceInstanceChange(instAddr, addrs.NotDeposed) + change := instChanges.Get(instAddr) if change != nil { // Don't take any resources that are yet to be deleted into account. // If the referenced resource is CreateBeforeDestroy, then orphaned