github.com/whtcorpsinc/MilevaDB-Prod@v0.0.0-20211104133533-f57f4be3b597/dbs/dbs.go (about) 1 // Copyright 2020 The ql Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSES/QL-LICENSE file. 4 5 // Copyright 2020 WHTCORPS INC, Inc. 6 // 7 // Licensed under the Apache License, Version 2.0 (the "License"); 8 // you may not use this file except in compliance with the License. 9 // You may obtain a copy of the License at 10 // 11 // http://www.apache.org/licenses/LICENSE-2.0 12 // 13 // Unless required by applicable law or agreed to in writing, software 14 // distributed under the License is distributed on an "AS IS" BASIS, 15 // See the License for the specific language governing permissions and 16 // limitations under the License. 17 18 package dbs 19 20 import ( 21 "context" 22 "fmt" 23 "sync" 24 "time" 25 26 "github.com/google/uuid" 27 "github.com/ngaut/pools" 28 "github.com/whtcorpsinc/BerolinaSQL/allegrosql" 29 "github.com/whtcorpsinc/BerolinaSQL/ast" 30 "github.com/whtcorpsinc/BerolinaSQL/perceptron" 31 "github.com/whtcorpsinc/errors" 32 "github.com/whtcorpsinc/failpoint" 33 pumpcli "github.com/whtcorpsinc/milevadb-tools/milevadb-binlog/pump_client" 34 "github.com/whtcorpsinc/milevadb/causet" 35 "github.com/whtcorpsinc/milevadb/config" 36 "github.com/whtcorpsinc/milevadb/dbs/soliton" 37 "github.com/whtcorpsinc/milevadb/ekv" 38 "github.com/whtcorpsinc/milevadb/metrics" 39 "github.com/whtcorpsinc/milevadb/schemareplicant" 40 goutil "github.com/whtcorpsinc/milevadb/soliton" 41 "github.com/whtcorpsinc/milevadb/soliton/logutil" 42 "github.com/whtcorpsinc/milevadb/spacetime" 43 "github.com/whtcorpsinc/milevadb/stochastikctx" 44 "github.com/whtcorpsinc/milevadb/stochastikctx/binloginfo" 45 "github.com/whtcorpsinc/milevadb/stochastikctx/variable" 46 "github.com/whtcorpsinc/milevadb/tenant" 47 "go.uber.org/zap" 48 ) 49 50 const ( 51 // currentVersion is for all new DBS jobs. 52 currentVersion = 1 53 // DBSTenantKey is the dbs tenant path that is saved to etcd, and it's exported for testing. 54 DBSTenantKey = "/milevadb/dbs/fg/tenant" 55 dbsPrompt = "dbs" 56 57 shardRowIDBitsMax = 15 58 59 batchAddingJobs = 10 60 61 // PartitionCountLimit is limit of the number of partitions in a causet. 62 // Reference linking https://dev.allegrosql.com/doc/refman/5.7/en/partitioning-limitations.html. 63 PartitionCountLimit = 8192 64 ) 65 66 // OnExist specifies what to do when a new object has a name defCauslision. 67 type OnExist uint8 68 69 const ( 70 // OnExistError throws an error on name defCauslision. 71 OnExistError OnExist = iota 72 // OnExistIgnore skips creating the new object. 73 OnExistIgnore 74 // OnExistReplace replaces the old object by the new object. This is only 75 // supported by VIEWs at the moment. For other object types, this is 76 // equivalent to OnExistError. 77 OnExistReplace 78 ) 79 80 var ( 81 // BlockDeferredCausetCountLimit is limit of the number of defCausumns in a causet. 82 // It's exported for testing. 83 BlockDeferredCausetCountLimit = uint32(512) 84 // EnableSplitBlockRegion is a flag to decide whether to split a new region for 85 // a newly created causet. It takes effect only if the CausetStorage supports split 86 // region. 87 EnableSplitBlockRegion = uint32(0) 88 ) 89 90 // DBS is responsible for uFIDelating schemaReplicant in data causetstore and maintaining in-memory SchemaReplicant cache. 91 type DBS interface { 92 CreateSchema(ctx stochastikctx.Context, name perceptron.CIStr, charsetInfo *ast.CharsetOpt) error 93 AlterSchema(ctx stochastikctx.Context, stmt *ast.AlterDatabaseStmt) error 94 DropSchema(ctx stochastikctx.Context, schemaReplicant perceptron.CIStr) error 95 CreateBlock(ctx stochastikctx.Context, stmt *ast.CreateBlockStmt) error 96 CreateView(ctx stochastikctx.Context, stmt *ast.CreateViewStmt) error 97 DropBlock(ctx stochastikctx.Context, blockIdent ast.Ident) (err error) 98 RecoverBlock(ctx stochastikctx.Context, recoverInfo *RecoverInfo) (err error) 99 DropView(ctx stochastikctx.Context, blockIdent ast.Ident) (err error) 100 CreateIndex(ctx stochastikctx.Context, blockIdent ast.Ident, keyType ast.IndexKeyType, indexName perceptron.CIStr, 101 defCausumnNames []*ast.IndexPartSpecification, indexOption *ast.IndexOption, ifNotExists bool) error 102 DropIndex(ctx stochastikctx.Context, blockIdent ast.Ident, indexName perceptron.CIStr, ifExists bool) error 103 AlterBlock(ctx stochastikctx.Context, blockIdent ast.Ident, spec []*ast.AlterBlockSpec) error 104 TruncateBlock(ctx stochastikctx.Context, blockIdent ast.Ident) error 105 RenameBlock(ctx stochastikctx.Context, oldBlockIdent, newBlockIdent ast.Ident, isAlterBlock bool) error 106 LockBlocks(ctx stochastikctx.Context, stmt *ast.LockBlocksStmt) error 107 UnlockBlocks(ctx stochastikctx.Context, lockedBlocks []perceptron.BlockLockTpInfo) error 108 CleanupBlockLock(ctx stochastikctx.Context, blocks []*ast.BlockName) error 109 UFIDelateBlockReplicaInfo(ctx stochastikctx.Context, physicalID int64, available bool) error 110 RepairBlock(ctx stochastikctx.Context, causet *ast.BlockName, createStmt *ast.CreateBlockStmt) error 111 CreateSequence(ctx stochastikctx.Context, stmt *ast.CreateSequenceStmt) error 112 DropSequence(ctx stochastikctx.Context, blockIdent ast.Ident, ifExists bool) (err error) 113 114 // CreateSchemaWithInfo creates a database (schemaReplicant) given its database info. 115 // 116 // If `tryRetainID` is true, this method will try to keep the database ID specified in 117 // the `info` rather than generating new ones. This is just a hint though, if the ID defCauslides 118 // with an existing database a new ID will always be used. 119 // 120 // WARNING: the DBS owns the `info` after calling this function, and will modify its fields 121 // in-place. If you want to keep using `info`, please call Clone() first. 122 CreateSchemaWithInfo( 123 ctx stochastikctx.Context, 124 info *perceptron.DBInfo, 125 onExist OnExist, 126 tryRetainID bool) error 127 128 // CreateBlockWithInfo creates a causet, view or sequence given its causet info. 129 // 130 // If `tryRetainID` is true, this method will try to keep the causet ID specified in the `info` 131 // rather than generating new ones. This is just a hint though, if the ID defCauslides with an 132 // existing causet a new ID will always be used. 133 // 134 // WARNING: the DBS owns the `info` after calling this function, and will modify its fields 135 // in-place. If you want to keep using `info`, please call Clone() first. 136 CreateBlockWithInfo( 137 ctx stochastikctx.Context, 138 schemaReplicant perceptron.CIStr, 139 info *perceptron.BlockInfo, 140 onExist OnExist, 141 tryRetainID bool) error 142 143 // Start campaigns the tenant and starts workers. 144 // ctxPool is used for the worker's delRangeManager and creates stochastik. 145 Start(ctxPool *pools.ResourcePool) error 146 // GetLease returns current schemaReplicant lease time. 147 GetLease() time.Duration 148 // Stats returns the DBS statistics. 149 Stats(vars *variable.StochastikVars) (map[string]interface{}, error) 150 // GetScope gets the status variables scope. 151 GetScope(status string) variable.ScopeFlag 152 // Stop stops DBS worker. 153 Stop() error 154 // RegisterEventCh registers event channel for dbs. 155 RegisterEventCh(chan<- *soliton.Event) 156 // SchemaSyncer gets the schemaReplicant syncer. 157 SchemaSyncer() soliton.SchemaSyncer 158 // TenantManager gets the tenant manager. 159 TenantManager() tenant.Manager 160 // GetID gets the dbs ID. 161 GetID() string 162 // GetBlockMaxRowID gets the max event ID of a normal causet or a partition. 163 GetBlockMaxHandle(startTS uint64, tbl causet.PhysicalBlock) (ekv.Handle, bool, error) 164 // SetBinlogClient sets the binlog client for DBS worker. It's exported for testing. 165 SetBinlogClient(*pumpcli.PumpsClient) 166 // GetHook gets the hook. It's exported for testing. 167 GetHook() Callback 168 } 169 170 type limitJobTask struct { 171 job *perceptron.Job 172 err chan error 173 } 174 175 // dbs is used to handle the memexs that define the structure or schemaReplicant of the database. 176 type dbs struct { 177 m sync.RWMutex 178 ctx context.Context 179 cancel context.CancelFunc 180 wg sync.WaitGroup // It's only used to deal with data race in restart_test. 181 limitJobCh chan *limitJobTask 182 183 *dbsCtx 184 workers map[workerType]*worker 185 sessPool *stochastikPool 186 delRangeMgr delRangeManager 187 } 188 189 // dbsCtx is the context when we use worker to handle DBS jobs. 190 type dbsCtx struct { 191 uuid string 192 causetstore ekv.CausetStorage 193 tenantManager tenant.Manager 194 schemaSyncer soliton.SchemaSyncer 195 dbsJobDoneCh chan struct{} 196 dbsEventCh chan<- *soliton.Event 197 lease time.Duration // lease is schemaReplicant lease. 198 binlogCli *pumpcli.PumpsClient // binlogCli is used for Binlog. 199 infoHandle *schemareplicant.Handle 200 blockLockCkr soliton.DeadBlockLockChecker 201 202 // hook may be modified. 203 mu struct { 204 sync.RWMutex 205 hook Callback 206 interceptor Interceptor 207 } 208 } 209 210 func (dc *dbsCtx) isTenant() bool { 211 isTenant := dc.tenantManager.IsTenant() 212 logutil.BgLogger().Debug("[dbs] check whether is the DBS tenant", zap.Bool("isTenant", isTenant), zap.String("selfID", dc.uuid)) 213 if isTenant { 214 metrics.DBSCounter.WithLabelValues(metrics.DBSTenant + "_" + allegrosql.MilevaDBReleaseVersion).Inc() 215 } 216 return isTenant 217 } 218 219 // RegisterEventCh registers passed channel for dbs Event. 220 func (d *dbs) RegisterEventCh(ch chan<- *soliton.Event) { 221 d.dbsEventCh = ch 222 } 223 224 // asyncNotifyEvent will notify the dbs event to outside world, say statistic handle. When the channel is full, we may 225 // give up notify and log it. 226 func asyncNotifyEvent(d *dbsCtx, e *soliton.Event) { 227 if d.dbsEventCh != nil { 228 if d.lease == 0 { 229 // If lease is 0, it's always used in test. 230 select { 231 case d.dbsEventCh <- e: 232 default: 233 } 234 return 235 } 236 for i := 0; i < 10; i++ { 237 select { 238 case d.dbsEventCh <- e: 239 return 240 default: 241 logutil.BgLogger().Warn("[dbs] fail to notify DBS event", zap.String("event", e.String())) 242 time.Sleep(time.Microsecond * 10) 243 } 244 } 245 } 246 } 247 248 // NewDBS creates a new DBS. 249 func NewDBS(ctx context.Context, options ...Option) DBS { 250 return newDBS(ctx, options...) 251 } 252 253 func newDBS(ctx context.Context, options ...Option) *dbs { 254 opt := &Options{ 255 Hook: &BaseCallback{}, 256 } 257 for _, o := range options { 258 o(opt) 259 } 260 261 id := uuid.New().String() 262 var manager tenant.Manager 263 var syncer soliton.SchemaSyncer 264 var deadLockCkr soliton.DeadBlockLockChecker 265 if etcdCli := opt.EtcdCli; etcdCli == nil { 266 // The etcdCli is nil if the causetstore is localstore which is only used for testing. 267 // So we use mockTenantManager and MockSchemaSyncer. 268 manager = tenant.NewMockManager(ctx, id) 269 syncer = NewMockSchemaSyncer() 270 } else { 271 manager = tenant.NewTenantManager(ctx, etcdCli, dbsPrompt, id, DBSTenantKey) 272 syncer = soliton.NewSchemaSyncer(ctx, etcdCli, id, manager) 273 deadLockCkr = soliton.NewDeadBlockLockChecker(etcdCli) 274 } 275 276 dbsCtx := &dbsCtx{ 277 uuid: id, 278 causetstore: opt.CausetStore, 279 lease: opt.Lease, 280 dbsJobDoneCh: make(chan struct{}, 1), 281 tenantManager: manager, 282 schemaSyncer: syncer, 283 binlogCli: binloginfo.GetPumpsClient(), 284 infoHandle: opt.InfoHandle, 285 blockLockCkr: deadLockCkr, 286 } 287 dbsCtx.mu.hook = opt.Hook 288 dbsCtx.mu.interceptor = &BaseInterceptor{} 289 d := &dbs{ 290 ctx: ctx, 291 dbsCtx: dbsCtx, 292 limitJobCh: make(chan *limitJobTask, batchAddingJobs), 293 } 294 295 return d 296 } 297 298 // Stop implements DBS.Stop interface. 299 func (d *dbs) Stop() error { 300 d.m.Lock() 301 defer d.m.Unlock() 302 303 d.close() 304 logutil.BgLogger().Info("[dbs] stop DBS", zap.String("ID", d.uuid)) 305 return nil 306 } 307 308 func (d *dbs) newDeleteRangeManager(mock bool) delRangeManager { 309 var delRangeMgr delRangeManager 310 if !mock { 311 delRangeMgr = newDelRangeManager(d.causetstore, d.sessPool) 312 logutil.BgLogger().Info("[dbs] start delRangeManager OK", zap.Bool("is a emulator", !d.causetstore.SupportDeleteRange())) 313 } else { 314 delRangeMgr = newMockDelRangeManager() 315 } 316 317 delRangeMgr.start() 318 return delRangeMgr 319 } 320 321 // Start implements DBS.Start interface. 322 func (d *dbs) Start(ctxPool *pools.ResourcePool) error { 323 logutil.BgLogger().Info("[dbs] start DBS", zap.String("ID", d.uuid), zap.Bool("runWorker", RunWorker)) 324 d.ctx, d.cancel = context.WithCancel(d.ctx) 325 326 d.wg.Add(1) 327 go d.limitDBSJobs() 328 329 // If RunWorker is true, we need campaign tenant and do DBS job. 330 // Otherwise, we needn't do that. 331 if RunWorker { 332 err := d.tenantManager.CampaignTenant() 333 if err != nil { 334 return errors.Trace(err) 335 } 336 337 d.workers = make(map[workerType]*worker, 2) 338 d.sessPool = newStochastikPool(ctxPool) 339 d.delRangeMgr = d.newDeleteRangeManager(ctxPool == nil) 340 d.workers[generalWorker] = newWorker(d.ctx, generalWorker, d.sessPool, d.delRangeMgr) 341 d.workers[addIdxWorker] = newWorker(d.ctx, addIdxWorker, d.sessPool, d.delRangeMgr) 342 for _, worker := range d.workers { 343 worker.wg.Add(1) 344 w := worker 345 go w.start(d.dbsCtx) 346 347 metrics.DBSCounter.WithLabelValues(fmt.Sprintf("%s_%s", metrics.CreateDBS, worker.String())).Inc() 348 349 // When the start function is called, we will send a fake job to let worker 350 // checks tenant firstly and try to find whether a job exists and run. 351 asyncNotify(worker.dbsJobCh) 352 } 353 354 go d.schemaSyncer.StartCleanWork() 355 if config.BlockLockEnabled() { 356 d.wg.Add(1) 357 go d.startCleanDeadBlockLock() 358 } 359 metrics.DBSCounter.WithLabelValues(metrics.StartCleanWork).Inc() 360 } 361 362 variable.RegisterStatistics(d) 363 364 metrics.DBSCounter.WithLabelValues(metrics.CreateDBSInstance).Inc() 365 return nil 366 } 367 368 func (d *dbs) close() { 369 if isChanClosed(d.ctx.Done()) { 370 return 371 } 372 373 startTime := time.Now() 374 d.cancel() 375 d.wg.Wait() 376 d.tenantManager.Cancel() 377 d.schemaSyncer.Close() 378 379 for _, worker := range d.workers { 380 worker.close() 381 } 382 // d.delRangeMgr using stochastik from d.sessPool. 383 // Put it before d.sessPool.close to reduce the time spent by d.sessPool.close. 384 if d.delRangeMgr != nil { 385 d.delRangeMgr.clear() 386 } 387 if d.sessPool != nil { 388 d.sessPool.close() 389 } 390 391 logutil.BgLogger().Info("[dbs] DBS closed", zap.String("ID", d.uuid), zap.Duration("take time", time.Since(startTime))) 392 } 393 394 // GetLease implements DBS.GetLease interface. 395 func (d *dbs) GetLease() time.Duration { 396 d.m.RLock() 397 lease := d.lease 398 d.m.RUnlock() 399 return lease 400 } 401 402 // GetSchemaReplicantWithInterceptor gets the schemareplicant binding to d. It's exported for testing. 403 // Please don't use this function, it is used by TestParallelDBSBeforeRunDBSJob to intercept the calling of d.infoHandle.Get(), use d.infoHandle.Get() instead. 404 // Otherwise, the TestParallelDBSBeforeRunDBSJob will hang up forever. 405 func (d *dbs) GetSchemaReplicantWithInterceptor(ctx stochastikctx.Context) schemareplicant.SchemaReplicant { 406 is := d.infoHandle.Get() 407 408 d.mu.RLock() 409 defer d.mu.RUnlock() 410 return d.mu.interceptor.OnGetSchemaReplicant(ctx, is) 411 } 412 413 func (d *dbs) genGlobalIDs(count int) ([]int64, error) { 414 var ret []int64 415 err := ekv.RunInNewTxn(d.causetstore, true, func(txn ekv.Transaction) error { 416 failpoint.Inject("mockGenGlobalIDFail", func(val failpoint.Value) { 417 if val.(bool) { 418 failpoint.Return(errors.New("gofail genGlobalIDs error")) 419 } 420 }) 421 422 m := spacetime.NewMeta(txn) 423 var err error 424 ret, err = m.GenGlobalIDs(count) 425 return err 426 }) 427 428 return ret, err 429 } 430 431 // SchemaSyncer implements DBS.SchemaSyncer interface. 432 func (d *dbs) SchemaSyncer() soliton.SchemaSyncer { 433 return d.schemaSyncer 434 } 435 436 // TenantManager implements DBS.TenantManager interface. 437 func (d *dbs) TenantManager() tenant.Manager { 438 return d.tenantManager 439 } 440 441 // GetID implements DBS.GetID interface. 442 func (d *dbs) GetID() string { 443 return d.uuid 444 } 445 446 func checkJobMaxInterval(job *perceptron.Job) time.Duration { 447 // The job of adding index takes more time to process. 448 // So it uses the longer time. 449 if job.Type == perceptron.CausetActionAddIndex || job.Type == perceptron.CausetActionAddPrimaryKey { 450 return 3 * time.Second 451 } 452 if job.Type == perceptron.CausetActionCreateBlock || job.Type == perceptron.CausetActionCreateSchema { 453 return 500 * time.Millisecond 454 } 455 return 1 * time.Second 456 } 457 458 func (d *dbs) asyncNotifyWorker(jobTp perceptron.CausetActionType) { 459 // If the workers don't run, we needn't to notify workers. 460 if !RunWorker { 461 return 462 } 463 464 if jobTp == perceptron.CausetActionAddIndex || jobTp == perceptron.CausetActionAddPrimaryKey { 465 asyncNotify(d.workers[addIdxWorker].dbsJobCh) 466 } else { 467 asyncNotify(d.workers[generalWorker].dbsJobCh) 468 } 469 } 470 471 func (d *dbs) doDBSJob(ctx stochastikctx.Context, job *perceptron.Job) error { 472 if isChanClosed(d.ctx.Done()) { 473 return d.ctx.Err() 474 } 475 476 // Get a global job ID and put the DBS job in the queue. 477 job.Query, _ = ctx.Value(stochastikctx.QueryString).(string) 478 task := &limitJobTask{job, make(chan error)} 479 d.limitJobCh <- task 480 err := <-task.err 481 482 ctx.GetStochastikVars().StmtCtx.IsDBSJobInQueue = true 483 484 // Notice worker that we push a new job and wait the job done. 485 d.asyncNotifyWorker(job.Type) 486 logutil.BgLogger().Info("[dbs] start DBS job", zap.String("job", job.String()), zap.String("query", job.Query)) 487 488 var historyJob *perceptron.Job 489 jobID := job.ID 490 // For a job from start to end, the state of it will be none -> delete only -> write only -> reorganization -> public 491 // For every state changes, we will wait as lease 2 * lease time, so here the ticker check is 10 * lease. 492 // But we use etcd to speed up, normally it takes less than 0.5s now, so we use 0.5s or 1s or 3s as the max value. 493 ticker := time.NewTicker(chooseLeaseTime(10*d.lease, checkJobMaxInterval(job))) 494 startTime := time.Now() 495 metrics.JobsGauge.WithLabelValues(job.Type.String()).Inc() 496 defer func() { 497 ticker.Stop() 498 metrics.JobsGauge.WithLabelValues(job.Type.String()).Dec() 499 metrics.HandleJobHistogram.WithLabelValues(job.Type.String(), metrics.RetLabel(err)).Observe(time.Since(startTime).Seconds()) 500 }() 501 for { 502 failpoint.Inject("storeCloseInLoop", func(_ failpoint.Value) { 503 d.cancel() 504 }) 505 506 select { 507 case <-d.dbsJobDoneCh: 508 case <-ticker.C: 509 case <-d.ctx.Done(): 510 logutil.BgLogger().Error("[dbs] doDBSJob will quit because context done", zap.Error(d.ctx.Err())) 511 err := d.ctx.Err() 512 return err 513 } 514 515 historyJob, err = d.getHistoryDBSJob(jobID) 516 if err != nil { 517 logutil.BgLogger().Error("[dbs] get history DBS job failed, check again", zap.Error(err)) 518 continue 519 } else if historyJob == nil { 520 logutil.BgLogger().Debug("[dbs] DBS job is not in history, maybe not run", zap.Int64("jobID", jobID)) 521 continue 522 } 523 524 // If a job is a history job, the state must be JobStateSynced or JobStateRollbackDone or JobStateCancelled. 525 if historyJob.IsSynced() { 526 logutil.BgLogger().Info("[dbs] DBS job is finished", zap.Int64("jobID", jobID)) 527 return nil 528 } 529 530 if historyJob.Error != nil { 531 return errors.Trace(historyJob.Error) 532 } 533 // Only for JobStateCancelled job which is adding defCausumns or drop defCausumns. 534 if historyJob.IsCancelled() && (historyJob.Type == perceptron.CausetActionAddDeferredCausets || historyJob.Type == perceptron.CausetActionDropDeferredCausets) { 535 logutil.BgLogger().Info("[dbs] DBS job is cancelled", zap.Int64("jobID", jobID)) 536 return nil 537 } 538 panic("When the state is JobStateRollbackDone or JobStateCancelled, historyJob.Error should never be nil") 539 } 540 } 541 542 func (d *dbs) callHookOnChanged(err error) error { 543 d.mu.RLock() 544 defer d.mu.RUnlock() 545 546 err = d.mu.hook.OnChanged(err) 547 return errors.Trace(err) 548 } 549 550 // SetBinlogClient implements DBS.SetBinlogClient interface. 551 func (d *dbs) SetBinlogClient(binlogCli *pumpcli.PumpsClient) { 552 d.binlogCli = binlogCli 553 } 554 555 // GetHook implements DBS.GetHook interface. 556 func (d *dbs) GetHook() Callback { 557 d.mu.Lock() 558 defer d.mu.Unlock() 559 560 return d.mu.hook 561 } 562 563 func (d *dbs) startCleanDeadBlockLock() { 564 defer func() { 565 goutil.Recover(metrics.LabelDBS, "startCleanDeadBlockLock", nil, false) 566 d.wg.Done() 567 }() 568 569 ticker := time.NewTicker(time.Second * 10) 570 defer ticker.Stop() 571 for { 572 select { 573 case <-ticker.C: 574 if !d.tenantManager.IsTenant() { 575 continue 576 } 577 deadLockBlocks, err := d.blockLockCkr.GetDeadLockedBlocks(d.ctx, d.infoHandle.Get().AllSchemas()) 578 if err != nil { 579 logutil.BgLogger().Info("[dbs] get dead causet dagger failed.", zap.Error(err)) 580 continue 581 } 582 for se, blocks := range deadLockBlocks { 583 err := d.CleanDeadBlockLock(blocks, se) 584 if err != nil { 585 logutil.BgLogger().Info("[dbs] clean dead causet dagger failed.", zap.Error(err)) 586 } 587 } 588 case <-d.ctx.Done(): 589 return 590 } 591 } 592 } 593 594 // RecoverInfo contains information needed by DBS.RecoverBlock. 595 type RecoverInfo struct { 596 SchemaID int64 597 BlockInfo *perceptron.BlockInfo 598 DropJobID int64 599 SnapshotTS uint64 600 CurAutoIncID int64 601 CurAutoRandID int64 602 }