143 lines
3.0 KiB
Go
143 lines
3.0 KiB
Go
// Package tombstone verifies integrity of tombstones.
|
|
package tombstone
|
|
|
|
import (
|
|
"errors"
|
|
"flag"
|
|
"fmt"
|
|
"io"
|
|
"os"
|
|
"path/filepath"
|
|
"time"
|
|
|
|
"github.com/influxdata/influxdb/v2/tsdb/engine/tsm1"
|
|
)
|
|
|
|
// 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
|
|
}
|