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  }