Skip to content

feat: add query plan hash field for expressions#2738

Merged
SkArchon merged 6 commits intomainfrom
milinda/eng-9334-add-more-hash-values-to-differentiate-between-skip-and
Apr 6, 2026
Merged

feat: add query plan hash field for expressions#2738
SkArchon merged 6 commits intomainfrom
milinda/eng-9334-add-more-hash-values-to-differentiate-between-skip-and

Conversation

@SkArchon
Copy link
Copy Markdown
Contributor

@SkArchon SkArchon commented Apr 2, 2026

Right now we have sha256Hash and hash, the first is a hash on the raw query body, the second is a hash (number) on the normalized operation (BEFORE variable remapping).

Right now hash can be "good enough" but does not consider variable remapping. It is "good enough" because it only does not consider variable remapping, so unless the variable names do not change the hash would not change.

We would like to expose the hash of the fully normalized operation, the operation hash that is used for query plan caches. This is useful as it is a 1 to 1 mapping for the query plan cache key. Users can use this to cross check cache hit ratios, cache warmer performance, assign an id to normalized queries that are cached. We introduce the "QueryPlanHash" for this.

Summary by CodeRabbit

  • New Features

    • Access logs and expressions now include normalized operation hash and variables hash for finer-grained GraphQL operation tracking.
  • Telemetry

    • Telemetry now attributes the normalized operation hash for better correlation and analysis.
  • Tests

    • Expanded test coverage validating hash behavior, normalization, and access-log expression mappings.

Checklist

Open Source AI Manifesto

This project follows the principles of the Open Source AI Manifesto. Please ensure your contribution aligns with its principles.

@SkArchon SkArchon changed the title feat: Add more alternative hashing fields feat: add more alternative hashing fields Apr 2, 2026
@github-actions github-actions Bot added the router label Apr 2, 2026
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 2, 2026

Walkthrough

Adds request.operation.queryPlanHash to the expression context, populates it during GraphQL prehandling and telemetry emission, adds bucket classification for the new property, and extends access-log tests to validate operation hash and query-plan hash behaviors across variable-name and skip/include variations.

Changes

Cohort / File(s) Summary
Access-log tests
router-tests/observability/structured_logging_test.go
Added two parallel subtests under TestAccessLogs validating sha256_hash, operation_hash, query_plan_hash, and plan_cache_hit behavior across skip/include variable variations and across queries that differ only by variable names.
GraphQL prehandler
router/core/graphql_prehandler.go
Populate requestContext.expressionContext.Request.Operation.QueryPlanHash (decimal string of operation.internalHash) during operation normalization and emit telemetry attribute expr.BucketQueryPlanHash.
Expression context struct
router/internal/expr/expr.go
Added QueryPlanHash string to the exported Operation struct with expr:"queryPlanHash" tag.
Bucket visitor & tests
router/internal/expr/request_operation_bucket_visitor.go, router/internal/expr/request_operation_bucket_visitor_test.go
Introduced BucketQueryPlanHash, mapped request.operation.queryPlanHash (dot and bracket access) to that bucket, adjusted bucket priority ordering and tests to include the new bucket.
Documentation
docs-website/router/configuration/template-expressions.mdx
Documented request.operation.queryPlanHash; clarified semantics of request.operation.hash and request.operation.sha256Hash regarding normalization, variable remapping, skip/include influence, and raw-query hashing.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 75.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main change: adding a query plan hash field for expressions, which aligns with the primary objective of exposing the hash used for query plan caches.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions
Copy link
Copy Markdown

github-actions Bot commented Apr 2, 2026

Router-nonroot image scan passed

✅ No security vulnerabilities found in image:

ghcr.io/wundergraph/cosmo/router:sha-d96bae7dfa053cc50dcf259250f0f8f6d91636f2-nonroot

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@router-tests/observability/structured_logging_test.go`:
- Around line 4378-4390: The test is changing both the GraphQL operation name
and variable name so operation_hash may differ for unrelated reasons; keep the
operation name constant to isolate variable-remapping behavior by making both
requests use the same OperationName value and the same operation identifier
inside the Query string while still using different Variables; update the two
xEnv.MakeGraphQLRequestOK calls (GraphQLRequest.OperationName and the "query
EmployeeA"/"query EmployeeB" identifiers in Query) to the same name (e.g.,
"Employee") and leave Variables as {"myId":4} and {"eid":4} respectively.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 3b28bed1-5c0c-4d11-b8a0-02e33bfb51f5

📥 Commits

Reviewing files that changed from the base of the PR and between 3a1b099 and 18e099a.

📒 Files selected for processing (5)
  • router-tests/observability/structured_logging_test.go
  • router/core/graphql_prehandler.go
  • router/internal/expr/expr.go
  • router/internal/expr/request_operation_bucket_visitor.go
  • router/internal/expr/request_operation_bucket_visitor_test.go

Comment thread router-tests/observability/structured_logging_test.go
@codecov
Copy link
Copy Markdown

codecov Bot commented Apr 2, 2026

Codecov Report

❌ Patch coverage is 50.00000% with 2 lines in your changes missing coverage. Please review.
✅ Project coverage is 63.46%. Comparing base (d376585) to head (09817c0).
⚠️ Report is 3 commits behind head on main.

Files with missing lines Patch % Lines
.../internal/expr/request_operation_bucket_visitor.go 0.00% 0 Missing and 2 partials ⚠️
Additional details and impacted files
@@             Coverage Diff             @@
##             main    #2738       +/-   ##
===========================================
+ Coverage   45.74%   63.46%   +17.72%     
===========================================
  Files        1035      251      -784     
  Lines      139075    26767   -112308     
  Branches     8631        0     -8631     
===========================================
- Hits        63613    16987    -46626     
+ Misses      73735     8414    -65321     
+ Partials     1727     1366      -361     
Files with missing lines Coverage Δ
router/core/graphql_prehandler.go 84.69% <100.00%> (+0.04%) ⬆️
router/internal/expr/expr.go 95.74% <ø> (ø)
.../internal/expr/request_operation_bucket_visitor.go 80.00% <0.00%> (-3.02%) ⬇️

... and 790 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@router/internal/expr/request_operation_bucket_visitor_test.go`:
- Around line 155-181: Update the stale inline comment that documents the bucket
priority chain to include the new NormalizedHash field; specifically, wherever
the priority list references hash/variables/hash variants, add NormalizedHash so
it reflects that request.operation.normalizedHash maps to BucketNormalizedHash
and request.operation.variablesHash maps to BucketHash (which shares priority
with hash); ensure the comment mentions both dot and bracket notation handling
consistent with the visitor's property extraction logic that produces
BucketNormalizedHash and BucketHash.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 6f37b328-050c-41a3-ade7-4fb9df98ab02

