github.com/pingcap/br@v5.3.0-alpha.0.20220125034240-ec59c7b6ce30+incompatible/pkg/backup/schema.go (about) 1 // Copyright 2020 PingCAP, Inc. Licensed under Apache-2.0. 2 3 package backup 4 5 import ( 6 "context" 7 "encoding/json" 8 "fmt" 9 "time" 10 11 "github.com/opentracing/opentracing-go" 12 "github.com/pingcap/errors" 13 backuppb "github.com/pingcap/kvproto/pkg/backup" 14 "github.com/pingcap/log" 15 "github.com/pingcap/parser/model" 16 "github.com/pingcap/tidb/kv" 17 "github.com/pingcap/tidb/statistics/handle" 18 "github.com/pingcap/tipb/go-tipb" 19 "go.uber.org/zap" 20 "golang.org/x/sync/errgroup" 21 22 "github.com/pingcap/br/pkg/checksum" 23 "github.com/pingcap/br/pkg/glue" 24 "github.com/pingcap/br/pkg/logutil" 25 "github.com/pingcap/br/pkg/metautil" 26 "github.com/pingcap/br/pkg/summary" 27 "github.com/pingcap/br/pkg/utils" 28 ) 29 30 const ( 31 // DefaultSchemaConcurrency is the default number of the concurrent 32 // backup schema tasks. 33 DefaultSchemaConcurrency = 64 34 ) 35 36 type scheamInfo struct { 37 tableInfo *model.TableInfo 38 dbInfo *model.DBInfo 39 crc64xor uint64 40 totalKvs uint64 41 totalBytes uint64 42 stats *handle.JSONTable 43 } 44 45 // Schemas is task for backuping schemas. 46 type Schemas struct { 47 // name -> schema 48 schemas map[string]*scheamInfo 49 } 50 51 func newBackupSchemas() *Schemas { 52 return &Schemas{ 53 schemas: make(map[string]*scheamInfo), 54 } 55 } 56 57 func (ss *Schemas) addSchema( 58 dbInfo *model.DBInfo, tableInfo *model.TableInfo, 59 ) { 60 name := fmt.Sprintf("%s.%s", 61 utils.EncloseName(dbInfo.Name.L), utils.EncloseName(tableInfo.Name.L)) 62 ss.schemas[name] = &scheamInfo{ 63 tableInfo: tableInfo, 64 dbInfo: dbInfo, 65 } 66 } 67 68 // BackupSchemas backups table info, including checksum and stats. 69 func (ss *Schemas) BackupSchemas( 70 ctx context.Context, 71 metaWriter *metautil.MetaWriter, 72 store kv.Storage, 73 statsHandle *handle.Handle, 74 backupTS uint64, 75 concurrency uint, 76 copConcurrency uint, 77 skipChecksum bool, 78 updateCh glue.Progress, 79 ) error { 80 if span := opentracing.SpanFromContext(ctx); span != nil && span.Tracer() != nil { 81 span1 := span.Tracer().StartSpan("Schemas.BackupSchemas", opentracing.ChildOf(span.Context())) 82 defer span1.Finish() 83 ctx = opentracing.ContextWithSpan(ctx, span1) 84 } 85 86 workerPool := utils.NewWorkerPool(concurrency, "Schemas") 87 errg, ectx := errgroup.WithContext(ctx) 88 startAll := time.Now() 89 op := metautil.AppendSchema 90 metaWriter.StartWriteMetasAsync(ctx, op) 91 for _, s := range ss.schemas { 92 schema := s 93 // Because schema.dbInfo is a pointer that many tables point to. 94 // Remove "add Temporary-prefix into dbName" from closure to prevent concurrent operations. 95 if utils.IsSysDB(schema.dbInfo.Name.L) { 96 schema.dbInfo.Name = utils.TemporaryDBName(schema.dbInfo.Name.O) 97 } 98 workerPool.ApplyOnErrorGroup(errg, func() error { 99 logger := log.With( 100 zap.String("db", schema.dbInfo.Name.O), 101 zap.String("table", schema.tableInfo.Name.O), 102 ) 103 104 if !skipChecksum { 105 logger.Info("table checksum start") 106 start := time.Now() 107 checksumResp, err := calculateChecksum( 108 ectx, schema.tableInfo, store.GetClient(), backupTS, copConcurrency) 109 if err != nil { 110 return errors.Trace(err) 111 } 112 schema.crc64xor = checksumResp.Checksum 113 schema.totalKvs = checksumResp.TotalKvs 114 schema.totalBytes = checksumResp.TotalBytes 115 logger.Info("table checksum finished", 116 zap.Uint64("Crc64Xor", checksumResp.Checksum), 117 zap.Uint64("TotalKvs", checksumResp.TotalKvs), 118 zap.Uint64("TotalBytes", checksumResp.TotalBytes), 119 zap.Duration("take", time.Since(start))) 120 } 121 if statsHandle != nil { 122 jsonTable, err := statsHandle.DumpStatsToJSON( 123 schema.dbInfo.Name.String(), schema.tableInfo, nil) 124 if err != nil { 125 logger.Error("dump table stats failed", logutil.ShortError(err)) 126 } 127 schema.stats = jsonTable 128 } 129 // Send schema to metawriter 130 dbBytes, err := json.Marshal(schema.dbInfo) 131 if err != nil { 132 return errors.Trace(err) 133 } 134 tableBytes, err := json.Marshal(schema.tableInfo) 135 if err != nil { 136 return errors.Trace(err) 137 } 138 var statsBytes []byte 139 if schema.stats != nil { 140 statsBytes, err = json.Marshal(schema.stats) 141 if err != nil { 142 return errors.Trace(err) 143 } 144 } 145 s := &backuppb.Schema{ 146 Db: dbBytes, 147 Table: tableBytes, 148 Crc64Xor: schema.crc64xor, 149 TotalKvs: schema.totalKvs, 150 TotalBytes: schema.totalBytes, 151 Stats: statsBytes, 152 } 153 154 if err := metaWriter.Send(s, op); err != nil { 155 return errors.Trace(err) 156 } 157 updateCh.Inc() 158 return nil 159 }) 160 } 161 if err := errg.Wait(); err != nil { 162 return errors.Trace(err) 163 } 164 log.Info("backup checksum", zap.Duration("take", time.Since(startAll))) 165 summary.CollectDuration("backup checksum", time.Since(startAll)) 166 return metaWriter.FinishWriteMetas(ctx, op) 167 } 168 169 // Len returns the number of schemas. 170 func (ss *Schemas) Len() int { 171 return len(ss.schemas) 172 } 173 174 func calculateChecksum( 175 ctx context.Context, 176 table *model.TableInfo, 177 client kv.Client, 178 backupTS uint64, 179 concurrency uint, 180 ) (*tipb.ChecksumResponse, error) { 181 exe, err := checksum.NewExecutorBuilder(table, backupTS). 182 SetConcurrency(concurrency). 183 Build() 184 if err != nil { 185 return nil, errors.Trace(err) 186 } 187 checksumResp, err := exe.Execute(ctx, client, func() { 188 // TODO: update progress here. 189 }) 190 if err != nil { 191 return nil, errors.Trace(err) 192 } 193 return checksumResp, nil 194 }