Skip to content

BE-373: Stabilize policy resolution benchmark results with post-seed ANALYZE#8366

Open
indietyp wants to merge 1 commit intomainfrom
bm/be-373-stabilize-policy-resolution-benchmark-results-with-post-seed
Open

BE-373: Stabilize policy resolution benchmark results with post-seed ANALYZE#8366
indietyp wants to merge 1 commit intomainfrom
bm/be-373-stabilize-policy-resolution-benchmark-results-with-post-seed

Conversation

@indietyp
Copy link
Member

@indietyp indietyp commented Feb 6, 2026

🌟 What is the purpose of this PR?

Improve benchmark consistency by analyzing database tables before running policy benchmarks. This ensures the query planner has accurate row counts, reducing variance between benchmark runs by up to 80%.

🔍 What does this change?

  • Adds an explicit ANALYZE on all policy-resolution-related tables (principal, actor, actor_group, actor_role, role, team, team_hierarchy, policy_edition, policy_action) in setup_benchmark_for_seed after seeding completes
  • The root cause: run_benchmark_matrix groups configs by seed level in a std::collections::HashMap, which has non-deterministic iteration order. When "large" is processed first, autoanalyze hasn't run yet and the planner uses stale statistics; when processed last, autoanalyze may have completed. For the low-selectivity case (ViewEntityType), only 1 system policy matches out of ~32,000 — the query plan choice between short-circuiting and materializing the full principal-to-policy join causes the dramatic variance.

🛡 What tests cover this?

  • The change affects benchmark code, not production code, and is designed to make benchmark results more consistent

❓ How to test this?

  1. Run policy benchmarks multiple times
  2. Observe more consistent benchmark results between runs compared to before this change

Pre-Merge Checklist 🚀

🚢 Has this modified a publishable library?

This PR:

  • does not modify any publishable blocks or libraries, or modifications do not need publishing

📜 Does this require a change to the docs?

The changes in this PR:

  • are internal and do not require a docs change

🕸️ Does this require a change to the Turbo Graph?

The changes in this PR:

  • do not affect the execution graph

@vercel
Copy link

vercel bot commented Feb 6, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
hash Ready Ready Preview, Comment Feb 6, 2026 9:15am
3 Skipped Deployments
Project Deployment Actions Updated (UTC)
hashdotdesign Ignored Ignored Preview Feb 6, 2026 9:15am
hashdotdesign-tokens Ignored Ignored Preview Feb 6, 2026 9:15am
petrinaut Skipped Skipped Feb 6, 2026 9:15am

@cursor
Copy link

cursor bot commented Feb 6, 2026

PR Summary

Low Risk
Benchmark-only change that runs an extra ANALYZE query; no production logic or data paths are modified.

Overview
Stabilizes policy resolution benchmark runs by explicitly running ANALYZE on key policy/principal tables after seeding in setup_benchmark_for_seed.

This ensures Postgres has up-to-date statistics before benchmarks execute, reducing run-to-run variance caused by non-deterministic seed-group ordering and auto-analyze timing.

Written by Cursor Bugbot for commit e74613d. This will update automatically on new commits. Configure here.

@vercel vercel bot temporarily deployed to Preview – petrinaut February 6, 2026 09:08 Inactive
@github-actions github-actions bot added the area/tests New or updated tests label Feb 6, 2026
Copy link
Member Author

indietyp commented Feb 6, 2026

This stack of pull requests is managed by Graphite. Learn more about stacking.

@augmentcode
Copy link

augmentcode bot commented Feb 6, 2026

🤖 Augment PR Summary

Summary: Stabilizes policy-resolution benchmark results by ensuring Postgres has up-to-date planner statistics after seeding.

Changes: Adds an explicit ANALYZE over the principal/actor/team/policy tables in setup_benchmark_for_seed before running the benchmark matrix.

🤖 Was this summary useful? React with 👍 or 👎

Copy link

@augmentcode augmentcode bot left a comment

Choose a reason for hiding this comment

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

