github.com/pingcap/br@v5.3.0-alpha.0.20220125034240-ec59c7b6ce30+incompatible/pkg/task/backup_raw.go (about)

     1  // Copyright 2020 PingCAP, Inc. Licensed under Apache-2.0.
     2  
     3  package task
     4  
     5  import (
     6  	"bytes"
     7  	"context"
     8  
     9  	"github.com/pingcap/br/pkg/metautil"
    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/spf13/cobra"
    16  	"github.com/spf13/pflag"
    17  	"go.uber.org/zap"
    18  
    19  	"github.com/pingcap/br/pkg/backup"
    20  	berrors "github.com/pingcap/br/pkg/errors"
    21  	"github.com/pingcap/br/pkg/glue"
    22  	"github.com/pingcap/br/pkg/rtree"
    23  	"github.com/pingcap/br/pkg/storage"
    24  	"github.com/pingcap/br/pkg/summary"
    25  	"github.com/pingcap/br/pkg/utils"
    26  )
    27  
    28  const (
    29  	flagKeyFormat        = "format"
    30  	flagTiKVColumnFamily = "cf"
    31  	flagStartKey         = "start"
    32  	flagEndKey           = "end"
    33  )
    34  
    35  // RawKvConfig is the common config for rawkv backup and restore.
    36  type RawKvConfig struct {
    37  	Config
    38  
    39  	StartKey []byte `json:"start-key" toml:"start-key"`
    40  	EndKey   []byte `json:"end-key" toml:"end-key"`
    41  	CF       string `json:"cf" toml:"cf"`
    42  	CompressionConfig
    43  	RemoveSchedulers bool `json:"remove-schedulers" toml:"remove-schedulers"`
    44  }
    45  
    46  // DefineRawBackupFlags defines common flags for the backup command.
    47  func DefineRawBackupFlags(command *cobra.Command) {
    48  	command.Flags().StringP(flagKeyFormat, "", "hex", "start/end key format, support raw|escaped|hex")
    49  	command.Flags().StringP(flagTiKVColumnFamily, "", "default", "backup specify cf, correspond to tikv cf")
    50  	command.Flags().StringP(flagStartKey, "", "", "backup raw kv start key, key is inclusive")
    51  	command.Flags().StringP(flagEndKey, "", "", "backup raw kv end key, key is exclusive")
    52  	command.Flags().String(flagCompressionType, "zstd",
    53  		"backup sst file compression algorithm, value can be one of 'lz4|zstd|snappy'")
    54  	command.Flags().Bool(flagRemoveSchedulers, false,
    55  		"disable the balance, shuffle and region-merge schedulers in PD to speed up backup")
    56  	// This flag can impact the online cluster, so hide it in case of abuse.
    57  	_ = command.Flags().MarkHidden(flagRemoveSchedulers)
    58  }
    59  
    60  // ParseFromFlags parses the raw kv backup&restore common flags from the flag set.
    61  func (cfg *RawKvConfig) ParseFromFlags(flags *pflag.FlagSet) error {
    62  	format, err := flags.GetString(flagKeyFormat)
    63  	if err != nil {
    64  		return errors.Trace(err)
    65  	}
    66  	start, err := flags.GetString(flagStartKey)
    67  	if err != nil {
    68  		return errors.Trace(err)
    69  	}
    70  	cfg.StartKey, err = utils.ParseKey(format, start)
    71  	if err != nil {
    72  		return errors.Trace(err)
    73  	}
    74  	end, err := flags.GetString(flagEndKey)
    75  	if err != nil {
    76  		return errors.Trace(err)
    77  	}
    78  	cfg.EndKey, err = utils.ParseKey(format, end)
    79  	if err != nil {
    80  		return errors.Trace(err)
    81  	}
    82  
    83  	if len(cfg.StartKey) > 0 && len(cfg.EndKey) > 0 && bytes.Compare(cfg.StartKey, cfg.EndKey) >= 0 {
    84  		return errors.Annotate(berrors.ErrBackupInvalidRange, "endKey must be greater than startKey")
    85  	}
    86  	cfg.CF, err = flags.GetString(flagTiKVColumnFamily)
    87  	if err != nil {
    88  		return errors.Trace(err)
    89  	}
    90  	if err = cfg.Config.ParseFromFlags(flags); err != nil {
    91  		return errors.Trace(err)
    92  	}
    93  	return nil
    94  }
    95  
    96  // ParseBackupConfigFromFlags parses the backup-related flags from the flag set.
    97  func (cfg *RawKvConfig) ParseBackupConfigFromFlags(flags *pflag.FlagSet) error {
    98  	err := cfg.ParseFromFlags(flags)
    99  	if err != nil {
   100  		return errors.Trace(err)
   101  	}
   102  
   103  	compressionCfg, err := parseCompressionFlags(flags)
   104  	if err != nil {
   105  		return errors.Trace(err)
   106  	}
   107  	cfg.CompressionConfig = *compressionCfg
   108  
   109  	cfg.RemoveSchedulers, err = flags.GetBool(flagRemoveSchedulers)
   110  	if err != nil {
   111  		return errors.Trace(err)
   112  	}
   113  	level, err := flags.GetInt32(flagCompressionLevel)
   114  	if err != nil {
   115  		return errors.Trace(err)
   116  	}
   117  	cfg.CompressionLevel = level
   118  
   119  	return nil
   120  }
   121  
   122  // RunBackupRaw starts a backup task inside the current goroutine.
   123  func RunBackupRaw(c context.Context, g glue.Glue, cmdName string, cfg *RawKvConfig) error {
   124  	cfg.adjust()
   125  
   126  	defer summary.Summary(cmdName)
   127  	ctx, cancel := context.WithCancel(c)
   128  	defer cancel()
   129  
   130  	if span := opentracing.SpanFromContext(ctx); span != nil && span.Tracer() != nil {
   131  		span1 := span.Tracer().StartSpan("task.RunBackupRaw", opentracing.ChildOf(span.Context()))
   132  		defer span1.Finish()
   133  		ctx = opentracing.ContextWithSpan(ctx, span1)
   134  	}
   135  
   136  	u, err := storage.ParseBackend(cfg.Storage, &cfg.BackendOptions)
   137  	if err != nil {
   138  		return errors.Trace(err)
   139  	}
   140  	// Backup raw does not need domain.
   141  	needDomain := false
   142  	mgr, err := NewMgr(ctx, g, cfg.PD, cfg.TLS, GetKeepalive(&cfg.Config), cfg.CheckRequirements, needDomain)
   143  	if err != nil {
   144  		return errors.Trace(err)
   145  	}
   146  	defer mgr.Close()
   147  
   148  	client, err := backup.NewBackupClient(ctx, mgr)
   149  	if err != nil {
   150  		return errors.Trace(err)
   151  	}
   152  	opts := storage.ExternalStorageOptions{
   153  		NoCredentials:   cfg.NoCreds,
   154  		SendCredentials: cfg.SendCreds,
   155  		SkipCheckPath:   cfg.SkipCheckPath,
   156  	}
   157  	if err = client.SetStorage(ctx, u, &opts); err != nil {
   158  		return errors.Trace(err)
   159  	}
   160  
   161  	backupRange := rtree.Range{StartKey: cfg.StartKey, EndKey: cfg.EndKey}
   162  
   163  	if cfg.RemoveSchedulers {
   164  		restore, e := mgr.RemoveSchedulers(ctx)
   165  		defer func() {
   166  			if ctx.Err() != nil {
   167  				log.Warn("context canceled, try shutdown")
   168  				ctx = context.Background()
   169  			}
   170  			if restoreE := restore(ctx); restoreE != nil {
   171  				log.Warn("failed to restore removed schedulers, you may need to restore them manually", zap.Error(restoreE))
   172  			}
   173  		}()
   174  		if e != nil {
   175  			return errors.Trace(err)
   176  		}
   177  	}
   178  
   179  	brVersion := g.GetVersion()
   180  	clusterVersion, err := mgr.GetClusterVersion(ctx)
   181  	if err != nil {
   182  		return errors.Trace(err)
   183  	}
   184  
   185  	// The number of regions need to backup
   186  	approximateRegions, err := mgr.GetRegionCount(ctx, backupRange.StartKey, backupRange.EndKey)
   187  	if err != nil {
   188  		return errors.Trace(err)
   189  	}
   190  
   191  	summary.CollectInt("backup total regions", approximateRegions)
   192  
   193  	// Backup
   194  	// Redirect to log if there is no log file to avoid unreadable output.
   195  	updateCh := g.StartProgress(
   196  		ctx, cmdName, int64(approximateRegions), !cfg.LogProgress)
   197  
   198  	progressCallBack := func(unit backup.ProgressUnit) {
   199  		if unit == backup.RangeUnit {
   200  			return
   201  		}
   202  		updateCh.Inc()
   203  	}
   204  
   205  	req := backuppb.BackupRequest{
   206  		ClusterId:        client.GetClusterID(),
   207  		StartVersion:     0,
   208  		EndVersion:       0,
   209  		RateLimit:        cfg.RateLimit,
   210  		Concurrency:      cfg.Concurrency,
   211  		IsRawKv:          true,
   212  		Cf:               cfg.CF,
   213  		CompressionType:  cfg.CompressionType,
   214  		CompressionLevel: cfg.CompressionLevel,
   215  	}
   216  	metaWriter := metautil.NewMetaWriter(client.GetStorage(), metautil.MetaFileSize, false)
   217  	metaWriter.StartWriteMetasAsync(ctx, metautil.AppendDataFile)
   218  	err = client.BackupRange(ctx, backupRange.StartKey, backupRange.EndKey, req, metaWriter, progressCallBack)
   219  	if err != nil {
   220  		return errors.Trace(err)
   221  	}
   222  	// Backup has finished
   223  	updateCh.Close()
   224  	rawRanges := []*backuppb.RawRange{{StartKey: backupRange.StartKey, EndKey: backupRange.EndKey, Cf: cfg.CF}}
   225  	metaWriter.Update(func(m *backuppb.BackupMeta) {
   226  		m.StartVersion = req.StartVersion
   227  		m.EndVersion = req.EndVersion
   228  		m.IsRawKv = req.IsRawKv
   229  		m.RawRanges = rawRanges
   230  		m.ClusterId = req.ClusterId
   231  		m.ClusterVersion = clusterVersion
   232  		m.BrVersion = brVersion
   233  	})
   234  	err = metaWriter.FinishWriteMetas(ctx, metautil.AppendDataFile)
   235  	if err != nil {
   236  		return errors.Trace(err)
   237  	}
   238  	g.Record(summary.BackupDataSize, metaWriter.ArchiveSize())
   239  
   240  	// Set task summary to success status.
   241  	summary.SetSuccessStatus(true)
   242  	return nil
   243  }