Skip to content

statistics: skip stale stats meta during initialization - #69116

Merged
ti-chi-bot[bot] merged 3 commits into
pingcap:masterfrom
winoros:stats-init-skip-invalid
Jun 23, 2026
Merged

ti-chi-bot[bot] merged 3 commits into
pingcap:masterfrom
winoros:stats-init-skip-invalid

Conversation

@winoros

@winoros winoros commented Jun 11, 2026 •

Copy link
Copy Markdown
Member

What problem does this PR solve?

Issue Number: close #69115

Problem Summary:

Stats initialization rebuilds the in-memory stats cache from mysql.stats_meta. Dropped tables can leave stale stats metadata until stats GC removes it, so initialization should avoid loading stats entries whose physical table IDs no longer exist in the current InfoSchema.

What changed and how does it work?

This PR passes the current InfoSchema into the lite stats initialization path and validates each physical table ID while loading stats metadata.

  • Existing table IDs are loaded as before.
  • Existing partition physical IDs are still accepted.
  • Stale table or partition IDs that are no longer visible in InfoSchema are skipped.

The tests cover both lite and non-lite stats initialization, including stale dropped-table metadata and valid partition physical IDs.

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

Unit test:

go test -run 'TestLiteInitStatsWithTableIDs|TestDropTableBeforeConcurrentlyInitStats|TestDropTableBeforeNonLiteInitStats' -tags=intest,deadlock -count=1
make lint
git diff --check

Release note

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

None

Summary by CodeRabbit

Release Notes

  • Bug Fixes

    • Improved lite statistics initialization to use the current schema context, preventing stats metadata from loading for physical tables that were dropped.
    • Kept behavior consistent across lite refresh paths (session-based and current-instance initialization), including for table-ID and partitioned-table scenarios.
  • Tests

    • Updated and expanded lite initialization/refresh tests to pass the schema context and verify correct handling for dropped and partitioned tables, including cancellation and sync-load cases.

@ti-chi-bot ti-chi-bot Bot added do-not-merge/work-in-progress Indicates that a PR should not merge because it is a work in progress. release-note-none Denotes a PR that doesn't merit a release note. labels Jun 11, 2026
@ti-chi-bot

ti-chi-bot Bot commented Jun 11, 2026

Copy link
Copy Markdown

Skipping CI for Draft Pull Request.
If you want CI signal for your change, please convert it to an actual PR.
You can still manually trigger a test run with /test all

@coderabbitai

coderabbitai Bot commented Jun 11, 2026 •

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 5622a568-782c-4bed-bcc3-1fa87a802ced

📥 Commits

Reviewing files that changed from the base of the PR and between e33bd43 and 344e44d.

📒 Files selected for processing (4)
  • pkg/statistics/handle/bootstrap.go
  • pkg/statistics/handle/util/BUILD.bazel
  • pkg/statistics/handle/util/table_info.go
  • pkg/statistics/handle/util/util_test.go
🚧 Files skipped from review as they are similar to previous changes (1)
  • pkg/statistics/handle/bootstrap.go

📝 Walkthrough

Walkthrough

The PR updates statistics initialization to skip stale metadata for dropped tables. InitStatsLite now requires an InfoSchema parameter, and new table info utilities enable partition-aware lookups. The core filtering logic validates each physical table ID against the current schema before adding to the cache. All production call sites and tests are updated to pass the current InfoSchema.

Changes

Schema-aware stats metadata filtering

Layer / File(s) Summary
Table info utilities for partition-aware lookups
pkg/statistics/handle/util/table_info.go, pkg/statistics/handle/util/util_test.go, pkg/statistics/handle/util/BUILD.bazel
TableInfoGetter interface adds TableItemByIDForInitStats method for lightweight metadata lookup. Implementation handles InfoSchema V1 partition-to-parent resolution via cached mapping helper. partitionID2TableIDForInitStats optimized to return cached mapping directly. New test validates correct table and partition resolution without partition scan overhead.
Core schema-aware stats metadata filtering
pkg/statistics/handle/bootstrap.go
initStatsMeta4Chunk accepts InfoSchema and skips physical table IDs that no longer exist using TableItemByIDForInitStats. initStatsMeta signature updated to thread InfoSchema through paging loop. InitStatsLite public API now requires InfoSchema parameter. Session wrappers pass InfoSchema into initStatsMeta for both lite and full initialization paths, applying same dropped-table filtering to all paths.
Production code call sites
pkg/domain/domain.go, pkg/executor/simple.go
Domain initialization and executor refresh stats pass current InfoSchema to InitStatsLite for schema-aware filtering during startup and stats refresh operations.
Basic test call site updates
pkg/planner/cardinality/selectivity_test.go, pkg/server/stat_test.go, pkg/statistics/handle/handletest/handle_test.go, pkg/statistics/handle/syncload/stats_syncload_test.go
Test suites updated to pass InfoSchema to InitStatsLite invocations, including selectivity estimation, cancellation behavior, index pruning, and sync-load test scenarios.
Dropped table and partition test coverage
pkg/statistics/handle/handletest/initstats/init_stats_test.go
TestLiteInitStatsWithTableIDs adds dropped and partitioned tables, captures partition IDs, and validates absence of stats for dropped tables while retaining partition stats through explicit error checks. testDropTableBeforeInitStats exercises full lifecycle: create, analyze, drop tables; verify dropped table IDs excluded from cache while kept tables and partitions remain present.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~12 minutes