Review completed. No suggestions at this time.

Comment augment review to trigger a new review at any time.

@github-actions
Copy link
Contributor

github-actions bot commented Feb 6, 2026

Benchmark results

@rust/hash-graph-benches – Integrations

policy_resolution_large

Function Value Mean Flame graphs
resolve_policies_for_actor user: empty, selectivity: high, policies: 2002 $$27.2 \mathrm{ms} \pm 145 \mathrm{μs}\left({\color{gray}2.98 \mathrm{\%}}\right) $$ Flame Graph
resolve_policies_for_actor user: empty, selectivity: low, policies: 1 $$3.26 \mathrm{ms} \pm 14.7 \mathrm{μs}\left({\color{gray}-0.029 \mathrm{\%}}\right) $$ Flame Graph
resolve_policies_for_actor user: empty, selectivity: medium, policies: 1001 $$12.2 \mathrm{ms} \pm 89.5 \mathrm{μs}\left({\color{gray}0.878 \mathrm{\%}}\right) $$ Flame Graph
resolve_policies_for_actor user: seeded, selectivity: high, policies: 3314 $$42.4 \mathrm{ms} \pm 279 \mathrm{μs}\left({\color{gray}1.70 \mathrm{\%}}\right) $$ Flame Graph
resolve_policies_for_actor user: seeded, selectivity: low, policies: 1 $$14.2 \mathrm{ms} \pm 91.8 \mathrm{μs}\left({\color{gray}4.97 \mathrm{\%}}\right) $$ Flame Graph
resolve_policies_for_actor user: seeded, selectivity: medium, policies: 1526 $$24.1 \mathrm{ms} \pm 131 \mathrm{μs}\left({\color{gray}3.72 \mathrm{\%}}\right) $$ Flame Graph
resolve_policies_for_actor user: system, selectivity: high, policies: 2078 $$28.1 \mathrm{ms} \pm 150 \mathrm{μs}\left({\color{lightgreen}-33.439 \mathrm{\%}}\right) $$ Flame Graph
resolve_policies_for_actor user: system, selectivity: low, policies: 1 $$3.55 \mathrm{ms} \pm 18.1 \mathrm{μs}\left({\color{lightgreen}-82.275 \mathrm{\%}}\right) $$ Flame Graph
resolve_policies_for_actor user: system, selectivity: medium, policies: 1033 $$13.3 \mathrm{ms} \pm 76.4 \mathrm{μs}\left({\color{lightgreen}-50.725 \mathrm{\%}}\right) $$ Flame Graph

policy_resolution_medium

Function Value Mean Flame graphs
resolve_policies_for_actor user: empty, selectivity: high, policies: 102 $$3.61 \mathrm{ms} \pm 14.3 \mathrm{μs}\left({\color{gray}1.20 \mathrm{\%}}\right) $$ Flame Graph
resolve_policies_for_actor user: empty, selectivity: low, policies: 1 $$2.82 \mathrm{ms} \pm 15.9 \mathrm{μs}\left({\color{gray}-0.887 \mathrm{\%}}\right) $$ Flame Graph
resolve_policies_for_actor user: empty, selectivity: medium, policies: 51 $$3.17 \mathrm{ms} \pm 14.9 \mathrm{μs}\left({\color{gray}0.522 \mathrm{\%}}\right) $$ Flame Graph
resolve_policies_for_actor user: seeded, selectivity: high, policies: 269 $$4.94 \mathrm{ms} \pm 26.0 \mathrm{μs}\left({\color{gray}0.445 \mathrm{\%}}\right) $$ Flame Graph
resolve_policies_for_actor user: seeded, selectivity: low, policies: 1 $$3.35 \mathrm{ms} \pm 16.4 \mathrm{μs}\left({\color{gray}-0.015 \mathrm{\%}}\right) $$ Flame Graph
resolve_policies_for_actor user: seeded, selectivity: medium, policies: 107 $$3.92 \mathrm{ms} \pm 24.8 \mathrm{μs}\left({\color{gray}1.02 \mathrm{\%}}\right) $$ Flame Graph
resolve_policies_for_actor user: system, selectivity: high, policies: 133 $$4.23 \mathrm{ms} \pm 18.5 \mathrm{μs}\left({\color{gray}1.09 \mathrm{\%}}\right) $$ Flame Graph
resolve_policies_for_actor user: system, selectivity: low, policies: 1 $$3.22 \mathrm{ms} \pm 13.6 \mathrm{μs}\left({\color{gray}-1.733 \mathrm{\%}}\right) $$ Flame Graph
resolve_policies_for_actor user: system, selectivity: medium, policies: 63 $$3.83 \mathrm{ms} \pm 14.3 \mathrm{μs}\left({\color{gray}-0.153 \mathrm{\%}}\right) $$ Flame Graph

