github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/cli/debug.go (about)

     1  // Copyright 2016 The Cockroach Authors.
     2  //
     3  // Use of this software is governed by the Business Source License
     4  // included in the file licenses/BSL.txt.
     5  //
     6  // As of the Change Date specified in that file, in accordance with
     7  // the Business Source License, use of this software will be governed
     8  // by the Apache License, Version 2.0, included in the file
     9  // licenses/APL.txt.
    10  
    11  package cli
    12  
    13  import (
    14  	"bufio"
    15  	"bytes"
    16  	"context"
    17  	gohex "encoding/hex"
    18  	"fmt"
    19  	"io"
    20  	"math"
    21  	"os"
    22  	"path/filepath"
    23  	"regexp"
    24  	"sort"
    25  	"strconv"
    26  	"strings"
    27  	"time"
    28  
    29  	"github.com/cockroachdb/cockroach/pkg/base"
    30  	"github.com/cockroachdb/cockroach/pkg/cli/syncbench"
    31  	"github.com/cockroachdb/cockroach/pkg/config"
    32  	"github.com/cockroachdb/cockroach/pkg/config/zonepb"
    33  	"github.com/cockroachdb/cockroach/pkg/gossip"
    34  	"github.com/cockroachdb/cockroach/pkg/keys"
    35  	"github.com/cockroachdb/cockroach/pkg/kv/kvserver"
    36  	"github.com/cockroachdb/cockroach/pkg/kv/kvserver/gc"
    37  	"github.com/cockroachdb/cockroach/pkg/kv/kvserver/kvserverpb"
    38  	"github.com/cockroachdb/cockroach/pkg/kv/kvserver/rditer"
    39  	"github.com/cockroachdb/cockroach/pkg/kv/kvserver/stateloader"
    40  	"github.com/cockroachdb/cockroach/pkg/roachpb"
    41  	"github.com/cockroachdb/cockroach/pkg/server"
    42  	"github.com/cockroachdb/cockroach/pkg/server/serverpb"
    43  	"github.com/cockroachdb/cockroach/pkg/server/status/statuspb"
    44  	"github.com/cockroachdb/cockroach/pkg/sql/execinfrapb"
    45  	"github.com/cockroachdb/cockroach/pkg/storage"
    46  	"github.com/cockroachdb/cockroach/pkg/storage/enginepb"
    47  	"github.com/cockroachdb/cockroach/pkg/ts/tspb"
    48  	"github.com/cockroachdb/cockroach/pkg/util/envutil"
    49  	"github.com/cockroachdb/cockroach/pkg/util/flagutil"
    50  	"github.com/cockroachdb/cockroach/pkg/util/hlc"
    51  	"github.com/cockroachdb/cockroach/pkg/util/humanizeutil"
    52  	"github.com/cockroachdb/cockroach/pkg/util/log"
    53  	"github.com/cockroachdb/cockroach/pkg/util/protoutil"
    54  	"github.com/cockroachdb/cockroach/pkg/util/stop"
    55  	"github.com/cockroachdb/cockroach/pkg/util/sysutil"
    56  	"github.com/cockroachdb/cockroach/pkg/util/timeutil"
    57  	"github.com/cockroachdb/cockroach/pkg/util/uuid"
    58  	"github.com/cockroachdb/errors"
    59  	"github.com/cockroachdb/pebble"
    60  	"github.com/cockroachdb/pebble/tool"
    61  	"github.com/gogo/protobuf/jsonpb"
    62  	"github.com/kr/pretty"
    63  	"github.com/spf13/cobra"
    64  )
    65  
    66  var debugKeysCmd = &cobra.Command{
    67  	Use:   "keys <directory>",
    68  	Short: "dump all the keys in a store",
    69  	Long: `
    70  Pretty-prints all keys in a store.
    71  `,
    72  	Args: cobra.ExactArgs(1),
    73  	RunE: MaybeDecorateGRPCError(runDebugKeys),
    74  }
    75  
    76  var debugBallastCmd = &cobra.Command{
    77  	Use:   "ballast <file>",
    78  	Short: "create a ballast file",
    79  	Long: `
    80  Create a ballast file to fill the store directory up to a given amount
    81  `,
    82  	Args: cobra.ExactArgs(1),
    83  	RunE: runDebugBallast,
    84  }
    85  
    86  // PopulateRocksDBConfigHook is a callback set by CCL code.
    87  // It populates any needed fields in the RocksDBConfig.
    88  // It must do nothing in OSS code.
    89  var PopulateRocksDBConfigHook func(*base.StorageConfig) error
    90  
    91  func parsePositiveInt(arg string) (int64, error) {
    92  	i, err := strconv.ParseInt(arg, 10, 64)
    93  	if err != nil {
    94  		return 0, err
    95  	}
    96  	if i < 1 {
    97  		return 0, fmt.Errorf("illegal val: %d < 1", i)
    98  	}
    99  	return i, nil
   100  }
   101  
   102  func parseRangeID(arg string) (roachpb.RangeID, error) {
   103  	rangeIDInt, err := parsePositiveInt(arg)
   104  	if err != nil {
   105  		return 0, err
   106  	}
   107  	return roachpb.RangeID(rangeIDInt), nil
   108  }
   109  
   110  // OpenEngineOptions tunes the behavior of OpenEngine.
   111  type OpenEngineOptions struct {
   112  	ReadOnly  bool
   113  	MustExist bool
   114  }
   115  
   116  // OpenExistingStore opens the rocksdb engine rooted at 'dir'.
   117  // If 'readOnly' is true, opens the store in read-only mode.
   118  func OpenExistingStore(dir string, stopper *stop.Stopper, readOnly bool) (storage.Engine, error) {
   119  	return OpenEngine(dir, stopper, OpenEngineOptions{ReadOnly: readOnly, MustExist: true})
   120  }
   121  
   122  // OpenEngine opens the engine at 'dir'. Depending on the supplied options,
   123  // an empty engine might be initialized.
   124  func OpenEngine(dir string, stopper *stop.Stopper, opts OpenEngineOptions) (storage.Engine, error) {
   125  	maxOpenFiles, err := server.SetOpenFileLimitForOneStore()
   126  	if err != nil {
   127  		return nil, err
   128  	}
   129  
   130  	storageConfig := base.StorageConfig{
   131  		Settings:  serverCfg.Settings,
   132  		Dir:       dir,
   133  		MustExist: opts.MustExist,
   134  	}
   135  	if PopulateRocksDBConfigHook != nil {
   136  		if err := PopulateRocksDBConfigHook(&storageConfig); err != nil {
   137  			return nil, err
   138  		}
   139  	}
   140  
   141  	var db storage.Engine
   142  	storageEngine := resolveStorageEngineType(context.Background(), storage.DefaultStorageEngine, storageConfig)
   143  
   144  	switch storageEngine {
   145  	case enginepb.EngineTypePebble:
   146  		cfg := storage.PebbleConfig{
   147  			StorageConfig: storageConfig,
   148  			Opts:          storage.DefaultPebbleOptions(),
   149  		}
   150  		cfg.Opts.Cache = pebble.NewCache(server.DefaultCacheSize)
   151  		defer cfg.Opts.Cache.Unref()
   152  
   153  		cfg.Opts.MaxOpenFiles = int(maxOpenFiles)
   154  		cfg.Opts.ReadOnly = opts.ReadOnly
   155  
   156  		db, err = storage.NewPebble(context.Background(), cfg)
   157  
   158  	case enginepb.EngineTypeRocksDB:
   159  		cache := storage.NewRocksDBCache(server.DefaultCacheSize)
   160  		defer cache.Release()
   161  
   162  		cfg := storage.RocksDBConfig{
   163  			StorageConfig: storageConfig,
   164  			MaxOpenFiles:  maxOpenFiles,
   165  			ReadOnly:      opts.ReadOnly,
   166  		}
   167  
   168  		db, err = storage.NewRocksDB(cfg, cache)
   169  	}
   170  
   171  	if err != nil {
   172  		return nil, err
   173  	}
   174  
   175  	stopper.AddCloser(db)
   176  	return db, nil
   177  }
   178  
   179  func printKey(kv storage.MVCCKeyValue) (bool, error) {
   180  	fmt.Printf("%s %s: ", kv.Key.Timestamp, kv.Key.Key)
   181  	if debugCtx.sizes {
   182  		fmt.Printf(" %d %d", len(kv.Key.Key), len(kv.Value))
   183  	}
   184  	fmt.Printf("\n")
   185  	return false, nil
   186  }
   187  
   188  func runDebugKeys(cmd *cobra.Command, args []string) error {
   189  	stopper := stop.NewStopper()
   190  	defer stopper.Stop(context.Background())
   191  
   192  	db, err := OpenExistingStore(args[0], stopper, true /* readOnly */)
   193  	if err != nil {
   194  		return err
   195  	}
   196  
   197  	printer := printKey
   198  	if debugCtx.values {
   199  		printer = func(kv storage.MVCCKeyValue) (bool, error) {
   200  			kvserver.PrintKeyValue(kv)
   201  			return false, nil
   202  		}
   203  	}
   204  
   205  	results := 0
   206  	return db.Iterate(debugCtx.startKey.Key, debugCtx.endKey.Key, func(kv storage.MVCCKeyValue) (bool, error) {
   207  		done, err := printer(kv)
   208  		if done || err != nil {
   209  			return done, err
   210  		}
   211  		results++
   212  		return results == debugCtx.maxResults, nil
   213  	})
   214  }
   215  
   216  func runDebugBallast(cmd *cobra.Command, args []string) error {
   217  	ballastFile := args[0] // we use cobra.ExactArgs(1)
   218  	dataDirectory := filepath.Dir(ballastFile)
   219  
   220  	fs, err := sysutil.StatFS(dataDirectory)
   221  	if err != nil {
   222  		return errors.Wrapf(err, "failed to stat filesystem %s", dataDirectory)
   223  	}
   224  	total := fs.TotalBlocks * fs.BlockSize
   225  	free := fs.AvailBlocks * fs.BlockSize
   226  
   227  	used := total - free
   228  	var targetUsage int64
   229  	p := debugCtx.ballastSize.Percent
   230  	if math.Abs(p) > 100 {
   231  		return errors.Errorf("absolute percentage value %f greater than 100", p)
   232  	}
   233  	b := debugCtx.ballastSize.InBytes
   234  	if p != 0 && b != 0 {
   235  		return errors.New("expected exactly one of percentage or bytes non-zero, found both")
   236  	}
   237  	switch {
   238  	case p > 0:
   239  		fillRatio := p / float64(100)
   240  		targetUsage = used + int64((fillRatio)*float64(total))
   241  	case p < 0:
   242  		// Negative means leave the absolute %age of disk space.
   243  		fillRatio := 1.0 + (p / float64(100))
   244  		targetUsage = int64((fillRatio) * float64(total))
   245  	case b > 0:
   246  		targetUsage = used + b
   247  	case b < 0:
   248  		// Negative means leave that many bytes of disk space.
   249  		targetUsage = total + b
   250  	default:
   251  		return errors.New("expected exactly one of percentage or bytes non-zero, found none")
   252  	}
   253  	if used > targetUsage {
   254  		return errors.Errorf(
   255  			"Used space %s already more than needed to be filled %s\n",
   256  			humanizeutil.IBytes(used),
   257  			humanizeutil.IBytes(targetUsage),
   258  		)
   259  	}
   260  	if used == targetUsage {
   261  		return nil
   262  	}
   263  	ballastSize := targetUsage - used
   264  	if err := sysutil.CreateLargeFile(ballastFile, ballastSize); err != nil {
   265  		return errors.Wrap(err, "failed to fallocate to ballast file")
   266  	}
   267  	return nil
   268  }
   269  
   270  var debugRangeDataCmd = &cobra.Command{
   271  	Use:   "range-data <directory> <range id>",
   272  	Short: "dump all the data in a range",
   273  	Long: `
   274  Pretty-prints all keys and values in a range. By default, includes unreplicated
   275  state like the raft HardState. With --replicated, only includes data covered by
   276   the consistency checker.
   277  `,
   278  	Args: cobra.ExactArgs(2),
   279  	RunE: MaybeDecorateGRPCError(runDebugRangeData),
   280  }
   281  
   282  func runDebugRangeData(cmd *cobra.Command, args []string) error {
   283  	stopper := stop.NewStopper()
   284  	defer stopper.Stop(context.Background())
   285  
   286  	db, err := OpenExistingStore(args[0], stopper, true /* readOnly */)
   287  	if err != nil {
   288  		return err
   289  	}
   290  
   291  	rangeID, err := parseRangeID(args[1])
   292  	if err != nil {
   293  		return err
   294  	}
   295  
   296  	desc, err := loadRangeDescriptor(db, rangeID)
   297  	if err != nil {
   298  		return err
   299  	}
   300  
   301  	iter := rditer.NewReplicaDataIterator(&desc, db, debugCtx.replicated, false /* seekEnd */)
   302  	defer iter.Close()
   303  	results := 0
   304  	for ; ; iter.Next() {
   305  		if ok, err := iter.Valid(); err != nil {
   306  			return err
   307  		} else if !ok {
   308  			break
   309  		}
   310  		kvserver.PrintKeyValue(storage.MVCCKeyValue{
   311  			Key:   iter.Key(),
   312  			Value: iter.Value(),
   313  		})
   314  		results++
   315  		if results == debugCtx.maxResults {
   316  			break
   317  		}
   318  	}
   319  	return nil
   320  }
   321  
   322  var debugRangeDescriptorsCmd = &cobra.Command{
   323  	Use:   "range-descriptors <directory>",
   324  	Short: "print all range descriptors in a store",
   325  	Long: `
   326  Prints all range descriptors in a store with a history of changes.
   327  `,
   328  	Args: cobra.ExactArgs(1),
   329  	RunE: MaybeDecorateGRPCError(runDebugRangeDescriptors),
   330  }
   331  
   332  func loadRangeDescriptor(
   333  	db storage.Engine, rangeID roachpb.RangeID,
   334  ) (roachpb.RangeDescriptor, error) {
   335  	var desc roachpb.RangeDescriptor
   336  	handleKV := func(kv storage.MVCCKeyValue) (bool, error) {
   337  		if kv.Key.Timestamp == (hlc.Timestamp{}) {
   338  			// We only want values, not MVCCMetadata.
   339  			return false, nil
   340  		}
   341  		if err := kvserver.IsRangeDescriptorKey(kv.Key); err != nil {
   342  			// Range descriptor keys are interleaved with others, so if it
   343  			// doesn't parse as a range descriptor just skip it.
   344  			return false, nil //nolint:returnerrcheck
   345  		}
   346  		if len(kv.Value) == 0 {
   347  			// RangeDescriptor was deleted (range merged away).
   348  			return false, nil
   349  		}
   350  		if err := (roachpb.Value{RawBytes: kv.Value}).GetProto(&desc); err != nil {
   351  			log.Warningf(context.Background(), "ignoring range descriptor due to error %s: %+v", err, kv)
   352  			return false, nil
   353  		}
   354  		return desc.RangeID == rangeID, nil
   355  	}
   356  
   357  	// Range descriptors are stored by key, so we have to scan over the
   358  	// range-local data to find the one for this RangeID.
   359  	start := keys.LocalRangePrefix
   360  	end := keys.LocalRangeMax
   361  
   362  	if err := db.Iterate(start, end, handleKV); err != nil {
   363  		return roachpb.RangeDescriptor{}, err
   364  	}
   365  	if desc.RangeID == rangeID {
   366  		return desc, nil
   367  	}
   368  	return roachpb.RangeDescriptor{}, fmt.Errorf("range descriptor %d not found", rangeID)
   369  }
   370  
   371  func runDebugRangeDescriptors(cmd *cobra.Command, args []string) error {
   372  	stopper := stop.NewStopper()
   373  	defer stopper.Stop(context.Background())
   374  
   375  	db, err := OpenExistingStore(args[0], stopper, true /* readOnly */)
   376  	if err != nil {
   377  		return err
   378  	}
   379  
   380  	start := keys.LocalRangePrefix
   381  	end := keys.LocalRangeMax
   382  
   383  	return db.Iterate(start, end, func(kv storage.MVCCKeyValue) (bool, error) {
   384  		if kvserver.IsRangeDescriptorKey(kv.Key) != nil {
   385  			return false, nil
   386  		}
   387  		kvserver.PrintKeyValue(kv)
   388  		return false, nil
   389  	})
   390  }
   391  
   392  var debugDecodeKeyCmd = &cobra.Command{
   393  	Use:   "decode-key",
   394  	Short: "decode <key>",
   395  	Long: `
   396  Decode a hexadecimal-encoded key and pretty-print it. For example:
   397  
   398  	$ decode-key BB89F902ADB43000151C2D1ED07DE6C009
   399  	/Table/51/1/44938288/1521140384.514565824,0
   400  `,
   401  	Args: cobra.ArbitraryArgs,
   402  	RunE: func(cmd *cobra.Command, args []string) error {
   403  		for _, arg := range args {
   404  			b, err := gohex.DecodeString(arg)
   405  			if err != nil {
   406  				return err
   407  			}
   408  			k, err := storage.DecodeMVCCKey(b)
   409  			if err != nil {
   410  				return err
   411  			}
   412  			fmt.Println(k)
   413  		}
   414  		return nil
   415  	},
   416  }
   417  
   418  var debugDecodeValueCmd = &cobra.Command{
   419  	Use:   "decode-value",
   420  	Short: "decode-value <key> <value>",
   421  	Long: `
   422  Decode and print a hexadecimal-encoded key-value pair.
   423  
   424  	$ decode-value <TBD> <TBD>
   425  	<TBD>
   426  `,
   427  	Args: cobra.ExactArgs(2),
   428  	RunE: func(cmd *cobra.Command, args []string) error {
   429  		var bs [][]byte
   430  		for _, arg := range args {
   431  			b, err := gohex.DecodeString(arg)
   432  			if err != nil {
   433  				return err
   434  			}
   435  			bs = append(bs, b)
   436  		}
   437  
   438  		isTS := bytes.HasPrefix(bs[0], keys.TimeseriesPrefix)
   439  		k, err := storage.DecodeMVCCKey(bs[0])
   440  		if err != nil {
   441  			// Older versions of the consistency checker give you diffs with a raw_key that
   442  			// is already a roachpb.Key, so make a half-assed attempt to support both.
   443  			if !isTS {
   444  				fmt.Printf("unable to decode key: %v, assuming it's a roachpb.Key with fake timestamp;\n"+
   445  					"if the result below looks like garbage, then it likely is:\n\n", err)
   446  			}
   447  			k = storage.MVCCKey{
   448  				Key:       bs[0],
   449  				Timestamp: hlc.Timestamp{WallTime: 987654321},
   450  			}
   451  		}
   452  
   453  		kvserver.PrintKeyValue(storage.MVCCKeyValue{
   454  			Key:   k,
   455  			Value: bs[1],
   456  		})
   457  		return nil
   458  	},
   459  }
   460  
   461  var debugRaftLogCmd = &cobra.Command{
   462  	Use:   "raft-log <directory> <range id>",
   463  	Short: "print the raft log for a range",
   464  	Long: `
   465  Prints all log entries in a store for the given range.
   466  `,
   467  	Args: cobra.ExactArgs(2),
   468  	RunE: MaybeDecorateGRPCError(runDebugRaftLog),
   469  }
   470  
   471  func runDebugRaftLog(cmd *cobra.Command, args []string) error {
   472  	stopper := stop.NewStopper()
   473  	defer stopper.Stop(context.Background())
   474  
   475  	db, err := OpenExistingStore(args[0], stopper, true /* readOnly */)
   476  	if err != nil {
   477  		return err
   478  	}
   479  
   480  	rangeID, err := parseRangeID(args[1])
   481  	if err != nil {
   482  		return err
   483  	}
   484  
   485  	start := keys.RaftLogPrefix(rangeID)
   486  	end := keys.RaftLogPrefix(rangeID).PrefixEnd()
   487  	fmt.Printf("Printing keys %s -> %s (RocksDB keys: %#x - %#x )\n",
   488  		start, end,
   489  		string(storage.EncodeKey(storage.MakeMVCCMetadataKey(start))),
   490  		string(storage.EncodeKey(storage.MakeMVCCMetadataKey(end))))
   491  
   492  	return db.Iterate(start, end, func(kv storage.MVCCKeyValue) (bool, error) {
   493  		kvserver.PrintKeyValue(kv)
   494  		return false, nil
   495  	})
   496  }
   497  
   498  var debugGCCmd = &cobra.Command{
   499  	Use:   "estimate-gc <directory> [range id] [ttl-in-seconds]",
   500  	Short: "find out what a GC run would do",
   501  	Long: `
   502  Sets up (but does not run) a GC collection cycle, giving insight into how much
   503  work would be done (assuming all intent resolution and pushes succeed).
   504  
   505  Without a RangeID specified on the command line, runs the analysis for all
   506  ranges individually.
   507  
   508  Uses a configurable GC policy, with a default 24 hour TTL, for old versions.
   509  `,
   510  	Args: cobra.RangeArgs(1, 2),
   511  	RunE: MaybeDecorateGRPCError(runDebugGCCmd),
   512  }
   513  
   514  func runDebugGCCmd(cmd *cobra.Command, args []string) error {
   515  	stopper := stop.NewStopper()
   516  	defer stopper.Stop(context.Background())
   517  
   518  	var rangeID roachpb.RangeID
   519  	gcTTLInSeconds := int64((24 * time.Hour).Seconds())
   520  	switch len(args) {
   521  	case 3:
   522  		var err error
   523  		if rangeID, err = parseRangeID(args[1]); err != nil {
   524  			return errors.Wrapf(err, "unable to parse %v as range ID", args[1])
   525  		}
   526  		if gcTTLInSeconds, err = parsePositiveInt(args[2]); err != nil {
   527  			return errors.Wrapf(err, "unable to parse %v as TTL", args[2])
   528  		}
   529  
   530  	case 2:
   531  		var err error
   532  		if rangeID, err = parseRangeID(args[1]); err != nil {
   533  			return err
   534  		}
   535  	}
   536  
   537  	db, err := OpenExistingStore(args[0], stopper, true /* readOnly */)
   538  	if err != nil {
   539  		return err
   540  	}
   541  
   542  	start := keys.RangeDescriptorKey(roachpb.RKeyMin)
   543  	end := keys.RangeDescriptorKey(roachpb.RKeyMax)
   544  
   545  	var descs []roachpb.RangeDescriptor
   546  
   547  	if _, err := storage.MVCCIterate(context.Background(), db, start, end, hlc.MaxTimestamp,
   548  		storage.MVCCScanOptions{Inconsistent: true}, func(kv roachpb.KeyValue) (bool, error) {
   549  			var desc roachpb.RangeDescriptor
   550  			_, suffix, _, err := keys.DecodeRangeKey(kv.Key)
   551  			if err != nil {
   552  				return false, err
   553  			}
   554  			if !bytes.Equal(suffix, keys.LocalRangeDescriptorSuffix) {
   555  				return false, nil
   556  			}
   557  			if err := kv.Value.GetProto(&desc); err != nil {
   558  				return false, err
   559  			}
   560  			if desc.RangeID == rangeID || rangeID == 0 {
   561  				descs = append(descs, desc)
   562  			}
   563  			return desc.RangeID == rangeID, nil
   564  		}); err != nil {
   565  		return err
   566  	}
   567  
   568  	if len(descs) == 0 {
   569  		return fmt.Errorf("no range matching the criteria found")
   570  	}
   571  
   572  	for _, desc := range descs {
   573  		snap := db.NewSnapshot()
   574  		defer snap.Close()
   575  		policy := zonepb.GCPolicy{TTLSeconds: int32(gcTTLInSeconds)}
   576  		now := hlc.Timestamp{WallTime: timeutil.Now().UnixNano()}
   577  		thresh := gc.CalculateThreshold(now, policy)
   578  		info, err := gc.Run(
   579  			context.Background(),
   580  			&desc, snap,
   581  			now, thresh, policy,
   582  			gc.NoopGCer{},
   583  			func(_ context.Context, _ []roachpb.Intent) error { return nil },
   584  			func(_ context.Context, _ *roachpb.Transaction, _ []roachpb.LockUpdate) error { return nil },
   585  		)
   586  		if err != nil {
   587  			return err
   588  		}
   589  		fmt.Printf("RangeID: %d [%s, %s):\n", desc.RangeID, desc.StartKey, desc.EndKey)
   590  		_, _ = pretty.Println(info)
   591  	}
   592  	return nil
   593  }
   594  
   595  var debugRocksDBCmd = &cobra.Command{
   596  	Use:   "rocksdb",
   597  	Short: "run the RocksDB 'ldb' tool",
   598  	Long: `
   599  Runs the RocksDB 'ldb' tool, which provides various subcommands for examining
   600  raw store data. 'cockroach debug rocksdb' accepts the same arguments and flags
   601  as 'ldb'.
   602  
   603  https://github.com/facebook/rocksdb/wiki/Administration-and-Data-Access-Tool#ldb-tool
   604  `,
   605  	// LDB does its own flag parsing.
   606  	// TODO(mberhault): support encrypted stores.
   607  	DisableFlagParsing: true,
   608  	Run: func(cmd *cobra.Command, args []string) {
   609  		storage.RunLDB(args)
   610  	},
   611  }
   612  
   613  var debugPebbleCmd = &cobra.Command{
   614  	Use:   "pebble [command]",
   615  	Short: "run a Pebble introspection tool command",
   616  	Long: `
   617  Allows the use of pebble tools, such as to introspect manifests, SSTables, etc.
   618  `,
   619  }
   620  
   621  var debugSSTDumpCmd = &cobra.Command{
   622  	Use:   "sst_dump",
   623  	Short: "run the RocksDB 'sst_dump' tool",
   624  	Long: `
   625  Runs the RocksDB 'sst_dump' tool
   626  `,
   627  	// sst_dump does its own flag parsing.
   628  	// TODO(mberhault): support encrypted stores.
   629  	DisableFlagParsing: true,
   630  	Run: func(cmd *cobra.Command, args []string) {
   631  		storage.RunSSTDump(args)
   632  	},
   633  }
   634  
   635  var debugEnvCmd = &cobra.Command{
   636  	Use:   "env",
   637  	Short: "output environment settings",
   638  	Long: `
   639  Output environment variables that influence configuration.
   640  `,
   641  	Args: cobra.NoArgs,
   642  	Run: func(cmd *cobra.Command, args []string) {
   643  		env := envutil.GetEnvReport()
   644  		fmt.Print(env)
   645  	},
   646  }
   647  
   648  var debugCompactCmd = &cobra.Command{
   649  	Use:   "compact <directory>",
   650  	Short: "compact the sstables in a store",
   651  	Long: `
   652  Compact the sstables in a store.
   653  `,
   654  	Args: cobra.ExactArgs(1),
   655  	RunE: MaybeDecorateGRPCError(runDebugCompact),
   656  }
   657  
   658  func runDebugCompact(cmd *cobra.Command, args []string) error {
   659  	stopper := stop.NewStopper()
   660  	defer stopper.Stop(context.Background())
   661  
   662  	db, err := OpenExistingStore(args[0], stopper, false /* readOnly */)
   663  	if err != nil {
   664  		return err
   665  	}
   666  
   667  	{
   668  		approxBytesBefore, err := db.ApproximateDiskBytes(roachpb.KeyMin, roachpb.KeyMax)
   669  		if err != nil {
   670  			return errors.Wrap(err, "while computing approximate size before compaction")
   671  		}
   672  		fmt.Printf("approximate reported database size before compaction: %s\n", humanizeutil.IBytes(int64(approxBytesBefore)))
   673  	}
   674  
   675  	if err := db.Compact(); err != nil {
   676  		return errors.Wrap(err, "while compacting")
   677  	}
   678  
   679  	{
   680  		approxBytesAfter, err := db.ApproximateDiskBytes(roachpb.KeyMin, roachpb.KeyMax)
   681  		if err != nil {
   682  			return errors.Wrap(err, "while computing approximate size after compaction")
   683  		}
   684  		fmt.Printf("approximate reported database size after compaction: %s\n", humanizeutil.IBytes(int64(approxBytesAfter)))
   685  	}
   686  	return nil
   687  }
   688  
   689  var debugSSTablesCmd = &cobra.Command{
   690  	Use:   "sstables <directory>",
   691  	Short: "list the sstables in a store",
   692  	Long: `
   693  
   694  List the sstables in a store. The output format is 1 or more lines of:
   695  
   696    level [ total size #files ]: file sizes
   697  
   698  Only non-empty levels are shown. For levels greater than 0, the files span
   699  non-overlapping ranges of the key space. Level-0 is special in that sstables
   700  are created there by flushing the mem-table, thus every level-0 sstable must be
   701  consulted to see if it contains a particular key. Within a level, the file
   702  sizes are displayed in decreasing order and bucketed by the number of files of
   703  that size. The following example shows 3-level output. In Level-3, there are 19
   704  total files and 14 files that are 129 MiB in size.
   705  
   706    1 [   8M  3 ]: 7M 1M 63K
   707    2 [ 110M  7 ]: 31M 30M 13M[2] 10M 8M 5M
   708    3 [   2G 19 ]: 129M[14] 122M 93M 24M 18M 9M
   709  
   710  The suffixes K, M, G and T are used for terseness to represent KiB, MiB, GiB
   711  and TiB.
   712  `,
   713  	Args: cobra.ExactArgs(1),
   714  	RunE: MaybeDecorateGRPCError(runDebugSSTables),
   715  }
   716  
   717  func runDebugSSTables(cmd *cobra.Command, args []string) error {
   718  	stopper := stop.NewStopper()
   719  	defer stopper.Stop(context.Background())
   720  
   721  	db, err := OpenExistingStore(args[0], stopper, true /* readOnly */)
   722  	if err != nil {
   723  		return err
   724  	}
   725  
   726  	fmt.Printf("%s", db.GetSSTables())
   727  	return nil
   728  }
   729  
   730  var debugGossipValuesCmd = &cobra.Command{
   731  	Use:   "gossip-values",
   732  	Short: "dump all the values in a node's gossip instance",
   733  	Long: `
   734  Pretty-prints the values in a node's gossip instance.
   735  
   736  Can connect to a running server to get the values or can be provided with
   737  a JSON file captured from a node's /_status/gossip/ debug endpoint.
   738  `,
   739  	Args: cobra.NoArgs,
   740  	RunE: MaybeDecorateGRPCError(runDebugGossipValues),
   741  }
   742  
   743  func runDebugGossipValues(cmd *cobra.Command, args []string) error {
   744  	ctx, cancel := context.WithCancel(context.Background())
   745  	defer cancel()
   746  	// If a file is provided, use it. Otherwise, try talking to the running node.
   747  	var gossipInfo *gossip.InfoStatus
   748  	if debugCtx.inputFile != "" {
   749  		file, err := os.Open(debugCtx.inputFile)
   750  		if err != nil {
   751  			return err
   752  		}
   753  		defer file.Close()
   754  		gossipInfo = new(gossip.InfoStatus)
   755  		if err := jsonpb.Unmarshal(file, gossipInfo); err != nil {
   756  			return errors.Wrap(err, "failed to parse provided file as gossip.InfoStatus")
   757  		}
   758  	} else {
   759  		conn, _, finish, err := getClientGRPCConn(ctx, serverCfg)
   760  		if err != nil {
   761  			return err
   762  		}
   763  		defer finish()
   764  
   765  		status := serverpb.NewStatusClient(conn)
   766  		gossipInfo, err = status.Gossip(ctx, &serverpb.GossipRequest{})
   767  		if err != nil {
   768  			return errors.Wrap(err, "failed to retrieve gossip from server")
   769  		}
   770  	}
   771  
   772  	output, err := parseGossipValues(gossipInfo)
   773  	if err != nil {
   774  		return err
   775  	}
   776  	fmt.Println(output)
   777  	return nil
   778  }
   779  
   780  func parseGossipValues(gossipInfo *gossip.InfoStatus) (string, error) {
   781  	var output []string
   782  	for key, info := range gossipInfo.Infos {
   783  		bytes, err := info.Value.GetBytes()
   784  		if err != nil {
   785  			return "", errors.Wrapf(err, "failed to extract bytes for key %q", key)
   786  		}
   787  		if key == gossip.KeyClusterID || key == gossip.KeySentinel {
   788  			clusterID, err := uuid.FromBytes(bytes)
   789  			if err != nil {
   790  				return "", errors.Wrapf(err, "failed to parse value for key %q", key)
   791  			}
   792  			output = append(output, fmt.Sprintf("%q: %v", key, clusterID))
   793  		} else if key == gossip.KeySystemConfig {
   794  			if debugCtx.printSystemConfig {
   795  				var config config.SystemConfigEntries
   796  				if err := protoutil.Unmarshal(bytes, &config); err != nil {
   797  					return "", errors.Wrapf(err, "failed to parse value for key %q", key)
   798  				}
   799  				output = append(output, fmt.Sprintf("%q: %+v", key, config))
   800  			} else {
   801  				output = append(output, fmt.Sprintf("%q: omitted", key))
   802  			}
   803  		} else if key == gossip.KeyFirstRangeDescriptor {
   804  			var desc roachpb.RangeDescriptor
   805  			if err := protoutil.Unmarshal(bytes, &desc); err != nil {
   806  				return "", errors.Wrapf(err, "failed to parse value for key %q", key)
   807  			}
   808  			output = append(output, fmt.Sprintf("%q: %v", key, desc))
   809  		} else if gossip.IsNodeIDKey(key) {
   810  			var desc roachpb.NodeDescriptor
   811  			if err := protoutil.Unmarshal(bytes, &desc); err != nil {
   812  				return "", errors.Wrapf(err, "failed to parse value for key %q", key)
   813  			}
   814  			output = append(output, fmt.Sprintf("%q: %+v", key, desc))
   815  		} else if strings.HasPrefix(key, gossip.KeyStorePrefix) {
   816  			var desc roachpb.StoreDescriptor
   817  			if err := protoutil.Unmarshal(bytes, &desc); err != nil {
   818  				return "", errors.Wrapf(err, "failed to parse value for key %q", key)
   819  			}
   820  			output = append(output, fmt.Sprintf("%q: %+v", key, desc))
   821  		} else if strings.HasPrefix(key, gossip.KeyNodeLivenessPrefix) {
   822  			var liveness kvserverpb.Liveness
   823  			if err := protoutil.Unmarshal(bytes, &liveness); err != nil {
   824  				return "", errors.Wrapf(err, "failed to parse value for key %q", key)
   825  			}
   826  			output = append(output, fmt.Sprintf("%q: %+v", key, liveness))
   827  		} else if strings.HasPrefix(key, gossip.KeyNodeHealthAlertPrefix) {
   828  			var healthAlert statuspb.HealthCheckResult
   829  			if err := protoutil.Unmarshal(bytes, &healthAlert); err != nil {
   830  				return "", errors.Wrapf(err, "failed to parse value for key %q", key)
   831  			}
   832  			output = append(output, fmt.Sprintf("%q: %+v", key, healthAlert))
   833  		} else if strings.HasPrefix(key, gossip.KeyDistSQLNodeVersionKeyPrefix) {
   834  			var version execinfrapb.DistSQLVersionGossipInfo
   835  			if err := protoutil.Unmarshal(bytes, &version); err != nil {
   836  				return "", errors.Wrapf(err, "failed to parse value for key %q", key)
   837  			}
   838  			output = append(output, fmt.Sprintf("%q: %+v", key, version))
   839  		} else if strings.HasPrefix(key, gossip.KeyDistSQLDrainingPrefix) {
   840  			var drainingInfo execinfrapb.DistSQLDrainingInfo
   841  			if err := protoutil.Unmarshal(bytes, &drainingInfo); err != nil {
   842  				return "", errors.Wrapf(err, "failed to parse value for key %q", key)
   843  			}
   844  			output = append(output, fmt.Sprintf("%q: %+v", key, drainingInfo))
   845  		} else if strings.HasPrefix(key, gossip.KeyTableStatAddedPrefix) {
   846  			gossipedTime := timeutil.Unix(0, info.OrigStamp)
   847  			output = append(output, fmt.Sprintf("%q: %v", key, gossipedTime))
   848  		} else if strings.HasPrefix(key, gossip.KeyGossipClientsPrefix) {
   849  			output = append(output, fmt.Sprintf("%q: %v", key, string(bytes)))
   850  		}
   851  	}
   852  
   853  	sort.Strings(output)
   854  	return strings.Join(output, "\n"), nil
   855  }
   856  
   857  var debugTimeSeriesDumpCmd = &cobra.Command{
   858  	Use:   "tsdump",
   859  	Short: "dump all the raw timeseries values in a cluster",
   860  	Long: `
   861  Dumps all of the raw timeseries values in a cluster.
   862  `,
   863  	RunE: MaybeDecorateGRPCError(runTimeSeriesDump),
   864  }
   865  
   866  func runTimeSeriesDump(cmd *cobra.Command, args []string) error {
   867  	ctx, cancel := context.WithCancel(context.Background())
   868  	defer cancel()
   869  
   870  	conn, _, finish, err := getClientGRPCConn(ctx, serverCfg)
   871  	if err != nil {
   872  		return err
   873  	}
   874  	defer finish()
   875  
   876  	tsClient := tspb.NewTimeSeriesClient(conn)
   877  	stream, err := tsClient.Dump(context.Background(), &tspb.DumpRequest{})
   878  	if err != nil {
   879  		log.Fatalf(context.Background(), "%v", err)
   880  	}
   881  
   882  	var name, source string
   883  	for {
   884  		data, err := stream.Recv()
   885  		if err == io.EOF {
   886  			return nil
   887  		}
   888  		if err != nil {
   889  			return err
   890  		}
   891  		if name != data.Name || source != data.Source {
   892  			name, source = data.Name, data.Source
   893  			fmt.Printf("%s %s\n", name, source)
   894  		}
   895  		for _, d := range data.Datapoints {
   896  			fmt.Printf("%d %v\n", d.TimestampNanos, d.Value)
   897  		}
   898  	}
   899  }
   900  
   901  var debugSyncBenchCmd = &cobra.Command{
   902  	Use:   "syncbench [directory]",
   903  	Short: "Run a performance test for WAL sync speed",
   904  	Long: `
   905  `,
   906  	Args:   cobra.MaximumNArgs(1),
   907  	Hidden: true,
   908  	RunE:   MaybeDecorateGRPCError(runDebugSyncBench),
   909  }
   910  
   911  var syncBenchOpts = syncbench.Options{
   912  	Concurrency: 1,
   913  	Duration:    10 * time.Second,
   914  	LogOnly:     true,
   915  }
   916  
   917  func runDebugSyncBench(cmd *cobra.Command, args []string) error {
   918  	syncBenchOpts.Dir = "./testdb"
   919  	if len(args) == 1 {
   920  		syncBenchOpts.Dir = args[0]
   921  	}
   922  	return syncbench.Run(syncBenchOpts)
   923  }
   924  
   925  var debugUnsafeRemoveDeadReplicasCmd = &cobra.Command{
   926  	Use:   "unsafe-remove-dead-replicas --dead-store-ids=[store ID,...] [path]",
   927  	Short: "Unsafely remove all other replicas from the given range",
   928  	Long: `
   929  
   930  This command is UNSAFE and should only be used with the supervision of
   931  a Cockroach Labs engineer. It is a last-resort option to recover data
   932  after multiple node failures. The recovered data is not guaranteed to
   933  be consistent.
   934  
   935  The --dead-store-ids flag takes a comma-separated list of dead store
   936  IDs and scans this store for any ranges whose only live replica is on
   937  this store. These range descriptors will be edited to forcibly remove
   938  the dead stores, allowing the range to recover from this single
   939  replica.
   940  
   941  Must only be used when the dead stores are lost and unrecoverable. If
   942  the dead stores were to rejoin the cluster after this command was
   943  used, data may be corrupted.
   944  
   945  This comand will prompt for confirmation before committing its changes.
   946  
   947  After this command is used, the node should not be restarted until at
   948  least 10 seconds have passed since it was stopped. Restarting it too
   949  early may lead to things getting stuck (if it happens, it can be fixed
   950  by restarting a second time).
   951  `,
   952  	Args: cobra.ExactArgs(1),
   953  	RunE: MaybeDecorateGRPCError(runDebugUnsafeRemoveDeadReplicas),
   954  }
   955  
   956  var removeDeadReplicasOpts struct {
   957  	deadStoreIDs []int
   958  }
   959  
   960  func runDebugUnsafeRemoveDeadReplicas(cmd *cobra.Command, args []string) error {
   961  	stopper := stop.NewStopper()
   962  	defer stopper.Stop(context.Background())
   963  
   964  	db, err := OpenExistingStore(args[0], stopper, false /* readOnly */)
   965  	if err != nil {
   966  		return err
   967  	}
   968  
   969  	deadStoreIDs := map[roachpb.StoreID]struct{}{}
   970  	for _, id := range removeDeadReplicasOpts.deadStoreIDs {
   971  		deadStoreIDs[roachpb.StoreID(id)] = struct{}{}
   972  	}
   973  	batch, err := removeDeadReplicas(db, deadStoreIDs)
   974  	if err != nil {
   975  		return err
   976  	} else if batch == nil {
   977  		fmt.Printf("Nothing to do\n")
   978  		return nil
   979  	}
   980  	defer batch.Close()
   981  
   982  	fmt.Printf("Proceed with the above rewrites? [y/N] ")
   983  
   984  	reader := bufio.NewReader(os.Stdin)
   985  	line, err := reader.ReadString('\n')
   986  	if err != nil {
   987  		return err
   988  	}
   989  	fmt.Printf("\n")
   990  	if line[0] == 'y' || line[0] == 'Y' {
   991  		fmt.Printf("Committing\n")
   992  		if err := batch.Commit(true); err != nil {
   993  			return err
   994  		}
   995  	} else {
   996  		fmt.Printf("Aborting\n")
   997  	}
   998  	return nil
   999  }
  1000  
  1001  func removeDeadReplicas(
  1002  	db storage.Engine, deadStoreIDs map[roachpb.StoreID]struct{},
  1003  ) (storage.Batch, error) {
  1004  	clock := hlc.NewClock(hlc.UnixNano, 0)
  1005  
  1006  	ctx := context.Background()
  1007  
  1008  	storeIdent, err := kvserver.ReadStoreIdent(ctx, db)
  1009  	if err != nil {
  1010  		return nil, err
  1011  	}
  1012  	fmt.Printf("Scanning replicas on store %s for dead peers %v\n", storeIdent.String(),
  1013  		removeDeadReplicasOpts.deadStoreIDs)
  1014  
  1015  	if _, ok := deadStoreIDs[storeIdent.StoreID]; ok {
  1016  		return nil, errors.Errorf("This store's ID (%s) marked as dead, aborting", storeIdent.StoreID)
  1017  	}
  1018  
  1019  	var newDescs []roachpb.RangeDescriptor
  1020  
  1021  	err = kvserver.IterateRangeDescriptors(ctx, db, func(desc roachpb.RangeDescriptor) (bool, error) {
  1022  		hasSelf := false
  1023  		numDeadPeers := 0
  1024  		allReplicas := desc.Replicas().All()
  1025  		maxLivePeer := roachpb.StoreID(-1)
  1026  		for _, rep := range allReplicas {
  1027  			if rep.StoreID == storeIdent.StoreID {
  1028  				hasSelf = true
  1029  			}
  1030  			if _, ok := deadStoreIDs[rep.StoreID]; ok {
  1031  				numDeadPeers++
  1032  			} else {
  1033  				if rep.StoreID > maxLivePeer {
  1034  					maxLivePeer = rep.StoreID
  1035  				}
  1036  			}
  1037  		}
  1038  		if hasSelf && numDeadPeers > 0 && storeIdent.StoreID == maxLivePeer {
  1039  			canMakeProgress := desc.Replicas().CanMakeProgress(func(rep roachpb.ReplicaDescriptor) bool {
  1040  				_, ok := deadStoreIDs[rep.StoreID]
  1041  				return !ok
  1042  			})
  1043  			if canMakeProgress {
  1044  				return false, nil
  1045  			}
  1046  
  1047  			// Rewrite the range as having a single replica. The winning
  1048  			// replica is picked arbitrarily: the one with the highest store
  1049  			// ID. This is not always the best option: it may lose writes
  1050  			// that were committed on another surviving replica that had
  1051  			// applied more of the raft log. However, in practice when we
  1052  			// have multiple surviving replicas but still need this tool
  1053  			// (because the replication factor was 4 or higher), we see that
  1054  			// the logs are nearly always in sync and the choice doesn't
  1055  			// matter. Correctly picking the replica with the longer log
  1056  			// would complicate the use of this tool.
  1057  			newDesc := desc
  1058  			// Rewrite the replicas list. Bump the replica ID so that in
  1059  			// case there are other surviving nodes that were members of the
  1060  			// old incarnation of the range, they no longer recognize this
  1061  			// revived replica (because they are not in sync with it).
  1062  			replicas := []roachpb.ReplicaDescriptor{{
  1063  				NodeID:    storeIdent.NodeID,
  1064  				StoreID:   storeIdent.StoreID,
  1065  				ReplicaID: desc.NextReplicaID,
  1066  			}}
  1067  			newDesc.SetReplicas(roachpb.MakeReplicaDescriptors(replicas))
  1068  			newDesc.NextReplicaID++
  1069  			fmt.Printf("Replica %s -> %s\n", &desc, &newDesc)
  1070  			newDescs = append(newDescs, newDesc)
  1071  		}
  1072  		return false, nil
  1073  	})
  1074  	if err != nil {
  1075  		return nil, err
  1076  	}
  1077  
  1078  	if len(newDescs) == 0 {
  1079  		return nil, nil
  1080  	}
  1081  
  1082  	batch := db.NewBatch()
  1083  	for _, desc := range newDescs {
  1084  		// Write the rewritten descriptor to the range-local descriptor
  1085  		// key. We do not update the meta copies of the descriptor.
  1086  		// Instead, we leave them in a temporarily inconsistent state and
  1087  		// they will be overwritten when the cluster recovers and
  1088  		// up-replicates this range from its single copy to multiple
  1089  		// copies. We rely on the fact that all range descriptor updates
  1090  		// start with a CPut on the range-local copy followed by a blind
  1091  		// Put to the meta copy.
  1092  		//
  1093  		// For example, if we have replicas on s1-s4 but s3 and s4 are
  1094  		// dead, we will rewrite the replica on s2 to have s2 as its only
  1095  		// member only. When the cluster is restarted (and the dead nodes
  1096  		// remain dead), the rewritten replica will be the only one able
  1097  		// to make progress. It will elect itself leader and upreplicate.
  1098  		//
  1099  		// The old replica on s1 is untouched by this process. It will
  1100  		// eventually either be overwritten by a new replica when s2
  1101  		// upreplicates, or it will be destroyed by the replica GC queue
  1102  		// after upreplication has happened and s1 is no longer a member.
  1103  		// (Note that in the latter case, consistency between s1 and s2 no
  1104  		// longer matters; the consistency checker will only run on nodes
  1105  		// that the new leader believes are members of the range).
  1106  		//
  1107  		// Note that this tool does not guarantee fully consistent
  1108  		// results; the most recent writes to the raft log may have been
  1109  		// lost. In the most unfortunate cases, this means that we would
  1110  		// be "winding back" a split or a merge, which is almost certainly
  1111  		// to result in irrecoverable corruption (for example, not only
  1112  		// will individual values stored in the meta ranges diverge, but
  1113  		// there will be keys not represented by any ranges or vice
  1114  		// versa).
  1115  		key := keys.RangeDescriptorKey(desc.StartKey)
  1116  		sl := stateloader.Make(desc.RangeID)
  1117  		ms, err := sl.LoadMVCCStats(ctx, batch)
  1118  		if err != nil {
  1119  			return nil, errors.Wrap(err, "loading MVCCStats")
  1120  		}
  1121  		err = storage.MVCCPutProto(ctx, batch, &ms, key, clock.Now(), nil /* txn */, &desc)
  1122  		if wiErr := (*roachpb.WriteIntentError)(nil); errors.As(err, &wiErr) {
  1123  			if len(wiErr.Intents) != 1 {
  1124  				return nil, errors.Errorf("expected 1 intent, found %d: %s", len(wiErr.Intents), wiErr)
  1125  			}
  1126  			intent := wiErr.Intents[0]
  1127  			// We rely on the property that transactions involving the range
  1128  			// descriptor always start on the range-local descriptor's key.
  1129  			// This guarantees that when the transaction commits, the intent
  1130  			// will be resolved synchronously. If we see an intent on this
  1131  			// key, we know that the transaction did not commit and we can
  1132  			// abort it.
  1133  			//
  1134  			// TODO(nvanbenschoten): This need updating for parallel
  1135  			// commits. If the transaction record is in the STAGING state,
  1136  			// we can't just delete it. Simplest solution to this is to
  1137  			// avoid parallel commits for membership change transactions; if
  1138  			// we can't do that I don't think we'll be able to recover them
  1139  			// with an offline tool.
  1140  			fmt.Printf("Conflicting intent found on %s. Aborting txn %s to resolve.\n", key, intent.Txn.ID)
  1141  
  1142  			// A crude form of the intent resolution process: abort the
  1143  			// transaction by deleting its record.
  1144  			txnKey := keys.TransactionKey(intent.Txn.Key, intent.Txn.ID)
  1145  			if err := storage.MVCCDelete(ctx, batch, &ms, txnKey, hlc.Timestamp{}, nil); err != nil {
  1146  				return nil, err
  1147  			}
  1148  			update := roachpb.LockUpdate{
  1149  				Span:   roachpb.Span{Key: intent.Key},
  1150  				Txn:    intent.Txn,
  1151  				Status: roachpb.ABORTED,
  1152  			}
  1153  			if _, err := storage.MVCCResolveWriteIntent(ctx, batch, &ms, update); err != nil {
  1154  				return nil, err
  1155  			}
  1156  			// With the intent resolved, we can try again.
  1157  			if err := storage.MVCCPutProto(ctx, batch, &ms, key, clock.Now(),
  1158  				nil /* txn */, &desc); err != nil {
  1159  				return nil, err
  1160  			}
  1161  		} else if err != nil {
  1162  			batch.Close()
  1163  			return nil, err
  1164  		}
  1165  		if err := sl.SetMVCCStats(ctx, batch, &ms); err != nil {
  1166  			return nil, errors.Wrap(err, "updating MVCCStats")
  1167  		}
  1168  	}
  1169  
  1170  	return batch, nil
  1171  }
  1172  
  1173  var debugMergeLogsCommand = &cobra.Command{
  1174  	Use:   "merge-logs <log file globs>",
  1175  	Short: "merge multiple log files from different machines into a single stream",
  1176  	Long: `
  1177  Takes a list of glob patterns (not left exclusively to the shell because of
  1178  MAX_ARG_STRLEN, usually 128kB) pointing to log files and merges them into a
  1179  single stream printed to stdout. Files not matching the log file name pattern
  1180  are ignored. If log lines appear out of order within a file (which happens), the
  1181  timestamp is ratcheted to the highest value seen so far. The command supports
  1182  efficient time filtering as well as multiline regexp pattern matching via flags.
  1183  If the filter regexp contains captures, such as '^abc(hello)def(world)', only
  1184  the captured parts will be printed.
  1185  `,
  1186  	Args: cobra.MinimumNArgs(1),
  1187  	RunE: runDebugMergeLogs,
  1188  }
  1189  
  1190  var debugMergeLogsOpts = struct {
  1191  	from    time.Time
  1192  	to      time.Time
  1193  	filter  *regexp.Regexp
  1194  	program *regexp.Regexp
  1195  	file    *regexp.Regexp
  1196  	prefix  string
  1197  }{
  1198  	program: regexp.MustCompile("^cockroach.*$"),
  1199  	file:    regexp.MustCompile(log.FilePattern),
  1200  }
  1201  
  1202  func runDebugMergeLogs(cmd *cobra.Command, args []string) error {
  1203  	o := debugMergeLogsOpts
  1204  	s, err := newMergedStreamFromPatterns(context.Background(),
  1205  		args, o.file, o.program, o.from, o.to)
  1206  	if err != nil {
  1207  		return err
  1208  	}
  1209  	return writeLogStream(s, cmd.OutOrStdout(), o.filter, o.prefix)
  1210  }
  1211  
  1212  // DebugCmdsForRocksDB lists debug commands that access rocksdb through the engine
  1213  // and need encryption flags (injected by CCL code).
  1214  // Note: do NOT include commands that just call rocksdb code without setting up an engine.
  1215  var DebugCmdsForRocksDB = []*cobra.Command{
  1216  	debugCheckStoreCmd,
  1217  	debugCompactCmd,
  1218  	debugGCCmd,
  1219  	debugKeysCmd,
  1220  	debugRaftLogCmd,
  1221  	debugRangeDataCmd,
  1222  	debugRangeDescriptorsCmd,
  1223  	debugSSTablesCmd,
  1224  }
  1225  
  1226  // All other debug commands go here.
  1227  var debugCmds = append(DebugCmdsForRocksDB,
  1228  	debugBallastCmd,
  1229  	debugDecodeKeyCmd,
  1230  	debugDecodeValueCmd,
  1231  	debugRocksDBCmd,
  1232  	debugSSTDumpCmd,
  1233  	debugGossipValuesCmd,
  1234  	debugTimeSeriesDumpCmd,
  1235  	debugSyncBenchCmd,
  1236  	debugSyncTestCmd,
  1237  	debugUnsafeRemoveDeadReplicasCmd,
  1238  	debugEnvCmd,
  1239  	debugZipCmd,
  1240  	debugMergeLogsCommand,
  1241  )
  1242  
  1243  // DebugCmd is the root of all debug commands. Exported to allow modification by CCL code.
  1244  var DebugCmd = &cobra.Command{
  1245  	Use:   "debug [command]",
  1246  	Short: "debugging commands",
  1247  	Long: `Various commands for debugging.
  1248  
  1249  These commands are useful for extracting data from the data files of a
  1250  process that has failed and cannot restart.
  1251  `,
  1252  	RunE: usageAndErr,
  1253  }
  1254  
  1255  // mvccValueFormatter is an fmt.Formatter for MVCC values.
  1256  type mvccValueFormatter struct {
  1257  	kv  storage.MVCCKeyValue
  1258  	err error
  1259  }
  1260  
  1261  // Format implements the fmt.Formatter interface.
  1262  func (m mvccValueFormatter) Format(f fmt.State, c rune) {
  1263  	if m.err != nil {
  1264  		errors.FormatError(m.err, f, c)
  1265  		return
  1266  	}
  1267  	fmt.Fprint(f, kvserver.SprintKeyValue(m.kv, false /* printKey */))
  1268  }
  1269  
  1270  func init() {
  1271  	DebugCmd.AddCommand(debugCmds...)
  1272  
  1273  	// Note: we hook up FormatValue here in order to avoid a circular dependency
  1274  	// between kvserver and storage.
  1275  	storage.MVCCComparer.FormatValue = func(key, value []byte) fmt.Formatter {
  1276  		decoded, err := storage.DecodeMVCCKey(key)
  1277  		if err != nil {
  1278  			return mvccValueFormatter{err: err}
  1279  		}
  1280  		return mvccValueFormatter{kv: storage.MVCCKeyValue{Key: decoded, Value: value}}
  1281  	}
  1282  
  1283  	// To be able to read Cockroach-written RocksDB manifests/SSTables, comparator
  1284  	// and merger functions must be specified to pebble that match the ones used
  1285  	// to write those files.
  1286  	pebbleTool := tool.New(tool.Mergers(storage.MVCCMerger),
  1287  		tool.DefaultComparer(storage.MVCCComparer))
  1288  	debugPebbleCmd.AddCommand(pebbleTool.Commands...)
  1289  	DebugCmd.AddCommand(debugPebbleCmd)
  1290  
  1291  	f := debugSyncBenchCmd.Flags()
  1292  	f.IntVarP(&syncBenchOpts.Concurrency, "concurrency", "c", syncBenchOpts.Concurrency,
  1293  		"number of concurrent writers")
  1294  	f.DurationVarP(&syncBenchOpts.Duration, "duration", "d", syncBenchOpts.Duration,
  1295  		"duration to run the test for")
  1296  	f.BoolVarP(&syncBenchOpts.LogOnly, "log-only", "l", syncBenchOpts.LogOnly,
  1297  		"only write to the WAL, not to sstables")
  1298  
  1299  	f = debugUnsafeRemoveDeadReplicasCmd.Flags()
  1300  	f.IntSliceVar(&removeDeadReplicasOpts.deadStoreIDs, "dead-store-ids", nil,
  1301  		"list of dead store IDs")
  1302  
  1303  	f = debugMergeLogsCommand.Flags()
  1304  	f.Var(flagutil.Time(&debugMergeLogsOpts.from), "from",
  1305  		"time before which messages should be filtered")
  1306  	f.Var(flagutil.Time(&debugMergeLogsOpts.to), "to",
  1307  		"time after which messages should be filtered")
  1308  	f.Var(flagutil.Regexp(&debugMergeLogsOpts.filter), "filter",
  1309  		"re which filters log messages")
  1310  	f.Var(flagutil.Regexp(&debugMergeLogsOpts.file), "file-pattern",
  1311  		"re which filters log files based on path, also used with prefix and program-filter")
  1312  	f.Var(flagutil.Regexp(&debugMergeLogsOpts.program), "program-filter",
  1313  		"re which filter log files that operates on the capture group named \"program\" in file-pattern, "+
  1314  			"if no such group exists, program-filter is ignored")
  1315  	f.StringVar(&debugMergeLogsOpts.prefix, "prefix", "${host}> ",
  1316  		"expansion template (see regexp.Expand) used as prefix to merged log messages evaluated on file-pattern")
  1317  }