Suggested reviewers

  • AilinKid
  • terry1purcell
  • XuHuaiyu

Poem

🐰 Stats were scattered, tables gone but not forgot,
Till InfoSchema whispered, "Hey, that table's not!"
Now lite init checks the schema's truth so fine—
Dropped tables filtered out in perfect line! ✨

🚥 Pre-merge checks | ✅ 5 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main change: skipping stale stats metadata during initialization by validating physical table IDs against the current InfoSchema.
Linked Issues check ✅ Passed All code changes directly implement the requirements from issue #69115: validating physical table IDs against the current InfoSchema before loading stats metadata, preserving valid tables/partitions, and skipping stale records.
Out of Scope Changes check ✅ Passed All changes are in-scope: modified stats initialization to accept and use InfoSchema parameter for validation, updated callers to pass InfoSchema, added tests for dropped-table scenarios, and updated Bazel build configuration to support new dependencies.
Description check ✅ Passed A detailed description explaining the problem, solution, and testing approach is provided in the PR objectives.

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

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

Warning

Tools execution failed with the following error:

Failed to run tools: 13 INTERNAL: Received RST_STREAM with code 2 (Internal server error)


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.

@ti-chi-bot ti-chi-bot Bot added size/L Denotes a PR that changes 100-499 lines, ignoring generated files. component/statistics sig/planner SIG: Planner labels Jun 11, 2026
@tiprow

tiprow Bot commented Jun 11, 2026

Copy link
Copy Markdown

Skipping CI for Draft Pull Request.
If you want CI signal for your change, please convert it to an actual PR.
You can still manually trigger a test run with /test all

@winoros
winoros marked this pull request as ready for review June 11, 2026 16:13
@ti-chi-bot ti-chi-bot Bot removed the do-not-merge/work-in-progress Indicates that a PR should not merge because it is a work in progress. label Jun 11, 2026

@0xPoe 0xPoe left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

rest LGTM

defer store.Close()
se := session.CreateSessionAndSetID(t, store)
session.MustExec(t, se, "use test")
session.MustExec(t, se, "set @@tidb_partition_prune_mode='static'")

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Why would we still use static mode here? It is about to be decapitated.

tk.MustExec("analyze table t all columns;")
tk.MustExec("drop table t")
h := dom.StatsHandle()
tk.MustExec("set @@tidb_partition_prune_mode='static'")

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

ditto

