Skip to content
This repository was archived by the owner on Aug 13, 2019. It is now read-only.

Commit a13e880

Browse files
author
Krasi Georgiev
committed
first draft
Signed-off-by: Krasi Georgiev <[email protected]>
1 parent def6e5a commit a13e880

File tree

3 files changed

+154
-4
lines changed

3 files changed

+154
-4
lines changed

cmd/tsdb/main.go

+65
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,8 @@ func main() {
4646
benchSamplesFile = benchWriteCmd.Arg("file", "input file with samples data, default is (../../testdata/20k.series)").Default("../../testdata/20k.series").String()
4747
listCmd = cli.Command("ls", "list db blocks")
4848
listPath = listCmd.Arg("db path", "database path (default is benchout/storage)").Default("benchout/storage").String()
49+
scanCmd = cli.Command("scan", "scans the db and lists corrupted blocks")
50+
scanPath = scanCmd.Arg("dir", "database path (default is current dir ./)").Default("./").String()
4951
)
5052

5153
switch kingpin.MustParse(cli.Parse(os.Args[1:])) {
@@ -62,10 +64,73 @@ func main() {
6264
exitWithError(err)
6365
}
6466
printBlocks(db.Blocks())
67+
case scanCmd.FullCommand():
68+
useless, err := tsdb.Scan(*scanPath)
69+
if err != nil {
70+
exitWithError(err)
71+
}
72+
73+
if len(useless) == 0 {
74+
fmt.Println("Hooray! The db has no corrupted blocks (or the scan tool is broken).\U0001f638")
75+
return
76+
}
77+
78+
for err, paths := range useless {
79+
fmt.Println(err)
80+
for _, path := range paths {
81+
fmt.Println(path)
82+
}
83+
switch err.(type) {
84+
case tsdb.ErrorTmp:
85+
fmt.Println(`
86+
Scanning found some temporary files!
87+
These are usually caused by a crash or incomplete compaction and
88+
are safe to delete as long as you are sure that no other application is currently using this database folder.`)
89+
case tsdb.ErrorOpen:
90+
fmt.Println(`
91+
Scanning found some blocks that cannot be opened!
92+
Deleting these will allow a clean startup, but you will loose all data in the listed time ranges.`)
93+
case tsdb.ErrorSequence:
94+
fmt.Println(`
95+
Scanning found some overlapping blocks!
96+
Deleting these will allow a clean startup, but you will loose all data in the listed time ranges.`)
97+
}
98+
99+
if yes := prompt(); yes {
100+
for _, path := range paths {
101+
if err := os.Remove(path); err != nil {
102+
fmt.Printf("error deleting: %v, %v", path, err)
103+
}
104+
fmt.Printf("%v \n", path)
105+
}
106+
}
107+
}
65108
}
66109
flag.CommandLine.Set("log.level", "debug")
67110
}
68111

112+
func prompt() bool {
113+
fmt.Printf("Do you want to delete these (y/N)? ")
114+
var s string
115+
_, err := fmt.Scanln(&s)
116+
if err != nil {
117+
exitWithError(err)
118+
}
119+
120+
s = strings.TrimSpace(s)
121+
s = strings.ToLower(s)
122+
123+
if s == "y" || s == "yes" {
124+
return true
125+
}
126+
if s == "n" || s == "no" {
127+
return false
128+
}
129+
fmt.Printf("%v is not a valid answer \n", s)
130+
prompt()
131+
return false
132+
}
133+
69134
type writeBenchmark struct {
70135
outPath string
71136
samplesFile string

db.go

+4-4
Original file line numberDiff line numberDiff line change
@@ -523,8 +523,8 @@ func (db *DB) reload(deleteable ...string) (err error) {
523523
exist[meta.ULID] = struct{}{}
524524
}
525525

526-
if err := validateBlockSequence(blocks); err != nil {
527-
return errors.Wrap(err, "invalid block sequence")
526+
if overlaps := validateBlockSequence(blocks); overlaps != nil {
527+
return errors.Errorf("invalid block sequence , block time ranges overlap: %s", overlaps)
528528
}
529529

530530
// Swap in new blocks first for subsequently created readers to be seen.
@@ -557,7 +557,7 @@ func (db *DB) reload(deleteable ...string) (err error) {
557557
}
558558

559559
// validateBlockSequence returns error if given block meta files indicate that some blocks overlaps within sequence.
560-
func validateBlockSequence(bs []*Block) error {
560+
func validateBlockSequence(bs []*Block) Overlaps {
561561
if len(bs) <= 1 {
562562
return nil
563563
}
@@ -569,7 +569,7 @@ func validateBlockSequence(bs []*Block) error {
569569

570570
overlaps := OverlappingBlocks(metas)
571571
if len(overlaps) > 0 {
572-
return errors.Errorf("block time ranges overlap: %s", overlaps)
572+
return overlaps
573573
}
574574

575575
return nil

scan.go

+85
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
package tsdb
2+
3+
import (
4+
"fmt"
5+
"os"
6+
"path/filepath"
7+
8+
"github.com/pkg/errors"
9+
)
10+
11+
type ErrorOpen string
12+
13+
func (e ErrorOpen) Error() string {
14+
return fmt.Sprintf("block open error: %v", e)
15+
}
16+
17+
type ErrorSequence string
18+
19+
func (e ErrorSequence) Error() string {
20+
return fmt.Sprintf("block sequence error %v", e)
21+
}
22+
23+
type ErrorTmp struct{}
24+
25+
func (e ErrorTmp) Error() string {
26+
return fmt.Sprintf("abandoned tmp files")
27+
}
28+
29+
// Scan checks the integrity of the tsdb for a given directory and returns a list of files and folders that can be deleted.
30+
// It returns a map of error to explain the reason and slice of dirs for further processing.
31+
func Scan(dir string) (map[error][]string, error) {
32+
useless := make(map[error][]string)
33+
34+
if _, err := os.Stat(dir); os.IsNotExist(err) {
35+
return nil, err
36+
}
37+
38+
db := &DB{
39+
dir: dir,
40+
logger: nil,
41+
opts: &Options{},
42+
}
43+
44+
dirs, err := blockDirs(db.dir)
45+
if err != nil {
46+
return nil, errors.Wrap(err, "find blocks")
47+
}
48+
49+
// Scan for blocks that can't be opened.
50+
var errOpen ErrorOpen
51+
for _, dir := range dirs {
52+
b, err := OpenBlock(dir, nil)
53+
if err != nil {
54+
errOpen = ErrorOpen(err.Error())
55+
useless[errOpen] = append(useless[errOpen], dir)
56+
break
57+
}
58+
59+
db.blocks = append(db.blocks, b)
60+
}
61+
62+
// Scan for overlaping blocks.
63+
if overlaps := validateBlockSequence(db.blocks); overlaps != nil {
64+
var dirs []string
65+
for _, b := range db.blocks {
66+
dirs = append(dirs, b.Dir())
67+
}
68+
useless[ErrorSequence(overlaps.String())] = dirs
69+
}
70+
71+
// Scan for temporary files.
72+
var tmpFiles []string
73+
filepath.Walk(db.dir, func(path string, f os.FileInfo, _ error) error {
74+
if !f.IsDir() {
75+
if filepath.Ext(path) == ".tmp" {
76+
tmpFiles = append(tmpFiles, path)
77+
}
78+
}
79+
return nil
80+
})
81+
if len(tmpFiles) > 0 {
82+
useless[ErrorTmp{}] = tmpFiles
83+
}
84+
return useless, nil
85+
}

0 commit comments

Comments
 (0)