policy_resolution_none

Function Value Mean Flame graphs
resolve_policies_for_actor user: empty, selectivity: high, policies: 2 $$2.58 \mathrm{ms} \pm 11.6 \mathrm{μs}\left({\color{red}8.13 \mathrm{\%}}\right) $$ Flame Graph
resolve_policies_for_actor user: empty, selectivity: low, policies: 1 $$2.54 \mathrm{ms} \pm 10.9 \mathrm{μs}\left({\color{red}9.08 \mathrm{\%}}\right) $$ Flame Graph
resolve_policies_for_actor user: empty, selectivity: medium, policies: 1 $$2.66 \mathrm{ms} \pm 10.7 \mathrm{μs}\left({\color{red}9.62 \mathrm{\%}}\right) $$ Flame Graph
resolve_policies_for_actor user: system, selectivity: high, policies: 8 $$2.87 \mathrm{ms} \pm 12.5 \mathrm{μs}\left({\color{red}9.63 \mathrm{\%}}\right) $$ Flame Graph
resolve_policies_for_actor user: system, selectivity: low, policies: 1 $$2.73 \mathrm{ms} \pm 17.6 \mathrm{μs}\left({\color{red}8.70 \mathrm{\%}}\right) $$ Flame Graph
resolve_policies_for_actor user: system, selectivity: medium, policies: 3 $$2.98 \mathrm{ms} \pm 17.6 \mathrm{μs}\left({\color{red}10.6 \mathrm{\%}}\right) $$ Flame Graph

policy_resolution_small

Function Value Mean Flame graphs
resolve_policies_for_actor user: empty, selectivity: high, policies: 52 $$2.92 \mathrm{ms} \pm 13.2 \mathrm{μs}\left({\color{gray}4.97 \mathrm{\%}}\right) $$ Flame Graph
resolve_policies_for_actor user: empty, selectivity: low, policies: 1 $$2.63 \mathrm{ms} \pm 13.1 \mathrm{μs}\left({\color{red}8.43 \mathrm{\%}}\right) $$ Flame Graph
resolve_policies_for_actor user: empty, selectivity: medium, policies: 25 $$2.77 \mathrm{ms} \pm 15.9 \mathrm{μs}\left({\color{red}6.82 \mathrm{\%}}\right) $$ Flame Graph
resolve_policies_for_actor user: seeded, selectivity: high, policies: 94 $$3.32 \mathrm{ms} \pm 16.0 \mathrm{μs}\left({\color{red}6.56 \mathrm{\%}}\right) $$ Flame Graph
resolve_policies_for_actor user: seeded, selectivity: low, policies: 1 $$2.85 \mathrm{ms} \pm 10.3 \mathrm{μs}\left({\color{red}6.99 \mathrm{\%}}\right) $$ Flame Graph
resolve_policies_for_actor user: seeded, selectivity: medium, policies: 26 $$3.11 \mathrm{ms} \pm 12.5 \mathrm{μs}\left({\color{red}7.88 \mathrm{\%}}\right) $$ Flame Graph
resolve_policies_for_actor user: system, selectivity: high, policies: 66 $$3.27 \mathrm{ms} \pm 17.7 \mathrm{μs}\left({\color{red}9.07 \mathrm{\%}}\right) $$ Flame Graph
resolve_policies_for_actor user: system, selectivity: low, policies: 1 $$2.84 \mathrm{ms} \pm 15.0 \mathrm{μs}\left({\color{red}7.71 \mathrm{\%}}\right) $$ Flame Graph
resolve_policies_for_actor user: system, selectivity: medium, policies: 29 $$3.08 \mathrm{ms} \pm 12.2 \mathrm{μs}\left({\color{red}7.70 \mathrm{\%}}\right) $$ Flame Graph

