github.com/petermattis/pebble@v0.0.0-20190905164901-ab51a2166067/tool/wal.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  	"bytes"
     9  	"fmt"
    10  	"io"
    11  
    12  	"github.com/petermattis/pebble"
    13  	"github.com/petermattis/pebble/internal/base"
    14  	"github.com/petermattis/pebble/internal/record"
    15  	"github.com/petermattis/pebble/vfs"
    16  	"github.com/spf13/cobra"
    17  )
    18  
    19  // walT implements WAL-level tools, including both configuration state and the
    20  // commands themselves.
    21  type walT struct {
    22  	Root *cobra.Command
    23  	Dump *cobra.Command
    24  
    25  	fmtKey   formatter
    26  	fmtValue formatter
    27  }
    28  
    29  func newWAL(opts *base.Options) *walT {
    30  	w := &walT{}
    31  	w.fmtKey.mustSet("quoted")
    32  	w.fmtValue.mustSet("size")
    33  
    34  	w.Root = &cobra.Command{
    35  		Use:   "wal",
    36  		Short: "WAL introspection tools",
    37  	}
    38  	w.Dump = &cobra.Command{
    39  		Use:   "dump <wal-files>",
    40  		Short: "print WAL contents",
    41  		Long: `
    42  Print the contents of the WAL files.
    43  `,
    44  		Args: cobra.MinimumNArgs(1),
    45  		Run:  w.runDump,
    46  	}
    47  
    48  	w.Root.AddCommand(w.Dump)
    49  
    50  	w.Dump.Flags().Var(
    51  		&w.fmtKey, "key", "key formatter")
    52  	w.Dump.Flags().Var(
    53  		&w.fmtValue, "value", "value formatter")
    54  	return w
    55  }
    56  
    57  func (w *walT) runDump(cmd *cobra.Command, args []string) {
    58  	for _, arg := range args {
    59  		func() {
    60  			// Parse the filename in order to extract the file number. This is
    61  			// necessary in case WAL recycling was used (which it is usually is). If
    62  			// we can't parse the filename or it isn't a log file, we'll plow ahead
    63  			// anyways (which will likely fail when we try to read the file).
    64  			_, fileNum, ok := base.ParseFilename(arg)
    65  			if !ok {
    66  				fileNum = 0
    67  			}
    68  
    69  			f, err := vfs.Default.Open(arg)
    70  			if err != nil {
    71  				fmt.Fprintf(stderr, "%s\n", err)
    72  				return
    73  			}
    74  			defer f.Close()
    75  
    76  			fmt.Fprintf(stdout, "%s\n", arg)
    77  
    78  			var b pebble.Batch
    79  			var buf bytes.Buffer
    80  			rr := record.NewReader(f, fileNum)
    81  			for {
    82  				offset := rr.Offset()
    83  				r, err := rr.Next()
    84  				if err == nil {
    85  					buf.Reset()
    86  					_, err = io.Copy(&buf, r)
    87  				}
    88  				if err != nil {
    89  					// It is common to encounter a zeroed or invalid chunk due to WAL
    90  					// preallocation and WAL recycling. We need to distinguish these
    91  					// errors from EOF in order to recognize that the record was
    92  					// truncated, but want to otherwise treat them like EOF.
    93  					switch err {
    94  					case record.ErrZeroedChunk:
    95  						fmt.Fprintf(stdout, "EOF [%s] (may be due to WAL preallocation)\n", err)
    96  					case record.ErrInvalidChunk:
    97  						fmt.Fprintf(stdout, "EOF [%s] (may be due to WAL recycling)\n", err)
    98  					default:
    99  						fmt.Fprintf(stdout, "%s\n", err)
   100  					}
   101  					return
   102  				}
   103  
   104  				b = pebble.Batch{}
   105  				if err := b.SetRepr(buf.Bytes()); err != nil {
   106  					fmt.Fprintf(stdout, "corrupt log file %q: %v", arg, err)
   107  					return
   108  				}
   109  				fmt.Fprintf(stdout, "%d(%d) seq=%d count=%d\n",
   110  					offset, len(b.Repr()), b.SeqNum(), b.Count())
   111  				for r := b.Reader(); ; {
   112  					kind, ukey, value, ok := r.Next()
   113  					if !ok {
   114  						break
   115  					}
   116  					fmt.Fprintf(stdout, "    %s(", kind)
   117  					switch kind {
   118  					case base.InternalKeyKindDelete:
   119  						w.fmtKey.fn(stdout, ukey)
   120  					case base.InternalKeyKindSet:
   121  						w.fmtKey.fn(stdout, ukey)
   122  						stdout.Write([]byte{','})
   123  						w.fmtValue.fn(stdout, value)
   124  					case base.InternalKeyKindMerge:
   125  						w.fmtKey.fn(stdout, ukey)
   126  						stdout.Write([]byte{','})
   127  						w.fmtValue.fn(stdout, value)
   128  					case base.InternalKeyKindLogData:
   129  						w.fmtValue.fn(stdout, ukey)
   130  					case base.InternalKeyKindRangeDelete:
   131  						w.fmtKey.fn(stdout, ukey)
   132  						stdout.Write([]byte{','})
   133  						w.fmtKey.fn(stdout, value)
   134  					}
   135  					fmt.Fprintf(stdout, ")\n")
   136  				}
   137  			}
   138  		}()
   139  	}
   140  }