📥 Commits

Reviewing files that changed from the base of the PR and between 18e099a and 3015d88.

📒 Files selected for processing (3)
  • router/core/graphql_prehandler.go
  • router/internal/expr/request_operation_bucket_visitor.go
  • router/internal/expr/request_operation_bucket_visitor_test.go
🚧 Files skipped from review as they are similar to previous changes (2)
  • router/internal/expr/request_operation_bucket_visitor.go
  • router/core/graphql_prehandler.go

Comment thread router/internal/expr/request_operation_bucket_visitor_test.go Outdated
@SkArchon SkArchon requested review from a team as code owners April 3, 2026 15:38
@SkArchon SkArchon requested a review from JivusAyrus April 3, 2026 15:38
@SkArchon SkArchon changed the title feat: add more alternative hashing fields feat: add query plan hash field for expressions Apr 3, 2026
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
docs-website/router/configuration/template-expressions.mdx (1)

67-69: Tighten hash-field descriptions into shorter reference sentences.

These lines are clear but a bit dense. Splitting them improves scanability and aligns better with repo docs style.

✍️ Suggested wording
-- `request.operation.hash` - Hash of the normalized operation. Computed before variable remapping, so queries with different variable names produce different hashes. Identical queries with different skip/include variable values also produce different hashes because normalization inlines those directives.
-- `request.operation.queryPlanHash` - The Hash used as the query plan cache key. Computed after variable remapping (e.g. `$myId` and `$eid` both become `$0`) and includes skip/include variable values. Two requests share the same `queryPlanHash` when they resolve to the same query plan.
-- `request.operation.sha256Hash` - SHA-256 hash of the original operation query string sent by the client. Identical for all requests that send the same raw query body, regardless of variable values.
+- `request.operation.hash` - Hash of the normalized operation. Computed before variable remapping. Different variable names produce different hashes. Different skip/include values also produce different hashes.
+- `request.operation.queryPlanHash` - Hash used as the query plan cache key. Computed after variable remapping. Includes skip/include effects. Requests that resolve to the same plan share this value.
+- `request.operation.sha256Hash` - SHA-256 hash of the raw operation query string sent by the client. Identical for requests with the same raw query body, regardless of variable values.

As per coding guidelines: "Prefer short, declarative sentences. If a sentence has more than one comma-separated clause, consider splitting it."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs-website/router/configuration/template-expressions.mdx` around lines 67 -
69, The three long descriptions for request.operation.hash,
request.operation.queryPlanHash, and request.operation.sha256Hash should be
rewritten into shorter, declarative reference sentences; update the lines that
define `request.operation.hash` to say it is the normalized-operation hash
computed before variable remapping and that it treats different skip/include
values as different hashes, update `request.operation.queryPlanHash` to state it
is the query-plan cache key computed after variable remapping and includes
skip/include values so identical plans share the same value, and update
`request.operation.sha256Hash` to say it is the SHA‑256 of the raw query string
sent by the client and is identical for identical raw queries regardless of
variables.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@docs-website/router/configuration/template-expressions.mdx`:
- Around line 67-69: The three long descriptions for request.operation.hash,
request.operation.queryPlanHash, and request.operation.sha256Hash should be
rewritten into shorter, declarative reference sentences; update the lines that
define `request.operation.hash` to say it is the normalized-operation hash
computed before variable remapping and that it treats different skip/include
values as different hashes, update `request.operation.queryPlanHash` to state it
is the query-plan cache key computed after variable remapping and includes
skip/include values so identical plans share the same value, and update
`request.operation.sha256Hash` to say it is the SHA‑256 of the raw query string
sent by the client and is identical for identical raw queries regardless of
variables.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: f1e3716c-3e6a-4a00-b989-2f481443e9a5

📥 Commits

Reviewing files that changed from the base of the PR and between 3015d88 and 3eda3cf.

📒 Files selected for processing (6)
  • docs-website/router/configuration/template-expressions.mdx
  • router-tests/observability/structured_logging_test.go
  • router/core/graphql_prehandler.go
  • router/internal/expr/expr.go
  • router/internal/expr/request_operation_bucket_visitor.go
  • router/internal/expr/request_operation_bucket_visitor_test.go
🚧 Files skipped from review as they are similar to previous changes (2)
  • router/core/graphql_prehandler.go
  • router/internal/expr/request_operation_bucket_visitor_test.go

Copy link
Copy Markdown
Contributor

@StarpTech StarpTech left a comment

Choose a reason for hiding this comment

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

LGTM

@SkArchon SkArchon merged commit 65e05e3 into main Apr 6, 2026
39 checks passed
@SkArchon SkArchon deleted the milinda/eng-9334-add-more-hash-values-to-differentiate-between-skip-and branch April 6, 2026 09:44
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants