Skip to content

planner: Skip plan cache regeneration if binding exists#67134

Merged
ti-chi-bot[bot] merged 6 commits into
pingcap:masterfrom
terry1purcell:bindcache
Mar 30, 2026
Merged

planner: Skip plan cache regeneration if binding exists#67134
ti-chi-bot[bot] merged 6 commits into
pingcap:masterfrom
terry1purcell:bindcache

Conversation

@terry1purcell
Copy link
Copy Markdown
Contributor

@terry1purcell terry1purcell commented Mar 19, 2026

What problem does this PR solve?

Issue Number: close #67335

Problem Summary:

What changed and how does it work?

Check List

Tests

  • Unit test
  • Integration test
  • Manual test (add detailed scripts or steps below)
  • No need to test
    • I checked and no code files have been changed.

Side effects

  • Performance regression: Consumes more CPU
  • Performance regression: Consumes more Memory
  • Breaking backward compatibility

Documentation

  • Affects user behaviors
  • Contains syntax changes
  • Contains variable changes
  • Contains experimental features
  • Changes MySQL compatibility

Release note

Please refer to Release Notes Language Style Guide to write a quality release note.

None

Summary by CodeRabbit

  • New Features

    • Added system variable tidb_plan_cache_skip_stats_on_binding (default ON) to control whether the plan cache skips stats-version invalidation when a SQL binding is active, improving plan reuse.
  • Chores

    • Bootstrap version bumped and upgrade logic added to initialize the new variable.
    • Adjusted test sharding counts for relevant test suites.
  • Tests

    • Added unit, integration, and upgrade tests covering plan-cache behavior with bindings and the new variable.

@ti-chi-bot ti-chi-bot Bot added do-not-merge/needs-linked-issue release-note-none Denotes a PR that doesn't merit a release note. labels Mar 19, 2026
@pantheon-ai
Copy link
Copy Markdown

pantheon-ai Bot commented Mar 19, 2026

@terry1purcell I've received your pull request and will start the review. I'll conduct a thorough review covering code quality, potential issues, and implementation details.

⏳ This process typically takes 10-30 minutes depending on the complexity of the changes.

ℹ️ Learn more details on Pantheon AI.

@ti-chi-bot ti-chi-bot Bot added sig/planner SIG: Planner size/M Denotes a PR that changes 30-99 lines, ignoring generated files. labels Mar 19, 2026
@tiprow
Copy link
Copy Markdown

tiprow Bot commented Mar 19, 2026

Hi @terry1purcell. Thanks for your PR.

PRs from untrusted users cannot be marked as trusted with /ok-to-test in this repo meaning untrusted PR authors can never trigger tests themselves. Collaborators can still trigger tests on the PR using /test all.

I understand the commands that are listed here.

Details

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes-sigs/prow repository.

@hawkingrei
Copy link
Copy Markdown
Member

/ok-to-test

@ti-chi-bot ti-chi-bot Bot added the ok-to-test Indicates a PR is ready to be tested. label Mar 19, 2026
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Mar 19, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds sysvar tidb_plan_cache_skip_stats_on_binding and SessionVars field; plan-cache key generation now omits stats-version when a binding is matched and skipping is enabled; bootstraps the new sysvar at version 256; adds unit and integration tests and updates test shard counts and expectations.

Changes

Cohort / File(s) Summary
Plan cache key logic
pkg/planner/core/plan_cache_utils.go
NewPlanCacheKey now includes the stats-version hash only when PlanCacheInvalidationOnFreshStats is enabled AND `(binding == ""
Sysvar & session integration
pkg/sessionctx/vardef/tidb_vars.go, pkg/sessionctx/variable/session.go, pkg/sessionctx/variable/sysvar.go
Add constant TiDBPlanCacheSkipStatsOnBinding (default ON); add SessionVars.PlanCacheSkipStatsOnBinding and register GLOBAL
Bootstrap upgrade
pkg/session/upgrade_def.go
Introduce version256, add upgradeToVer256, bump currentBootstrapVersion; upgradeToVer256 initializes global tidb_plan_cache_skip_stats_on_binding to ON if absent.
Unit tests (plan cache)
pkg/planner/core/casetest/plancache/plan_cache_suite_test.go
Add TestPlanCacheSkipStatsOnBinding covering prepared plan-cache behavior: no binding, binding with skip=ON (stats excluded), binding with skip=OFF (stats included).
Integration tests (SQL)
tests/integrationtest/t/planner/core/plan_cache.test, tests/integrationtest/r/planner/core/plan_cache.result
Add SQL tests that prepare a statement, create/drop a binding, toggle tidb_plan_cache_invalidation_on_fresh_stats and tidb_plan_cache_skip_stats_on_binding, and assert cache/binding behavior across ANALYZE.
Bootstrap tests & bazel adjustments
pkg/session/test/bootstraptest/bootstrap_upgrade_test.go, pkg/session/test/bootstraptest/BUILD.bazel, pkg/planner/core/casetest/plancache/BUILD.bazel
Add TestUpgradeVersion256PlanCacheSkipStatsOnBinding; increase shard_count for bootstraptest 38→39 and plancache_test 44→45; update tests to expect bootstrap version 256.
Misc tests
br/pkg/restore/snap_client/systable_restore_test.go
Update expected session.CurrentBootstrapVersion check from 255 → 256.

Sequence Diagram(s)

sequenceDiagram
    participant Client as Client
    participant Planner as Planner
    participant BindingSvc as "Bindings"
    participant PlanCache as "PlanCache"
    participant StatsSvc as "Stats"
    participant CacheStore as "CacheStore"

    Client->>Planner: PREPARE / EXECUTE statement
    Planner->>BindingSvc: check for matched binding
    BindingSvc-->>Planner: binding or none
    Planner->>PlanCache: NewPlanCacheKey(binding, SessionVars...)
    Note over PlanCache: includes statsVerHash only if\nPlanCacheInvalidationOnFreshStats && (binding=="" || !PlanCacheSkipStatsOnBinding)
    PlanCache->>CacheStore: lookup key
    CacheStore-->>PlanCache: hit / miss
    alt ANALYZE occurs
        StatsSvc->>PlanCache: update stats version
        PlanCache->>CacheStore: invalidate entries depending on key composition
    end
    PlanCache-->>Planner: plan (from cache or rebuilt)
    Planner-->>Client: execute / return result
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Suggested reviewers

  • hawkingrei
  • henrybw

Poem

🐇 I nudged the cache and hid a stat,
When bindings whisper, I tip my hat,
Keys skip the noise and hop along,
Tests hum softly, steady and strong,
Carrots, bindings—code and song.

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 44.44% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Description check ❓ Inconclusive The description includes the required issue number reference (close #67335) but is largely empty with minimal explanation of what changed and how it works. Add detailed explanation of the implementation logic, the new system variable, and how the feature works to meet template requirements.
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly describes the main change: skipping plan cache regeneration when a SQL binding exists, which aligns with the primary objective and the linked issue.
Linked Issues check ✅ Passed The PR successfully implements the objective to avoid plan cache invalidation when a valid binding exists by introducing the PlanCacheSkipStatsOnBinding variable and updating the plan cache key logic.
Out of Scope Changes check ✅ Passed All changes are directly related to implementing the plan cache skipping feature for queries with bindings; no out-of-scope modifications detected.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

Copy link
Copy Markdown

@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

🧹 Nitpick comments (1)
pkg/planner/core/plan_cache_utils.go (1)

384-387: Deduplicate the stats-hash gate to prevent hash-length/content drift.

The same condition is repeated in two places. Extracting it once makes future edits safer.

♻️ Proposed refactor
-	if vars.GetSessionVars().PlanCacheInvalidationOnFreshStats && (binding == "" || !vars.GetSessionVars().PlanCacheSkipStatsOnBinding) {
+	includeStatsVerHash := vars.PlanCacheInvalidationOnFreshStats &&
+		(binding == "" || !vars.PlanCacheSkipStatsOnBinding)
+	if includeStatsVerHash {
 		// statsVerHash: skipped when a binding is matched and PlanCacheSkipStatsOnBinding is on,
 		// because a binding pins the plan via hints so stats changes cannot alter the chosen plan.
 		hashLen += 8
 	}
@@
-	if sctx.GetSessionVars().PlanCacheInvalidationOnFreshStats && (binding == "" || !sctx.GetSessionVars().PlanCacheSkipStatsOnBinding) {
+	if includeStatsVerHash {
 		var statsVerHash uint64
 		for _, t := range stmt.tables {
 			statsVerHash += getLatestVersionFromStatsTable(sctx, t.Meta(), t.Meta().ID) // use '+' as the hash function for simplicity
 		}
 		hash = codec.EncodeUint(hash, statsVerHash)
 	}

Also applies to: 459-462

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

In `@pkg/planner/core/plan_cache_utils.go` around lines 384 - 387, The repeated
condition checking vars.GetSessionVars().PlanCacheInvalidationOnFreshStats &&
(binding == "" || !vars.GetSessionVars().PlanCacheSkipStatsOnBinding) should be
extracted into a single local boolean (e.g., shouldIncludeStatsHash) near the
top of the function in pkg/planner/core/plan_cache_utils.go and then used in
both spots (the current hashLen += 8 branch and the other occurrence around
lines 459-462) to avoid drift; update references to use that boolean when
deciding to include the stats hash and remove the duplicated condition
expressions.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@pkg/session/upgrade_def.go`:
- Around line 2082-2084: Add automated tests to validate the new
tidb_plan_cache_skip_stats_on_binding variable set by upgradeToVer256: create a
bootstrap upgrade unit test (following the TestUpgradeWithAnalyzeColumnOptions
pattern) that runs upgradeToVer256 and asserts the global variable initialized
by initGlobalVariableIfNotExists(vardef.TiDBPlanCacheSkipStatsOnBinding,
vardef.On) exists and is ON; additionally add SQL integration tests that
exercise plan cache + binding + fresh-stats scenarios with the variable enabled
(create bindings, generate fresh stats, and assert the planner/plan cache
behavior changes expected when tidb_plan_cache_skip_stats_on_binding is ON).

