github.com/pingcap/tiflow@v0.0.0-20240520035814-5bf52d54e205/dm/syncer/shardddl/optimist_test.go (about) 1 // Copyright 2020 PingCAP, Inc. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // See the License for the specific language governing permissions and 12 // limitations under the License. 13 14 package shardddl 15 16 import ( 17 "context" 18 "fmt" 19 20 . "github.com/pingcap/check" 21 tiddl "github.com/pingcap/tidb/pkg/ddl" 22 "github.com/pingcap/tidb/pkg/parser" 23 "github.com/pingcap/tidb/pkg/parser/ast" 24 "github.com/pingcap/tidb/pkg/parser/model" 25 "github.com/pingcap/tidb/pkg/sessionctx" 26 "github.com/pingcap/tidb/pkg/util/mock" 27 "github.com/pingcap/tiflow/dm/pkg/log" 28 "github.com/pingcap/tiflow/dm/pkg/shardddl/optimism" 29 "github.com/pingcap/tiflow/dm/pkg/terror" 30 ) 31 32 type testOptimist struct{} 33 34 var _ = Suite(&testOptimist{}) 35 36 // clear keys in etcd test cluster. 37 func clearOptimistTestSourceInfoOperation(c *C) { 38 c.Assert(optimism.ClearTestInfoOperationColumn(etcdTestCli), IsNil) 39 } 40 41 func createTableInfo(c *C, p *parser.Parser, se sessionctx.Context, tableID int64, sql string) *model.TableInfo { 42 node, err := p.ParseOneStmt(sql, "utf8mb4", "utf8mb4_bin") 43 if err != nil { 44 c.Fatalf("fail to parse stmt, %v", err) 45 } 46 createStmtNode, ok := node.(*ast.CreateTableStmt) 47 if !ok { 48 c.Fatalf("%s is not a CREATE TABLE statement", sql) 49 } 50 info, err := tiddl.MockTableInfo(se, createStmtNode, tableID) 51 if err != nil { 52 c.Fatalf("fail to create table info, %v", err) 53 } 54 return info 55 } 56 57 func (t *testOptimist) TestOptimist(c *C) { 58 defer clearOptimistTestSourceInfoOperation(c) 59 60 var ( 61 task = "task-optimist" 62 source = "mysql-replicate-1" 63 sourceTables = map[string]map[string]map[string]map[string]struct{}{ 64 "foo": {"bar": { 65 "foo-1": {"bar-1": struct{}{}, "bar-2": struct{}{}}, 66 "foo-2": {"bar-3": struct{}{}, "bar-4": struct{}{}}, 67 }}, 68 } 69 downSchema, downTable = "foo", "bar" 70 ID = fmt.Sprintf("%s-`%s`.`%s`", task, downSchema, downTable) 71 72 logger = log.L() 73 o = NewOptimist(&logger, etcdTestCli, task, source) 74 75 p = parser.New() 76 se = mock.NewContext() 77 tblID int64 = 222 78 DDLs1 = []string{"ALTER TABLE bar ADD COLUMN c1 TEXT"} 79 DDLs2 = []string{"ALTER TABLE bar ADD COLUMN c1 DATETIME"} 80 tiBefore = createTableInfo(c, p, se, tblID, `CREATE TABLE bar (id INT PRIMARY KEY)`) 81 tiAfter1 = createTableInfo(c, p, se, tblID, `CREATE TABLE bar (id INT PRIMARY KEY, c1 TEXT)`) 82 tiAfter2 = createTableInfo(c, p, se, tblID, `CREATE TABLE bar (id INT PRIMARY KEY, c1 DATETIME)`) 83 info1 = o.ConstructInfo("foo-1", "bar-1", downSchema, downTable, DDLs1, tiBefore, []*model.TableInfo{tiAfter1}) 84 op1 = optimism.NewOperation(ID, task, source, info1.UpSchema, info1.UpTable, DDLs1, optimism.ConflictNone, "", false, []string{}) 85 info2 = o.ConstructInfo("foo-1", "bar-2", downSchema, downTable, DDLs2, tiBefore, []*model.TableInfo{tiAfter2}) 86 op2 = optimism.NewOperation(ID, task, source, info2.UpSchema, info2.UpTable, DDLs2, optimism.ConflictDetected, terror.ErrShardDDLOptimismTrySyncFail.Generate(ID, "conflict").Error(), false, []string{}) 87 88 infoCreate = o.ConstructInfo("foo-new", "bar-new", downSchema, downTable, 89 []string{`CREATE TABLE bar (id INT PRIMARY KEY)`}, tiBefore, []*model.TableInfo{tiBefore}) // same table info. 90 infoDrop = o.ConstructInfo("foo-new", "bar-new", downSchema, downTable, 91 []string{`DROP TABLE bar`}, nil, nil) // both table infos are nil. 92 ) 93 94 ctx, cancel := context.WithCancel(context.Background()) 95 defer cancel() 96 97 tables := o.Tables() 98 c.Assert(len(tables), Equals, 0) 99 100 // init with some source tables. 101 err := o.Init(sourceTables) 102 c.Assert(err, IsNil) 103 stm, _, err := optimism.GetAllSourceTables(etcdTestCli) 104 c.Assert(err, IsNil) 105 c.Assert(stm, HasLen, 1) 106 c.Assert(stm[task], HasLen, 1) 107 c.Assert(stm[task][source], DeepEquals, o.tables) 108 109 tables = o.Tables() 110 c.Assert(len(tables), Equals, 4) 111 112 // no info and operation in pending. 113 c.Assert(o.PendingInfo(), IsNil) 114 c.Assert(o.PendingOperation(), IsNil) 115 116 // put shard DDL info. 117 rev1, err := o.PutInfo(info1) 118 c.Assert(err, IsNil) 119 c.Assert(rev1, Greater, int64(0)) 120 121 // have info in pending. 122 info1c := o.PendingInfo() 123 c.Assert(info1c, NotNil) 124 c.Assert(*info1c, DeepEquals, info1) 125 126 // put the lock operation. 127 rev2, putted, err := optimism.PutOperation(etcdTestCli, false, op1, rev1) 128 c.Assert(err, IsNil) 129 c.Assert(rev2, Greater, rev1) 130 c.Assert(putted, IsTrue) 131 132 // wait for the lock operation. 133 op1c, err := o.GetOperation(ctx, info1, rev1) 134 c.Assert(err, IsNil) 135 op1.Revision = rev2 136 c.Assert(op1c, DeepEquals, op1) 137 138 // have operation in pending. 139 op1cc := o.PendingOperation() 140 c.Assert(op1cc, NotNil) 141 c.Assert(*op1cc, DeepEquals, op1) 142 143 // mark the operation as done. 144 c.Assert(o.DoneOperation(op1), IsNil) 145 146 // verify the operation and info. 147 ifm, _, err := optimism.GetAllInfo(etcdTestCli) 148 c.Assert(err, IsNil) 149 c.Assert(ifm, HasLen, 1) 150 c.Assert(ifm[task], HasLen, 1) 151 c.Assert(ifm[task][source], HasLen, 1) 152 c.Assert(ifm[task][source][info1.UpSchema], HasLen, 1) 153 info1WithVer := info1 154 info1WithVer.Version = 1 155 info1WithVer.Revision = rev1 156 c.Assert(ifm[task][source][info1.UpSchema][info1.UpTable], DeepEquals, info1WithVer) 157 opc := op1c 158 opc.Done = true 159 opm, _, err := optimism.GetAllOperations(etcdTestCli) 160 c.Assert(err, IsNil) 161 c.Assert(opm, HasLen, 1) 162 c.Assert(opm[task], HasLen, 1) 163 c.Assert(opm[task][source], HasLen, 1) 164 c.Assert(opm[task][source][op1.UpSchema], HasLen, 1) 165 // Revision is in DoneOperation, skip this check 166 opc.Revision = opm[task][source][op1.UpSchema][op1.UpTable].Revision 167 c.Assert(opm[task][source][op1.UpSchema][op1.UpTable], DeepEquals, opc) 168 169 // no info and operation in pending now. 170 c.Assert(o.PendingInfo(), IsNil) 171 c.Assert(o.PendingOperation(), IsNil) 172 173 // handle `CREATE TABLE`. 174 rev3, err := o.AddTable(infoCreate) 175 c.Assert(err, IsNil) 176 c.Assert(rev3, Greater, rev2) 177 178 // handle `DROP TABLE`. 179 rev4, err := o.RemoveTable(infoDrop) 180 c.Assert(err, IsNil) 181 c.Assert(rev4, Greater, rev3) 182 ifm, _, err = optimism.GetAllInfo(etcdTestCli) 183 c.Assert(err, IsNil) 184 c.Assert(ifm[task][source][infoDrop.UpSchema], IsNil) 185 c.Assert(o.tables.Tables[infoCreate.DownSchema][infoCreate.DownTable][infoCreate.UpSchema], IsNil) 186 187 // put another info. 188 rev5, err := o.PutInfo(info2) 189 c.Assert(err, IsNil) 190 c.Assert(o.PendingInfo(), NotNil) 191 c.Assert(*o.PendingInfo(), DeepEquals, info2) 192 c.Assert(o.PendingOperation(), IsNil) 193 194 // put another lock operation. 195 rev6, putted, err := optimism.PutOperation(etcdTestCli, false, op2, rev5) 196 c.Assert(err, IsNil) 197 c.Assert(rev6, Greater, rev5) 198 c.Assert(putted, IsTrue) 199 // wait for the lock operation. 200 _, err = o.GetOperation(ctx, info2, rev5) 201 c.Assert(err, IsNil) 202 c.Assert(o.PendingOperation(), NotNil) 203 op2.Revision = rev6 204 c.Assert(*o.PendingOperation(), DeepEquals, op2) 205 206 // reset the optimist. 207 o.Reset() 208 c.Assert(o.PendingInfo(), IsNil) 209 c.Assert(o.PendingOperation(), IsNil) 210 }