Skip to content

ValuesSDKEquivalent has very poor performance comparing large numbers of deeply nested objects with O(n^2) complexity #1497

@drewtul

Description

@drewtul

SDK version

HEAD
...

Relevant provider source code

		{
			cty.SetVal([]cty.Value{
				cty.ObjectVal(map[string]cty.Value{
					"a": cty.StringVal("1"),
					"b": cty.StringVal(""),
				}),
				cty.ObjectVal(map[string]cty.Value{
					"a": cty.StringVal("1"),
					"b": cty.NullVal(cty.String),
				}),
				cty.ObjectVal(map[string]cty.Value{
					"a": cty.NullVal(cty.String),
					"b": cty.StringVal(""),
				}),
			}),
			cty.SetVal([]cty.Value{
				cty.ObjectVal(map[string]cty.Value{
					"a": cty.StringVal("1"),
					"b": cty.StringVal(""),
				}),
				cty.ObjectVal(map[string]cty.Value{
					"a": cty.StringVal(""),
					"b": cty.NullVal(cty.String),
				}),
				cty.ObjectVal(map[string]cty.Value{
					"a": cty.NullVal(cty.String),
					"b": cty.NullVal(cty.String),
				}),
			}),
			true,
		},
...

Terraform Configuration Files

resource "aws_wafv2_web_acl" "example" {
  name        = "example"
  description = "Example WAF ACL"
  scope       = "CLOUDFRONT"

  lifecycle {
    ignore_changes = [
      rule,
      custom_response_body,
      token_domains
    ]
  }

  default_action {
    allow {}
  }

  visibility_config {
    cloudwatch_metrics_enabled = false
    metric_name                = "All"
    sampled_requests_enabled   = false
  }
}

Expected Behavior

Users attempting to apply a change to a aws_wafv2_web_acl with a very large number (>500) rules with ignore_changes on the rules should be able to apply quickly.

I'm looking to optimise ValuesSDKEquivalent trying to understand if the above test case should pass or not, it currently passes, but logically the sets are not equivalent.

Actual Behavior

The apply may take a very long time or never succeed or take a very long time, most of it spent in ValuesSDKEquivalent comparing all the rules.

Steps to Reproduce

  1. terraform init
  2. terraform apply
  3. Modify the resource outside of terraform using the AWS CLI or SDK to add a large number of rules (>500)
  4. terraform plan - Notice length of plan time for no change
  5. Modify description of aws_wafv2_web_acl
  6. terraform plan - Notice length of plan time for single attribute change
  7. terraform apply - Notice length of time before apply ever calls the AWS API to update the resource.

During both plan and apply a large amount of CPU and memory is consumed as well as taking an excessive amount of time to complete timing out in multi-hour CICD runs.

I would propose changing the loop in valuesSDKEquivalentSets to:

	for ai, av := range as {
		for bi, bv := range bs {
			if beqs[bi] {
				// continue on already matched items in `bs`
				// this allows this `av` to match against later values in `bs` where an earlier `as` valus matched this `bv`
				// this is a case where both sets contain two values each that are ValuesSDKEquivalent to each other
				continue
			}
			if ValuesSDKEquivalent(av, bv) {
				aeqs[ai] = true
				beqs[bi] = true
				// break on first match in `bs` against `av`
				// this leaves any further matches in `bs` for later values in `as`
				break
			}
		}

This changes it such that

  1. items in as are compared to bs till finding their first match.
  2. bs that are already matched are skipped for comparison allowing later as of 'equivalent' value are matched against later values in bs

This reduces the number of comparisons required significantly, but fails the above test case that is not currently in the unit tests.

References

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions