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  }