github.com/petermattis/pebble@v0.0.0-20190905164901-ab51a2166067/tool/db.go (about)

     1  // Copyright 2019 The LevelDB-Go and Pebble Authors. All rights reserved. Use
     2  // of this source code is governed by a BSD-style license that can be found in
     3  // the LICENSE file.
     4  
     5  package tool
     6  
     7  import (
     8  	"fmt"
     9  
    10  	"github.com/petermattis/pebble"
    11  	"github.com/petermattis/pebble/internal/base"
    12  	"github.com/petermattis/pebble/sstable"
    13  	"github.com/spf13/cobra"
    14  )
    15  
    16  // dbT implements db-level tools, including both configuration state and the
    17  // commands themselves.
    18  type dbT struct {
    19  	Root  *cobra.Command
    20  	Check *cobra.Command
    21  	LSM   *cobra.Command
    22  	Scan  *cobra.Command
    23  
    24  	// Configuration.
    25  	opts      *sstable.Options
    26  	comparers sstable.Comparers
    27  	mergers   sstable.Mergers
    28  
    29  	// Flags.
    30  	comparerName string
    31  	mergerName   string
    32  	fmtKey       formatter
    33  	fmtValue     formatter
    34  	start        key
    35  	end          key
    36  }
    37  
    38  func newDB(
    39  	opts *base.Options, comparers sstable.Comparers, mergers sstable.Mergers,
    40  ) *dbT {
    41  	d := &dbT{
    42  		opts:      opts,
    43  		comparers: comparers,
    44  		mergers:   mergers,
    45  	}
    46  	d.fmtKey.mustSet("quoted")
    47  	d.fmtValue.mustSet("[%x]")
    48  
    49  	d.Root = &cobra.Command{
    50  		Use:   "db",
    51  		Short: "DB introspection tools",
    52  	}
    53  	d.Check = &cobra.Command{
    54  		Use:   "check <dir>",
    55  		Short: "verify checksums and metadata",
    56  		Long: `
    57  Verify sstable, manifest, and WAL checksums. Requires that the specified
    58  database not be in use by another process.
    59  `,
    60  		Args: cobra.ExactArgs(1),
    61  		Run:  d.runCheck,
    62  	}
    63  	d.LSM = &cobra.Command{
    64  		Use:   "lsm <dir>",
    65  		Short: "print LSM structure",
    66  		Long: `
    67  Print the structure of the LSM tree. Requires that the specified database not
    68  be in use by another process.
    69  `,
    70  		Args: cobra.ExactArgs(1),
    71  		Run:  d.runLSM,
    72  	}
    73  	d.Scan = &cobra.Command{
    74  		Use:   "scan <dir>",
    75  		Short: "print db records",
    76  		Long: `
    77  Print the records in the DB. Requires that the specified database not be in use
    78  by another process.
    79  `,
    80  		Args: cobra.ExactArgs(1),
    81  		Run:  d.runScan,
    82  	}
    83  
    84  	d.Root.AddCommand(d.Check, d.LSM, d.Scan)
    85  
    86  	for _, cmd := range []*cobra.Command{d.Check, d.LSM, d.Scan} {
    87  		cmd.Flags().StringVar(
    88  			&d.comparerName, "comparer", "", "comparer name (use default if empty)")
    89  		cmd.Flags().StringVar(
    90  			&d.mergerName, "merger", "", "merger name (use default if empty)")
    91  	}
    92  
    93  	d.Scan.Flags().Var(
    94  		&d.fmtKey, "key", "key formatter")
    95  	d.Scan.Flags().Var(
    96  		&d.fmtValue, "value", "value formatter")
    97  	d.Scan.Flags().Var(
    98  		&d.start, "start", "start key for the scan")
    99  	d.Scan.Flags().Var(
   100  		&d.end, "end", "end key for the scan")
   101  	return d
   102  }
   103  
   104  func (d *dbT) openDB(dir string) (*pebble.DB, error) {
   105  	if d.comparerName != "" {
   106  		d.opts.Comparer = d.comparers[d.comparerName]
   107  		if d.opts.Comparer == nil {
   108  			return nil, fmt.Errorf("unknown comparer %q", d.comparerName)
   109  		}
   110  	}
   111  	if d.mergerName != "" {
   112  		d.opts.Merger = d.mergers[d.mergerName]
   113  		if d.opts.Merger == nil {
   114  			return nil, fmt.Errorf("unknown merger %q", d.mergerName)
   115  		}
   116  	}
   117  	return pebble.Open(dir, d.opts)
   118  }
   119  
   120  func (d *dbT) runCheck(cmd *cobra.Command, args []string) {
   121  	// The check command is equivalent to scanning over all of the records, but
   122  	// not outputting anything.
   123  	d.fmtKey.Set("null")
   124  	d.fmtValue.Set("null")
   125  	d.start, d.end = nil, nil
   126  	d.runScan(cmd, args)
   127  }
   128  
   129  func (d *dbT) runLSM(cmd *cobra.Command, args []string) {
   130  	db, err := d.openDB(args[0])
   131  	if err != nil {
   132  		fmt.Fprintf(stdout, "%s\n", err)
   133  		return
   134  	}
   135  
   136  	fmt.Fprintf(stdout, "%s", db.Metrics())
   137  
   138  	if err := db.Close(); err != nil {
   139  		fmt.Fprintf(stdout, "%s\n", err)
   140  	}
   141  }
   142  
   143  func (d *dbT) runScan(cmd *cobra.Command, args []string) {
   144  	db, err := d.openDB(args[0])
   145  	if err != nil {
   146  		fmt.Fprintf(stdout, "%s\n", err)
   147  		return
   148  	}
   149  
   150  	start := timeNow()
   151  	fmtKeys := d.fmtKey.spec != "null"
   152  	fmtValues := d.fmtValue.spec != "null"
   153  	var count int64
   154  
   155  	iter := db.NewIter(&pebble.IterOptions{
   156  		UpperBound: d.end,
   157  	})
   158  	for valid := iter.SeekGE(d.start); valid; valid = iter.Next() {
   159  		if fmtKeys || fmtValues {
   160  			needDelimiter := false
   161  			if fmtKeys {
   162  				d.fmtKey.fn(stdout, iter.Key())
   163  				needDelimiter = true
   164  			}
   165  			if fmtValues {
   166  				if needDelimiter {
   167  					stdout.Write([]byte{' '})
   168  				}
   169  				d.fmtValue.fn(stdout, iter.Value())
   170  			}
   171  			stdout.Write([]byte{'\n'})
   172  		}
   173  
   174  		count++
   175  	}
   176  
   177  	if err := iter.Close(); err != nil {
   178  		fmt.Fprintf(stdout, "%s\n", err)
   179  	}
   180  
   181  	elapsed := timeNow().Sub(start)
   182  
   183  	plural := ""
   184  	if count != 1 {
   185  		plural = "s"
   186  	}
   187  	fmt.Fprintf(stdout, "scanned %d record%s in %0.1fs\n",
   188  		count, plural, elapsed.Seconds())
   189  
   190  	if err := db.Close(); err != nil {
   191  		fmt.Fprintf(stdout, "%s\n", err)
   192  	}
   193  }