influxdb/cmd/influx_inspect/verify/tombstone/verify.go

143 lines
3.0 KiB
Go
Raw Normal View History

2020-04-24 14:58:24 +00:00
// Package tombstone verifies integrity of tombstones.
package tombstone
import (
"errors"
"flag"
"fmt"
"io"
"os"
"path/filepath"
"time"
2020-08-26 17:46:47 +00:00
"github.com/influxdata/influxdb/v2/tsdb/engine/tsm1"
2020-04-24 14:58:24 +00:00
)
// Command represents the program execution for "influx_inspect verify-tombstone".
type Command struct {
Stderr io.Writer
Stdout io.Writer
}
// NewCommand returns a new instance of Command.
func NewCommand() *Command {
return &Command{
Stderr: os.Stderr,
Stdout: os.Stdout,
}
}
// Run executes the command.
func (cmd *Command) Run(args ...string) error {
runner := verifier{w: cmd.Stdout}
fs := flag.NewFlagSet("verify-tombstone", flag.ExitOnError)
fs.StringVar(&runner.path, "path", os.Getenv("HOME")+"/.influxdb", "path to find tombstone files")
v := fs.Bool("v", false, "verbose: emit periodic progress")
vv := fs.Bool("vv", false, "very verbose: emit every tombstone entry key and time range")
vvv := fs.Bool("vvv", false, "very very verbose: emit every tombstone entry key and RFC3339Nano time range")
fs.SetOutput(cmd.Stdout)
if err := fs.Parse(args); err != nil {
return err
}
if *v {
runner.verbosity = verbose
}
if *vv {
runner.verbosity = veryVerbose
}
if *vvv {
runner.verbosity = veryVeryVerbose
}
return runner.Run()
}
const (
quiet = iota
verbose
veryVerbose
veryVeryVerbose
)
type verifier struct {
path string
verbosity int
w io.Writer
files []string
f string
}
func (v *verifier) loadFiles() error {
return filepath.Walk(v.path, func(path string, f os.FileInfo, err error) error {
if err != nil {
return err
}
if filepath.Ext(path) == "."+tsm1.TombstoneFileExtension {
v.files = append(v.files, path)
}
return nil
})
}
func (v *verifier) Next() bool {
if len(v.files) == 0 {
return false
}
v.f, v.files = v.files[0], v.files[1:]
return true
}
func (v *verifier) Run() error {
if err := v.loadFiles(); err != nil {
return err
}
var failed bool
start := time.Now()
for v.Next() {
if v.verbosity > quiet {
fmt.Fprintf(v.w, "Verifying: %q\n", v.f)
}
tombstoner := tsm1.NewTombstoner(v.f, nil)
if !tombstoner.HasTombstones() {
fmt.Fprintf(v.w, "%s has no tombstone entries", v.f)
continue
}
var totalEntries int64
err := tombstoner.Walk(func(t tsm1.Tombstone) error {
totalEntries++
if v.verbosity > quiet && totalEntries%(10*1e6) == 0 {
fmt.Fprintf(v.w, "Verified %d tombstone entries\n", totalEntries)
} else if v.verbosity > verbose {
var min interface{} = t.Min
var max interface{} = t.Max
if v.verbosity > veryVerbose {
min = time.Unix(0, t.Min)
max = time.Unix(0, t.Max)
}
fmt.Printf("key: %q, min: %v, max: %v\n", t.Key, min, max)
}
return nil
})
if err != nil {
fmt.Fprintf(v.w, "%q failed to walk tombstone entries: %v. Last okay entry: %d\n", v.f, err, totalEntries)
failed = true
continue
}
fmt.Fprintf(v.w, "Completed verification for %q in %v.\nVerified %d entries\n\n", v.f, time.Since(start), totalEntries)
}
if failed {
return errors.New("failed tombstone verification")
}
return nil
}