github.com/pingcap/tiflow@v0.0.0-20240520035814-5bf52d54e205/dm/pkg/shardddl/pessimism/info_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 pessimism
    15  
    16  import (
    17  	"context"
    18  	"fmt"
    19  	"sync"
    20  	"testing"
    21  	"time"
    22  
    23  	. "github.com/pingcap/check"
    24  	"github.com/pingcap/tidb/pkg/util/dbutil"
    25  	"github.com/pingcap/tiflow/dm/common"
    26  	"github.com/pingcap/tiflow/dm/pkg/utils"
    27  	clientv3 "go.etcd.io/etcd/client/v3"
    28  	"go.etcd.io/etcd/tests/v3/integration"
    29  )
    30  
    31  var etcdTestCli *clientv3.Client
    32  
    33  func TestInfo(t *testing.T) {
    34  	integration.BeforeTestExternal(t)
    35  	mockCluster := integration.NewClusterV3(t, &integration.ClusterConfig{Size: 1})
    36  	defer mockCluster.Terminate(t)
    37  
    38  	etcdTestCli = mockCluster.RandClient()
    39  
    40  	TestingT(t)
    41  }
    42  
    43  // clear keys in etcd test cluster.
    44  func clearTestInfoOperation(c *C) {
    45  	clearInfo := clientv3.OpDelete(common.ShardDDLPessimismInfoKeyAdapter.Path(), clientv3.WithPrefix())
    46  	clearOp := clientv3.OpDelete(common.ShardDDLPessimismOperationKeyAdapter.Path(), clientv3.WithPrefix())
    47  	_, err := etcdTestCli.Txn(context.Background()).Then(clearInfo, clearOp).Commit()
    48  	c.Assert(err, IsNil)
    49  }
    50  
    51  type testForEtcd struct{}
    52  
    53  var _ = Suite(&testForEtcd{})
    54  
    55  func (t *testForEtcd) TestInfoJSON(c *C) {
    56  	i1 := NewInfo("test", "mysql-replica-1", "foo", "bar", []string{
    57  		"ALTER TABLE bar ADD COLUMN c1 INT",
    58  		"ALTER TABLE bar ADD COLUMN c2 INT",
    59  	})
    60  
    61  	j, err := i1.toJSON()
    62  	c.Assert(err, IsNil)
    63  	c.Assert(j, Equals, `{"task":"test","source":"mysql-replica-1","schema":"foo","table":"bar","ddls":["ALTER TABLE bar ADD COLUMN c1 INT","ALTER TABLE bar ADD COLUMN c2 INT"]}`)
    64  	c.Assert(j, Equals, i1.String())
    65  
    66  	i2, err := infoFromJSON(j)
    67  	c.Assert(err, IsNil)
    68  	c.Assert(i2, DeepEquals, i1)
    69  }
    70  
    71  func (t *testForEtcd) TestInfoEtcd(c *C) {
    72  	defer clearTestInfoOperation(c)
    73  
    74  	var (
    75  		source1 = "mysql-replica-1"
    76  		source2 = "mysql-replica-2"
    77  		task1   = "task-1"
    78  		task2   = "task-2"
    79  		i11     = NewInfo(task1, source1, "foo", "bar", []string{
    80  			"ALTER TABLE bar ADD COLUMN c1 INT",
    81  		})
    82  		i12 = NewInfo(task1, source2, "foo", "bar", []string{
    83  			"ALTER TABLE bar ADD COLUMN c2 INT",
    84  		})
    85  		i21 = NewInfo(task2, source1, "foo", "bar", []string{
    86  			"ALTER TABLE bar ADD COLUMN c3 INT",
    87  		})
    88  	)
    89  
    90  	// put the same key twice.
    91  	rev1, err := PutInfo(etcdTestCli, i11)
    92  	c.Assert(err, IsNil)
    93  	rev2, err := PutInfo(etcdTestCli, i11)
    94  	c.Assert(err, IsNil)
    95  	c.Assert(rev2, Greater, rev1)
    96  
    97  	// get with only 1 info.
    98  	ifm, rev3, err := GetAllInfo(etcdTestCli)
    99  	c.Assert(err, IsNil)
   100  	c.Assert(rev3, Equals, rev2)
   101  	c.Assert(ifm, HasLen, 1)
   102  	c.Assert(ifm, HasKey, task1)
   103  	c.Assert(ifm[task1], HasLen, 1)
   104  	c.Assert(ifm[task1][source1], DeepEquals, i11)
   105  
   106  	// put another key and get again with 2 info.
   107  	rev4, err := PutInfo(etcdTestCli, i12)
   108  	c.Assert(err, IsNil)
   109  	ifm, _, err = GetAllInfo(etcdTestCli)
   110  	c.Assert(err, IsNil)
   111  	c.Assert(ifm, HasLen, 1)
   112  	c.Assert(ifm, HasKey, task1)
   113  	c.Assert(ifm[task1], HasLen, 2)
   114  	c.Assert(ifm[task1][source1], DeepEquals, i11)
   115  	c.Assert(ifm[task1][source2], DeepEquals, i12)
   116  
   117  	// start the watcher.
   118  	wch := make(chan Info, 10)
   119  	ech := make(chan error, 10)
   120  	ctx, cancel := context.WithCancel(context.Background())
   121  	var wg sync.WaitGroup
   122  	wg.Add(1)
   123  	go func() {
   124  		defer wg.Done()
   125  		WatchInfoPut(ctx, etcdTestCli, rev4+1, wch, ech) // revision+1
   126  		close(wch)                                       // close the chan
   127  		close(ech)
   128  	}()
   129  
   130  	// put another key for a different task.
   131  	_, err = PutInfo(etcdTestCli, i21)
   132  	c.Assert(err, IsNil)
   133  	// wait response of WatchInfoPut, increase waiting time when resource shortage
   134  	utils.WaitSomething(10, 500*time.Millisecond, func() bool {
   135  		return len(wch) != 0
   136  	})
   137  	cancel()
   138  	wg.Wait()
   139  
   140  	// watch should only get i21.
   141  	c.Assert(len(wch), Equals, 1)
   142  	c.Assert(len(ech), Equals, 0)
   143  	c.Assert(<-wch, DeepEquals, i21)
   144  
   145  	// delete i12.
   146  	deleteOp := deleteInfoOp(i12)
   147  	_, err = etcdTestCli.Txn(context.Background()).Then(deleteOp).Commit()
   148  	c.Assert(err, IsNil)
   149  
   150  	// get again.
   151  	ifm, _, err = GetAllInfo(etcdTestCli)
   152  	c.Assert(err, IsNil)
   153  	c.Assert(ifm, HasLen, 2)
   154  	c.Assert(ifm, HasKey, task1)
   155  	c.Assert(ifm, HasKey, task2)
   156  	c.Assert(ifm[task1], HasLen, 1)
   157  	c.Assert(ifm[task1][source1], DeepEquals, i11)
   158  	c.Assert(ifm[task2], HasLen, 1)
   159  	c.Assert(ifm[task2][source1], DeepEquals, i21)
   160  }
   161  
   162  func (t *testForEtcd) TestPutInfoIfOpNotDone(c *C) {
   163  	defer clearTestInfoOperation(c)
   164  
   165  	var (
   166  		source = "mysql-replica-1"
   167  		task   = "test-put-info-if-no-op"
   168  		schema = "foo"
   169  		table  = "bar"
   170  		DDLs   = []string{"ALTER TABLE bar ADD COLUMN c1 INT"}
   171  		ID     = fmt.Sprintf("%s-%s", task, dbutil.TableName(schema, table))
   172  		info   = NewInfo(task, source, schema, table, DDLs)
   173  		op     = NewOperation(ID, task, source, DDLs, false, false)
   174  	)
   175  
   176  	// put info success because no operation exist.
   177  	rev1, putted, err := PutInfoIfOpNotDone(etcdTestCli, info)
   178  	c.Assert(err, IsNil)
   179  	c.Assert(rev1, Greater, int64(0))
   180  	c.Assert(putted, IsTrue)
   181  
   182  	// put a non-done operation.
   183  	rev2, putted, err := PutOperations(etcdTestCli, false, op)
   184  	c.Assert(err, IsNil)
   185  	c.Assert(rev2, Greater, rev1)
   186  	c.Assert(putted, IsTrue)
   187  
   188  	// still can put info.
   189  	rev3, putted, err := PutInfoIfOpNotDone(etcdTestCli, info)
   190  	c.Assert(err, IsNil)
   191  	c.Assert(rev3, Greater, rev2)
   192  	c.Assert(putted, IsTrue)
   193  
   194  	// change op to `done` and put it.
   195  	op.Done = true
   196  	rev4, putted, err := PutOperations(etcdTestCli, false, op)
   197  	c.Assert(err, IsNil)
   198  	c.Assert(rev4, Greater, rev3)
   199  	c.Assert(putted, IsTrue)
   200  
   201  	// can't put info anymore.
   202  	rev5, putted, err := PutInfoIfOpNotDone(etcdTestCli, info)
   203  	c.Assert(err, IsNil)
   204  	c.Assert(rev5, Equals, rev4)
   205  	c.Assert(putted, IsFalse)
   206  
   207  	// try put anther info, but still can't put it.
   208  	info.DDLs = []string{"ALTER TABLE bar ADD COLUMN c2 INT"}
   209  	rev6, putted, err := PutInfoIfOpNotDone(etcdTestCli, info)
   210  	c.Assert(err, IsNil)
   211  	c.Assert(rev6, Equals, rev5)
   212  	c.Assert(putted, IsFalse)
   213  }