github.com/petermattis/pebble@v0.0.0-20190905164901-ab51a2166067/tool/sstable.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  	"sort"
    10  	"text/tabwriter"
    11  
    12  	"github.com/petermattis/pebble/internal/base"
    13  	"github.com/petermattis/pebble/sstable"
    14  	"github.com/petermattis/pebble/vfs"
    15  	"github.com/spf13/cobra"
    16  )
    17  
    18  // sstableT implements sstable-level tools, including both configuration state
    19  // and the commands themselves.
    20  type sstableT struct {
    21  	Root       *cobra.Command
    22  	Check      *cobra.Command
    23  	Layout     *cobra.Command
    24  	Properties *cobra.Command
    25  	Scan       *cobra.Command
    26  
    27  	// Configuration and state.
    28  	opts      *sstable.Options
    29  	comparers sstable.Comparers
    30  	mergers   sstable.Mergers
    31  	dbNum     uint64
    32  
    33  	// Flags.
    34  	fmtKey   formatter
    35  	fmtValue formatter
    36  	start    key
    37  	end      key
    38  	verbose  bool
    39  }
    40  
    41  func newSSTable(
    42  	opts *base.Options, comparers sstable.Comparers, mergers sstable.Mergers,
    43  ) *sstableT {
    44  	s := &sstableT{
    45  		opts:      opts,
    46  		comparers: comparers,
    47  		mergers:   mergers,
    48  	}
    49  	s.fmtKey.mustSet("quoted")
    50  	s.fmtValue.mustSet("[%x]")
    51  
    52  	s.Root = &cobra.Command{
    53  		Use:   "sstable",
    54  		Short: "sstable introspection tools",
    55  	}
    56  	s.Check = &cobra.Command{
    57  		Use:   "check <sstables>",
    58  		Short: "verify checksums and metadata",
    59  		Long:  ``,
    60  		Args:  cobra.MinimumNArgs(1),
    61  		Run:   s.runCheck,
    62  	}
    63  	s.Layout = &cobra.Command{
    64  		Use:   "layout <sstables>",
    65  		Short: "print sstable block and record layout",
    66  		Long: `
    67  Print the layout for the sstables. The -v flag controls whether record layout
    68  is displayed or omitted.
    69  `,
    70  		Args: cobra.MinimumNArgs(1),
    71  		Run:  s.runLayout,
    72  	}
    73  	s.Properties = &cobra.Command{
    74  		Use:   "properties <sstables>",
    75  		Short: "print sstable properties",
    76  		Long: `
    77  Print the properties for the sstables. The -v flag controls whether the
    78  properties are pretty-printed or displayed in a verbose/raw format.
    79  `,
    80  		Args: cobra.MinimumNArgs(1),
    81  		Run:  s.runProperties,
    82  	}
    83  	s.Scan = &cobra.Command{
    84  		Use:   "scan <sstables>",
    85  		Short: "print sstable records",
    86  		Long: `
    87  Print the records in the sstables. The sstables are scanned in command line
    88  order which means the records will be printed in that order.
    89  `,
    90  		Args: cobra.MinimumNArgs(1),
    91  		Run:  s.runScan,
    92  	}
    93  
    94  	s.Root.AddCommand(s.Check, s.Layout, s.Properties, s.Scan)
    95  	for _, cmd := range []*cobra.Command{s.Layout, s.Properties} {
    96  		cmd.Flags().BoolVarP(
    97  			&s.verbose, "verbose", "v", false,
    98  			"verbose output")
    99  	}
   100  
   101  	s.Layout.Flags().Var(
   102  		&s.fmtKey, "key", "key formatter")
   103  	s.Layout.Flags().Var(
   104  		&s.fmtValue, "value", "value formatter")
   105  	s.Scan.Flags().Var(
   106  		&s.fmtKey, "key", "key formatter")
   107  	s.Scan.Flags().Var(
   108  		&s.fmtValue, "value", "value formatter")
   109  	s.Scan.Flags().Var(
   110  		&s.start, "start", "start key for the scan")
   111  	s.Scan.Flags().Var(
   112  		&s.end, "end", "end key for the scan")
   113  
   114  	return s
   115  }
   116  
   117  func (s *sstableT) newReader(f vfs.File) (*sstable.Reader, error) {
   118  	s.dbNum++
   119  	return sstable.NewReader(f, s.dbNum, 0, s.opts, s.comparers, s.mergers)
   120  }
   121  
   122  func (s *sstableT) runCheck(cmd *cobra.Command, args []string) {
   123  	for _, arg := range args {
   124  		func() {
   125  			f, err := vfs.Default.Open(arg)
   126  			if err != nil {
   127  				fmt.Fprintf(stderr, "%s\n", err)
   128  				return
   129  			}
   130  
   131  			fmt.Fprintf(stdout, "%s\n", arg)
   132  
   133  			r, err := s.newReader(f)
   134  			defer r.Close()
   135  
   136  			if err != nil {
   137  				fmt.Fprintf(stdout, "%s\n", err)
   138  				return
   139  			}
   140  
   141  			iter := r.NewIter(nil, nil)
   142  			for key, _ := iter.First(); key != nil; key, _ = iter.Next() {
   143  			}
   144  			if err := iter.Close(); err != nil {
   145  				fmt.Fprintf(stdout, "%s\n", err)
   146  			}
   147  		}()
   148  	}
   149  }
   150  
   151  func (s *sstableT) runLayout(cmd *cobra.Command, args []string) {
   152  	for _, arg := range args {
   153  		func() {
   154  			f, err := vfs.Default.Open(arg)
   155  			if err != nil {
   156  				fmt.Fprintf(stderr, "%s\n", err)
   157  				return
   158  			}
   159  
   160  			fmt.Fprintf(stdout, "%s\n", arg)
   161  
   162  			r, err := s.newReader(f)
   163  			defer r.Close()
   164  
   165  			if err != nil {
   166  				fmt.Fprintf(stdout, "%s\n", err)
   167  				return
   168  			}
   169  
   170  			l, err := r.Layout()
   171  			if err != nil {
   172  				fmt.Fprintf(stderr, "%s\n", err)
   173  				return
   174  			}
   175  			fmtRecord := func(key *base.InternalKey, value []byte) {
   176  				formatKeyValue(stdout, s.fmtKey, s.fmtValue, key, value)
   177  			}
   178  			if s.fmtKey.spec == "null" && s.fmtValue.spec == "null" {
   179  				fmtRecord = nil
   180  			}
   181  			l.Describe(stdout, s.verbose, r, fmtRecord)
   182  		}()
   183  	}
   184  }
   185  
   186  func (s *sstableT) runProperties(cmd *cobra.Command, args []string) {
   187  	for _, arg := range args {
   188  		func() {
   189  			f, err := vfs.Default.Open(arg)
   190  			if err != nil {
   191  				fmt.Fprintf(stderr, "%s\n", err)
   192  				return
   193  			}
   194  
   195  			fmt.Fprintf(stdout, "%s\n", arg)
   196  
   197  			r, err := s.newReader(f)
   198  			defer r.Close()
   199  
   200  			if err != nil {
   201  				fmt.Fprintf(stdout, "%s\n", err)
   202  				return
   203  			}
   204  
   205  			if s.verbose {
   206  				fmt.Fprintf(stdout, "%s", r.Properties.String())
   207  				return
   208  			}
   209  
   210  			stat, err := f.Stat()
   211  			if err != nil {
   212  				fmt.Fprintf(stderr, "%s\n", err)
   213  				return
   214  			}
   215  
   216  			formatNull := func(s string) string {
   217  				switch s {
   218  				case "", "nullptr":
   219  					return "-"
   220  				}
   221  				return s
   222  			}
   223  
   224  			tw := tabwriter.NewWriter(stdout, 2, 1, 2, ' ', 0)
   225  			fmt.Fprintf(tw, "version\t%d\n", r.Properties.Version)
   226  			fmt.Fprintf(tw, "size\t\n")
   227  			fmt.Fprintf(tw, "  file\t%d\n", stat.Size())
   228  			fmt.Fprintf(tw, "  data\t%d\n", r.Properties.DataSize)
   229  			fmt.Fprintf(tw, "    blocks\t%d\n", r.Properties.NumDataBlocks)
   230  			fmt.Fprintf(tw, "  index\t%d\n", r.Properties.IndexSize)
   231  			fmt.Fprintf(tw, "    blocks\t%d\n", 1+r.Properties.IndexPartitions)
   232  			fmt.Fprintf(tw, "    top-level\t%d\n", r.Properties.TopLevelIndexSize)
   233  			fmt.Fprintf(tw, "  filter\t%d\n", r.Properties.FilterSize)
   234  			fmt.Fprintf(tw, "  raw-key\t%d\n", r.Properties.RawKeySize)
   235  			fmt.Fprintf(tw, "  raw-value\t%d\n", r.Properties.RawValueSize)
   236  			fmt.Fprintf(tw, "records\t%d\n", r.Properties.NumEntries)
   237  			fmt.Fprintf(tw, "  set\t%d\n", r.Properties.NumEntries-
   238  				(r.Properties.NumDeletions+r.Properties.NumRangeDeletions+r.Properties.NumMergeOperands))
   239  			fmt.Fprintf(tw, "  delete\t%d\n", r.Properties.NumDeletions)
   240  			fmt.Fprintf(tw, "  range-delete\t%d\n", r.Properties.NumRangeDeletions)
   241  			fmt.Fprintf(tw, "  merge\t%d\n", r.Properties.NumMergeOperands)
   242  			fmt.Fprintf(tw, "  global-seq-num\t%d\n", r.Properties.GlobalSeqNum)
   243  			fmt.Fprintf(tw, "index\t\n")
   244  			fmt.Fprintf(tw, "  key\t")
   245  			if r.Properties.IndexKeyIsUserKey != 0 {
   246  				fmt.Fprintf(tw, "user key\n")
   247  			} else {
   248  				fmt.Fprintf(tw, "internal key\n")
   249  			}
   250  			fmt.Fprintf(tw, "  value\t")
   251  			if r.Properties.IndexValueIsDeltaEncoded != 0 {
   252  				fmt.Fprintf(tw, "delta encoded\n")
   253  			} else {
   254  				fmt.Fprintf(tw, "raw encoded\n")
   255  			}
   256  			fmt.Fprintf(tw, "comparer\t%s\n", r.Properties.ComparerName)
   257  			fmt.Fprintf(tw, "merger\t%s\n", formatNull(r.Properties.MergerName))
   258  			fmt.Fprintf(tw, "filter\t%s\n", formatNull(r.Properties.FilterPolicyName))
   259  			fmt.Fprintf(tw, "  prefix\t%t\n", r.Properties.PrefixFiltering)
   260  			fmt.Fprintf(tw, "  whole-key\t%t\n", r.Properties.WholeKeyFiltering)
   261  			fmt.Fprintf(tw, "compression\t%s\n", r.Properties.CompressionName)
   262  			fmt.Fprintf(tw, "  options\t%s\n", r.Properties.CompressionOptions)
   263  			fmt.Fprintf(tw, "user properties\t\n")
   264  			fmt.Fprintf(tw, "  collectors\t%s\n", r.Properties.PropertyCollectorNames)
   265  			keys := make([]string, 0, len(r.Properties.UserProperties))
   266  			for key := range r.Properties.UserProperties {
   267  				keys = append(keys, key)
   268  			}
   269  			sort.Strings(keys)
   270  			for _, key := range keys {
   271  				fmt.Fprintf(tw, "  %s\t%s\n", key, r.Properties.UserProperties[key])
   272  			}
   273  			tw.Flush()
   274  		}()
   275  	}
   276  }
   277  
   278  func (s *sstableT) runScan(cmd *cobra.Command, args []string) {
   279  	for _, arg := range args {
   280  		func() {
   281  			f, err := vfs.Default.Open(arg)
   282  			if err != nil {
   283  				fmt.Fprintf(stderr, "%s\n", err)
   284  				return
   285  			}
   286  
   287  			fmt.Fprintf(stdout, "%s\n", arg)
   288  
   289  			r, err := s.newReader(f)
   290  			defer r.Close()
   291  
   292  			if err != nil {
   293  				fmt.Fprintf(stdout, "%s\n", err)
   294  				return
   295  			}
   296  
   297  			iter := r.NewIter(nil, s.end)
   298  			var lastKey base.InternalKey
   299  			for key, value := iter.SeekGE(s.start); key != nil; key, value = iter.Next() {
   300  				formatKeyValue(stdout, s.fmtKey, s.fmtValue, key, value)
   301  				if base.InternalCompare(r.Compare, lastKey, *key) >= 0 {
   302  					fmt.Fprintf(stdout, "    WARNING: OUT OF ORDER KEYS!\n")
   303  				}
   304  				lastKey.Trailer = key.Trailer
   305  				lastKey.UserKey = append(lastKey.UserKey[:0], key.UserKey...)
   306  			}
   307  			if err := iter.Close(); err != nil {
   308  				fmt.Fprintf(stdout, "%s\n", err)
   309  			}
   310  		}()
   311  	}
   312  }