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 }