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 }