From 90e727194074b352ffe826fca411c1776b5562cd Mon Sep 17 00:00:00 2001 From: Weizhen Wang Date: Tue, 17 Jan 2023 13:55:49 +0800 Subject: [PATCH 1/2] testkit: reset the resource manager after test finishes (#40638) --- resourcemanager/BUILD.bazel | 1 + resourcemanager/rm.go | 11 +++++++++++ testkit/BUILD.bazel | 1 + testkit/mockstore.go | 2 ++ 4 files changed, 15 insertions(+) diff --git a/resourcemanager/BUILD.bazel b/resourcemanager/BUILD.bazel index 968a73b4a0f95..c6e7983df34be 100644 --- a/resourcemanager/BUILD.bazel +++ b/resourcemanager/BUILD.bazel @@ -13,6 +13,7 @@ go_library( "//resourcemanager/util", "//util", "//util/cpu", + "@com_github_google_uuid//:uuid", "@com_github_pingcap_log//:log", "@org_uber_go_zap//:zap", ], diff --git a/resourcemanager/rm.go b/resourcemanager/rm.go index aa2af6f949465..ed73cda8e1abf 100644 --- a/resourcemanager/rm.go +++ b/resourcemanager/rm.go @@ -17,6 +17,7 @@ package resourcemanager import ( "time" + "github.com/google/uuid" "github.com/pingcap/tidb/resourcemanager/scheduler" "github.com/pingcap/tidb/resourcemanager/util" tidbutil "github.com/pingcap/tidb/util" @@ -26,6 +27,11 @@ import ( // GlobalResourceManager is a global resource manager var GlobalResourceManager = NewResourceManger() +// RandomName is to get a random name for register pool. It is just for test. +func RandomName() string { + return uuid.New().String() +} + // ResourceManager is a resource manager type ResourceManager struct { poolMap *util.ShardPoolMap @@ -85,3 +91,8 @@ func (r *ResourceManager) registerPool(name string, pool *util.PoolContainer) er func (r *ResourceManager) Unregister(name string) { r.poolMap.Del(name) } + +// Reset is to Reset resource manager. it is just for test. +func (r *ResourceManager) Reset() { + r.poolMap = util.NewShardPoolMap() +} diff --git a/testkit/BUILD.bazel b/testkit/BUILD.bazel index 4e0e24091db27..c28ef0614eb04 100644 --- a/testkit/BUILD.bazel +++ b/testkit/BUILD.bazel @@ -21,6 +21,7 @@ go_library( "//parser/ast", "//parser/terror", "//planner/core", + "//resourcemanager", "//session", "//session/txninfo", "//sessionctx/variable", diff --git a/testkit/mockstore.go b/testkit/mockstore.go index 12afe0e0f2f68..9756d5bb65804 100644 --- a/testkit/mockstore.go +++ b/testkit/mockstore.go @@ -24,6 +24,7 @@ import ( "github.com/pingcap/tidb/ddl/schematracker" "github.com/pingcap/tidb/domain" "github.com/pingcap/tidb/kv" + "github.com/pingcap/tidb/resourcemanager" "github.com/pingcap/tidb/session" "github.com/pingcap/tidb/store/driver" "github.com/pingcap/tidb/store/mockstore" @@ -91,6 +92,7 @@ func bootstrap(t testing.TB, store kv.Storage, lease time.Duration) *domain.Doma err := store.Close() require.NoError(t, err) view.Stop() + resourcemanager.GlobalResourceManager.Reset() }) return dom } From 45e85d9bd46ee90f13e298ec015193a63c491fe2 Mon Sep 17 00:00:00 2001 From: Yuanjia Zhang Date: Tue, 17 Jan 2023 15:17:48 +0800 Subject: [PATCH 2/2] planner: disable plan-cache for plans with IndexMerge accessing Multi-Valued Index (#40646) ref pingcap/tidb#40191 --- planner/core/find_best_task.go | 1 + planner/core/indexmerge_path.go | 2 +- planner/core/indexmerge_path_test.go | 13 ++++++++ planner/core/optimizer.go | 20 ------------- planner/core/physical_plans.go | 2 ++ planner/core/plan_cache.go | 44 ++++++++++++++++++++++++++-- planner/core/task.go | 2 ++ planner/util/path.go | 2 ++ 8 files changed, 63 insertions(+), 23 deletions(-) diff --git a/planner/core/find_best_task.go b/planner/core/find_best_task.go index 867541b97fb99..c2d3e1a62d7bc 100644 --- a/planner/core/find_best_task.go +++ b/planner/core/find_best_task.go @@ -1136,6 +1136,7 @@ func (ds *DataSource) convertToIndexMergeScan(prop *property.PhysicalProperty, c cop.tablePlan = ts cop.idxMergePartPlans = scans cop.idxMergeIsIntersection = path.IndexMergeIsIntersection + cop.idxMergeAccessMVIndex = path.IndexMergeAccessMVIndex if remainingFilters != nil { cop.rootTaskConds = remainingFilters } diff --git a/planner/core/indexmerge_path.go b/planner/core/indexmerge_path.go index 1b384aef1fd02..22c5e75cdd023 100644 --- a/planner/core/indexmerge_path.go +++ b/planner/core/indexmerge_path.go @@ -618,7 +618,7 @@ func (ds *DataSource) generateIndexMerge4MVIndex(normalPathCnt int, filters []ex // buildPartialPathUp4MVIndex builds these partial paths up to a complete index merge path. func (ds *DataSource) buildPartialPathUp4MVIndex(partialPaths []*util.AccessPath, isIntersection bool, remainingFilters []expression.Expression) *util.AccessPath { - indexMergePath := &util.AccessPath{PartialIndexPaths: partialPaths} + indexMergePath := &util.AccessPath{PartialIndexPaths: partialPaths, IndexMergeAccessMVIndex: true} indexMergePath.IndexMergeIsIntersection = isIntersection indexMergePath.TableFilters = remainingFilters diff --git a/planner/core/indexmerge_path_test.go b/planner/core/indexmerge_path_test.go index ecb0d60a395e4..1119cfb5c666e 100644 --- a/planner/core/indexmerge_path_test.go +++ b/planner/core/indexmerge_path_test.go @@ -136,3 +136,16 @@ index i_int((cast(j->'$.int' as signed array))))`) result.Check(testkit.Rows(output[i].Plan...)) } } + +func TestMVIndexIndexMergePlanCache(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec(`create table t(j json, index kj((cast(j as signed array))))`) + + tk.MustExec("prepare st from 'select /*+ use_index_merge(t, kj) */ * from t where (1 member of (j))'") + tk.MustQuery("show warnings").Check(testkit.Rows("Warning 1105 skip plan-cache: query accesses generated columns is un-cacheable")) + tk.MustExec("execute st") + tk.MustExec("execute st") + tk.MustQuery("select @@last_plan_from_cache").Check(testkit.Rows("0")) +} diff --git a/planner/core/optimizer.go b/planner/core/optimizer.go index c38b8b6f9d39a..a5db249fd2be1 100644 --- a/planner/core/optimizer.go +++ b/planner/core/optimizer.go @@ -1001,26 +1001,6 @@ func propagateProbeParents(plan PhysicalPlan, probeParents []PhysicalPlan) { } } -// useTiFlash used to check whether the plan use the TiFlash engine. -func useTiFlash(p PhysicalPlan) bool { - switch x := p.(type) { - case *PhysicalTableReader: - switch x.StoreType { - case kv.TiFlash: - return true - default: - return false - } - default: - if len(p.Children()) > 0 { - for _, plan := range p.Children() { - return useTiFlash(plan) - } - } - } - return false -} - func enableParallelApply(sctx sessionctx.Context, plan PhysicalPlan) PhysicalPlan { if !sctx.GetSessionVars().EnableParallelApply { return plan diff --git a/planner/core/physical_plans.go b/planner/core/physical_plans.go index 537b0826eb381..02d01aad5db4c 100644 --- a/planner/core/physical_plans.go +++ b/planner/core/physical_plans.go @@ -551,6 +551,8 @@ type PhysicalIndexMergeReader struct { // IsIntersectionType means whether it's intersection type or union type. // Intersection type is for expressions connected by `AND` and union type is for `OR`. IsIntersectionType bool + // AccessMVIndex indicates whether this IndexMergeReader access a MVIndex. + AccessMVIndex bool // PartialPlans flats the partialPlans to construct executor pb. PartialPlans [][]PhysicalPlan diff --git a/planner/core/plan_cache.go b/planner/core/plan_cache.go index b39208ae7574d..98b49c1bcc452 100644 --- a/planner/core/plan_cache.go +++ b/planner/core/plan_cache.go @@ -286,7 +286,9 @@ func generateNewPlan(ctx context.Context, sctx sessionctx.Context, isNonPrepared } // check whether this plan is cacheable. - checkPlanCacheability(sctx, p, len(paramTypes)) + if stmtCtx.UseCache { + checkPlanCacheability(sctx, p, len(paramTypes)) + } // put this plan into the plan cache. if stmtCtx.UseCache { @@ -341,7 +343,10 @@ func checkPlanCacheability(sctx sessionctx.Context, p Plan, paramNum int) { return } - // TODO: plans accessing MVIndex are un-cacheable + if accessMVIndexWithIndexMerge(pp) { + stmtCtx.SetSkipPlanCache(errors.New("skip plan-cache: the plan with IndexMerge accessing Multi-Valued Index is un-cacheable")) + return + } } // RebuildPlan4CachedPlan will rebuild this plan under current user parameters. @@ -725,6 +730,41 @@ func containTableDual(p PhysicalPlan) bool { return childContainTableDual } +func accessMVIndexWithIndexMerge(p PhysicalPlan) bool { + if idxMerge, ok := p.(*PhysicalIndexMergeReader); ok { + if idxMerge.AccessMVIndex { + return true + } + } + + for _, c := range p.Children() { + if accessMVIndexWithIndexMerge(c) { + return true + } + } + return false +} + +// useTiFlash used to check whether the plan use the TiFlash engine. +func useTiFlash(p PhysicalPlan) bool { + switch x := p.(type) { + case *PhysicalTableReader: + switch x.StoreType { + case kv.TiFlash: + return true + default: + return false + } + default: + if len(p.Children()) > 0 { + for _, plan := range p.Children() { + return useTiFlash(plan) + } + } + } + return false +} + // GetBindSQL4PlanCache used to get the bindSQL for plan cache to build the plan cache key. func GetBindSQL4PlanCache(sctx sessionctx.Context, stmt *PlanCacheStmt) (string, bool) { useBinding := sctx.GetSessionVars().UsePlanBaselines diff --git a/planner/core/task.go b/planner/core/task.go index d68806c5ef1d8..5d7ca6e5fd424 100644 --- a/planner/core/task.go +++ b/planner/core/task.go @@ -86,6 +86,7 @@ type copTask struct { idxMergePartPlans []PhysicalPlan idxMergeIsIntersection bool + idxMergeAccessMVIndex bool // rootTaskConds stores select conditions containing virtual columns. // These conditions can't push to TiKV, so we have to add a selection for rootTask @@ -688,6 +689,7 @@ func (t *copTask) convertToRootTaskImpl(ctx sessionctx.Context) *rootTask { partialPlans: t.idxMergePartPlans, tablePlan: t.tablePlan, IsIntersectionType: t.idxMergeIsIntersection, + AccessMVIndex: t.idxMergeAccessMVIndex, }.Init(ctx, t.idxMergePartPlans[0].SelectBlockOffset()) p.PartitionInfo = t.partitionInfo setTableScanToTableRowIDScan(p.tablePlan) diff --git a/planner/util/path.go b/planner/util/path.go index 2ee67eea8b6aa..68b11bbf5751f 100644 --- a/planner/util/path.go +++ b/planner/util/path.go @@ -54,6 +54,8 @@ type AccessPath struct { // It's only valid for a IndexMerge path. // Intersection type is for expressions connected by `AND` and union type is for `OR`. IndexMergeIsIntersection bool + // IndexMergeAccessMVIndex indicates whether this IndexMerge path accesses a MVIndex. + IndexMergeAccessMVIndex bool StoreType kv.StoreType