---

Nitpick comments:
In `@pkg/planner/core/plan_cache_utils.go`:
- Around line 384-387: The repeated condition checking
vars.GetSessionVars().PlanCacheInvalidationOnFreshStats && (binding == "" ||
!vars.GetSessionVars().PlanCacheSkipStatsOnBinding) should be extracted into a
single local boolean (e.g., shouldIncludeStatsHash) near the top of the function
in pkg/planner/core/plan_cache_utils.go and then used in both spots (the current
hashLen += 8 branch and the other occurrence around lines 459-462) to avoid
drift; update references to use that boolean when deciding to include the stats
hash and remove the duplicated condition expressions.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 713b2bc7-ac0f-4e0d-a59f-b8d74af78262

📥 Commits

Reviewing files that changed from the base of the PR and between b52c9b8 and 556436c.

📒 Files selected for processing (5)
  • pkg/planner/core/plan_cache_utils.go
  • pkg/session/upgrade_def.go
  • pkg/sessionctx/vardef/tidb_vars.go
  • pkg/sessionctx/variable/session.go
  • pkg/sessionctx/variable/sysvar.go

Comment thread pkg/session/upgrade_def.go
@codecov
Copy link
Copy Markdown

codecov Bot commented Mar 19, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 78.9355%. Comparing base (23f315b) to head (5b4e1c2).
⚠️ Report is 20 commits behind head on master.

Additional details and impacted files
@@               Coverage Diff                @@
##             master     #67134        +/-   ##
================================================
+ Coverage   77.8320%   78.9355%   +1.1034%     
================================================
  Files          2023       1960        -63     
  Lines        556131     544623     -11508     
================================================
- Hits         432848     429901      -2947     
+ Misses       121539     113696      -7843     
+ Partials       1744       1026       -718     
Flag Coverage Δ
integration 44.7186% <80.0000%> (-3.4081%) ⬇️
unit 76.8360% <100.0000%> (+0.4652%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

Components Coverage Δ
dumpling 61.5065% <ø> (ø)
parser ∅ <ø> (∅)
br 66.4829% <ø> (+5.5826%) ⬆️
🚀 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.

@ti-chi-bot ti-chi-bot Bot added size/L Denotes a PR that changes 100-499 lines, ignoring generated files. and removed size/M Denotes a PR that changes 30-99 lines, ignoring generated files. labels Mar 19, 2026
Copy link
Copy Markdown

@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)
tests/integrationtest/t/planner/core/plan_cache.test (1)

1659-1660: Assert the default of tidb_plan_cache_skip_stats_on_binding too.

This block only proves the explicit ON/OFF paths. Since pkg/sessionctx/variable/sysvar.go:3409-3412 gives the new switch an explicit default, a bad default would still leave this scenario green. Please add one assertion around the DEFAULT reset as well.

Suggested addition
 set tidb_plan_cache_invalidation_on_fresh_stats = DEFAULT;
 set tidb_plan_cache_skip_stats_on_binding = DEFAULT;
+select @@session.tidb_plan_cache_skip_stats_on_binding;

Also applies to: 1686-1687

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

