Skip to content

compile,planner: improve determinism of plan/wasm bundle builds#8732

Open
philipaconrad wants to merge 1 commit into
open-policy-agent:mainfrom
philipaconrad:philip/planner-determinism-fixes
Open

compile,planner: improve determinism of plan/wasm bundle builds#8732
philipaconrad wants to merge 1 commit into
open-policy-agent:mainfrom
philipaconrad:philip/planner-determinism-fixes

Conversation

@philipaconrad
Copy link
Copy Markdown
Member

What code changed, and why?

This PR fixes an issue where plan and wasm bundle build targets could produce different output bytes across separate opa build invocations for the exact same inputs. There were two underlying causes, both from Golang random map iteration order leaking through to the order-sensitive planner.

Causes:

  • compilePlan (v1/compile) and planQuery (v1/rego) iterated over the compiler's module map without sorting keys first. This caused the planner to have iteration-dependent variations in its output. This was fixed by sorting the module names before use.

  • planRules (internal/planner) sorted rules by length of the rule name ref, which is not a unique value. Because the sorting of the rules was using an unstable sorting algorithm (the default in Golang), and the rule names were coming from iterating over a map type in the rule trie, this had edge cases where non-deterministic output ordering could creep in. This was fixed by adding a ref Compare call as a tie-breaker to get a stable sorting order over rule names, regardless of iteration order in the rule trie.

This commit also adds regression tests that assert plan output is independent of module and rule ordering. The two fixes are needed together because both sets of issues hit the planner from different angles, and are mostly independent of each other.

How to test?

  • New tests added under internal/planner and v1/compile.

@philipaconrad philipaconrad self-assigned this Jun 3, 2026
@philipaconrad philipaconrad force-pushed the philip/planner-determinism-fixes branch from 030a404 to 9a96da6 Compare June 3, 2026 21:04

// We sort rules by ref length, to ensure that when merged, we can detect conflicts when one
// rule attempts to override values (deep and shallow) defined by another rule.
sort.Slice(rules, func(i, j int) bool {
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Per the golang docs, it's possible to get several different, valid sorting orders, depending on how the inputs arrive.

@philipaconrad philipaconrad force-pushed the philip/planner-determinism-fixes branch 2 times, most recently from 088abae to d0f7a02 Compare June 3, 2026 21:50
@philipaconrad philipaconrad removed their assignment Jun 3, 2026
This commit fixes an issue where `plan` and `wasm` bundle build
targets could produce different output bytes across separate
invocations of `opa build` for the same inputs.

There were two underlying causes, both from Golang random map
iteration order leaking through to the order-sensitive planner.

Causes:
- compilePlan (v1/compile) and planQuery (v1/rego) iterated over the
  compiler's module map without sorting keys first. This caused the
  planner to have iteration-dependent variations in output. This was
  fixed by sorting the module names before use.

- planRules (internal/planner) sorted rules by length of the rule
  name ref, which is not a unique value. Because the sorting of the
  rules was using an unstable sorting algorithm, and the rule names
  were coming from iterating over a `map` type in the rule trie, this
  had edge cases where non-deterministic output ordering could creep
  in. This was fixed by adding a ref `Compare` call as a tie-breaker
  to get a stable sorting order, regardless of iteration order in the
  rule trie.

This commit also adds regression tests that assert plan output is
independent of module and rule ordering. The two fixes are needed
together because both sets of issues hit the planner from different
angles, and are mostly independent of each other.

Signed-off-by: Philip Conrad <philip_conrad@apple.com>
@philipaconrad philipaconrad force-pushed the philip/planner-determinism-fixes branch from d0f7a02 to 1a4dcb5 Compare June 4, 2026 15:22
@philipaconrad
Copy link
Copy Markdown
Member Author

ℹ️ Made a small refactor to use util.KeysSorted(), instead of manually creating the sorted keys lists. I had copy/pasted the boilerplate around yesterday, and remembered we had something more concise available that does the exact same job.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant