github.com/pingcap/tiflow@v0.0.0-20240520035814-5bf52d54e205/dm/pkg/shardddl/pessimism/operation_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  	"time"
    20  
    21  	. "github.com/pingcap/check"
    22  	"go.etcd.io/etcd/api/v3/mvccpb"
    23  	clientv3 "go.etcd.io/etcd/client/v3"
    24  )
    25  
    26  func (t *testForEtcd) TestOperationJSON(c *C) {
    27  	o1 := NewOperation("test-ID", "test", "mysql-replica-1", []string{
    28  		"ALTER TABLE bar ADD COLUMN c1 INT",
    29  	}, true, false)
    30  
    31  	j, err := o1.toJSON()
    32  	c.Assert(err, IsNil)
    33  	c.Assert(j, Equals, `{"id":"test-ID","task":"test","source":"mysql-replica-1","ddls":["ALTER TABLE bar ADD COLUMN c1 INT"],"exec":true,"done":false}`)
    34  	c.Assert(j, Equals, o1.String())
    35  
    36  	o2, err := operationFromJSON(j)
    37  	c.Assert(err, IsNil)
    38  	c.Assert(o2, DeepEquals, o1)
    39  }
    40  
    41  func watchExactOperations(
    42  	ctx context.Context, cli *clientv3.Client, watchTp mvccpb.Event_EventType,
    43  	task, source string, revision int64, opCnt int,
    44  ) ([]Operation, error) {
    45  	opCh := make(chan Operation, 10)
    46  	errCh := make(chan error, 10)
    47  	done := make(chan struct{})
    48  	subCtx, cancel := context.WithCancel(ctx)
    49  	go func() {
    50  		watchOperation(subCtx, cli, watchTp, task, source, revision, opCh, errCh)
    51  		close(done)
    52  	}()
    53  	defer func() {
    54  		cancel()
    55  		<-done
    56  	}()
    57  	var ops []Operation
    58  	for i := 0; i < opCnt; i++ {
    59  		select {
    60  		case op := <-opCh:
    61  			ops = append(ops, op)
    62  		case err := <-errCh:
    63  			return nil, err
    64  		case <-ctx.Done():
    65  			return nil, ctx.Err()
    66  		}
    67  	}
    68  	// Wait 100ms to check if there is unexpected operation.
    69  	select {
    70  	case op := <-opCh:
    71  		return nil, fmt.Errorf("unpexecped operation %s", op.String())
    72  	case <-time.After(time.Millisecond * 100):
    73  	}
    74  	return ops, nil
    75  }
    76  
    77  func (t *testForEtcd) TestOperationEtcd(c *C) {
    78  	defer clearTestInfoOperation(c)
    79  
    80  	var (
    81  		task1   = "test1"
    82  		task2   = "test2"
    83  		ID1     = "test1-`foo`.`bar`"
    84  		ID2     = "test2-`foo`.`bar`"
    85  		source1 = "mysql-replica-1"
    86  		source2 = "mysql-replica-2"
    87  		source3 = "mysql-replica-3"
    88  		DDLs    = []string{"ALTER TABLE bar ADD COLUMN c1 INT"}
    89  		op11    = NewOperation(ID1, task1, source1, DDLs, true, false)
    90  		op12    = NewOperation(ID1, task1, source2, DDLs, true, false)
    91  		op13    = NewOperation(ID1, task1, source3, DDLs, true, false)
    92  		op21    = NewOperation(ID2, task2, source1, DDLs, false, true)
    93  	)
    94  
    95  	// put the same keys twice.
    96  	rev1, succ, err := PutOperations(etcdTestCli, false, op11, op12)
    97  	c.Assert(err, IsNil)
    98  	c.Assert(succ, IsTrue)
    99  	rev2, succ, err := PutOperations(etcdTestCli, false, op11, op12)
   100  	c.Assert(err, IsNil)
   101  	c.Assert(succ, IsTrue)
   102  	c.Assert(rev2, Greater, rev1)
   103  
   104  	// start the watcher with the same revision as the last PUT for the specified task and source.
   105  	ops, err := watchExactOperations(context.Background(), etcdTestCli, mvccpb.PUT, task1, source1, rev2, 1)
   106  	c.Assert(err, IsNil)
   107  	// watch should only get op11.
   108  	c.Assert(ops[0], DeepEquals, op11)
   109  
   110  	// put for another task.
   111  	rev3, succ, err := PutOperations(etcdTestCli, false, op21)
   112  	c.Assert(err, IsNil)
   113  	c.Assert(succ, IsTrue)
   114  
   115  	// start the watch with an older revision for all tasks and sources.
   116  	ops, err = watchExactOperations(context.Background(), etcdTestCli, mvccpb.PUT, "", "", rev2, 3)
   117  	c.Assert(err, IsNil)
   118  	// watch should get 3 operations.
   119  	c.Assert(ops[0], DeepEquals, op11)
   120  	c.Assert(ops[1], DeepEquals, op12)
   121  	c.Assert(ops[2], DeepEquals, op21)
   122  
   123  	// get all operations.
   124  	opm, rev4, err := GetAllOperations(etcdTestCli)
   125  	c.Assert(err, IsNil)
   126  	c.Assert(rev4, Equals, rev3)
   127  	c.Assert(opm, HasLen, 2)
   128  	c.Assert(opm, HasKey, task1)
   129  	c.Assert(opm, HasKey, task2)
   130  	c.Assert(opm[task1], HasLen, 2)
   131  	c.Assert(opm[task1][source1], DeepEquals, op11)
   132  	c.Assert(opm[task1][source2], DeepEquals, op12)
   133  	c.Assert(opm[task2], HasLen, 1)
   134  	c.Assert(opm[task2][source1], DeepEquals, op21)
   135  
   136  	// put for `skipDone` with `done` in etcd, the operations should not be skipped.
   137  	// case: all of kvs "the `done` field is not `true`".
   138  	rev5, succ, err := PutOperations(etcdTestCli, true, op11, op12)
   139  	c.Assert(err, IsNil)
   140  	c.Assert(succ, IsTrue)
   141  	c.Assert(rev5, Greater, rev4)
   142  
   143  	// delete op11.
   144  	rev6, err := DeleteOperations(etcdTestCli, op11)
   145  	c.Assert(err, IsNil)
   146  	c.Assert(rev6, Greater, rev5)
   147  
   148  	// start watch with an older revision for the deleted op11.
   149  	ops, err = watchExactOperations(context.Background(), etcdTestCli, mvccpb.DELETE, op11.Task, op11.Source, rev5, 1)
   150  	c.Assert(err, IsNil)
   151  	// watch should got the previous deleted operation.
   152  	op11d := ops[0]
   153  	c.Assert(op11d.IsDeleted, IsTrue)
   154  	op11d.IsDeleted = false // reset to false
   155  	c.Assert(op11d, DeepEquals, op11)
   156  
   157  	// get again, op11 should be deleted.
   158  	opm, _, err = GetAllOperations(etcdTestCli)
   159  	c.Assert(err, IsNil)
   160  	c.Assert(opm[task1], HasLen, 1)
   161  	c.Assert(opm[task1][source2], DeepEquals, op12)
   162  
   163  	// put for `skipDone` with `done` in etcd, the operations should not be skipped.
   164  	// case: all of kvs "not exist".
   165  	rev7, succ, err := PutOperations(etcdTestCli, true, op11, op13)
   166  	c.Assert(err, IsNil)
   167  	c.Assert(succ, IsTrue)
   168  	c.Assert(rev7, Greater, rev6)
   169  
   170  	// get again, op11 and op13 should be putted.
   171  	opm, _, err = GetAllOperations(etcdTestCli)
   172  	c.Assert(err, IsNil)
   173  	c.Assert(opm[task1], HasLen, 3)
   174  	c.Assert(opm[task1][source1], DeepEquals, op11)
   175  	c.Assert(opm[task1][source2], DeepEquals, op12)
   176  	c.Assert(opm[task1][source3], DeepEquals, op13)
   177  
   178  	// update op12 to `done`.
   179  	op12c := op12
   180  	op12c.Done = true
   181  	putOp, err := putOperationOp(op12c)
   182  	c.Assert(err, IsNil)
   183  	txnResp, err := etcdTestCli.Txn(context.Background()).Then(putOp).Commit()
   184  	c.Assert(err, IsNil)
   185  
   186  	// delete op13.
   187  	rev8, err := DeleteOperations(etcdTestCli, op13)
   188  	c.Assert(err, IsNil)
   189  	c.Assert(rev8, Greater, txnResp.Header.Revision)
   190  
   191  	// put for `skipDone` with `done` in etcd, the operations should be skipped.
   192  	// case: any of kvs ("exist" and "the `done` field is `true`").
   193  	rev9, succ, err := PutOperations(etcdTestCli, true, op12, op13)
   194  	c.Assert(err, IsNil)
   195  	c.Assert(succ, IsFalse)
   196  	c.Assert(rev9, Equals, rev8)
   197  
   198  	// get again, op13 not putted.
   199  	opm, _, err = GetAllOperations(etcdTestCli)
   200  	c.Assert(err, IsNil)
   201  	c.Assert(opm[task1], HasLen, 2)
   202  	c.Assert(opm[task1][source1], DeepEquals, op11)
   203  	c.Assert(opm[task1][source2], DeepEquals, op12c)
   204  
   205  	// FIXME: the right result:
   206  	//   the operations should *NOT* be skipped.
   207  	// case:
   208  	//   - some of kvs "exist" and "the `done` field is not `true`"
   209  	//   - some of kvs "not exist"
   210  	// after FIXED, this test case will fail and need to be updated.
   211  	rev10, succ, err := PutOperations(etcdTestCli, true, op11, op13)
   212  	c.Assert(err, IsNil)
   213  	c.Assert(succ, IsFalse)
   214  	c.Assert(rev10, Equals, rev9)
   215  }