github.com/pingcap/ticdc@v0.0.0-20220526033649-485a10ef2652/tests/multi_source/main.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 main 15 16 import ( 17 "context" 18 "database/sql" 19 "flag" 20 "fmt" 21 "os" 22 "reflect" 23 "runtime" 24 "strings" 25 "sync" 26 "time" 27 28 "go.uber.org/zap" 29 30 "github.com/pingcap/errors" 31 "github.com/pingcap/log" 32 "github.com/pingcap/ticdc/tests/util" 33 ) 34 35 func main() { 36 cfg := util.NewConfig() 37 err := cfg.Parse(os.Args[1:]) 38 switch errors.Cause(err) { 39 case nil: 40 case flag.ErrHelp: 41 os.Exit(0) 42 default: 43 log.S().Errorf("parse cmd flags err %s\n", err) 44 os.Exit(2) 45 } 46 47 sourceDB0, err := util.CreateDB(cfg.SourceDBCfg[0]) 48 if err != nil { 49 log.S().Fatal(err) 50 } 51 defer func() { 52 if err := util.CloseDB(sourceDB0); err != nil { 53 log.S().Errorf("Failed to close source database: %s\n", err) 54 } 55 }() 56 sourceDB1, err := util.CreateDB(cfg.SourceDBCfg[1]) 57 if err != nil { 58 log.S().Fatal(err) 59 } 60 defer func() { 61 if err := util.CloseDB(sourceDB1); err != nil { 62 log.S().Errorf("Failed to close source database: %s\n", err) 63 } 64 }() 65 ctx, cancel := context.WithCancel(context.Background()) 66 defer cancel() 67 go switchAsyncCommit(ctx, sourceDB0) 68 util.MustExec(sourceDB0, "create database mark;") 69 runDDLTest([]*sql.DB{sourceDB0, sourceDB1}) 70 util.MustExec(sourceDB0, "create table mark.finish_mark(a int primary key);") 71 } 72 73 // for every DDL, run the DDL continuously, and one goroutine for one TiDB instance to do some DML op 74 func runDDLTest(srcs []*sql.DB) { 75 runTime := time.Second * 5 76 start := time.Now() 77 defer func() { 78 log.S().Infof("runDDLTest take %v", time.Since(start)) 79 }() 80 81 for i, ddlFunc := range []func(context.Context, *sql.DB){ 82 createDropSchemaDDL, truncateDDL, addDropColumnDDL, 83 modifyColumnDDL, addDropIndexDDL, 84 } { 85 testName := getFunctionName(ddlFunc) 86 log.S().Info("running ddl test: ", i, " ", testName) 87 88 var wg sync.WaitGroup 89 ctx, cancel := context.WithTimeout(context.Background(), runTime) 90 91 for idx, src := range srcs { 92 wg.Add(1) 93 go func(i int, s *sql.DB) { 94 dml(ctx, s, testName, i) 95 wg.Done() 96 }(idx, src) 97 } 98 99 time.Sleep(time.Millisecond) 100 101 wg.Add(1) 102 go func() { 103 ddlFunc(ctx, srcs[0]) 104 wg.Done() 105 }() 106 107 wg.Wait() 108 time.Sleep(5 * time.Second) 109 cancel() 110 111 util.MustExec(srcs[0], fmt.Sprintf("create table mark.finish_mark_%d(a int primary key);", i)) 112 } 113 } 114 115 func switchAsyncCommit(ctx context.Context, db *sql.DB) { 116 ticker := time.NewTicker(5 * time.Second) 117 defer ticker.Stop() 118 enabled := false 119 for { 120 select { 121 case <-ctx.Done(): 122 return 123 case <-ticker.C: 124 if enabled { 125 util.MustExec(db, "set global tidb_enable_async_commit = off") 126 } else { 127 util.MustExec(db, "set global tidb_enable_async_commit = on") 128 } 129 enabled = !enabled 130 } 131 } 132 } 133 134 func getFunctionName(i interface{}) string { 135 strs := strings.Split(runtime.FuncForPC(reflect.ValueOf(i).Pointer()).Name(), ".") 136 return strs[len(strs)-1] 137 } 138 139 func createDropSchemaDDL(ctx context.Context, db *sql.DB) { 140 testName := getFunctionName(createDropSchemaDDL) 141 /* 142 mysql> use test; 143 Database changed 144 mysql> create table test1(id int); 145 Query OK, 0 rows affected (0.05 sec) 146 147 mysql> drop database test; 148 Query OK, 3 rows affected (0.02 sec) 149 150 mysql> create database test; 151 Query OK, 1 row affected (0.02 sec) 152 153 mysql> create table test1(id int); 154 ERROR 1046 (3D000): No database selected 155 */ 156 // drop the database used will make the session become No database selected 157 // this make later code use *sql.DB* fail as expected 158 // so we setback the used db before close the conn 159 conn, err := db.Conn(ctx) 160 if err != nil { 161 log.S().Fatal(err) 162 } 163 defer func() { 164 conn.Close() 165 }() 166 167 for { 168 mustCreateTableWithConn(ctx, conn, testName) 169 select { 170 case <-ctx.Done(): 171 return 172 default: 173 } 174 time.Sleep(100 * time.Millisecond) 175 util.MustExecWithConn(ctx, conn, "drop database test") 176 } 177 } 178 179 func truncateDDL(ctx context.Context, db *sql.DB) { 180 testName := getFunctionName(truncateDDL) 181 mustCreateTable(db, testName) 182 183 sql := fmt.Sprintf("truncate table test.`%s`", testName) 184 for { 185 select { 186 case <-ctx.Done(): 187 return 188 default: 189 } 190 util.MustExec(db, sql) 191 time.Sleep(100 * time.Millisecond) 192 } 193 } 194 195 func ignoreableError(err error) bool { 196 knownErrorList := []string{ 197 "Error 1146:", // table doesn't exist 198 "Error 1049", // database doesn't exist 199 } 200 for _, e := range knownErrorList { 201 if strings.HasPrefix(err.Error(), e) { 202 return true 203 } 204 } 205 return false 206 } 207 208 func dml(ctx context.Context, db *sql.DB, table string, id int) { 209 var err error 210 var i int 211 var insertSuccess int 212 var deleteSuccess int 213 insertSQL := fmt.Sprintf("insert into test.`%s`(id1, id2) values(?,?)", table) 214 deleteSQL := fmt.Sprintf("delete from test.`%s` where id1 = ? or id2 = ?", table) 215 for i = 0; ; i++ { 216 _, err = db.Exec(insertSQL, i+id*100000000, i+id*100000000+1) 217 if err == nil { 218 insertSuccess++ 219 if insertSuccess%100 == 0 { 220 log.S().Info(id, " insert success: ", insertSuccess) 221 } 222 } 223 if err != nil && !ignoreableError(err) { 224 log.Fatal("unexpected error when executing sql", zap.Error(err)) 225 } 226 227 if i%2 == 0 { 228 result, err := db.Exec(deleteSQL, i+id*100000000, i+id*100000000+1) 229 if err == nil { 230 rows, _ := result.RowsAffected() 231 if rows != 0 { 232 deleteSuccess++ 233 if deleteSuccess%100 == 0 { 234 log.S().Info(id, " delete success: ", deleteSuccess) 235 } 236 } 237 } 238 if err != nil && !ignoreableError(err) { 239 log.Fatal("unexpected error when executing sql", zap.Error(err)) 240 } 241 } 242 243 select { 244 case <-ctx.Done(): 245 return 246 default: 247 } 248 } 249 } 250 251 func addDropColumnDDL(ctx context.Context, db *sql.DB) { 252 testName := getFunctionName(addDropColumnDDL) 253 mustCreateTable(db, testName) 254 255 for value := 1; ; value++ { 256 select { 257 case <-ctx.Done(): 258 return 259 default: 260 } 261 sql := fmt.Sprintf("alter table test.`%s` drop column v1", testName) 262 util.MustExec(db, sql) 263 time.Sleep(100 * time.Millisecond) 264 265 var notNULL string 266 var defaultValue interface{} 267 268 if value%5 == 0 { 269 // use default <value> not null 270 notNULL = "not null" 271 defaultValue = value 272 } else if value%5 == 1 { 273 // use default null 274 defaultValue = nil 275 } else { 276 // use default <value> 277 defaultValue = value 278 } 279 sql = fmt.Sprintf("alter table test.`%s` add column v1 int default ? %s", testName, notNULL) 280 util.MustExec(db, sql, defaultValue) 281 time.Sleep(100 * time.Millisecond) 282 } 283 } 284 285 func modifyColumnDDL(ctx context.Context, db *sql.DB) { 286 testName := getFunctionName(modifyColumnDDL) 287 mustCreateTable(db, testName) 288 sql := fmt.Sprintf("alter table test.`%s` modify column v1 int default ?", testName) 289 for value := 1; ; value++ { 290 select { 291 case <-ctx.Done(): 292 return 293 default: 294 } 295 296 var defaultValue interface{} 297 // use default null per five modify 298 if value%5 == 0 { 299 defaultValue = nil 300 } else { 301 defaultValue = value 302 } 303 util.MustExec(db, sql, defaultValue) 304 time.Sleep(100 * time.Millisecond) 305 } 306 } 307 308 func addDropIndexDDL(ctx context.Context, db *sql.DB) { 309 testName := getFunctionName(addDropIndexDDL) 310 mustCreateTable(db, testName) 311 312 for value := 1; ; value++ { 313 select { 314 case <-ctx.Done(): 315 return 316 default: 317 } 318 sql := fmt.Sprintf("drop index id1 on test.`%s`;", testName) 319 util.MustExec(db, sql) 320 time.Sleep(100 * time.Millisecond) 321 322 sql = fmt.Sprintf("create unique index `id1` on test.`%s` (id1);", testName) 323 util.MustExec(db, sql) 324 time.Sleep(100 * time.Millisecond) 325 326 sql = fmt.Sprintf("drop index id2 on test.`%s`;", testName) 327 util.MustExec(db, sql) 328 time.Sleep(100 * time.Millisecond) 329 330 sql = fmt.Sprintf("create unique index `id2` on test.`%s` (id2);", testName) 331 util.MustExec(db, sql) 332 time.Sleep(100 * time.Millisecond) 333 } 334 } 335 336 const ( 337 createDatabaseSQL = "create database if not exists test" 338 createTableSQL = ` 339 create table if not exists test.%s 340 ( 341 id1 int unique key not null, 342 id2 int unique key not null, 343 v1 int default null 344 ) 345 ` 346 ) 347 348 func mustCreateTable(db *sql.DB, tableName string) { 349 util.MustExec(db, createDatabaseSQL) 350 sql := fmt.Sprintf(createTableSQL, tableName) 351 util.MustExec(db, sql) 352 } 353 354 func mustCreateTableWithConn(ctx context.Context, conn *sql.Conn, tableName string) { 355 util.MustExecWithConn(ctx, conn, createDatabaseSQL) 356 sql := fmt.Sprintf(createTableSQL, tableName) 357 util.MustExecWithConn(ctx, conn, sql) 358 }