github.com/pingcap/ticdc@v0.0.0-20220526033649-485a10ef2652/pkg/cyclic/mark/mark.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 mark 15 16 import ( 17 "context" 18 "database/sql" 19 "fmt" 20 "strings" 21 22 "github.com/go-sql-driver/mysql" 23 "github.com/pingcap/errors" 24 "github.com/pingcap/log" 25 cerror "github.com/pingcap/ticdc/pkg/errors" 26 "github.com/pingcap/ticdc/pkg/quotes" 27 "github.com/pingcap/ticdc/pkg/security" 28 "go.uber.org/zap" 29 ) 30 31 const ( 32 // SchemaName is the name of schema where all mark tables are created 33 SchemaName string = "tidb_cdc" 34 tableName string = "repl_mark" 35 36 // CyclicReplicaIDCol is the name of replica ID in mark tables 37 CyclicReplicaIDCol string = "replica_id" 38 39 // OptCyclicConfig is the key that adds to changefeed options 40 // automatically is cyclic replication is on. 41 OptCyclicConfig string = "_cyclic_relax_sql_mode" 42 ) 43 44 // GetMarkTableName returns mark table name regards to the tableID 45 func GetMarkTableName(sourceSchema, sourceTable string) (schema, table string) { // nolint:exported 46 // TODO(neil) better unquote or just crc32 the name. 47 table = strings.Join([]string{tableName, sourceSchema, sourceTable}, "_") 48 schema = SchemaName 49 return 50 } 51 52 // IsMarkTable tells whether the table is a mark table or not. 53 func IsMarkTable(schema, table string) bool { 54 const quoteSchemaName = "`" + SchemaName + "`" 55 const quotetableName = "`" + tableName 56 57 if schema == SchemaName || schema == quoteSchemaName { 58 return true 59 } 60 if strings.HasPrefix(table, quotetableName) { 61 return true 62 } 63 return strings.HasPrefix(table, tableName) 64 } 65 66 // TableName is an interface gets schema and table name. 67 // Note it is only used for avoiding import model.TableName. 68 type TableName interface { 69 GetSchema() string 70 GetTable() string 71 } 72 73 // CreateMarkTables creates mark table regard to the table name. 74 // 75 // Note table name is only for avoid write hotspot there is *NO* guarantee 76 // normal tables and mark tables are one:one map. 77 func CreateMarkTables(ctx context.Context, upstreamDSN string, upstreamCred *security.Credential, tables ...TableName) error { 78 tlsCfg, err := upstreamCred.ToTLSConfig() 79 if err != nil { 80 return cerror.WrapError(cerror.ErrCreateMarkTableFailed, 81 errors.Annotate(err, "fail to open upstream TiDB connection")) 82 } 83 if tlsCfg != nil { 84 tlsName := "cli-marktable" 85 err = mysql.RegisterTLSConfig(tlsName, tlsCfg) 86 if err != nil { 87 return cerror.WrapError(cerror.ErrCreateMarkTableFailed, 88 errors.Annotate(err, "fail to open upstream TiDB connection")) 89 } 90 if strings.Contains(upstreamDSN, "?") && strings.Contains(upstreamDSN, "=") { 91 upstreamDSN += ("&tls=" + tlsName) 92 } else { 93 upstreamDSN += ("?tls=" + tlsName) 94 } 95 } 96 db, err := sql.Open("mysql", upstreamDSN) 97 if err != nil { 98 return cerror.WrapError(cerror.ErrCreateMarkTableFailed, 99 errors.Annotate(err, "Open upsteam database connection failed")) 100 } 101 err = db.PingContext(ctx) 102 if err != nil { 103 return cerror.WrapError(cerror.ErrCreateMarkTableFailed, 104 errors.Annotate(err, "fail to open upstream TiDB connection")) 105 } 106 107 userTableCount := 0 108 for _, name := range tables { 109 if IsMarkTable(name.GetSchema(), name.GetTable()) { 110 continue 111 } 112 userTableCount++ 113 schema, table := GetMarkTableName(name.GetSchema(), name.GetTable()) 114 _, err = db.ExecContext(ctx, fmt.Sprintf("CREATE DATABASE IF NOT EXISTS %s;", schema)) 115 if err != nil { 116 return cerror.WrapError(cerror.ErrCreateMarkTableFailed, 117 errors.Annotate(err, "fail to create mark database")) 118 } 119 _, err = db.ExecContext(ctx, fmt.Sprintf( 120 `CREATE TABLE IF NOT EXISTS %s 121 ( 122 bucket INT NOT NULL, 123 %s BIGINT UNSIGNED NOT NULL, 124 val BIGINT DEFAULT 0, 125 start_timestamp BIGINT DEFAULT 0, 126 PRIMARY KEY (bucket, %s) 127 );`, quotes.QuoteSchema(schema, table), CyclicReplicaIDCol, CyclicReplicaIDCol)) 128 if err != nil { 129 return cerror.WrapError(cerror.ErrCreateMarkTableFailed, 130 errors.Annotatef(err, "fail to create mark table %s", table)) 131 } 132 } 133 log.Info("create upstream mark done", zap.Int("count", userTableCount)) 134 return nil 135 }