github.com/pingcap/tiflow@v0.0.0-20240520035814-5bf52d54e205/dm/relay/meta_test.go (about) 1 // Copyright 2019 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 relay 15 16 import ( 17 "fmt" 18 "os" 19 "path" 20 "strings" 21 22 "github.com/go-mysql-org/go-mysql/mysql" 23 . "github.com/pingcap/check" 24 "github.com/pingcap/tiflow/dm/pkg/gtid" 25 ) 26 27 var _ = Suite(&testMetaSuite{}) 28 29 type testMetaSuite struct{} 30 31 type MetaTestCase struct { 32 uuid string 33 uuidWithSuffix string 34 pos mysql.Position 35 gset mysql.GTIDSet 36 } 37 38 func (r *testMetaSuite) TestLocalMeta(c *C) { 39 dir := c.MkDir() 40 41 gset0, _ := gtid.ParserGTID("mysql", "") 42 gset1, _ := gtid.ParserGTID("mysql", "85ab69d1-b21f-11e6-9c5e-64006a8978d2:1-12") 43 gset2, _ := gtid.ParserGTID("mysql", "85ab69d1-b21f-11e6-9c5e-64006a8978d2:13-23") 44 gset3, _ := gtid.ParserGTID("mysql", "85ab69d1-b21f-11e6-9c5e-64006a8978d2:24-33") 45 gset4, _ := gtid.ParserGTID("mysql", "85ab69d1-b21f-11e6-9c5e-64006a8978d2:34-46") 46 gset5, _ := gtid.ParserGTID("mysql", "85ab69d1-b21f-11e6-9c5e-64006a8978d2:45-56") 47 48 cases := []MetaTestCase{ 49 { 50 uuid: "server-a-uuid", 51 uuidWithSuffix: "server-a-uuid.000001", 52 pos: mysql.Position{Name: "mysql-bin.000003", Pos: 123}, 53 gset: gset1, 54 }, 55 { 56 uuid: "server-b-uuid", 57 uuidWithSuffix: "server-b-uuid.000002", 58 pos: mysql.Position{Name: "mysql-bin.000001", Pos: 234}, 59 gset: gset2, 60 }, 61 { 62 uuid: "server-b-uuid", // server-b-uuid again 63 uuidWithSuffix: "server-b-uuid.000003", 64 pos: mysql.Position{Name: "mysql-bin.000002", Pos: 345}, 65 gset: gset3, 66 }, 67 { 68 uuid: "server-c-uuid", 69 uuidWithSuffix: "server-c-uuid.000004", 70 pos: mysql.Position{Name: "mysql-bin.000004", Pos: 678}, 71 gset: gset4, 72 }, 73 } 74 75 // load, but empty 76 lm := NewLocalMeta("mysql", dir) 77 err := lm.Load() 78 c.Assert(err, IsNil) 79 80 uuid, pos := lm.Pos() 81 c.Assert(uuid, Equals, "") 82 c.Assert(pos, DeepEquals, minCheckpoint) 83 uuid, gset := lm.GTID() 84 c.Assert(uuid, Equals, "") 85 c.Assert(gset, DeepEquals, gset0) 86 87 err = lm.Save(minCheckpoint, nil) 88 c.Assert(err, NotNil) 89 90 err = lm.Flush() 91 c.Assert(err, NotNil) 92 93 dirty := lm.Dirty() 94 c.Assert(dirty, IsFalse) 95 96 // set currentSubDir because lm.doFlush need it 97 currentUUID := "uuid.000001" 98 c.Assert(os.MkdirAll(path.Join(dir, currentUUID), 0o777), IsNil) 99 setLocalMetaWithCurrentUUID := func() { 100 lm = NewLocalMeta("mysql", dir) 101 lm.(*LocalMeta).currentSubDir = currentUUID 102 } 103 104 // adjust to start pos 105 setLocalMetaWithCurrentUUID() 106 latestBinlogName := "mysql-bin.000009" 107 latestGTIDStr := "85ab69d1-b21f-11e6-9c5e-64006a8978d2:45-57" 108 cs0 := cases[0] 109 adjusted, err := lm.AdjustWithStartPos(cs0.pos.Name, cs0.gset.String(), false, latestBinlogName, latestGTIDStr) 110 c.Assert(err, IsNil) 111 c.Assert(adjusted, IsTrue) 112 uuid, pos = lm.Pos() 113 c.Assert(uuid, Equals, currentUUID) 114 c.Assert(pos.Name, Equals, cs0.pos.Name) 115 uuid, gset = lm.GTID() 116 c.Assert(uuid, Equals, currentUUID) 117 c.Assert(gset.String(), Equals, "") 118 119 // adjust to start pos with enableGTID 120 setLocalMetaWithCurrentUUID() 121 adjusted, err = lm.AdjustWithStartPos(cs0.pos.Name, cs0.gset.String(), true, latestBinlogName, latestGTIDStr) 122 c.Assert(err, IsNil) 123 c.Assert(adjusted, IsTrue) 124 uuid, pos = lm.Pos() 125 c.Assert(uuid, Equals, currentUUID) 126 c.Assert(pos.Name, Equals, cs0.pos.Name) 127 uuid, gset = lm.GTID() 128 c.Assert(uuid, Equals, currentUUID) 129 c.Assert(gset, DeepEquals, cs0.gset) 130 131 // adjust to the last binlog if start pos is empty 132 setLocalMetaWithCurrentUUID() 133 adjusted, err = lm.AdjustWithStartPos("", cs0.gset.String(), false, latestBinlogName, latestGTIDStr) 134 c.Assert(err, IsNil) 135 c.Assert(adjusted, IsTrue) 136 uuid, pos = lm.Pos() 137 c.Assert(uuid, Equals, currentUUID) 138 c.Assert(pos.Name, Equals, latestBinlogName) 139 uuid, gset = lm.GTID() 140 c.Assert(uuid, Equals, currentUUID) 141 c.Assert(gset.String(), Equals, "") 142 143 setLocalMetaWithCurrentUUID() 144 adjusted, err = lm.AdjustWithStartPos("", "", true, latestBinlogName, latestGTIDStr) 145 c.Assert(err, IsNil) 146 c.Assert(adjusted, IsTrue) 147 uuid, pos = lm.Pos() 148 c.Assert(uuid, Equals, currentUUID) 149 c.Assert(pos.Name, Equals, latestBinlogName) 150 uuid, gset = lm.GTID() 151 c.Assert(uuid, Equals, currentUUID) 152 c.Assert(gset.String(), Equals, latestGTIDStr) 153 154 // reset 155 lm.(*LocalMeta).currentSubDir = "" 156 157 for _, cs := range cases { 158 err = lm.AddDir(cs.uuid, nil, nil, 0) 159 c.Assert(err, IsNil) 160 161 err = lm.Save(cs.pos, cs.gset) 162 c.Assert(err, IsNil) 163 164 currentUUID2, pos2 := lm.Pos() 165 c.Assert(currentUUID2, Equals, cs.uuidWithSuffix) 166 c.Assert(pos2, DeepEquals, cs.pos) 167 168 currentUUID, gset = lm.GTID() 169 c.Assert(currentUUID, Equals, cs.uuidWithSuffix) 170 c.Assert(gset, DeepEquals, cs.gset) 171 172 dirty = lm.Dirty() 173 c.Assert(dirty, IsTrue) 174 175 currentDir := lm.Dir() 176 c.Assert(strings.HasSuffix(currentDir, cs.uuidWithSuffix), IsTrue) 177 } 178 179 err = lm.Flush() 180 c.Assert(err, IsNil) 181 182 dirty = lm.Dirty() 183 c.Assert(dirty, IsFalse) 184 185 // try adjust to start pos again 186 csn1 := cases[len(cases)-1] 187 adjusted, err = lm.AdjustWithStartPos(cs0.pos.Name, cs0.gset.String(), false, "", "") 188 c.Assert(err, IsNil) 189 c.Assert(adjusted, IsFalse) 190 uuid, pos = lm.Pos() 191 c.Assert(uuid, Equals, csn1.uuidWithSuffix) 192 c.Assert(pos.Name, Equals, csn1.pos.Name) 193 uuid, gset = lm.GTID() 194 c.Assert(uuid, Equals, csn1.uuidWithSuffix) 195 c.Assert(gset, DeepEquals, csn1.gset) 196 197 // create a new LocalMeta, and load it 198 lm2 := NewLocalMeta("mysql", dir) 199 err = lm2.Load() 200 c.Assert(err, IsNil) 201 202 lastCase := cases[len(cases)-1] 203 204 uuid, pos = lm2.Pos() 205 c.Assert(uuid, Equals, lastCase.uuidWithSuffix) 206 c.Assert(pos, DeepEquals, lastCase.pos) 207 uuid, gset = lm2.GTID() 208 c.Assert(uuid, Equals, lastCase.uuidWithSuffix) 209 c.Assert(gset, DeepEquals, lastCase.gset) 210 211 // another case for AddDir, specify pos and GTID 212 cs := MetaTestCase{ 213 uuid: "server-c-uuid", 214 uuidWithSuffix: "server-c-uuid.000005", 215 pos: mysql.Position{Name: "mysql-bin.000005", Pos: 789}, 216 gset: gset5, 217 } 218 err = lm.AddDir(cs.uuid, &cs.pos, cs.gset, 0) 219 c.Assert(err, IsNil) 220 221 dirty = lm.Dirty() 222 c.Assert(dirty, IsFalse) 223 224 currentUUID, pos = lm.Pos() 225 c.Assert(currentUUID, Equals, cs.uuidWithSuffix) 226 c.Assert(pos, DeepEquals, cs.pos) 227 228 currentUUID, gset = lm.GTID() 229 c.Assert(currentUUID, Equals, cs.uuidWithSuffix) 230 c.Assert(gset, DeepEquals, cs.gset) 231 232 currentDir := lm.Dir() 233 c.Assert(strings.HasSuffix(currentDir, cs.uuidWithSuffix), IsTrue) 234 } 235 236 func (r *testMetaSuite) TestLocalMetaPotentialDataRace(c *C) { 237 var err error 238 lm := NewLocalMeta("mysql", "/FAKE_DIR") 239 uuidStr := "85ab69d1-b21f-11e6-9c5e-64006a8978d2" 240 initGSet, _ := gtid.ParserGTID("mysql", fmt.Sprintf("%s:1", uuidStr)) 241 lm.(*LocalMeta).currentSubDir = uuidStr 242 err = lm.Save( 243 mysql.Position{Name: "mysql-bin.000001", Pos: 234}, 244 initGSet, 245 ) 246 c.Assert(err, IsNil) 247 248 ch1 := make(chan error) 249 ch2 := make(chan error) 250 pendingCh := make(chan struct{}) 251 go func() { 252 <-pendingCh 253 var err error 254 defer func() { 255 ch1 <- err 256 }() 257 var theMGSet mysql.GTIDSet 258 for i := 2; i < 100; i++ { 259 theMGSet, err = mysql.ParseGTIDSet("mysql", fmt.Sprintf("%s:1-%d", uuidStr, i*10)) 260 if err != nil { 261 return 262 } 263 264 lastGTID := theMGSet 265 if err != nil { 266 return 267 } 268 err = lm.Save( 269 mysql.Position{Name: fmt.Sprintf("mysql-bin.%06d", i), Pos: 123}, 270 lastGTID, 271 ) 272 if err != nil { 273 return 274 } 275 } 276 }() 277 var gtidString string 278 go func() { 279 <-pendingCh 280 var err error 281 defer func() { 282 ch2 <- err 283 }() 284 for i := 0; i < 100; i++ { 285 _, currentGTID := lm.GTID() 286 gtidString = currentGTID.String() 287 } 288 }() 289 close(pendingCh) 290 ch1Err := <-ch1 291 ch2Err := <-ch2 292 c.Assert(ch1Err, IsNil) 293 c.Assert(ch2Err, IsNil) 294 c.Logf("GTID string from the go routine: %s", gtidString) 295 }