github.com/pingcap/ticdc@v0.0.0-20220526033649-485a10ef2652/pkg/cyclic/replication.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 cyclic contains scaffolds for implementing cyclic replication 15 // among mutliple TiDB clusters/MySQL. It uses a mark table to identify and 16 // filter duplicate DMLs. 17 // CDC needs to watch DMLs to mark tables and ignore all DDLs to mark tables. 18 // 19 // Note for now, mark tables must be create manually. 20 package cyclic 21 22 import ( 23 "fmt" 24 "strings" 25 26 "github.com/pingcap/ticdc/cdc/model" 27 "github.com/pingcap/ticdc/pkg/config" 28 "github.com/pingcap/ticdc/pkg/cyclic/mark" 29 "github.com/pingcap/ticdc/pkg/quotes" 30 ) 31 32 // RelaxSQLMode returns relaxed SQL mode, "STRICT_TRANS_TABLES" is removed. 33 func RelaxSQLMode(oldMode string) string { 34 toRemove := "STRICT_TRANS_TABLES" 35 36 if !strings.Contains(oldMode, toRemove) { 37 return oldMode 38 } 39 40 // concatenated by "," like: mode1,mode2 41 modes := strings.Split(oldMode, ",") 42 var newMode string 43 for idx := range modes { 44 m := modes[idx] 45 if strings.Contains(m, toRemove) { 46 continue 47 } 48 m = strings.TrimSpace(m) 49 if newMode == "" { 50 newMode = m 51 } else { 52 newMode = strings.Join([]string{newMode, modes[idx]}, ",") 53 } 54 } 55 return newMode 56 } 57 58 // Cyclic wraps a cyclic config. 59 type Cyclic struct { 60 config config.CyclicConfig 61 } 62 63 // UdpateSourceTableCyclicMark return a DML to update mark table regrad to 64 // the source table name, bucket and replicaID. 65 func (*Cyclic) UdpateSourceTableCyclicMark(sourceSchema, sourceTable string, bucket, replicaID uint64, startTs uint64) string { 66 schema, table := mark.GetMarkTableName(sourceSchema, sourceTable) 67 return fmt.Sprintf( 68 `INSERT INTO %s VALUES (%d, %d, 0, %d) ON DUPLICATE KEY UPDATE val = val + 1;`, 69 quotes.QuoteSchema(schema, table), bucket, replicaID, startTs) 70 } 71 72 // Enabled returns whether cyclic replication is enabled 73 func (c *Cyclic) Enabled() bool { 74 return c.config.Enable 75 } 76 77 // FilterReplicaID return a slice of replica IDs needs to be filtered. 78 func (c *Cyclic) FilterReplicaID() []uint64 { 79 return c.config.FilterReplicaID 80 } 81 82 // ReplicaID return a replica ID of this cluster. 83 func (c *Cyclic) ReplicaID() uint64 { 84 return c.config.ReplicaID 85 } 86 87 // NewCyclic creates a cyclic 88 func NewCyclic(config *config.CyclicConfig) *Cyclic { 89 if config == nil || config.ReplicaID == 0 { 90 return nil 91 } 92 return &Cyclic{ 93 config: *config, 94 } 95 } 96 97 // IsTablesPaired checks if normal tables are paired with mark tables. 98 func IsTablesPaired(tables []model.TableName) bool { 99 normalTables := make([]model.TableName, 0, len(tables)/2) 100 markMap := make(map[model.TableName]struct{}, len(tables)/2) 101 for _, table := range tables { 102 if mark.IsMarkTable(table.Schema, table.Table) { 103 markMap[table] = struct{}{} 104 } else { 105 normalTables = append(normalTables, table) 106 } 107 } 108 for _, table := range normalTables { 109 markTable := model.TableName{} 110 markTable.Schema, markTable.Table = mark.GetMarkTableName(table.Schema, table.Table) 111 _, ok := markMap[markTable] 112 if !ok { 113 return false 114 } 115 } 116 return true 117 }