LCOV - code coverage report
Current view: top level - pebble/tool - wal.go (source / functions) Hit Total Coverage
Test: 2024-05-31 08:16Z 907d8652 - tests + meta.lcov Lines: 93 124 75.0 %
Date: 2024-05-31 08:17:10 Functions: 0 0 -

          Line data    Source code
       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             :         "encoding/binary"
      10             :         "fmt"
      11             :         "io"
      12             : 
      13             :         "github.com/cockroachdb/pebble"
      14             :         "github.com/cockroachdb/pebble/internal/base"
      15             :         "github.com/cockroachdb/pebble/rangekey"
      16             :         "github.com/cockroachdb/pebble/record"
      17             :         "github.com/cockroachdb/pebble/sstable"
      18             :         "github.com/cockroachdb/pebble/wal"
      19             :         "github.com/spf13/cobra"
      20             : )
      21             : 
      22             : // walT implements WAL-level tools, including both configuration state and the
      23             : // commands themselves.
      24             : type walT struct {
      25             :         Root *cobra.Command
      26             :         Dump *cobra.Command
      27             : 
      28             :         opts     *pebble.Options
      29             :         fmtKey   keyFormatter
      30             :         fmtValue valueFormatter
      31             : 
      32             :         defaultComparer string
      33             :         comparers       sstable.Comparers
      34             :         verbose         bool
      35             : }
      36             : 
      37           1 : func newWAL(opts *pebble.Options, comparers sstable.Comparers, defaultComparer string) *walT {
      38           1 :         w := &walT{
      39           1 :                 opts: opts,
      40           1 :         }
      41           1 :         w.fmtKey.mustSet("quoted")
      42           1 :         w.fmtValue.mustSet("size")
      43           1 :         w.comparers = comparers
      44           1 :         w.defaultComparer = defaultComparer
      45           1 : 
      46           1 :         w.Root = &cobra.Command{
      47           1 :                 Use:   "wal",
      48           1 :                 Short: "WAL introspection tools",
      49           1 :         }
      50           1 :         w.Dump = &cobra.Command{
      51           1 :                 Use:   "dump <wal-files>",
      52           1 :                 Short: "print WAL contents",
      53           1 :                 Long: `
      54           1 : Print the contents of the WAL files.
      55           1 : `,
      56           1 :                 Args: cobra.MinimumNArgs(1),
      57           1 :                 Run:  w.runDump,
      58           1 :         }
      59           1 : 
      60           1 :         w.Root.AddCommand(w.Dump)
      61           1 :         w.Root.PersistentFlags().BoolVarP(&w.verbose, "verbose", "v", false, "verbose output")
      62           1 : 
      63           1 :         w.Dump.Flags().Var(
      64           1 :                 &w.fmtKey, "key", "key formatter")
      65           1 :         w.Dump.Flags().Var(
      66           1 :                 &w.fmtValue, "value", "value formatter")
      67           1 :         return w
      68           1 : }
      69             : 
      70           1 : func (w *walT) runDump(cmd *cobra.Command, args []string) {
      71           1 :         stdout, stderr := cmd.OutOrStdout(), cmd.OutOrStderr()
      72           1 :         w.fmtKey.setForComparer(w.defaultComparer, w.comparers)
      73           1 :         w.fmtValue.setForComparer(w.defaultComparer, w.comparers)
      74           1 : 
      75           1 :         for _, arg := range args {
      76           1 :                 func() {
      77           1 :                         // Parse the filename in order to extract the file number. This is
      78           1 :                         // necessary in case WAL recycling was used (which it is usually is). If
      79           1 :                         // we can't parse the filename or it isn't a log file, we'll plow ahead
      80           1 :                         // anyways (which will likely fail when we try to read the file).
      81           1 :                         fileNum, _, ok := wal.ParseLogFilename(arg)
      82           1 :                         if !ok {
      83           0 :                                 fileNum = 0
      84           0 :                         }
      85             : 
      86           1 :                         f, err := w.opts.FS.Open(arg)
      87           1 :                         if err != nil {
      88           0 :                                 fmt.Fprintf(stderr, "%s\n", err)
      89           0 :                                 return
      90           0 :                         }
      91           1 :                         defer f.Close()
      92           1 : 
      93           1 :                         fmt.Fprintf(stdout, "%s\n", arg)
      94           1 : 
      95           1 :                         var b pebble.Batch
      96           1 :                         var buf bytes.Buffer
      97           1 :                         rr := record.NewReader(f, base.DiskFileNum(fileNum))
      98           1 :                         for {
      99           1 :                                 offset := rr.Offset()
     100           1 :                                 r, err := rr.Next()
     101           1 :                                 if err == nil {
     102           1 :                                         buf.Reset()
     103           1 :                                         _, err = io.Copy(&buf, r)
     104           1 :                                 }
     105           1 :                                 if err != nil {
     106           1 :                                         // It is common to encounter a zeroed or invalid chunk due to WAL
     107           1 :                                         // preallocation and WAL recycling. We need to distinguish these
     108           1 :                                         // errors from EOF in order to recognize that the record was
     109           1 :                                         // truncated, but want to otherwise treat them like EOF.
     110           1 :                                         switch err {
     111           0 :                                         case record.ErrZeroedChunk:
     112           0 :                                                 fmt.Fprintf(stdout, "EOF [%s] (may be due to WAL preallocation)\n", err)
     113           0 :                                         case record.ErrInvalidChunk:
     114           0 :                                                 fmt.Fprintf(stdout, "EOF [%s] (may be due to WAL recycling)\n", err)
     115           1 :                                         default:
     116           1 :                                                 fmt.Fprintf(stdout, "%s\n", err)
     117             :                                         }
     118           1 :                                         return
     119             :                                 }
     120             : 
     121           1 :                                 b = pebble.Batch{}
     122           1 :                                 if err := b.SetRepr(buf.Bytes()); err != nil {
     123           0 :                                         fmt.Fprintf(stdout, "corrupt batch within log file %q: %v", arg, err)
     124           0 :                                         return
     125           0 :                                 }
     126           1 :                                 fmt.Fprintf(stdout, "%d(%d) seq=%d count=%d\n",
     127           1 :                                         offset, len(b.Repr()), b.SeqNum(), b.Count())
     128           1 :                                 for r, idx := b.Reader(), 0; ; idx++ {
     129           1 :                                         kind, ukey, value, ok, err := r.Next()
     130           1 :                                         if !ok {
     131           1 :                                                 if err != nil {
     132           0 :                                                         fmt.Fprintf(stdout, "corrupt batch within log file %q: %v", arg, err)
     133           0 :                                                 }
     134           1 :                                                 break
     135             :                                         }
     136           1 :                                         fmt.Fprintf(stdout, "    %s(", kind)
     137           1 :                                         switch kind {
     138           1 :                                         case base.InternalKeyKindDelete:
     139           1 :                                                 fmt.Fprintf(stdout, "%s", w.fmtKey.fn(ukey))
     140           1 :                                         case base.InternalKeyKindSet:
     141           1 :                                                 fmt.Fprintf(stdout, "%s,%s", w.fmtKey.fn(ukey), w.fmtValue.fn(ukey, value))
     142           0 :                                         case base.InternalKeyKindMerge:
     143           0 :                                                 fmt.Fprintf(stdout, "%s,%s", w.fmtKey.fn(ukey), w.fmtValue.fn(ukey, value))
     144           0 :                                         case base.InternalKeyKindLogData:
     145           0 :                                                 fmt.Fprintf(stdout, "<%d>", len(value))
     146           0 :                                         case base.InternalKeyKindIngestSST:
     147           0 :                                                 fileNum, _ := binary.Uvarint(ukey)
     148           0 :                                                 fmt.Fprintf(stdout, "%s", base.FileNum(fileNum))
     149           0 :                                         case base.InternalKeyKindSingleDelete:
     150           0 :                                                 fmt.Fprintf(stdout, "%s", w.fmtKey.fn(ukey))
     151           0 :                                         case base.InternalKeyKindSetWithDelete:
     152           0 :                                                 fmt.Fprintf(stdout, "%s", w.fmtKey.fn(ukey))
     153           0 :                                         case base.InternalKeyKindRangeDelete:
     154           0 :                                                 fmt.Fprintf(stdout, "%s,%s", w.fmtKey.fn(ukey), w.fmtKey.fn(value))
     155           1 :                                         case base.InternalKeyKindRangeKeySet, base.InternalKeyKindRangeKeyUnset, base.InternalKeyKindRangeKeyDelete:
     156           1 :                                                 ik := base.MakeInternalKey(ukey, b.SeqNum()+uint64(idx), kind)
     157           1 :                                                 s, err := rangekey.Decode(ik, value, nil)
     158           1 :                                                 if err != nil {
     159           0 :                                                         fmt.Fprintf(stdout, "%s: error decoding %s", w.fmtKey.fn(ukey), err)
     160           1 :                                                 } else {
     161           1 :                                                         fmt.Fprintf(stdout, "%s", s.Pretty(w.fmtKey.fn))
     162           1 :                                                 }
     163           0 :                                         case base.InternalKeyKindDeleteSized:
     164           0 :                                                 v, _ := binary.Uvarint(value)
     165           0 :                                                 fmt.Fprintf(stdout, "%s,%d", w.fmtKey.fn(ukey), v)
     166             :                                         }
     167           1 :                                         fmt.Fprintf(stdout, ")\n")
     168             :                                 }
     169             :                         }
     170             :                 }()
     171             :         }
     172             : }

Generated by: LCOV version 1.14