read_scaling_complete

Function Value Mean Flame graphs
entity_by_id;one_depth 1 entities $$38.3 \mathrm{ms} \pm 139 \mathrm{μs}\left({\color{gray}-1.032 \mathrm{\%}}\right) $$ Flame Graph
entity_by_id;one_depth 10 entities $$75.4 \mathrm{ms} \pm 255 \mathrm{μs}\left({\color{gray}0.556 \mathrm{\%}}\right) $$ Flame Graph
entity_by_id;one_depth 25 entities $$42.9 \mathrm{ms} \pm 151 \mathrm{μs}\left({\color{gray}1.96 \mathrm{\%}}\right) $$ Flame Graph
entity_by_id;one_depth 5 entities $$45.6 \mathrm{ms} \pm 252 \mathrm{μs}\left({\color{gray}1.31 \mathrm{\%}}\right) $$ Flame Graph
entity_by_id;one_depth 50 entities $$52.1 \mathrm{ms} \pm 242 \mathrm{μs}\left({\color{gray}-1.765 \mathrm{\%}}\right) $$ Flame Graph
entity_by_id;two_depth 1 entities $$40.1 \mathrm{ms} \pm 145 \mathrm{μs}\left({\color{gray}1.23 \mathrm{\%}}\right) $$ Flame Graph
entity_by_id;two_depth 10 entities $$416 \mathrm{ms} \pm 786 \mathrm{μs}\left({\color{gray}0.555 \mathrm{\%}}\right) $$ Flame Graph
entity_by_id;two_depth 25 entities $$93.3 \mathrm{ms} \pm 383 \mathrm{μs}\left({\color{gray}1.39 \mathrm{\%}}\right) $$ Flame Graph
entity_by_id;two_depth 5 entities $$83.2 \mathrm{ms} \pm 275 \mathrm{μs}\left({\color{gray}0.906 \mathrm{\%}}\right) $$ Flame Graph
entity_by_id;two_depth 50 entities $$275 \mathrm{ms} \pm 657 \mathrm{μs}\left({\color{gray}0.085 \mathrm{\%}}\right) $$ Flame Graph
entity_by_id;zero_depth 1 entities $$14.5 \mathrm{ms} \pm 57.6 \mathrm{μs}\left({\color{gray}1.55 \mathrm{\%}}\right) $$ Flame Graph
entity_by_id;zero_depth 10 entities $$14.8 \mathrm{ms} \pm 63.3 \mathrm{μs}\left({\color{gray}1.74 \mathrm{\%}}\right) $$ Flame Graph
entity_by_id;zero_depth 25 entities $$15.0 \mathrm{ms} \pm 66.8 \mathrm{μs}\left({\color{gray}0.382 \mathrm{\%}}\right) $$ Flame Graph
entity_by_id;zero_depth 5 entities $$14.4 \mathrm{ms} \pm 61.9 \mathrm{μs}\left({\color{gray}-1.520 \mathrm{\%}}\right) $$ Flame Graph
entity_by_id;zero_depth 50 entities $$17.9 \mathrm{ms} \pm 93.5 \mathrm{μs}\left({\color{gray}2.96 \mathrm{\%}}\right) $$ Flame Graph

read_scaling_linkless

