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  }