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  }