github.com/pingcap/ticdc@v0.0.0-20220526033649-485a10ef2652/cdc/sink/causality.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 sink 15 16 import ( 17 "encoding/binary" 18 19 "github.com/pingcap/log" 20 "go.uber.org/zap" 21 22 "github.com/pingcap/ticdc/cdc/model" 23 ) 24 25 // causality provides a simple mechanism to improve the concurrency of SQLs execution under the premise of ensuring correctness. 26 // causality groups sqls that maybe contain causal relationships, and syncer executes them linearly. 27 // if some conflicts exist in more than one groups, then syncer waits all SQLs that are grouped be executed and reset causality. 28 // this mechanism meets quiescent consistency to ensure correctness. 29 type causality struct { 30 relations map[string]int 31 } 32 33 func newCausality() *causality { 34 return &causality{ 35 relations: make(map[string]int), 36 } 37 } 38 39 func (c *causality) add(keys [][]byte, idx int) { 40 if len(keys) == 0 { 41 return 42 } 43 44 for _, key := range keys { 45 c.relations[string(key)] = idx 46 } 47 } 48 49 func (c *causality) reset() { 50 c.relations = make(map[string]int) 51 } 52 53 // detectConflict detects whether there is a conflict 54 func (c *causality) detectConflict(keys [][]byte) (bool, int) { 55 if len(keys) == 0 { 56 return false, 0 57 } 58 59 firstIdx := -1 60 for _, key := range keys { 61 if idx, ok := c.relations[string(key)]; ok { 62 if firstIdx == -1 { 63 firstIdx = idx 64 } else if firstIdx != idx { 65 return true, -1 66 } 67 } 68 } 69 70 return firstIdx != -1, firstIdx 71 } 72 73 func genTxnKeys(txn *model.SingleTableTxn) [][]byte { 74 if len(txn.Rows) == 0 { 75 return nil 76 } 77 keysSet := make(map[string]struct{}, len(txn.Rows)) 78 for _, row := range txn.Rows { 79 rowKeys := genRowKeys(row) 80 for _, key := range rowKeys { 81 keysSet[string(key)] = struct{}{} 82 } 83 } 84 keys := make([][]byte, 0, len(keysSet)) 85 for key := range keysSet { 86 keys = append(keys, []byte(key)) 87 } 88 return keys 89 } 90 91 func genRowKeys(row *model.RowChangedEvent) [][]byte { 92 var keys [][]byte 93 if len(row.Columns) != 0 { 94 for iIdx, idxCol := range row.IndexColumns { 95 key := genKeyList(row.Columns, iIdx, idxCol, row.Table.TableID) 96 if len(key) == 0 { 97 continue 98 } 99 keys = append(keys, key) 100 } 101 } 102 if len(row.PreColumns) != 0 { 103 for iIdx, idxCol := range row.IndexColumns { 104 key := genKeyList(row.PreColumns, iIdx, idxCol, row.Table.TableID) 105 if len(key) == 0 { 106 continue 107 } 108 keys = append(keys, key) 109 } 110 } 111 if len(keys) == 0 { 112 // use table ID as key if no key generated (no PK/UK), 113 // no concurrence for rows in the same table. 114 log.Debug("use table id as the key", zap.Int64("tableID", row.Table.TableID)) 115 tableKey := make([]byte, 8) 116 binary.BigEndian.PutUint64(tableKey, uint64(row.Table.TableID)) 117 keys = [][]byte{tableKey} 118 } 119 return keys 120 } 121 122 func genKeyList(columns []*model.Column, iIdx int, colIdx []int, tableID int64) []byte { 123 var key []byte 124 for _, i := range colIdx { 125 // if a column value is null, we can ignore this index 126 // If the index contain generated column, we can't use this key to detect conflict with other DML, 127 // Because such as insert can't specified the generated value. 128 if columns[i] == nil || columns[i].Value == nil || columns[i].Flag.IsGeneratedColumn() { 129 return nil 130 } 131 key = append(key, []byte(model.ColumnValueString(columns[i].Value))...) 132 key = append(key, 0) 133 } 134 if len(key) == 0 { 135 return nil 136 } 137 tableKey := make([]byte, 16) 138 binary.BigEndian.PutUint64(tableKey[:8], uint64(iIdx)) 139 binary.BigEndian.PutUint64(tableKey[8:], uint64(tableID)) 140 key = append(key, tableKey...) 141 return key 142 }