Function Value Mean Flame graphs
entity_by_id 1 entities $$14.9 \mathrm{ms} \pm 70.8 \mathrm{μs}\left({\color{gray}0.816 \mathrm{\%}}\right) $$ Flame Graph
entity_by_id 10 entities $$14.5 \mathrm{ms} \pm 50.2 \mathrm{μs}\left({\color{gray}0.265 \mathrm{\%}}\right) $$ Flame Graph
entity_by_id 100 entities $$14.5 \mathrm{ms} \pm 72.1 \mathrm{μs}\left({\color{gray}0.336 \mathrm{\%}}\right) $$ Flame Graph
entity_by_id 1000 entities $$15.1 \mathrm{ms} \pm 71.5 \mathrm{μs}\left({\color{gray}0.618 \mathrm{\%}}\right) $$ Flame Graph
entity_by_id 10000 entities $$21.9 \mathrm{ms} \pm 135 \mathrm{μs}\left({\color{gray}-0.026 \mathrm{\%}}\right) $$ Flame Graph

representative_read_entity

Function Value Mean Flame graphs
entity_by_id entity type ID: https://blockprotocol.org/@alice/types/entity-type/block/v/1 $$28.9 \mathrm{ms} \pm 253 \mathrm{μs}\left({\color{gray}-2.329 \mathrm{\%}}\right) $$ Flame Graph
entity_by_id entity type ID: https://blockprotocol.org/@alice/types/entity-type/book/v/1 $$29.6 \mathrm{ms} \pm 254 \mathrm{μs}\left({\color{gray}0.755 \mathrm{\%}}\right) $$ Flame Graph
entity_by_id entity type ID: https://blockprotocol.org/@alice/types/entity-type/building/v/1 $$29.2 \mathrm{ms} \pm 297 \mathrm{μs}\left({\color{gray}-2.677 \mathrm{\%}}\right) $$ Flame Graph
entity_by_id entity type ID: https://blockprotocol.org/@alice/types/entity-type/organization/v/1 $$28.9 \mathrm{ms} \pm 295 \mathrm{μs}\left({\color{gray}-1.101 \mathrm{\%}}\right) $$ Flame Graph
entity_by_id entity type ID: https://blockprotocol.org/@alice/types/entity-type/page/v/2 $$29.5 \mathrm{ms} \pm 278 \mathrm{μs}\left({\color{gray}1.08 \mathrm{\%}}\right) $$ Flame Graph
entity_by_id entity type ID: https://blockprotocol.org/@alice/types/entity-type/person/v/1 $$29.3 \mathrm{ms} \pm 260 \mathrm{μs}\left({\color{gray}2.19 \mathrm{\%}}\right) $$ Flame Graph
entity_by_id entity type ID: https://blockprotocol.org/@alice/types/entity-type/playlist/v/1 $$29.5 \mathrm{ms} \pm 293 \mathrm{μs}\left({\color{gray}0.548 \mathrm{\%}}\right) $$ Flame Graph
entity_by_id entity type ID: https://blockprotocol.org/@alice/types/entity-type/song/v/1 $$29.8 \mathrm{ms} \pm 270 \mathrm{μs}\left({\color{gray}2.20 \mathrm{\%}}\right) $$ Flame Graph
entity_by_id entity type ID: https://blockprotocol.org/@alice/types/entity-type/uk-address/v/1 $$29.9 \mathrm{ms} \pm 279 \mathrm{μs}\left({\color{gray}3.93 \mathrm{\%}}\right) $$ Flame Graph

representative_read_entity_type

Function Value Mean Flame graphs
get_entity_type_by_id Account ID: bf5a9ef5-dc3b-43cf-a291-6210c0321eba $$7.99 \mathrm{ms} \pm 30.5 \mathrm{μs}\left({\color{gray}-0.434 \mathrm{\%}}\right) $$ Flame Graph

representative_read_multiple_entities