Comment thread pkg/statistics/handle/bootstrap.go Outdated
}
for row := iter.Begin(); row != iter.End(); row = iter.Next() {
physicalID = row.GetInt64(1)
maxPhysicalID = max(physicalID, maxPhysicalID)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

maxPhysicalID is updated before the stale-ID skip. That means a dropped table with a high physical ID can still inflate full-init histogram/TopN/bucket paging ranges even though the PR skips its meta entry.

@0xPoe 0xPoe added the needs-cherry-pick-release-8.5 Should cherry pick this PR to release-8.5 branch. label Jun 11, 2026
@codecov

codecov Bot commented Jun 11, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 12.19512% with 36 lines in your changes missing coverage. Please review.
✅ Project coverage is 75.9141%. Comparing base (e370684) to head (344e44d).
⚠️ Report is 62 commits behind head on master.

Additional details and impacted files
@@               Coverage Diff                @@
##             master     #69116        +/-   ##
================================================
- Coverage   76.3235%   75.9141%   -0.4095%     
================================================
  Files          2041       2068        +27     
  Lines        562593     592384     +29791     
================================================
+ Hits         429391     449703     +20312     
- Misses       132291     139375      +7084     
- Partials        911       3306      +2395     
Flag Coverage Δ
integration 46.1386% <12.1951%> (+6.4188%) ⬆️

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

Components Coverage Δ
dumpling 60.4610% <ø> (ø)
parser ∅ <ø> (∅)
br 64.1401% <ø> (+1.3502%) ⬆️
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@winoros
winoros force-pushed the stats-init-skip-invalid branch from e31b910 to 9bc0ae8 Compare June 15, 2026 05:17

@0xPoe 0xPoe left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Do we need to consider the same from:

// TableInfoByIDForInitStats returns the table info specified by the physicalID for initializing stats.
func (c *tableInfoGetterImpl) TableInfoByIDForInitStats(is infoschema.InfoSchema, physicalID int64) (table.Table, bool) {
	// NOTE: For InfoSchema V2, partition lookups are efficient,
	// We can directly search for partitioned tables without maintaining a separate cache, making TableByID sufficient for both
	// regular tables and partitions.
	isV2, _ := infoschema.IsV2(is)
	if isV2 {
		return c.TableInfoByID(is, physicalID)
	}

	// For InfoSchema V1, partition lookups require a full scan of all tables, which is
	// expensive. We maintain a cached pid2tid map to optimize these lookups during stats
	// initialization. Locking is needed because this method may be called in parallel.
	// See FindTableByPartitionID implementation for more details on V1's search cost.
	c.forInitStatsAndInfoSchemaV1Only.mu.Lock()
	defer c.forInitStatsAndInfoSchemaV1Only.mu.Unlock()
	if is.SchemaMetaVersion() != c.forInitStatsAndInfoSchemaV1Only.schemaVersion {
		c.forInitStatsAndInfoSchemaV1Only.schemaVersion = is.SchemaMetaVersion()
		c.forInitStatsAndInfoSchemaV1Only.pid2tid = buildPartitionID2TableID(is)
	}
	if id, ok := c.forInitStatsAndInfoSchemaV1Only.pid2tid[physicalID]; ok {
		return is.TableByID(context.Background(), id)
	}
	return is.TableByID(context.Background(), physicalID)
}

Comment thread pkg/statistics/handle/handletest/initstats/init_stats_test.go Outdated
Comment thread pkg/statistics/handle/handletest/initstats/init_stats_test.go Outdated
@winoros

winoros commented Jun 18, 2026

Copy link
Copy Markdown
Member Author

Do we need to consider the same from:

// TableInfoByIDForInitStats returns the table info specified by the physicalID for initializing stats.
func (c *tableInfoGetterImpl) TableInfoByIDForInitStats(is infoschema.InfoSchema, physicalID int64) (table.Table, bool) {
	// NOTE: For InfoSchema V2, partition lookups are efficient,
	// We can directly search for partitioned tables without maintaining a separate cache, making TableByID sufficient for both
	// regular tables and partitions.
	isV2, _ := infoschema.IsV2(is)
	if isV2 {
		return c.TableInfoByID(is, physicalID)
	}

	// For InfoSchema V1, partition lookups require a full scan of all tables, which is
	// expensive. We maintain a cached pid2tid map to optimize these lookups during stats
	// initialization. Locking is needed because this method may be called in parallel.
	// See FindTableByPartitionID implementation for more details on V1's search cost.
	c.forInitStatsAndInfoSchemaV1Only.mu.Lock()
	defer c.forInitStatsAndInfoSchemaV1Only.mu.Unlock()
	if is.SchemaMetaVersion() != c.forInitStatsAndInfoSchemaV1Only.schemaVersion {
		c.forInitStatsAndInfoSchemaV1Only.schemaVersion = is.SchemaMetaVersion()
		c.forInitStatsAndInfoSchemaV1Only.pid2tid = buildPartitionID2TableID(is)
	}
	if id, ok := c.forInitStatsAndInfoSchemaV1Only.pid2tid[physicalID]; ok {
		return is.TableByID(context.Background(), id)
	}
	return is.TableByID(context.Background(), physicalID)
}

This is called when we need to fill the table meta for the statsTbl. It needs to return a table info. So I think we don't need to change it.

Co-authored-by: Dongpo Liu <poe.liu@pm.me>
@winoros

winoros commented Jun 18, 2026

Copy link
Copy Markdown
Member Author

/retest

@winoros

winoros commented Jun 23, 2026

Copy link
Copy Markdown
Member Author

We discussed in IM. @0xPoe's concern is that the API we use may have perf regression if the cluster is under infoschemav1. This is a valid concern. I'm restructuring the pr.

@0xPoe 0xPoe left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Thanks! :shipit:

@ti-chi-bot ti-chi-bot Bot added the needs-1-more-lgtm Indicates a PR needs 1 more LGTM. label Jun 23, 2026
@ti-chi-bot ti-chi-bot Bot added lgtm and removed needs-1-more-lgtm Indicates a PR needs 1 more LGTM. labels Jun 23, 2026
@ti-chi-bot

ti-chi-bot Bot commented Jun 23, 2026

Copy link
Copy Markdown

[LGTM Timeline notifier]

Timeline:

  • 2026-06-23 15:44:56.829515204 +0000 UTC m=+48780.492740717: ☑️ agreed by 0xPoe.
  • 2026-06-23 15:47:23.193190571 +0000 UTC m=+48926.856416084: ☑️ agreed by terry1purcell.

@YangKeao YangKeao left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

LGTM

@ti-chi-bot

ti-chi-bot Bot commented Jun 23, 2026

Copy link
Copy Markdown

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: 0xPoe, terry1purcell, YangKeao

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 Jun 23, 2026
@winoros

winoros commented Jun 23, 2026

Copy link
Copy Markdown
Member Author

/retest

@ti-chi-bot
ti-chi-bot Bot merged commit 69b0177 into pingcap:master Jun 23, 2026
36 checks passed
@ti-chi-bot

Copy link
Copy Markdown
Member

In response to a cherrypick label: new pull request created to branch release-8.5: #69416.
But this PR has conflicts, please resolve them!

@winoros
winoros deleted the stats-init-skip-invalid branch June 23, 2026 17:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

approved component/statistics lgtm needs-cherry-pick-release-8.5 Should cherry pick this PR to release-8.5 branch. 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.

statistics: skip stale stats metadata during initialization

5 participants