Skip to content

Commit d66a662

Browse files
authored
planner, statistics: skip plan cache for sync-load fallback plans (pingcap#67411)
close pingcap#66585
1 parent 4d20c4a commit d66a662

File tree

3 files changed

+86
-1
lines changed

3 files changed

+86
-1
lines changed

pkg/planner/core/casetest/planstats/BUILD.bazel

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ go_test(
99
],
1010
data = glob(["testdata/**"]),
1111
flaky = True,
12-
shard_count = 7,
12+
shard_count = 8,
1313
deps = [
1414
"//pkg/config",
1515
"//pkg/domain",
@@ -25,6 +25,7 @@ go_test(
2525
"//pkg/sessionctx",
2626
"//pkg/sessionctx/stmtctx",
2727
"//pkg/statistics",
28+
"//pkg/statistics/asyncload",
2829
"//pkg/statistics/handle/ddl/testutil",
2930
"//pkg/statistics/handle/types",
3031
"//pkg/testkit",

pkg/planner/core/casetest/planstats/plan_stats_test.go

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ import (
3737
"github.com/pingcap/tidb/pkg/sessionctx"
3838
"github.com/pingcap/tidb/pkg/sessionctx/stmtctx"
3939
"github.com/pingcap/tidb/pkg/statistics"
40+
"github.com/pingcap/tidb/pkg/statistics/asyncload"
4041
"github.com/pingcap/tidb/pkg/statistics/handle/ddl/testutil"
4142
"github.com/pingcap/tidb/pkg/statistics/handle/types"
4243
"github.com/pingcap/tidb/pkg/testkit"
@@ -404,6 +405,85 @@ func TestPlanStatsLoadTimeout(t *testing.T) {
404405
})
405406
}
406407

408+
func TestPreparedPlanCacheInvalidatedAfterSyncLoadTimeoutFallback(t *testing.T) {
409+
originConfig := config.GetGlobalConfig()
410+
newConfig := config.NewConfig()
411+
newConfig.Performance.StatsLoadConcurrency = -1 // no worker to consume channel
412+
newConfig.Performance.StatsLoadQueueSize = 1
413+
config.StoreGlobalConfig(newConfig)
414+
t.Cleanup(func() {
415+
config.StoreGlobalConfig(originConfig)
416+
})
417+
418+
store, dom := testkit.CreateMockStoreAndDomain(t)
419+
tk := testkit.NewTestKit(t, store)
420+
tk.MustExec("use test")
421+
originalPseudoTimeout := tk.MustQuery("select @@tidb_stats_load_pseudo_timeout").Rows()[0][0].(string)
422+
defer func() {
423+
tk.MustExec(fmt.Sprintf("set global tidb_stats_load_pseudo_timeout = %v", originalPseudoTimeout))
424+
}()
425+
426+
oriLease := dom.StatsHandle().Lease()
427+
dom.StatsHandle().SetLease(1)
428+
defer func() {
429+
dom.StatsHandle().SetLease(oriLease)
430+
}()
431+
432+
tk.MustExec("set global tidb_stats_load_pseudo_timeout = 1")
433+
tk.MustExec("set @@session.tidb_enable_prepared_plan_cache = 1")
434+
tk.MustExec("set @@session.tidb_plan_cache_invalidation_on_fresh_stats = 1")
435+
tk.MustExec("set @@session.tidb_stats_load_sync_wait = 1")
436+
tk.MustExec("drop table if exists t")
437+
tk.MustExec("create table t(a int primary key, b int)")
438+
tk.MustExec("insert into t values (1,1),(2,2),(3,3),(4,4),(5,5)")
439+
tk.MustExec("analyze table t all columns")
440+
require.NoError(t, dom.StatsHandle().Update(context.Background(), dom.InfoSchema()))
441+
442+
tbl, err := dom.InfoSchema().TableByName(context.Background(), ast.NewCIStr("test"), ast.NewCIStr("t"))
443+
require.NoError(t, err)
444+
tblInfo := tbl.Meta()
445+
colBID := tblInfo.Columns[1].ID
446+
447+
statsTbl := dom.StatsHandle().GetPhysicalTableStats(tblInfo.ID, tblInfo)
448+
colBStats := statsTbl.GetCol(colBID)
449+
require.NotNil(t, colBStats)
450+
require.True(t, colBStats.IsAllEvicted())
451+
452+
tk.MustExec("prepare st from 'select * from t where b > ?'")
453+
tk.MustExec("set @p = 2")
454+
tk.MustExec("execute st using @p")
455+
tk.MustQuery("select @@last_plan_from_cache").Check(testkit.Rows("0"))
456+
457+
// Wait for the async histogram load item corresponding to column b to be marked
458+
// as sync-load failed, which confirms the sync-load path timed out and fell back
459+
// to async loading for this full-load stats item.
460+
require.Eventually(t, func() bool {
461+
for _, item := range asyncload.AsyncLoadHistogramNeededItems.AllItems() {
462+
if item.TableID == tblInfo.ID && item.ID == colBID && !item.IsIndex && item.FullLoad && item.IsSyncLoadFailed {
463+
return true
464+
}
465+
}
466+
return false
467+
}, 5*time.Second, 100*time.Millisecond)
468+
469+
tk.MustExec("execute st using @p")
470+
tk.MustQuery("select @@last_plan_from_cache").Check(testkit.Rows("0"))
471+
require.Equal(t, 0, tk.Session().GetSessionPlanCache().Size())
472+
473+
require.NoError(t, dom.StatsHandle().LoadNeededHistograms(dom.InfoSchema()))
474+
statsTbl = dom.StatsHandle().GetPhysicalTableStats(tblInfo.ID, tblInfo)
475+
colBStats = statsTbl.GetCol(colBID)
476+
require.NotNil(t, colBStats)
477+
require.True(t, colBStats.IsFullLoad())
478+
479+
tk.MustExec("execute st using @p")
480+
tk.MustQuery("select @@last_plan_from_cache").Check(testkit.Rows("0"))
481+
require.Equal(t, 1, tk.Session().GetSessionPlanCache().Size())
482+
483+
tk.MustExec("execute st using @p")
484+
tk.MustQuery("select @@last_plan_from_cache").Check(testkit.Rows("1"))
485+
}
486+
407487
func TestPlanStatsStatusRecord(t *testing.T) {
408488
defer config.RestoreFunc()()
409489
config.UpdateGlobal(func(conf *config.Config) {

pkg/planner/core/rule/rule_collect_plan_stats.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,8 @@ import (
3939
// CollectPredicateColumnsPoint collects the columns that are used in the predicates.
4040
type CollectPredicateColumnsPoint struct{}
4141

42+
const skipPlanCacheReasonSyncLoadFallback = "sync-load timed out and fell back to pseudo stats"
43+
4244
// Optimize implements LogicalOptRule.<0th> interface.
4345
func (c *CollectPredicateColumnsPoint) Optimize(_ context.Context, plan base.LogicalPlan) (base.LogicalPlan, bool, error) {
4446
planChanged := false
@@ -349,6 +351,7 @@ func RequestLoadStats(ctx base.PlanContext, neededHistItems []model.StatsLoadIte
349351
if err != nil {
350352
stmtCtx.IsSyncStatsFailed = true
351353
if vardef.StatsLoadPseudoTimeout.Load() {
354+
stmtCtx.SetSkipPlanCache(skipPlanCacheReasonSyncLoadFallback)
352355
logutil.ErrVerboseLogger().Warn("RequestLoadStats failed", zap.Error(err))
353356
stmtCtx.AppendWarning(err)
354357
return nil
@@ -373,6 +376,7 @@ func SyncWaitStatsLoad(plan base.LogicalPlan) error {
373376
if err != nil {
374377
stmtCtx.IsSyncStatsFailed = true
375378
if vardef.StatsLoadPseudoTimeout.Load() {
379+
stmtCtx.SetSkipPlanCache(skipPlanCacheReasonSyncLoadFallback)
376380
logutil.ErrVerboseLogger().Warn("SyncWaitStatsLoad failed", zap.Error(err))
377381
stmtCtx.AppendWarning(err)
378382
return nil

0 commit comments

Comments
 (0)