github.com/pingcap/br@v5.3.0-alpha.0.20220125034240-ec59c7b6ce30+incompatible/pkg/checksum/executor.go (about) 1 // Copyright 2020 PingCAP, Inc. Licensed under Apache-2.0. 2 3 package checksum 4 5 import ( 6 "context" 7 8 "github.com/pingcap/br/pkg/metautil" 9 10 "github.com/gogo/protobuf/proto" 11 "github.com/pingcap/errors" 12 "github.com/pingcap/log" 13 "github.com/pingcap/parser/model" 14 "github.com/pingcap/tidb/distsql" 15 "github.com/pingcap/tidb/kv" 16 "github.com/pingcap/tidb/sessionctx/variable" 17 "github.com/pingcap/tidb/tablecodec" 18 "github.com/pingcap/tidb/util/ranger" 19 "github.com/pingcap/tipb/go-tipb" 20 "go.uber.org/zap" 21 ) 22 23 // ExecutorBuilder is used to build a "kv.Request". 24 type ExecutorBuilder struct { 25 table *model.TableInfo 26 ts uint64 27 28 oldTable *metautil.Table 29 30 concurrency uint 31 } 32 33 // NewExecutorBuilder returns a new executor builder. 34 func NewExecutorBuilder(table *model.TableInfo, ts uint64) *ExecutorBuilder { 35 return &ExecutorBuilder{ 36 table: table, 37 ts: ts, 38 39 concurrency: variable.DefDistSQLScanConcurrency, 40 } 41 } 42 43 // SetOldTable set a old table info to the builder. 44 func (builder *ExecutorBuilder) SetOldTable(oldTable *metautil.Table) *ExecutorBuilder { 45 builder.oldTable = oldTable 46 return builder 47 } 48 49 // SetConcurrency set the concurrency of the checksum executing. 50 func (builder *ExecutorBuilder) SetConcurrency(conc uint) *ExecutorBuilder { 51 builder.concurrency = conc 52 return builder 53 } 54 55 // Build builds a checksum executor. 56 func (builder *ExecutorBuilder) Build() (*Executor, error) { 57 reqs, err := buildChecksumRequest(builder.table, builder.oldTable, builder.ts, builder.concurrency) 58 if err != nil { 59 return nil, errors.Trace(err) 60 } 61 return &Executor{reqs: reqs}, nil 62 } 63 64 func buildChecksumRequest( 65 newTable *model.TableInfo, 66 oldTable *metautil.Table, 67 startTS uint64, 68 concurrency uint, 69 ) ([]*kv.Request, error) { 70 var partDefs []model.PartitionDefinition 71 if part := newTable.Partition; part != nil { 72 partDefs = part.Definitions 73 } 74 75 reqs := make([]*kv.Request, 0, (len(newTable.Indices)+1)*(len(partDefs)+1)) 76 var oldTableID int64 77 if oldTable != nil { 78 oldTableID = oldTable.Info.ID 79 } 80 rs, err := buildRequest(newTable, newTable.ID, oldTable, oldTableID, startTS, concurrency) 81 if err != nil { 82 return nil, errors.Trace(err) 83 } 84 reqs = append(reqs, rs...) 85 86 for _, partDef := range partDefs { 87 var oldPartID int64 88 if oldTable != nil { 89 for _, oldPartDef := range oldTable.Info.Partition.Definitions { 90 if oldPartDef.Name == partDef.Name { 91 oldPartID = oldPartDef.ID 92 } 93 } 94 } 95 rs, err := buildRequest(newTable, partDef.ID, oldTable, oldPartID, startTS, concurrency) 96 if err != nil { 97 return nil, errors.Trace(err) 98 } 99 reqs = append(reqs, rs...) 100 } 101 102 return reqs, nil 103 } 104 105 func buildRequest( 106 tableInfo *model.TableInfo, 107 tableID int64, 108 oldTable *metautil.Table, 109 oldTableID int64, 110 startTS uint64, 111 concurrency uint, 112 ) ([]*kv.Request, error) { 113 reqs := make([]*kv.Request, 0) 114 req, err := buildTableRequest(tableInfo, tableID, oldTable, oldTableID, startTS, concurrency) 115 if err != nil { 116 return nil, errors.Trace(err) 117 } 118 reqs = append(reqs, req) 119 120 for _, indexInfo := range tableInfo.Indices { 121 if indexInfo.State != model.StatePublic { 122 continue 123 } 124 var oldIndexInfo *model.IndexInfo 125 if oldTable != nil { 126 for _, oldIndex := range oldTable.Info.Indices { 127 if oldIndex.Name == indexInfo.Name { 128 oldIndexInfo = oldIndex 129 break 130 } 131 } 132 if oldIndexInfo == nil { 133 log.Panic("index not found in origin table, "+ 134 "please check the restore table has the same index info with origin table", 135 zap.Int64("table id", tableID), 136 zap.Stringer("table name", tableInfo.Name), 137 zap.Int64("origin table id", oldTableID), 138 zap.Stringer("origin table name", oldTable.Info.Name), 139 zap.Stringer("index name", indexInfo.Name)) 140 } 141 } 142 req, err = buildIndexRequest( 143 tableID, indexInfo, oldTableID, oldIndexInfo, startTS, concurrency) 144 if err != nil { 145 return nil, errors.Trace(err) 146 } 147 reqs = append(reqs, req) 148 } 149 150 return reqs, nil 151 } 152 153 func buildTableRequest( 154 tableInfo *model.TableInfo, 155 tableID int64, 156 oldTable *metautil.Table, 157 oldTableID int64, 158 startTS uint64, 159 concurrency uint, 160 ) (*kv.Request, error) { 161 var rule *tipb.ChecksumRewriteRule 162 if oldTable != nil { 163 rule = &tipb.ChecksumRewriteRule{ 164 OldPrefix: tablecodec.GenTableRecordPrefix(oldTableID), 165 NewPrefix: tablecodec.GenTableRecordPrefix(tableID), 166 } 167 } 168 169 checksum := &tipb.ChecksumRequest{ 170 ScanOn: tipb.ChecksumScanOn_Table, 171 Algorithm: tipb.ChecksumAlgorithm_Crc64_Xor, 172 Rule: rule, 173 } 174 175 var ranges []*ranger.Range 176 if tableInfo.IsCommonHandle { 177 ranges = ranger.FullNotNullRange() 178 } else { 179 ranges = ranger.FullIntRange(false) 180 } 181 182 var builder distsql.RequestBuilder 183 // Use low priority to reducing impact to other requests. 184 builder.Request.Priority = kv.PriorityLow 185 return builder.SetHandleRanges(nil, tableID, tableInfo.IsCommonHandle, ranges, nil). 186 SetStartTS(startTS). 187 SetChecksumRequest(checksum). 188 SetConcurrency(int(concurrency)). 189 Build() 190 } 191 192 func buildIndexRequest( 193 tableID int64, 194 indexInfo *model.IndexInfo, 195 oldTableID int64, 196 oldIndexInfo *model.IndexInfo, 197 startTS uint64, 198 concurrency uint, 199 ) (*kv.Request, error) { 200 var rule *tipb.ChecksumRewriteRule 201 if oldIndexInfo != nil { 202 rule = &tipb.ChecksumRewriteRule{ 203 OldPrefix: tablecodec.EncodeTableIndexPrefix(oldTableID, oldIndexInfo.ID), 204 NewPrefix: tablecodec.EncodeTableIndexPrefix(tableID, indexInfo.ID), 205 } 206 } 207 checksum := &tipb.ChecksumRequest{ 208 ScanOn: tipb.ChecksumScanOn_Index, 209 Algorithm: tipb.ChecksumAlgorithm_Crc64_Xor, 210 Rule: rule, 211 } 212 213 ranges := ranger.FullRange() 214 215 var builder distsql.RequestBuilder 216 // Use low priority to reducing impact to other requests. 217 builder.Request.Priority = kv.PriorityLow 218 return builder.SetIndexRanges(nil, tableID, indexInfo.ID, ranges). 219 SetStartTS(startTS). 220 SetChecksumRequest(checksum). 221 SetConcurrency(int(concurrency)). 222 Build() 223 } 224 225 func sendChecksumRequest( 226 ctx context.Context, client kv.Client, req *kv.Request, vars *kv.Variables, 227 ) (resp *tipb.ChecksumResponse, err error) { 228 res, err := distsql.Checksum(ctx, client, req, vars) 229 if err != nil { 230 return nil, errors.Trace(err) 231 } 232 defer func() { 233 if err1 := res.Close(); err1 != nil { 234 err = err1 235 } 236 }() 237 238 resp = &tipb.ChecksumResponse{} 239 240 for { 241 data, err := res.NextRaw(ctx) 242 if err != nil { 243 return nil, errors.Trace(err) 244 } 245 if data == nil { 246 break 247 } 248 checksum := &tipb.ChecksumResponse{} 249 if err = checksum.Unmarshal(data); err != nil { 250 return nil, errors.Trace(err) 251 } 252 updateChecksumResponse(resp, checksum) 253 } 254 255 return resp, nil 256 } 257 258 func updateChecksumResponse(resp, update *tipb.ChecksumResponse) { 259 resp.Checksum ^= update.Checksum 260 resp.TotalKvs += update.TotalKvs 261 resp.TotalBytes += update.TotalBytes 262 } 263 264 // Executor is a checksum executor. 265 type Executor struct { 266 reqs []*kv.Request 267 } 268 269 // Len returns the total number of checksum requests. 270 func (exec *Executor) Len() int { 271 return len(exec.reqs) 272 } 273 274 // Each executes the function to each requests in the executor. 275 func (exec *Executor) Each(f func(*kv.Request) error) error { 276 for _, req := range exec.reqs { 277 err := f(req) 278 if err != nil { 279 return errors.Trace(err) 280 } 281 } 282 return nil 283 } 284 285 // RawRequests extracts the raw requests associated with this executor. 286 // This is mainly used for debugging only. 287 func (exec *Executor) RawRequests() ([]*tipb.ChecksumRequest, error) { 288 res := make([]*tipb.ChecksumRequest, 0, len(exec.reqs)) 289 for _, req := range exec.reqs { 290 rawReq := new(tipb.ChecksumRequest) 291 if err := proto.Unmarshal(req.Data, rawReq); err != nil { 292 return nil, errors.Trace(err) 293 } 294 res = append(res, rawReq) 295 } 296 return res, nil 297 } 298 299 // Execute executes a checksum executor. 300 func (exec *Executor) Execute( 301 ctx context.Context, 302 client kv.Client, 303 updateFn func(), 304 ) (*tipb.ChecksumResponse, error) { 305 checksumResp := &tipb.ChecksumResponse{} 306 for _, req := range exec.reqs { 307 // Pointer to SessionVars.Killed 308 // Killed is a flag to indicate that this query is killed. 309 // 310 // It is useful in TiDB, however, it's a place holder in BR. 311 killed := uint32(0) 312 resp, err := sendChecksumRequest(ctx, client, req, kv.NewVariables(&killed)) 313 if err != nil { 314 return nil, errors.Trace(err) 315 } 316 updateChecksumResponse(checksumResp, resp) 317 updateFn() 318 } 319 return checksumResp, nil 320 }