In `@tests/integrationtest/t/planner/core/plan_cache.test` around lines 1659 -
1660, Add an assertion that verifies the DEFAULT value for
tidb_plan_cache_skip_stats_on_binding: after toggling ON/OFF and before
finishing the test add a statement that sets
tidb_plan_cache_skip_stats_on_binding = DEFAULT (or RESET it) and then query the
variable and assert it equals the declared default for
tidb_plan_cache_skip_stats_on_binding (the same default defined in
pkg/sessionctx/variable/sysvar.go). Use the same test harness helpers/assert
style used for the existing ON/OFF checks so the new assertion mirrors the other
expectations.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@tests/integrationtest/t/planner/core/plan_cache.test`:
- Around line 1659-1660: Add an assertion that verifies the DEFAULT value for
tidb_plan_cache_skip_stats_on_binding: after toggling ON/OFF and before
finishing the test add a statement that sets
tidb_plan_cache_skip_stats_on_binding = DEFAULT (or RESET it) and then query the
variable and assert it equals the declared default for
tidb_plan_cache_skip_stats_on_binding (the same default defined in
pkg/sessionctx/variable/sysvar.go). Use the same test harness helpers/assert
style used for the existing ON/OFF checks so the new assertion mirrors the other
expectations.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: fa16cb07-04b7-4b78-82d8-c088a09986b5

📥 Commits

Reviewing files that changed from the base of the PR and between 7363921 and c8fa690.

📒 Files selected for processing (3)
  • br/pkg/restore/snap_client/systable_restore_test.go
  • tests/integrationtest/r/planner/core/plan_cache.result
  • tests/integrationtest/t/planner/core/plan_cache.test

@terry1purcell
Copy link
Copy Markdown
Contributor Author

/retest-required

@terry1purcell terry1purcell changed the title planner: Skip plan cache regeneration if binding exists (WIP) planner: Skip plan cache regeneration if binding exists Mar 26, 2026
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR introduces a new session/global switch to allow prepared plan cache entries to remain reusable across statistics updates when a SQL binding is matched, reducing cache invalidations triggered by ANALYZE for bound queries (issue #67335).

Changes:

  • Add system variable tidb_plan_cache_skip_stats_on_binding (default ON) and bootstrap/upgrade logic to initialize it.
  • Update plan-cache key generation to optionally exclude the stats-version component when a binding is matched and the new variable is enabled.
  • Add regression tests (planner integration test + Go test + bootstrap upgrade test) and adjust Bazel sharding / bootstrap version expectations.

Reviewed changes

Copilot reviewed 12 out of 12 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
pkg/planner/core/plan_cache_utils.go Skip stats-version hashing in plan cache key when a binding is matched and the new sysvar is enabled.
pkg/sessionctx/variable/sysvar.go Register new bool sysvar and wire it to SessionVars.
pkg/sessionctx/variable/session.go Add SessionVars.PlanCacheSkipStatsOnBinding field.
pkg/sessionctx/vardef/tidb_vars.go Add sysvar name constant and default value.
pkg/session/upgrade_def.go Bump bootstrap version and add upgrade step to initialize the new sysvar.
pkg/session/test/bootstraptest/bootstrap_upgrade_test.go Add upgrade test ensuring sysvar is initialized on upgrade from v255.
tests/integrationtest/t/planner/core/plan_cache.test Add integration test coverage for binding + stats invalidation behavior.
tests/integrationtest/r/planner/core/plan_cache.result Update expected output for the new integration test.
pkg/session/test/bootstraptest/BUILD.bazel Increase shard count for test distribution.
pkg/planner/core/casetest/plancache/plan_cache_suite_test.go Add unit test covering binding + stats invalidation skip behavior.
pkg/planner/core/casetest/plancache/BUILD.bazel Increase shard count for test distribution.
br/pkg/restore/snap_client/systable_restore_test.go Update expected CurrentBootstrapVersion to 256.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread pkg/planner/core/plan_cache_utils.go Outdated
@ti-chi-bot ti-chi-bot Bot added the needs-1-more-lgtm Indicates a PR needs 1 more LGTM. label Mar 28, 2026
@ti-chi-bot ti-chi-bot Bot added the lgtm label Mar 28, 2026
@ti-chi-bot ti-chi-bot Bot removed the needs-1-more-lgtm Indicates a PR needs 1 more LGTM. label Mar 28, 2026
@ti-chi-bot
Copy link
Copy Markdown

ti-chi-bot Bot commented Mar 28, 2026

[LGTM Timeline notifier]

Timeline:

  • 2026-03-28 03:23:41.599527803 +0000 UTC m=+584217.635598053: ☑️ agreed by hawkingrei.
  • 2026-03-28 03:40:33.460648834 +0000 UTC m=+585229.496719104: ☑️ agreed by qw4990.

@ti-chi-bot
Copy link
Copy Markdown

ti-chi-bot Bot commented Mar 29, 2026

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: BornChanger, hawkingrei, qw4990, yudongusa

The full list of commands accepted by this bot can be found here.

The pull request process is described here

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@ti-chi-bot ti-chi-bot Bot added the approved label Mar 29, 2026
@terry1purcell
Copy link
Copy Markdown
Contributor Author

/retest-required

4 similar comments
@terry1purcell
Copy link
Copy Markdown
Contributor Author

/retest-required

@terry1purcell
Copy link
Copy Markdown
Contributor Author

/retest-required

@terry1purcell
Copy link
Copy Markdown
Contributor Author

/retest-required

@terry1purcell
Copy link
Copy Markdown
Contributor Author

/retest-required

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

Labels

approved lgtm ok-to-test Indicates a PR is ready to be tested. release-note-none Denotes a PR that doesn't merit a release note. sig/planner SIG: Planner size/L Denotes a PR that changes 100-499 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Planner: Avoid plan cache invalidation for SQL with binding

6 participants