github.com/pingcap/tiflow@v0.0.0-20240520035814-5bf52d54e205/dm/syncer/shardddl/pessimist_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  	"testing"
    19  	"time"
    20  
    21  	. "github.com/pingcap/check"
    22  	"github.com/pingcap/tiflow/dm/common"
    23  	"github.com/pingcap/tiflow/dm/pkg/log"
    24  	"github.com/pingcap/tiflow/dm/pkg/shardddl/pessimism"
    25  	clientv3 "go.etcd.io/etcd/client/v3"
    26  	"go.etcd.io/etcd/tests/v3/integration"
    27  )
    28  
    29  var etcdTestCli *clientv3.Client
    30  
    31  type testPessimist struct{}
    32  
    33  var _ = Suite(&testPessimist{})
    34  
    35  func TestShardDDL(t *testing.T) {
    36  	err := log.InitLogger(&log.Config{})
    37  	if err != nil {
    38  		t.Fatal(err)
    39  	}
    40  
    41  	integration.BeforeTestExternal(t)
    42  	mockCluster := integration.NewClusterV3(t, &integration.ClusterConfig{Size: 1})
    43  	defer mockCluster.Terminate(t)
    44  
    45  	etcdTestCli = mockCluster.RandClient()
    46  
    47  	TestingT(t)
    48  }
    49  
    50  // clear keys in etcd test cluster.
    51  func clearTestInfoOperation(c *C) {
    52  	clearInfo := clientv3.OpDelete(common.ShardDDLPessimismInfoKeyAdapter.Path(), clientv3.WithPrefix())
    53  	clearOp := clientv3.OpDelete(common.ShardDDLPessimismOperationKeyAdapter.Path(), clientv3.WithPrefix())
    54  	_, err := etcdTestCli.Txn(context.Background()).Then(clearInfo, clearOp).Commit()
    55  	c.Assert(err, IsNil)
    56  }
    57  
    58  func (t *testPessimist) TestPessimist(c *C) {
    59  	defer clearTestInfoOperation(c)
    60  
    61  	var (
    62  		task          = "task"
    63  		source        = "mysql-replicate-1"
    64  		schema, table = "foo", "bar"
    65  		DDLs          = []string{"ALTER TABLE bar ADD COLUMN c1 INT"}
    66  		ID            = "task-`foo`.`bar`"
    67  		op            = pessimism.NewOperation(ID, task, source, DDLs, true, false)
    68  
    69  		logger = log.L()
    70  		p      = NewPessimist(&logger, etcdTestCli, task, source)
    71  		info   = p.ConstructInfo(schema, table, DDLs)
    72  	)
    73  
    74  	ctx, cancel := context.WithCancel(context.Background())
    75  	defer cancel()
    76  
    77  	// no info and operation in pending
    78  	c.Assert(p.PendingInfo(), IsNil)
    79  	c.Assert(p.PendingOperation(), IsNil)
    80  
    81  	// put shard DDL info.
    82  	rev1, err := p.PutInfo(ctx, info)
    83  	c.Assert(err, IsNil)
    84  	c.Assert(rev1, Greater, int64(0))
    85  
    86  	// have info in pending
    87  	info2 := p.PendingInfo()
    88  	c.Assert(info2, NotNil)
    89  	c.Assert(*info2, DeepEquals, info)
    90  
    91  	// put the lock operation.
    92  	rev2, putted, err := pessimism.PutOperations(etcdTestCli, false, op)
    93  	c.Assert(err, IsNil)
    94  	c.Assert(rev2, Greater, rev1)
    95  	c.Assert(putted, IsTrue)
    96  
    97  	// wait for the lock operation.
    98  	op2, err := p.GetOperation(ctx, info, rev1)
    99  	c.Assert(err, IsNil)
   100  	c.Assert(op2, DeepEquals, op)
   101  
   102  	// have operation in pending.
   103  	op3 := p.PendingOperation()
   104  	c.Assert(op3, NotNil)
   105  	c.Assert(*op3, DeepEquals, op)
   106  
   107  	// mark the operation as done and delete the info.
   108  	c.Assert(p.DoneOperationDeleteInfo(op, info), IsNil)
   109  	// make this op reentrant.
   110  	c.Assert(p.DoneOperationDeleteInfo(op, info), IsNil)
   111  
   112  	// verify the operation and info.
   113  	opc := op2
   114  	opc.Done = true
   115  	opm, _, err := pessimism.GetAllOperations(etcdTestCli)
   116  	c.Assert(err, IsNil)
   117  	c.Assert(opm, HasLen, 1)
   118  	c.Assert(opm[task], HasLen, 1)
   119  	c.Assert(opm[task][source], DeepEquals, opc)
   120  	ifm, _, err := pessimism.GetAllInfo(etcdTestCli)
   121  	c.Assert(err, IsNil)
   122  	c.Assert(ifm, HasLen, 0)
   123  
   124  	// no info and operation in pending now.
   125  	c.Assert(p.PendingInfo(), IsNil)
   126  	c.Assert(p.PendingOperation(), IsNil)
   127  
   128  	// try to put info again, but timeout because a `done` operation exist in etcd.
   129  	ctx2, cancel2 := context.WithTimeout(ctx, time.Second)
   130  	defer cancel2()
   131  	_, err = p.PutInfo(ctx2, info)
   132  	c.Assert(err, Equals, context.DeadlineExceeded)
   133  
   134  	// start a goroutine to delete the `done` operation in background, then we can put info again.
   135  	go func() {
   136  		time.Sleep(500 * time.Millisecond) // wait `PutInfo` to start watch the deletion of the operation.
   137  		_, err2 := pessimism.DeleteOperations(etcdTestCli, op)
   138  		c.Assert(err2, IsNil)
   139  	}()
   140  
   141  	// put info again, but do not complete the flow.
   142  	_, err = p.PutInfo(ctx, info)
   143  	c.Assert(err, IsNil)
   144  	c.Assert(p.PendingInfo(), NotNil)
   145  
   146  	// put the lock operation again.
   147  	rev3, _, err := pessimism.PutOperations(etcdTestCli, false, op)
   148  	c.Assert(err, IsNil)
   149  	// wait for the lock operation.
   150  	_, err = p.GetOperation(ctx, info, rev3)
   151  	c.Assert(err, IsNil)
   152  	c.Assert(p.PendingOperation(), NotNil)
   153  
   154  	// reset the pessimist.
   155  	p.Reset()
   156  	c.Assert(p.PendingInfo(), IsNil)
   157  	c.Assert(p.PendingOperation(), IsNil)
   158  }