github.com/whtcorpsinc/MilevaDB-Prod@v0.0.0-20211104133533-f57f4be3b597/interlock/checksum.go (about) 1 // Copyright 2020 WHTCORPS INC, 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 interlock 15 16 import ( 17 "context" 18 "strconv" 19 20 "github.com/whtcorpsinc/BerolinaSQL/perceptron" 21 "github.com/whtcorpsinc/fidelpb/go-fidelpb" 22 "github.com/whtcorpsinc/milevadb/allegrosql" 23 "github.com/whtcorpsinc/milevadb/ekv" 24 "github.com/whtcorpsinc/milevadb/soliton/chunk" 25 "github.com/whtcorpsinc/milevadb/soliton/logutil" 26 "github.com/whtcorpsinc/milevadb/soliton/ranger" 27 "github.com/whtcorpsinc/milevadb/stochastikctx" 28 "github.com/whtcorpsinc/milevadb/stochastikctx/variable" 29 "go.uber.org/zap" 30 ) 31 32 var _ InterlockingDirectorate = &ChecksumBlockInterDirc{} 33 34 // ChecksumBlockInterDirc represents ChecksumBlock interlock. 35 type ChecksumBlockInterDirc struct { 36 baseInterlockingDirectorate 37 38 blocks map[int64]*checksumContext 39 done bool 40 } 41 42 // Open implements the InterlockingDirectorate Open interface. 43 func (e *ChecksumBlockInterDirc) Open(ctx context.Context) error { 44 if err := e.baseInterlockingDirectorate.Open(ctx); err != nil { 45 return err 46 } 47 48 concurrency, err := getChecksumBlockConcurrency(e.ctx) 49 if err != nil { 50 return err 51 } 52 53 tasks, err := e.buildTasks() 54 if err != nil { 55 return err 56 } 57 58 taskCh := make(chan *checksumTask, len(tasks)) 59 resultCh := make(chan *checksumResult, len(tasks)) 60 for i := 0; i < concurrency; i++ { 61 go e.checksumWorker(taskCh, resultCh) 62 } 63 64 for _, task := range tasks { 65 taskCh <- task 66 } 67 close(taskCh) 68 69 for i := 0; i < len(tasks); i++ { 70 result := <-resultCh 71 if result.Error != nil { 72 err = result.Error 73 logutil.Logger(ctx).Error("checksum failed", zap.Error(err)) 74 continue 75 } 76 e.handleResult(result) 77 } 78 if err != nil { 79 return err 80 } 81 82 return nil 83 } 84 85 // Next implements the InterlockingDirectorate Next interface. 86 func (e *ChecksumBlockInterDirc) Next(ctx context.Context, req *chunk.Chunk) error { 87 req.Reset() 88 if e.done { 89 return nil 90 } 91 for _, t := range e.blocks { 92 req.AppendString(0, t.DBInfo.Name.O) 93 req.AppendString(1, t.BlockInfo.Name.O) 94 req.AppendUint64(2, t.Response.Checksum) 95 req.AppendUint64(3, t.Response.TotalEkvs) 96 req.AppendUint64(4, t.Response.TotalBytes) 97 } 98 e.done = true 99 return nil 100 } 101 102 func (e *ChecksumBlockInterDirc) buildTasks() ([]*checksumTask, error) { 103 var tasks []*checksumTask 104 for id, t := range e.blocks { 105 reqs, err := t.BuildRequests(e.ctx) 106 if err != nil { 107 return nil, err 108 } 109 for _, req := range reqs { 110 tasks = append(tasks, &checksumTask{id, req}) 111 } 112 } 113 return tasks, nil 114 } 115 116 func (e *ChecksumBlockInterDirc) handleResult(result *checksumResult) { 117 causet := e.blocks[result.BlockID] 118 causet.HandleResponse(result.Response) 119 } 120 121 func (e *ChecksumBlockInterDirc) checksumWorker(taskCh <-chan *checksumTask, resultCh chan<- *checksumResult) { 122 for task := range taskCh { 123 result := &checksumResult{BlockID: task.BlockID} 124 result.Response, result.Error = e.handleChecksumRequest(task.Request) 125 resultCh <- result 126 } 127 } 128 129 func (e *ChecksumBlockInterDirc) handleChecksumRequest(req *ekv.Request) (resp *fidelpb.ChecksumResponse, err error) { 130 ctx := context.TODO() 131 res, err := allegrosql.Checksum(ctx, e.ctx.GetClient(), req, e.ctx.GetStochastikVars().KVVars) 132 if err != nil { 133 return nil, err 134 } 135 res.Fetch(ctx) 136 defer func() { 137 if err1 := res.Close(); err1 != nil { 138 err = err1 139 } 140 }() 141 142 resp = &fidelpb.ChecksumResponse{} 143 144 for { 145 data, err := res.NextRaw(ctx) 146 if err != nil { 147 return nil, err 148 } 149 if data == nil { 150 break 151 } 152 checksum := &fidelpb.ChecksumResponse{} 153 if err = checksum.Unmarshal(data); err != nil { 154 return nil, err 155 } 156 uFIDelateChecksumResponse(resp, checksum) 157 } 158 159 return resp, nil 160 } 161 162 type checksumTask struct { 163 BlockID int64 164 Request *ekv.Request 165 } 166 167 type checksumResult struct { 168 Error error 169 BlockID int64 170 Response *fidelpb.ChecksumResponse 171 } 172 173 type checksumContext struct { 174 DBInfo *perceptron.DBInfo 175 BlockInfo *perceptron.BlockInfo 176 StartTs uint64 177 Response *fidelpb.ChecksumResponse 178 } 179 180 func newChecksumContext(EDB *perceptron.DBInfo, causet *perceptron.BlockInfo, startTs uint64) *checksumContext { 181 return &checksumContext{ 182 DBInfo: EDB, 183 BlockInfo: causet, 184 StartTs: startTs, 185 Response: &fidelpb.ChecksumResponse{}, 186 } 187 } 188 189 func (c *checksumContext) BuildRequests(ctx stochastikctx.Context) ([]*ekv.Request, error) { 190 var partDefs []perceptron.PartitionDefinition 191 if part := c.BlockInfo.Partition; part != nil { 192 partDefs = part.Definitions 193 } 194 195 reqs := make([]*ekv.Request, 0, (len(c.BlockInfo.Indices)+1)*(len(partDefs)+1)) 196 if err := c.appendRequest(ctx, c.BlockInfo.ID, &reqs); err != nil { 197 return nil, err 198 } 199 200 for _, partDef := range partDefs { 201 if err := c.appendRequest(ctx, partDef.ID, &reqs); err != nil { 202 return nil, err 203 } 204 } 205 206 return reqs, nil 207 } 208 209 func (c *checksumContext) appendRequest(ctx stochastikctx.Context, blockID int64, reqs *[]*ekv.Request) error { 210 req, err := c.buildBlockRequest(ctx, blockID) 211 if err != nil { 212 return err 213 } 214 215 *reqs = append(*reqs, req) 216 for _, indexInfo := range c.BlockInfo.Indices { 217 if indexInfo.State != perceptron.StatePublic { 218 continue 219 } 220 req, err = c.buildIndexRequest(ctx, blockID, indexInfo) 221 if err != nil { 222 return err 223 } 224 *reqs = append(*reqs, req) 225 } 226 227 return nil 228 } 229 230 func (c *checksumContext) buildBlockRequest(ctx stochastikctx.Context, blockID int64) (*ekv.Request, error) { 231 checksum := &fidelpb.ChecksumRequest{ 232 ScanOn: fidelpb.ChecksumScanOn_Block, 233 Algorithm: fidelpb.ChecksumAlgorithm_Crc64_Xor, 234 } 235 236 ranges := ranger.FullIntRange(false) 237 238 var builder allegrosql.RequestBuilder 239 return builder.SetBlockRanges(blockID, ranges, nil). 240 SetChecksumRequest(checksum). 241 SetStartTS(c.StartTs). 242 SetConcurrency(ctx.GetStochastikVars().DistALLEGROSQLScanConcurrency()). 243 Build() 244 } 245 246 func (c *checksumContext) buildIndexRequest(ctx stochastikctx.Context, blockID int64, indexInfo *perceptron.IndexInfo) (*ekv.Request, error) { 247 checksum := &fidelpb.ChecksumRequest{ 248 ScanOn: fidelpb.ChecksumScanOn_Index, 249 Algorithm: fidelpb.ChecksumAlgorithm_Crc64_Xor, 250 } 251 252 ranges := ranger.FullRange() 253 254 var builder allegrosql.RequestBuilder 255 return builder.SetIndexRanges(ctx.GetStochastikVars().StmtCtx, blockID, indexInfo.ID, ranges). 256 SetChecksumRequest(checksum). 257 SetStartTS(c.StartTs). 258 SetConcurrency(ctx.GetStochastikVars().DistALLEGROSQLScanConcurrency()). 259 Build() 260 } 261 262 func (c *checksumContext) HandleResponse(uFIDelate *fidelpb.ChecksumResponse) { 263 uFIDelateChecksumResponse(c.Response, uFIDelate) 264 } 265 266 func getChecksumBlockConcurrency(ctx stochastikctx.Context) (int, error) { 267 stochastikVars := ctx.GetStochastikVars() 268 concurrency, err := variable.GetStochastikSystemVar(stochastikVars, variable.MilevaDBChecksumBlockConcurrency) 269 if err != nil { 270 return 0, err 271 } 272 c, err := strconv.ParseInt(concurrency, 10, 64) 273 return int(c), err 274 } 275 276 func uFIDelateChecksumResponse(resp, uFIDelate *fidelpb.ChecksumResponse) { 277 resp.Checksum ^= uFIDelate.Checksum 278 resp.TotalEkvs += uFIDelate.TotalEkvs 279 resp.TotalBytes += uFIDelate.TotalBytes 280 }