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 }