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 }