aws-cf-reverse-proxy: stable hash-keyed iteration to eliminate ordered_cache_behavior shuffle diffs#67
Merged
Conversation
…d_cache_behavior shuffle diffs CloudFront's ordered_cache_behavior and origin blocks are TypeList in the provider schema; terraform iterates the for_each map in sorted-key order and the resulting list is diffed by index. When local.origin_configs was keyed by raw path, inserting a new path that sorted earlier than an existing key (e.g. "/.well-known/agent-card.json" sorts before "/.well-known/agent.json" because "-" < ".") shifted every later entry down by one index and produced cosmetic ~ diffs across every behavior. Switch local.origin_configs and local.grpc_origin_configs to be keyed by "<sha256(path)[:8]>-<path>" — a stable hash-prefix that spreads entries over the keyspace so insertions don't predictably shift siblings. The path is appended after the hash purely so generated keys remain human-readable in plan output. path_pattern is now carried in the value so the dynamic blocks can stop reading it from .key. Public API is unchanged: var.origin_routes and var.grpc_routes keep their existing schemas. The output "origin_configs" is re-keyed by path_pattern so its observable shape stays the same for downstream consumers — only internal iteration order changes. The "__grpc__" prefix from #64 was no longer needed because gRPC keys now carry an unambiguous "grpc-" hash-prefixed namespace; the merge into all_origin_configs simplifies to a plain merge(). First apply after consumers bump to this version produces a one-time index shuffle as existing keys migrate from raw-path to hash-prefixed layout. End-state is byte-identical, but reviewers will see what looks like a major rewrite of every existing cache behavior in their first plan. Subsequent additions produce clean single-add diffs. Refs luthersystems/ui-infrastructure#240
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
CloudFront's
ordered_cache_behaviorandoriginblocks are TypeList in the AWS provider schema. Terraform iterates thefor_eachmap in sorted-key order, and the resulting list is diffed by index. Whenlocal.origin_configswas keyed by the rawpath_pattern, inserting a new path that sorts earlier than an existing key shifted every later entry down by one and produced cosmetic~diffs across every behavior — even though the end-state was byte-identical.This switches
local.origin_configsandlocal.grpc_origin_configsto be keyed by a stable hash-prefix of the path:${substr(sha256(path), 0, 8)}-${path}. The path is appended after the hash purely so generated keys remain human-readable in plan output.path_patternis now carried as a value field so the dynamic blocks can read it from.value.path_patterninstead of.key.Public API is unchanged.
var.origin_routesandvar.grpc_routeskeep their existingmap(string)schemas. The outputorigin_configsis re-keyed bypath_patternso the externally-observable shape is preserved for downstream consumers.The diff-shuffling problem (before)
Consumer
origin_routes:{ "/v1/*" = "https://app.platform-test..." "/insideout-a2a/*" = "https://app.platform-test..." "/.well-known/agent.json" = "https://app.platform-test..." "/.well-known/agent-card.json" = "https://app.platform-test..." # added }/.well-known/agent-card.jsonsorts BEFORE/.well-known/agent.jsonbecause-(0x2d) <.(0x2e). With raw-path keys, terraform iterates in sorted key order and the new entry lands at index 1 (right after/*), pushing every later entry down by 1 — producing~modifications for every existing behavior. Real-world example: luthersystems/ui-infrastructure#240.After (this PR)
Iteration order is now by hash prefix:
The new entry lands at its hash position; existing entries keep theirs.
Verified plan diff (simulated)
I simulated the post-refactor locals against
terraform_dataresources whoseinputis the iterated list-of-objects (the same internal representation adynamicTypeList block produces inside a resource). After applying baselineorigin_routesand re-planning with one new entry (/.health):Single
+block for the new entry; siblings render as unchanged. Same shape for the correspondingoriginblock inall_origin_configs.One-time first-apply shuffle on bump
Heads up to anyone reviewing the first plan after bumping to this version: the for_each map keys for every existing entry change from raw-path (
/v1/*) to hash-prefixed (72042627-/v1/*). Terraform will render this as what looks like a wholesale rewrite of everyordered_cache_behaviorandoriginblock in your distribution.The end-state is byte-identical. No CloudFront resource is destroyed or recreated —
aws_cloudfront_distributionis a single resource; the nested blocks are attributes that get updated in-place. After this one cosmetic plan, every subsequent route addition produces a clean single-add diff (verified above).If you want extra confidence, you can apply, then immediately re-plan — the second plan should be empty.
Test plan
terraform fmt -check -recursive aws-cf-reverse-proxy/cleanterraform validatefromaws-cf-reverse-proxy/tests/test1/passes+(no~shuffles); see verified plan diff aboveorigin_configsshape unchanged from a consumer's perspective (re-keyed by path_pattern, which equals the old raw-path key)Review notes
sha256()is deterministic across terraform versions and platforms.if v.path_pattern != "/*"is semantically equivalent to the oldif k != "/*"(path_pattern carries what the old raw key did)./*remains inall_origin_configs(used by theorigindynamic block) and is excluded only fromordered_cache_behavior, matching prior behavior.Refs
__grpc__namespace prefix from aws-cf-reverse-proxy: add per-behavior gRPC support via grpc_routes #64 is replaced by agrpc-hash-prefixed namespace; themerge()intoall_origin_configssimplifies to a plain merge.Release
Additive change with no API break — minor bump. Tag as
v55.19.0post-merge.