Function Value Mean Flame graphs
entity_by_property traversal_paths=0 0 $$44.9 \mathrm{ms} \pm 307 \mathrm{μs}\left({\color{gray}1.11 \mathrm{\%}}\right) $$
entity_by_property traversal_paths=255 1,resolve_depths=inherit:1;values:255;properties:255;links:127;link_dests:126;type:true $$93.6 \mathrm{ms} \pm 460 \mathrm{μs}\left({\color{gray}1.77 \mathrm{\%}}\right) $$
entity_by_property traversal_paths=2 1,resolve_depths=inherit:0;values:0;properties:0;links:0;link_dests:0;type:false $$51.4 \mathrm{ms} \pm 361 \mathrm{μs}\left({\color{gray}2.26 \mathrm{\%}}\right) $$
entity_by_property traversal_paths=2 1,resolve_depths=inherit:0;values:0;properties:0;links:1;link_dests:0;type:true $$59.7 \mathrm{ms} \pm 414 \mathrm{μs}\left({\color{gray}3.11 \mathrm{\%}}\right) $$
entity_by_property traversal_paths=2 1,resolve_depths=inherit:0;values:0;properties:2;links:1;link_dests:0;type:true $$67.4 \mathrm{ms} \pm 324 \mathrm{μs}\left({\color{gray}1.46 \mathrm{\%}}\right) $$
entity_by_property traversal_paths=2 1,resolve_depths=inherit:0;values:2;properties:2;links:1;link_dests:0;type:true $$74.3 \mathrm{ms} \pm 425 \mathrm{μs}\left({\color{gray}1.17 \mathrm{\%}}\right) $$
link_by_source_by_property traversal_paths=0 0 $$49.5 \mathrm{ms} \pm 316 \mathrm{μs}\left({\color{gray}3.85 \mathrm{\%}}\right) $$
link_by_source_by_property traversal_paths=255 1,resolve_depths=inherit:1;values:255;properties:255;links:127;link_dests:126;type:true $$76.1 \mathrm{ms} \pm 383 \mathrm{μs}\left({\color{gray}2.03 \mathrm{\%}}\right) $$
link_by_source_by_property traversal_paths=2 1,resolve_depths=inherit:0;values:0;properties:0;links:0;link_dests:0;type:false $$57.8 \mathrm{ms} \pm 302 \mathrm{μs}\left({\color{red}6.51 \mathrm{\%}}\right) $$
link_by_source_by_property traversal_paths=2 1,resolve_depths=inherit:0;values:0;properties:0;links:1;link_dests:0;type:true $$63.7 \mathrm{ms} \pm 355 \mathrm{μs}\left({\color{gray}3.11 \mathrm{\%}}\right) $$
link_by_source_by_property traversal_paths=2 1,resolve_depths=inherit:0;values:0;properties:2;links:1;link_dests:0;type:true $$65.8 \mathrm{ms} \pm 461 \mathrm{μs}\left({\color{gray}2.25 \mathrm{\%}}\right) $$
link_by_source_by_property traversal_paths=2 1,resolve_depths=inherit:0;values:2;properties:2;links:1;link_dests:0;type:true $$65.6 \mathrm{ms} \pm 307 \mathrm{μs}\left({\color{gray}2.10 \mathrm{\%}}\right) $$

scenarios

Function Value Mean Flame graphs
full_test query-limited $$128 \mathrm{ms} \pm 459 \mathrm{μs}\left({\color{lightgreen}-5.338 \mathrm{\%}}\right) $$ Flame Graph
full_test query-unlimited $$126 \mathrm{ms} \pm 548 \mathrm{μs}\left({\color{lightgreen}-6.803 \mathrm{\%}}\right) $$ Flame Graph
linked_queries query-limited $$78.6 \mathrm{ms} \pm 3.13 \mathrm{ms}\left({\color{lightgreen}-19.808 \mathrm{\%}}\right) $$ Flame Graph
linked_queries query-unlimited $$572 \mathrm{ms} \pm 950 \mathrm{μs}\left({\color{gray}-2.139 \mathrm{\%}}\right) $$ Flame Graph

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

Labels

area/tests New or updated tests

Development

Successfully merging this pull request may close these issues.

1 participant