github.com/pingcap/br@v5.3.0-alpha.0.20220125034240-ec59c7b6ce30+incompatible/pkg/lightning/lightning.go (about) 1 // Copyright 2019 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 lightning 15 16 import ( 17 "compress/gzip" 18 "context" 19 "encoding/json" 20 "fmt" 21 "io" 22 "net" 23 "net/http" 24 "net/http/pprof" 25 "os" 26 "sort" 27 "strconv" 28 "strings" 29 "sync" 30 "time" 31 32 "github.com/pingcap/errors" 33 "github.com/pingcap/failpoint" 34 "github.com/prometheus/client_golang/prometheus/promhttp" 35 "github.com/shurcooL/httpgzip" 36 "go.uber.org/zap" 37 "go.uber.org/zap/zapcore" 38 39 "github.com/pingcap/br/pkg/lightning/backend/local" 40 "github.com/pingcap/br/pkg/lightning/checkpoints" 41 "github.com/pingcap/br/pkg/lightning/common" 42 "github.com/pingcap/br/pkg/lightning/config" 43 "github.com/pingcap/br/pkg/lightning/glue" 44 "github.com/pingcap/br/pkg/lightning/log" 45 "github.com/pingcap/br/pkg/lightning/mydump" 46 "github.com/pingcap/br/pkg/lightning/restore" 47 "github.com/pingcap/br/pkg/lightning/web" 48 "github.com/pingcap/br/pkg/redact" 49 "github.com/pingcap/br/pkg/storage" 50 "github.com/pingcap/br/pkg/utils" 51 "github.com/pingcap/br/pkg/version/build" 52 ) 53 54 type Lightning struct { 55 globalCfg *config.GlobalConfig 56 globalTLS *common.TLS 57 // taskCfgs is the list of task configurations enqueued in the server mode 58 taskCfgs *config.List 59 ctx context.Context 60 shutdown context.CancelFunc // for whole lightning context 61 server http.Server 62 serverAddr net.Addr 63 serverLock sync.Mutex 64 65 cancelLock sync.Mutex 66 curTask *config.Config 67 cancel context.CancelFunc // for per task context, which maybe different from lightning context 68 } 69 70 func initEnv(cfg *config.GlobalConfig) error { 71 return log.InitLogger(&cfg.App.Config, cfg.TiDB.LogLevel) 72 } 73 74 func New(globalCfg *config.GlobalConfig) *Lightning { 75 if err := initEnv(globalCfg); err != nil { 76 fmt.Println("Failed to initialize environment:", err) 77 os.Exit(1) 78 } 79 80 tls, err := common.NewTLS(globalCfg.Security.CAPath, globalCfg.Security.CertPath, globalCfg.Security.KeyPath, globalCfg.App.StatusAddr) 81 if err != nil { 82 log.L().Fatal("failed to load TLS certificates", zap.Error(err)) 83 } 84 85 redact.InitRedact(globalCfg.Security.RedactInfoLog) 86 87 ctx, shutdown := context.WithCancel(context.Background()) 88 return &Lightning{ 89 globalCfg: globalCfg, 90 globalTLS: tls, 91 ctx: ctx, 92 shutdown: shutdown, 93 } 94 } 95 96 func (l *Lightning) GoServe() error { 97 handleSigUsr1(func() { 98 l.serverLock.Lock() 99 statusAddr := l.globalCfg.App.StatusAddr 100 shouldStartServer := len(statusAddr) == 0 101 if shouldStartServer { 102 l.globalCfg.App.StatusAddr = ":" 103 } 104 l.serverLock.Unlock() 105 106 if shouldStartServer { 107 // open a random port and start the server if SIGUSR1 is received. 108 if err := l.goServe(":", os.Stderr); err != nil { 109 log.L().Warn("failed to start HTTP server", log.ShortError(err)) 110 } 111 } else { 112 // just prints the server address if it is already started. 113 log.L().Info("already started HTTP server", zap.Stringer("address", l.serverAddr)) 114 } 115 }) 116 117 l.serverLock.Lock() 118 statusAddr := l.globalCfg.App.StatusAddr 119 l.serverLock.Unlock() 120 121 if len(statusAddr) == 0 { 122 return nil 123 } 124 return l.goServe(statusAddr, io.Discard) 125 } 126 127 func (l *Lightning) goServe(statusAddr string, realAddrWriter io.Writer) error { 128 mux := http.NewServeMux() 129 mux.Handle("/", http.RedirectHandler("/web/", http.StatusFound)) 130 mux.Handle("/metrics", promhttp.Handler()) 131 132 mux.HandleFunc("/debug/pprof/", pprof.Index) 133 mux.HandleFunc("/debug/pprof/cmdline", pprof.Cmdline) 134 mux.HandleFunc("/debug/pprof/profile", pprof.Profile) 135 mux.HandleFunc("/debug/pprof/symbol", pprof.Symbol) 136 mux.HandleFunc("/debug/pprof/trace", pprof.Trace) 137 138 handleTasks := http.StripPrefix("/tasks", http.HandlerFunc(l.handleTask)) 139 mux.Handle("/tasks", handleTasks) 140 mux.Handle("/tasks/", handleTasks) 141 mux.HandleFunc("/progress/task", handleProgressTask) 142 mux.HandleFunc("/progress/table", handleProgressTable) 143 mux.HandleFunc("/pause", handlePause) 144 mux.HandleFunc("/resume", handleResume) 145 mux.HandleFunc("/loglevel", handleLogLevel) 146 147 mux.Handle("/web/", http.StripPrefix("/web", httpgzip.FileServer(web.Res, httpgzip.FileServerOptions{ 148 IndexHTML: true, 149 ServeError: func(w http.ResponseWriter, req *http.Request, err error) { 150 if os.IsNotExist(err) && !strings.Contains(req.URL.Path, ".") { 151 http.Redirect(w, req, "/web/", http.StatusFound) 152 } else { 153 httpgzip.NonSpecific(w, req, err) 154 } 155 }, 156 }))) 157 158 listener, err := net.Listen("tcp", statusAddr) 159 if err != nil { 160 return err 161 } 162 l.serverAddr = listener.Addr() 163 log.L().Info("starting HTTP server", zap.Stringer("address", l.serverAddr)) 164 fmt.Fprintln(realAddrWriter, "started HTTP server on", l.serverAddr) 165 l.server.Handler = mux 166 listener = l.globalTLS.WrapListener(listener) 167 168 go func() { 169 err := l.server.Serve(listener) 170 log.L().Info("stopped HTTP server", log.ShortError(err)) 171 }() 172 return nil 173 } 174 175 // RunOnce is used by binary lightning and host when using lightning as a library. 176 // - for binary lightning, taskCtx could be context.Background which means taskCtx wouldn't be canceled directly by its 177 // cancel function, but only by Lightning.Stop or HTTP DELETE using l.cancel. and glue could be nil to let lightning 178 // use a default glue later. 179 // - for lightning as a library, taskCtx could be a meaningful context that get canceled outside, and glue could be a 180 // caller implemented glue. 181 func (l *Lightning) RunOnce(taskCtx context.Context, taskCfg *config.Config, glue glue.Glue) error { 182 if err := taskCfg.Adjust(taskCtx); err != nil { 183 return err 184 } 185 186 taskCfg.TaskID = time.Now().UnixNano() 187 failpoint.Inject("SetTaskID", func(val failpoint.Value) { 188 taskCfg.TaskID = int64(val.(int)) 189 }) 190 191 return l.run(taskCtx, taskCfg, glue) 192 } 193 194 func (l *Lightning) RunServer() error { 195 l.taskCfgs = config.NewConfigList() 196 log.L().Info( 197 "Lightning server is running, post to /tasks to start an import task", 198 zap.Stringer("address", l.serverAddr), 199 ) 200 201 for { 202 task, err := l.taskCfgs.Pop(l.ctx) 203 if err != nil { 204 return err 205 } 206 err = l.run(context.Background(), task, nil) 207 if err != nil { 208 restore.DeliverPauser.Pause() // force pause the progress on error 209 log.L().Error("tidb lightning encountered error", zap.Error(err)) 210 } 211 } 212 } 213 214 var taskCfgRecorderKey struct{} 215 216 func (l *Lightning) run(taskCtx context.Context, taskCfg *config.Config, g glue.Glue) (err error) { 217 build.LogInfo(build.Lightning) 218 log.L().Info("cfg", zap.Stringer("cfg", taskCfg)) 219 220 utils.LogEnvVariables() 221 222 ctx, cancel := context.WithCancel(taskCtx) 223 l.cancelLock.Lock() 224 l.cancel = cancel 225 l.curTask = taskCfg 226 l.cancelLock.Unlock() 227 web.BroadcastStartTask() 228 229 defer func() { 230 cancel() 231 l.cancelLock.Lock() 232 l.cancel = nil 233 l.cancelLock.Unlock() 234 web.BroadcastEndTask(err) 235 }() 236 237 failpoint.Inject("SkipRunTask", func() { 238 if recorder, ok := l.ctx.Value(&taskCfgRecorderKey).(chan *config.Config); ok { 239 select { 240 case recorder <- taskCfg: 241 case <-ctx.Done(): 242 failpoint.Return(ctx.Err()) 243 } 244 } 245 failpoint.Return(nil) 246 }) 247 248 if err := taskCfg.TiDB.Security.RegisterMySQL(); err != nil { 249 return err 250 } 251 defer func() { 252 // deregister TLS config with name "cluster" 253 if taskCfg.TiDB.Security == nil { 254 return 255 } 256 taskCfg.TiDB.Security.CAPath = "" 257 if err := taskCfg.TiDB.Security.RegisterMySQL(); err != nil { 258 log.L().Warn("failed to deregister TLS config", log.ShortError(err)) 259 } 260 }() 261 262 // initiation of default glue should be after RegisterMySQL, which is ready to be called after taskCfg.Adjust 263 // and also put it here could avoid injecting another two SkipRunTask failpoint to caller 264 if g == nil { 265 db, err := restore.DBFromConfig(taskCfg.TiDB) 266 if err != nil { 267 return err 268 } 269 g = glue.NewExternalTiDBGlue(db, taskCfg.TiDB.SQLMode) 270 } 271 272 u, err := storage.ParseBackend(taskCfg.Mydumper.SourceDir, nil) 273 if err != nil { 274 return errors.Annotate(err, "parse backend failed") 275 } 276 s, err := storage.New(ctx, u, &storage.ExternalStorageOptions{}) 277 if err != nil { 278 return errors.Annotate(err, "create storage failed") 279 } 280 281 loadTask := log.L().Begin(zap.InfoLevel, "load data source") 282 var mdl *mydump.MDLoader 283 mdl, err = mydump.NewMyDumpLoaderWithStore(ctx, taskCfg, s) 284 loadTask.End(zap.ErrorLevel, err) 285 if err != nil { 286 return errors.Trace(err) 287 } 288 err = checkSystemRequirement(taskCfg, mdl.GetDatabases()) 289 if err != nil { 290 log.L().Error("check system requirements failed", zap.Error(err)) 291 return errors.Trace(err) 292 } 293 // check table schema conflicts 294 err = checkSchemaConflict(taskCfg, mdl.GetDatabases()) 295 if err != nil { 296 log.L().Error("checkpoint schema conflicts with data files", zap.Error(err)) 297 return errors.Trace(err) 298 } 299 300 dbMetas := mdl.GetDatabases() 301 web.BroadcastInitProgress(dbMetas) 302 303 var procedure *restore.Controller 304 procedure, err = restore.NewRestoreController(ctx, dbMetas, taskCfg, s, g) 305 if err != nil { 306 log.L().Error("restore failed", log.ShortError(err)) 307 return errors.Trace(err) 308 } 309 defer procedure.Close() 310 311 err = procedure.Run(ctx) 312 return errors.Trace(err) 313 } 314 315 func (l *Lightning) Stop() { 316 l.cancelLock.Lock() 317 if l.cancel != nil { 318 l.cancel() 319 } 320 l.cancelLock.Unlock() 321 if err := l.server.Shutdown(l.ctx); err != nil { 322 log.L().Warn("failed to shutdown HTTP server", log.ShortError(err)) 323 } 324 l.shutdown() 325 } 326 327 func writeJSONError(w http.ResponseWriter, code int, prefix string, err error) { 328 type errorResponse struct { 329 Error string `json:"error"` 330 } 331 332 w.WriteHeader(code) 333 334 if err != nil { 335 prefix += ": " + err.Error() 336 } 337 _ = json.NewEncoder(w).Encode(errorResponse{Error: prefix}) 338 } 339 340 func parseTaskID(req *http.Request) (int64, string, error) { 341 path := strings.TrimPrefix(req.URL.Path, "/") 342 taskIDString := path 343 verb := "" 344 if i := strings.IndexByte(path, '/'); i >= 0 { 345 taskIDString = path[:i] 346 verb = path[i+1:] 347 } 348 349 taskID, err := strconv.ParseInt(taskIDString, 10, 64) 350 if err != nil { 351 return 0, "", err 352 } 353 354 return taskID, verb, nil 355 } 356 357 func (l *Lightning) handleTask(w http.ResponseWriter, req *http.Request) { 358 w.Header().Set("Content-Type", "application/json") 359 360 switch req.Method { 361 case http.MethodGet: 362 taskID, _, err := parseTaskID(req) 363 // golint tells us to refactor this with switch stmt. 364 // However switch stmt doesn't support init-statements, 365 // hence if we follow it things might be worse. 366 // Anyway, this chain of if-else isn't unacceptable. 367 //nolint:gocritic 368 if e, ok := err.(*strconv.NumError); ok && e.Num == "" { 369 l.handleGetTask(w) 370 } else if err == nil { 371 l.handleGetOneTask(w, req, taskID) 372 } else { 373 writeJSONError(w, http.StatusBadRequest, "invalid task ID", err) 374 } 375 case http.MethodPost: 376 l.handlePostTask(w, req) 377 case http.MethodDelete: 378 l.handleDeleteOneTask(w, req) 379 case http.MethodPatch: 380 l.handlePatchOneTask(w, req) 381 default: 382 w.Header().Set("Allow", http.MethodGet+", "+http.MethodPost+", "+http.MethodDelete+", "+http.MethodPatch) 383 writeJSONError(w, http.StatusMethodNotAllowed, "only GET, POST, DELETE and PATCH are allowed", nil) 384 } 385 } 386 387 func (l *Lightning) handleGetTask(w http.ResponseWriter) { 388 var response struct { 389 Current *int64 `json:"current"` 390 QueuedIDs []int64 `json:"queue"` 391 } 392 393 if l.taskCfgs != nil { 394 response.QueuedIDs = l.taskCfgs.AllIDs() 395 } else { 396 response.QueuedIDs = []int64{} 397 } 398 399 l.cancelLock.Lock() 400 if l.cancel != nil && l.curTask != nil { 401 response.Current = new(int64) 402 *response.Current = l.curTask.TaskID 403 } 404 l.cancelLock.Unlock() 405 406 w.WriteHeader(http.StatusOK) 407 _ = json.NewEncoder(w).Encode(response) 408 } 409 410 func (l *Lightning) handleGetOneTask(w http.ResponseWriter, req *http.Request, taskID int64) { 411 var task *config.Config 412 413 l.cancelLock.Lock() 414 if l.curTask != nil && l.curTask.TaskID == taskID { 415 task = l.curTask 416 } 417 l.cancelLock.Unlock() 418 419 if task == nil && l.taskCfgs != nil { 420 task, _ = l.taskCfgs.Get(taskID) 421 } 422 423 if task == nil { 424 writeJSONError(w, http.StatusNotFound, "task ID not found", nil) 425 return 426 } 427 428 json, err := json.Marshal(task) 429 if err != nil { 430 writeJSONError(w, http.StatusInternalServerError, "unable to serialize task", err) 431 return 432 } 433 434 writeBytesCompressed(w, req, json) 435 } 436 437 func (l *Lightning) handlePostTask(w http.ResponseWriter, req *http.Request) { 438 w.Header().Set("Cache-Control", "no-store") 439 440 if l.taskCfgs == nil { 441 // l.taskCfgs is non-nil only if Lightning is started with RunServer(). 442 // Without the server mode this pointer is default to be nil. 443 writeJSONError(w, http.StatusNotImplemented, "server-mode not enabled", nil) 444 return 445 } 446 447 type taskResponse struct { 448 ID int64 `json:"id"` 449 } 450 451 data, err := io.ReadAll(req.Body) 452 if err != nil { 453 writeJSONError(w, http.StatusBadRequest, "cannot read request", err) 454 return 455 } 456 log.L().Debug("received task config", zap.ByteString("content", data)) 457 458 cfg := config.NewConfig() 459 if err = cfg.LoadFromGlobal(l.globalCfg); err != nil { 460 writeJSONError(w, http.StatusInternalServerError, "cannot restore from global config", err) 461 return 462 } 463 if err = cfg.LoadFromTOML(data); err != nil { 464 writeJSONError(w, http.StatusBadRequest, "cannot parse task (must be TOML)", err) 465 return 466 } 467 if err = cfg.Adjust(l.ctx); err != nil { 468 writeJSONError(w, http.StatusBadRequest, "invalid task configuration", err) 469 return 470 } 471 472 l.taskCfgs.Push(cfg) 473 w.WriteHeader(http.StatusOK) 474 _ = json.NewEncoder(w).Encode(taskResponse{ID: cfg.TaskID}) 475 } 476 477 func (l *Lightning) handleDeleteOneTask(w http.ResponseWriter, req *http.Request) { 478 w.Header().Set("Content-Type", "application/json") 479 480 taskID, _, err := parseTaskID(req) 481 if err != nil { 482 writeJSONError(w, http.StatusBadRequest, "invalid task ID", err) 483 return 484 } 485 486 var cancel context.CancelFunc 487 cancelSuccess := false 488 489 l.cancelLock.Lock() 490 if l.cancel != nil && l.curTask != nil && l.curTask.TaskID == taskID { 491 cancel = l.cancel 492 l.cancel = nil 493 } 494 l.cancelLock.Unlock() 495 496 if cancel != nil { 497 cancel() 498 cancelSuccess = true 499 } else if l.taskCfgs != nil { 500 cancelSuccess = l.taskCfgs.Remove(taskID) 501 } 502 503 log.L().Info("canceled task", zap.Int64("taskID", taskID), zap.Bool("success", cancelSuccess)) 504 505 if cancelSuccess { 506 w.WriteHeader(http.StatusOK) 507 _, _ = w.Write([]byte("{}")) 508 } else { 509 writeJSONError(w, http.StatusNotFound, "task ID not found", nil) 510 } 511 } 512 513 func (l *Lightning) handlePatchOneTask(w http.ResponseWriter, req *http.Request) { 514 if l.taskCfgs == nil { 515 writeJSONError(w, http.StatusNotImplemented, "server-mode not enabled", nil) 516 return 517 } 518 519 taskID, verb, err := parseTaskID(req) 520 if err != nil { 521 writeJSONError(w, http.StatusBadRequest, "invalid task ID", err) 522 return 523 } 524 525 moveSuccess := false 526 switch verb { 527 case "front": 528 moveSuccess = l.taskCfgs.MoveToFront(taskID) 529 case "back": 530 moveSuccess = l.taskCfgs.MoveToBack(taskID) 531 default: 532 writeJSONError(w, http.StatusBadRequest, "unknown patch action", nil) 533 return 534 } 535 536 if moveSuccess { 537 w.WriteHeader(http.StatusOK) 538 _, _ = w.Write([]byte("{}")) 539 } else { 540 writeJSONError(w, http.StatusNotFound, "task ID not found", nil) 541 } 542 } 543 544 func writeBytesCompressed(w http.ResponseWriter, req *http.Request, b []byte) { 545 if !strings.Contains(req.Header.Get("Accept-Encoding"), "gzip") { 546 _, _ = w.Write(b) 547 return 548 } 549 550 w.Header().Set("Content-Encoding", "gzip") 551 w.WriteHeader(http.StatusOK) 552 gw, _ := gzip.NewWriterLevel(w, gzip.BestSpeed) 553 _, _ = gw.Write(b) 554 _ = gw.Close() 555 } 556 557 func handleProgressTask(w http.ResponseWriter, req *http.Request) { 558 w.Header().Set("Content-Type", "application/json") 559 res, err := web.MarshalTaskProgress() 560 if err == nil { 561 writeBytesCompressed(w, req, res) 562 } else { 563 w.WriteHeader(http.StatusInternalServerError) 564 _ = json.NewEncoder(w).Encode(err.Error()) 565 } 566 } 567 568 func handleProgressTable(w http.ResponseWriter, req *http.Request) { 569 w.Header().Set("Content-Type", "application/json") 570 tableName := req.URL.Query().Get("t") 571 res, err := web.MarshalTableCheckpoints(tableName) 572 if err == nil { 573 writeBytesCompressed(w, req, res) 574 } else { 575 if errors.IsNotFound(err) { 576 w.WriteHeader(http.StatusNotFound) 577 } else { 578 w.WriteHeader(http.StatusInternalServerError) 579 } 580 _ = json.NewEncoder(w).Encode(err.Error()) 581 } 582 } 583 584 func handlePause(w http.ResponseWriter, req *http.Request) { 585 w.Header().Set("Content-Type", "application/json") 586 587 switch req.Method { 588 case http.MethodGet: 589 w.WriteHeader(http.StatusOK) 590 fmt.Fprintf(w, `{"paused":%v}`, restore.DeliverPauser.IsPaused()) 591 592 case http.MethodPut: 593 w.WriteHeader(http.StatusOK) 594 restore.DeliverPauser.Pause() 595 log.L().Info("progress paused") 596 _, _ = w.Write([]byte("{}")) 597 598 default: 599 w.Header().Set("Allow", http.MethodGet+", "+http.MethodPut) 600 writeJSONError(w, http.StatusMethodNotAllowed, "only GET and PUT are allowed", nil) 601 } 602 } 603 604 func handleResume(w http.ResponseWriter, req *http.Request) { 605 w.Header().Set("Content-Type", "application/json") 606 607 switch req.Method { 608 case http.MethodPut: 609 w.WriteHeader(http.StatusOK) 610 restore.DeliverPauser.Resume() 611 log.L().Info("progress resumed") 612 _, _ = w.Write([]byte("{}")) 613 614 default: 615 w.Header().Set("Allow", http.MethodPut) 616 writeJSONError(w, http.StatusMethodNotAllowed, "only PUT is allowed", nil) 617 } 618 } 619 620 func handleLogLevel(w http.ResponseWriter, req *http.Request) { 621 w.Header().Set("Content-Type", "application/json") 622 623 var logLevel struct { 624 Level zapcore.Level `json:"level"` 625 } 626 627 switch req.Method { 628 case http.MethodGet: 629 logLevel.Level = log.Level() 630 w.WriteHeader(http.StatusOK) 631 _ = json.NewEncoder(w).Encode(logLevel) 632 633 case http.MethodPut, http.MethodPost: 634 if err := json.NewDecoder(req.Body).Decode(&logLevel); err != nil { 635 writeJSONError(w, http.StatusBadRequest, "invalid log level", err) 636 return 637 } 638 oldLevel := log.SetLevel(zapcore.InfoLevel) 639 log.L().Info("changed log level", zap.Stringer("old", oldLevel), zap.Stringer("new", logLevel.Level)) 640 log.SetLevel(logLevel.Level) 641 w.WriteHeader(http.StatusOK) 642 _, _ = w.Write([]byte("{}")) 643 644 default: 645 w.Header().Set("Allow", http.MethodGet+", "+http.MethodPut+", "+http.MethodPost) 646 writeJSONError(w, http.StatusMethodNotAllowed, "only GET, PUT and POST are allowed", nil) 647 } 648 } 649 650 func checkSystemRequirement(cfg *config.Config, dbsMeta []*mydump.MDDatabaseMeta) error { 651 // in local mode, we need to read&write a lot of L0 sst files, so we need to check system max open files limit 652 if cfg.TikvImporter.Backend == config.BackendLocal { 653 // estimate max open files = {top N(TableConcurrency) table sizes} / {MemoryTableSize} 654 tableTotalSizes := make([]int64, 0) 655 for _, dbs := range dbsMeta { 656 for _, tb := range dbs.Tables { 657 tableTotalSizes = append(tableTotalSizes, tb.TotalSize) 658 } 659 } 660 sort.Slice(tableTotalSizes, func(i, j int) bool { 661 return tableTotalSizes[i] > tableTotalSizes[j] 662 }) 663 topNTotalSize := int64(0) 664 for i := 0; i < len(tableTotalSizes) && i < cfg.App.TableConcurrency; i++ { 665 topNTotalSize += tableTotalSizes[i] 666 } 667 668 // region-concurrency: number of LocalWriters writing SST files. 669 // 2*totalSize/memCacheSize: number of Pebble MemCache files. 670 maxDBFiles := topNTotalSize / int64(cfg.TikvImporter.LocalWriterMemCacheSize) * 2 671 // the pebble db and all import routine need upto maxDBFiles fds for read and write. 672 maxOpenDBFiles := maxDBFiles * (1 + int64(cfg.TikvImporter.RangeConcurrency)) 673 estimateMaxFiles := local.Rlim_t(cfg.App.RegionConcurrency) + local.Rlim_t(maxOpenDBFiles) 674 if err := local.VerifyRLimit(estimateMaxFiles); err != nil { 675 return err 676 } 677 } 678 679 return nil 680 } 681 682 // checkSchemaConflict return error if checkpoint table scheme is conflict with data files 683 func checkSchemaConflict(cfg *config.Config, dbsMeta []*mydump.MDDatabaseMeta) error { 684 if cfg.Checkpoint.Enable && cfg.Checkpoint.Driver == config.CheckpointDriverMySQL { 685 for _, db := range dbsMeta { 686 if db.Name == cfg.Checkpoint.Schema { 687 for _, tb := range db.Tables { 688 if checkpoints.IsCheckpointTable(tb.Name) { 689 return errors.Errorf("checkpoint table `%s`.`%s` conflict with data files. Please change the `checkpoint.schema` config or set `checkpoint.driver` to \"file\" instead", db.Name, tb.Name) 690 } 691 } 692 } 693 } 694 } 695 return nil 696 }