From c0790c59d9d4fdb7883ca3472c873a0aa5f2327c Mon Sep 17 00:00:00 2001 From: Chao Wang Date: Tue, 14 Dec 2021 16:40:02 +0800 Subject: [PATCH 1/3] *: placement policy ref will be converted to direct options when recover/flashback table --- ddl/placement_policy_test.go | 90 ++++++++++++++++++++++++++++++++++++ ddl/table.go | 13 ++++++ executor/ddl.go | 48 +++++++++++++++++++ 3 files changed, 151 insertions(+) diff --git a/ddl/placement_policy_test.go b/ddl/placement_policy_test.go index c8626121515d9..22c0a23c09d9f 100644 --- a/ddl/placement_policy_test.go +++ b/ddl/placement_policy_test.go @@ -2055,3 +2055,93 @@ func (s *testDBSuite6) TestPDFail(c *C) { " PARTITION `p1` VALUES LESS THAN (1000) /*T![placement] PLACEMENT POLICY=`p1` */)")) checkAllBundlesNotChange(c, existBundles) } + +func (s *testDBSuite6) TestRecoverTableWithPlacementPolicy(c *C) { + clearAllBundles(c) + failpoint.Enable("github.com/pingcap/tidb/store/gcworker/ignoreDeleteRangeFailed", `return`) + defer func(originGC bool) { + failpoint.Disable("github.com/pingcap/tidb/store/gcworker/ignoreDeleteRangeFailed") + if originGC { + ddl.EmulatorGCEnable() + } else { + ddl.EmulatorGCDisable() + } + }(ddl.IsEmulatorGCEnable()) + ddl.EmulatorGCDisable() + + tk := testkit.NewTestKit(c, s.store) + tk.MustExec("use test") + tk.MustExec("drop placement policy if exists p1") + tk.MustExec("drop placement policy if exists p2") + tk.MustExec("drop placement policy if exists p3") + tk.MustExec("drop table if exists tp1, tp2") + + safePointSQL := `INSERT HIGH_PRIORITY INTO mysql.tidb VALUES ('tikv_gc_safe_point', '%[1]s', '') + ON DUPLICATE KEY + UPDATE variable_value = '%[1]s'` + tk.MustExec(fmt.Sprintf(safePointSQL, "20060102-15:04:05 -0700 MST")) + + tk.MustExec("create placement policy p1 primary_region='r1' regions='r1,r2'") + defer tk.MustExec("drop placement policy if exists p1") + + tk.MustExec("create placement policy p2 primary_region='r2' regions='r2,r3'") + defer tk.MustExec("drop placement policy if exists p2") + + tk.MustExec("create placement policy p3 primary_region='r3' regions='r3,r4'") + defer tk.MustExec("drop placement policy if exists p3") + + // test recover + tk.MustExec(`CREATE TABLE tp1 (id INT) placement policy p1 PARTITION BY RANGE (id) ( + PARTITION p0 VALUES LESS THAN (100) placement policy p2, + PARTITION p1 VALUES LESS THAN (1000), + PARTITION p2 VALUES LESS THAN (10000) placement policy p3 + );`) + defer tk.MustExec("drop table if exists tp1") + + tk.MustExec("drop table tp1") + tk.MustExec("recover table tp1") + tk.MustQuery("show create table tp1").Check(testkit.Rows("tp1 CREATE TABLE `tp1` (\n" + + " `id` int(11) DEFAULT NULL\n" + + ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin /*T![placement] PRIMARY_REGION=\"r1\" REGIONS=\"r1,r2\" */\n" + + "PARTITION BY RANGE (`id`)\n" + + "(PARTITION `p0` VALUES LESS THAN (100) /*T![placement] PRIMARY_REGION=\"r2\" REGIONS=\"r2,r3\" */,\n" + + " PARTITION `p1` VALUES LESS THAN (1000),\n" + + " PARTITION `p2` VALUES LESS THAN (10000) /*T![placement] PRIMARY_REGION=\"r3\" REGIONS=\"r3,r4\" */)")) + checkExistTableBundlesInPD(c, s.dom, "test", "tp1") + + // test flashback + tk.MustExec(`CREATE TABLE tp2 (id INT) placement policy p1 PARTITION BY RANGE (id) ( + PARTITION p0 VALUES LESS THAN (100) placement policy p2, + PARTITION p1 VALUES LESS THAN (1000), + PARTITION p2 VALUES LESS THAN (10000) placement policy p3 + );`) + defer tk.MustExec("drop table if exists tp2") + + tk.MustExec("drop table tp1") + tk.MustExec("drop table tp2") + tk.MustExec("flashback table tp2") + tk.MustQuery("show create table tp2").Check(testkit.Rows("tp2 CREATE TABLE `tp2` (\n" + + " `id` int(11) DEFAULT NULL\n" + + ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin /*T![placement] PRIMARY_REGION=\"r1\" REGIONS=\"r1,r2\" */\n" + + "PARTITION BY RANGE (`id`)\n" + + "(PARTITION `p0` VALUES LESS THAN (100) /*T![placement] PRIMARY_REGION=\"r2\" REGIONS=\"r2,r3\" */,\n" + + " PARTITION `p1` VALUES LESS THAN (1000),\n" + + " PARTITION `p2` VALUES LESS THAN (10000) /*T![placement] PRIMARY_REGION=\"r3\" REGIONS=\"r3,r4\" */)")) + checkExistTableBundlesInPD(c, s.dom, "test", "tp2") + + // test recover after police drop + tk.MustExec("drop table tp2") + tk.MustExec("drop placement policy p1") + tk.MustExec("drop placement policy p2") + tk.MustExec("drop placement policy p3") + + tk.MustExec("flashback table tp2 to tp3") + tk.MustQuery("show create table tp3").Check(testkit.Rows("tp3 CREATE TABLE `tp3` (\n" + + " `id` int(11) DEFAULT NULL\n" + + ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin /*T![placement] PRIMARY_REGION=\"r1\" REGIONS=\"r1,r2\" */\n" + + "PARTITION BY RANGE (`id`)\n" + + "(PARTITION `p0` VALUES LESS THAN (100) /*T![placement] PRIMARY_REGION=\"r2\" REGIONS=\"r2,r3\" */,\n" + + " PARTITION `p1` VALUES LESS THAN (1000),\n" + + " PARTITION `p2` VALUES LESS THAN (10000) /*T![placement] PRIMARY_REGION=\"r3\" REGIONS=\"r3,r4\" */)")) + checkExistTableBundlesInPD(c, s.dom, "test", "tp3") +} diff --git a/ddl/table.go b/ddl/table.go index 504f85f83faed..cd2b818450c57 100644 --- a/ddl/table.go +++ b/ddl/table.go @@ -319,6 +319,19 @@ func (w *worker) onRecoverTable(d *ddlCtx, t *meta.Meta, job *model.Job) (ver in job.Args[checkFlagIndexInJobArgs] = recoverTableCheckFlagDisableGC } + bundles, err := placement.NewFullTableBundles(t, tblInfo) + if err != nil { + job.State = model.JobStateCancelled + return ver, errors.Trace(err) + } + + // Send the placement bundle to PD. + err = infosync.PutRuleBundlesWithDefaultRetry(context.TODO(), bundles) + if err != nil { + job.State = model.JobStateCancelled + return ver, errors.Wrapf(err, "failed to notify PD the placement rules") + } + job.SchemaState = model.StateWriteOnly tblInfo.State = model.StateWriteOnly ver, err = updateVersionAndTableInfo(t, job, tblInfo, false) diff --git a/executor/ddl.go b/executor/ddl.go index 4c2be5828b8a8..f7a8362680569 100644 --- a/executor/ddl.go +++ b/executor/ddl.go @@ -30,6 +30,7 @@ import ( "github.com/pingcap/tidb/parser/mysql" "github.com/pingcap/tidb/parser/terror" "github.com/pingcap/tidb/planner/core" + "github.com/pingcap/tidb/sessionctx" "github.com/pingcap/tidb/sessionctx/variable" "github.com/pingcap/tidb/table" "github.com/pingcap/tidb/table/temptable" @@ -621,6 +622,10 @@ func (e *DDLExec) executeRecoverTable(s *ast.RecoverTableStmt) error { return err } + if tblInfo, err = recoverTablePlacement(e.ctx, job.StartTS, tblInfo); err != nil { + return err + } + recoverInfo := &ddl.RecoverInfo{ SchemaID: job.SchemaID, TableInfo: tblInfo, @@ -635,6 +640,44 @@ func (e *DDLExec) executeRecoverTable(s *ast.RecoverTableStmt) error { return err } +// recoverTablePlacement is used when recover/flashback table. +// It will replace the placement policy of table with the direct options because the original policy may be deleted +func recoverTablePlacement(ctx sessionctx.Context, snapshotTS uint64, tblInfo *model.TableInfo) (*model.TableInfo, error) { + is, err := domain.GetDomain(ctx).GetSnapshotInfoSchema(snapshotTS) + if err != nil { + return nil, err + } + + if ref := tblInfo.PlacementPolicyRef; ref != nil { + policy, ok := is.PolicyByName(ref.Name) + if !ok || policy.ID != ref.ID { + return nil, errors.Errorf("Cannot find policy with name '%s', ID: '%s'", ref.Name, ref.ID) + } + tblInfo.PlacementPolicyRef = nil + tblInfo.DirectPlacementOpts = policy.PlacementSettings + } + + if tblInfo.Partition != nil { + for idx := range tblInfo.Partition.Definitions { + def := &tblInfo.Partition.Definitions[idx] + ref := def.PlacementPolicyRef + if ref == nil { + continue + } + + policy, ok := is.PolicyByName(ref.Name) + if !ok || policy.ID != ref.ID { + return nil, errors.Errorf("Cannot find policy with name '%s', ID: '%s'", ref.Name, ref.ID) + } + + def.PlacementPolicyRef = nil + def.DirectPlacementOpts = policy.PlacementSettings + } + } + + return tblInfo, nil +} + func (e *DDLExec) getRecoverTableByJobID(s *ast.RecoverTableStmt, t *meta.Meta, dom *domain.Domain) (*model.Job, *model.TableInfo, error) { job, err := t.GetHistoryDDLJob(s.JobID) if err != nil { @@ -787,6 +830,11 @@ func (e *DDLExec) executeFlashbackTable(s *ast.FlashBackTableStmt) error { if err != nil { return err } + + if tblInfo, err = recoverTablePlacement(e.ctx, job.StartTS, tblInfo); err != nil { + return err + } + recoverInfo := &ddl.RecoverInfo{ SchemaID: job.SchemaID, TableInfo: tblInfo, From 3ca0720a20f5f9137bbec3209a0f7393f2431a6a Mon Sep 17 00:00:00 2001 From: Chao Wang Date: Tue, 14 Dec 2021 17:03:16 +0800 Subject: [PATCH 2/3] modify --- executor/ddl.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/executor/ddl.go b/executor/ddl.go index f7a8362680569..740b894315d05 100644 --- a/executor/ddl.go +++ b/executor/ddl.go @@ -651,7 +651,7 @@ func recoverTablePlacement(ctx sessionctx.Context, snapshotTS uint64, tblInfo *m if ref := tblInfo.PlacementPolicyRef; ref != nil { policy, ok := is.PolicyByName(ref.Name) if !ok || policy.ID != ref.ID { - return nil, errors.Errorf("Cannot find policy with name '%s', ID: '%s'", ref.Name, ref.ID) + return nil, errors.Errorf("Cannot find policy with name '%s', ID: '%d'", ref.Name, ref.ID) } tblInfo.PlacementPolicyRef = nil tblInfo.DirectPlacementOpts = policy.PlacementSettings @@ -667,7 +667,7 @@ func recoverTablePlacement(ctx sessionctx.Context, snapshotTS uint64, tblInfo *m policy, ok := is.PolicyByName(ref.Name) if !ok || policy.ID != ref.ID { - return nil, errors.Errorf("Cannot find policy with name '%s', ID: '%s'", ref.Name, ref.ID) + return nil, errors.Errorf("Cannot find policy with name '%s', ID: '%d'", ref.Name, ref.ID) } def.PlacementPolicyRef = nil From 625e924f368474a3f36a72b79325a00a195c85aa Mon Sep 17 00:00:00 2001 From: Chao Wang Date: Thu, 16 Dec 2021 15:16:37 +0800 Subject: [PATCH 3/3] reuse snapshotMeta --- executor/ddl.go | 25 ++++++++++--------------- 1 file changed, 10 insertions(+), 15 deletions(-) diff --git a/executor/ddl.go b/executor/ddl.go index 740b894315d05..255580c04d93b 100644 --- a/executor/ddl.go +++ b/executor/ddl.go @@ -30,7 +30,6 @@ import ( "github.com/pingcap/tidb/parser/mysql" "github.com/pingcap/tidb/parser/terror" "github.com/pingcap/tidb/planner/core" - "github.com/pingcap/tidb/sessionctx" "github.com/pingcap/tidb/sessionctx/variable" "github.com/pingcap/tidb/table" "github.com/pingcap/tidb/table/temptable" @@ -622,7 +621,7 @@ func (e *DDLExec) executeRecoverTable(s *ast.RecoverTableStmt) error { return err } - if tblInfo, err = recoverTablePlacement(e.ctx, job.StartTS, tblInfo); err != nil { + if tblInfo, err = recoverTablePlacement(m, tblInfo); err != nil { return err } @@ -642,17 +641,13 @@ func (e *DDLExec) executeRecoverTable(s *ast.RecoverTableStmt) error { // recoverTablePlacement is used when recover/flashback table. // It will replace the placement policy of table with the direct options because the original policy may be deleted -func recoverTablePlacement(ctx sessionctx.Context, snapshotTS uint64, tblInfo *model.TableInfo) (*model.TableInfo, error) { - is, err := domain.GetDomain(ctx).GetSnapshotInfoSchema(snapshotTS) - if err != nil { - return nil, err - } - +func recoverTablePlacement(snapshotMeta *meta.Meta, tblInfo *model.TableInfo) (*model.TableInfo, error) { if ref := tblInfo.PlacementPolicyRef; ref != nil { - policy, ok := is.PolicyByName(ref.Name) - if !ok || policy.ID != ref.ID { - return nil, errors.Errorf("Cannot find policy with name '%s', ID: '%d'", ref.Name, ref.ID) + policy, err := snapshotMeta.GetPolicy(ref.ID) + if err != nil { + return nil, errors.Trace(err) } + tblInfo.PlacementPolicyRef = nil tblInfo.DirectPlacementOpts = policy.PlacementSettings } @@ -665,9 +660,9 @@ func recoverTablePlacement(ctx sessionctx.Context, snapshotTS uint64, tblInfo *m continue } - policy, ok := is.PolicyByName(ref.Name) - if !ok || policy.ID != ref.ID { - return nil, errors.Errorf("Cannot find policy with name '%s', ID: '%d'", ref.Name, ref.ID) + policy, err := snapshotMeta.GetPolicy(ref.ID) + if err != nil { + return nil, errors.Trace(err) } def.PlacementPolicyRef = nil @@ -831,7 +826,7 @@ func (e *DDLExec) executeFlashbackTable(s *ast.FlashBackTableStmt) error { return err } - if tblInfo, err = recoverTablePlacement(e.ctx, job.StartTS, tblInfo); err != nil { + if tblInfo, err = recoverTablePlacement(m, tblInfo); err != nil { return err }