-
Notifications
You must be signed in to change notification settings - Fork 3.6k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
2 changed files
with
152 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,145 @@ | ||
// Package tsm verifies integrity of TSM files. | ||
package tombstone | ||
|
||
import ( | ||
"errors" | ||
"flag" | ||
"fmt" | ||
"io" | ||
"os" | ||
"path/filepath" | ||
"time" | ||
|
||
"github.com/influxdata/influxdb/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 { | ||
if *vv || *vvv { | ||
return errors.New("cannot set multiple verbosity levels") | ||
} | ||
runner.verbosity = verbose | ||
} else if *vv && *vvv { | ||
return errors.New("cannot set multiple verbosity levels") | ||
} else if *vv { | ||
runner.verbosity = veryVerbose | ||
} else 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 | ||
} |