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

Commit f7f2578

Browse files
author
Krasi Georgiev
committed
complete rewrite
Signed-off-by: Krasi Georgiev <[email protected]>
1 parent 66b0380 commit f7f2578

File tree

10 files changed

+417
-475
lines changed

10 files changed

+417
-475
lines changed

block.go

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -189,10 +189,13 @@ const (
189189
flagStd = 1
190190
)
191191

192-
const indexFilename = "index"
193-
const metaFilename = "meta.json"
192+
const (
193+
indexFilename = "index"
194+
metaFilename = "meta.json"
195+
chunksDirname = "chunks"
196+
)
194197

195-
func chunkDir(dir string) string { return filepath.Join(dir, "chunks") }
198+
func chunkDir(dir string) string { return filepath.Join(dir, chunksDirname) }
196199
func walDir(dir string) string { return filepath.Join(dir, "wal") }
197200

198201
func readMetaFile(dir string) (*BlockMeta, error) {

cmd/tsdb/main.go

Lines changed: 136 additions & 95 deletions
Original file line numberDiff line numberDiff line change
@@ -74,131 +74,172 @@ func main() {
7474
printBlocks(db.Blocks(), listCmdHumanReadable)
7575

7676
case scanCmd.FullCommand():
77-
var didSomething bool
78-
var tmpFiles []string
79-
filepath.Walk(*scanPath, func(path string, f os.FileInfo, _ error) error {
80-
if filepath.Ext(path) == ".tmp" {
81-
tmpFiles = append(tmpFiles, path)
82-
}
83-
84-
return nil
85-
})
86-
if len(tmpFiles) > 0 {
87-
didSomething = true
88-
fmt.Println(`
89-
These are usually caused by a crash or incomplete compaction and
90-
are safe to delete as long as you are sure that no other application is currently using this database.`)
91-
promptDelete(tmpFiles, scanCmdHumanReadable)
92-
}
77+
scanTmps(*scanPath, scanCmdHumanReadable)
9378

9479
scan, err := tsdb.NewDBScanner(*scanPath, logger)
95-
96-
unreadableBlocks, err := scan.Unreadable()
9780
if err != nil {
9881
exitWithError(err)
9982
}
100-
if len(unreadableBlocks) > 0 {
101-
didSomething = true
102-
fmt.Println("Unreadable blocks!")
103-
fmt.Println("Deleting these will remove all data in the listed time range.")
104-
promptDelete(unreadableBlocks, scanCmdHumanReadable)
105-
}
83+
scanTmbst(scan, scanCmdHumanReadable)
84+
scanIndexes(scan, scanCmdHumanReadable)
85+
scanOverlapping(scan, scanCmdHumanReadable)
10686

107-
repaired, err := scan.RepairIndex()
108-
if err != nil {
109-
exitWithError(err)
87+
fmt.Println("Scan complete!")
88+
fmt.Println("Hooray! The db is clean(or the scan tool is broken):\U0001f638")
89+
}
90+
flag.CommandLine.Set("log.level", "debug")
91+
}
92+
93+
func scanOverlapping(scan tsdb.Scanner, hformat *bool) {
94+
overlaps, err := scan.Overlapping()
95+
if err != nil {
96+
exitWithError(err)
97+
}
98+
if len(overlaps) > 0 {
99+
fmt.Println("Overlaping blocks.")
100+
fmt.Println("Deleting these will remove all data in the listed time range.")
101+
var blocksDel []*tsdb.Block
102+
for t, overBcks := range overlaps {
103+
fmt.Printf("overlapping blocks : %v-%v \n", time.Unix(t.Min/1000, 0).Format("06/01/02 15:04"), time.Unix(t.Max/1000, 0).Format("15:04 06/01/02"))
104+
105+
var largest int
106+
for i, b := range overBcks {
107+
if b.Meta().Stats.NumSamples > overBcks[largest].Meta().Stats.NumSamples {
108+
largest = i
109+
}
110+
}
111+
// Don't delete the largest block in the overlaps.
112+
blocksDel = append(overBcks[:largest], overBcks[largest+1:]...)
113+
fmt.Printf("\nBlock %v contains highest samples count and is ommited from the deletion list! \n\n", overBcks[largest])
110114
}
111-
if len(repaired) > 0 {
112-
didSomething = true
113-
fmt.Println("Corrupted indexes that were repaired.")
114-
for path, stats := range repaired {
115-
fmt.Printf("path:%v stats:%+v \n", path, stats)
115+
116+
var paths []string
117+
for _, b := range blocksDel {
118+
_, folder := path.Split(b.Dir())
119+
if _, err := ulid.Parse(folder); err != nil {
120+
fmt.Printf("\nskipping invalid block dir: %v :%v \n\n", b.Dir(), err)
121+
continue
116122
}
123+
paths = append(paths, b.Dir())
117124
}
118-
overlappingBlocks, err := scan.Overlapping()
119-
if err != nil {
120-
exitWithError(err)
125+
printBlocks(blocksDel, hformat)
126+
if prompt() {
127+
if err = dellAll(paths); err != nil {
128+
exitWithError(errors.Wrap(err, "deleting overlapping blocks"))
129+
}
121130
}
122-
if len(overlappingBlocks) > 0 {
123-
didSomething = true
124-
fmt.Println("Overlaping blocks.")
125-
fmt.Println("Deleting these will remove all data in the listed time range.")
126-
for t, blocks := range overlappingBlocks {
127-
fmt.Printf("overlapping blocks : %v-%v \n", time.Unix(t.Min/1000, 0).Format("06/01/02 15:04"), time.Unix(t.Max/1000, 0).Format("15:04 06/01/02"))
128-
129-
var biggestIndex int
130-
biggest := &tsdb.Block{}
131-
for i, b := range blocks {
132-
if b.Meta().Stats.NumSamples > biggest.Meta().Stats.NumSamples {
133-
biggest = b
134-
biggestIndex = i
135-
}
136-
}
137-
// Don't delete the bigest block in the overlaps.
138-
blocks = append(blocks[:biggestIndex], blocks[biggestIndex+1:]...)
139-
fmt.Printf("\nBlock %v contains most samples and is ommited from the deletion list! \n\n", biggest)
131+
}
132+
}
140133

141-
promptDelete(blocks, scanCmdHumanReadable)
142-
}
134+
func scanIndexes(scan tsdb.Scanner, hformat *bool) {
135+
unrepairable, repaired, err := scan.Indexes()
136+
if err != nil {
137+
exitWithError(err)
138+
}
143139

140+
if len(repaired) > 0 {
141+
fmt.Println("Corrupted indexes that were repaired.")
142+
for _, stats := range repaired {
143+
fmt.Printf("path:%v stats:%+v \n", stats.BlockDir, stats)
144144
}
145+
}
145146

146-
fmt.Println("Scan complete!")
147-
if !didSomething {
148-
fmt.Println("Hooray! The db is clean(or the scan tool is broken):\U0001f638")
149-
return
147+
if len(unrepairable) > 0 {
148+
for cause, bdirs := range unrepairable {
149+
fmt.Println("Blocks with unrepairable indexes:", cause)
150+
printFiles(bdirs, hformat)
151+
if prompt() {
152+
if err = dellAll(bdirs); err != nil {
153+
exitWithError(errors.Wrap(err, "deleting blocks with invalid indexes"))
154+
}
155+
}
150156
}
151157
}
152-
flag.CommandLine.Set("log.level", "debug")
153158
}
154159

155-
func promptDelete(i interface{}, humanReadable *bool) {
156-
var paths []string
157-
switch i.(type) {
158-
case []*tsdb.Block:
159-
blocks := i.([]*tsdb.Block)
160-
for _, b := range blocks {
161-
_, folder := path.Split(b.Dir())
162-
if _, err := ulid.Parse(folder); err != nil {
163-
exitWithError(fmt.Errorf("dir doesn't contain a valid ULID:%v", err))
160+
func scanTmbst(scan tsdb.Scanner, hformat *bool) {
161+
invalid, err := scan.Tombstones()
162+
if err != nil {
163+
exitWithError(errors.Wrap(err, "scannings Tombstones"))
164+
}
165+
166+
if len(invalid) > 0 {
167+
fmt.Println("Tombstones include data to be deleted so removing these will cancel deleting these timeseries.")
168+
for cause, files := range invalid {
169+
for _, p := range files {
170+
_, file := filepath.Split(p)
171+
if file != "tombstone" {
172+
exitWithError(fmt.Errorf("path doesn't contain a valid tombstone filename: %v", p))
173+
}
174+
}
175+
fmt.Println("invalid tombstones:", cause)
176+
printFiles(files, hformat)
177+
if prompt() {
178+
if err = dellAll(files); err != nil {
179+
exitWithError(errors.Wrap(err, "deleting Tombstones"))
180+
}
164181
}
165-
paths = append(paths, b.Dir())
166182
}
167-
printBlocks(blocks, humanReadable)
168-
case []string:
169-
paths = i.([]string)
183+
}
184+
}
170185

171-
for _, p := range paths {
186+
func scanTmps(scanPath string, hformat *bool) {
187+
var files []string
188+
filepath.Walk(scanPath, func(path string, f os.FileInfo, _ error) error {
189+
if filepath.Ext(path) == ".tmp" {
190+
files = append(files, path)
191+
}
192+
return nil
193+
})
194+
if len(files) > 0 {
195+
fmt.Println(`
196+
These are usually caused by a crash or incomplete compaction and
197+
are safe to delete as long as you are sure that no other application is currently using this database.`)
198+
for _, p := range files {
172199
if filepath.Ext(p) != ".tmp" {
173200
exitWithError(fmt.Errorf("path doesn't contain a valid tmp extension: %v", p))
174201
}
175202
}
176-
printTmps(paths, humanReadable)
203+
printFiles(files, hformat)
204+
if prompt() {
205+
if err := dellAll(files); err != nil {
206+
exitWithError(errors.Wrap(err, "deleting Tombstones"))
207+
}
208+
}
177209
}
210+
}
178211

179-
fmt.Printf("DELETE (y/N)? ")
180-
var s string
181-
_, err := fmt.Scanln(&s)
182-
if err != nil {
183-
exitWithError(err)
212+
func dellAll(paths []string) error {
213+
for _, p := range paths {
214+
if err := os.RemoveAll(p); err != nil {
215+
return fmt.Errorf("error deleting: %v, %v", p, err)
216+
}
184217
}
218+
return nil
219+
}
220+
221+
func prompt() bool {
222+
for x := 0; x < 3; x++ {
223+
fmt.Println("DELETE (y/N)?")
224+
var s string
225+
_, err := fmt.Scanln(&s)
226+
if err != nil {
227+
exitWithError(err)
228+
}
185229

186-
s = strings.TrimSpace(s)
187-
s = strings.ToLower(s)
230+
s = strings.TrimSpace(s)
231+
s = strings.ToLower(s)
188232

189-
if s == "y" || s == "yes" {
190-
for _, p := range paths {
191-
if err := os.RemoveAll(p); err != nil {
192-
fmt.Printf("error deleting: %v, %v", p, err)
193-
}
233+
if s == "y" || s == "yes" {
234+
return true
194235
}
195-
return
196-
}
197-
if s == "n" || s == "no" {
198-
return
236+
if s == "n" || s == "no" {
237+
return false
238+
}
239+
fmt.Println(s, "is not a valid answer")
199240
}
200-
fmt.Printf("%v is not a valid answer \n", s)
201-
promptDelete(i, humanReadable)
241+
fmt.Printf("Bailing out, too many invalid answers! \n\n")
242+
return false
202243
}
203244

204245
type writeBenchmark struct {
@@ -479,12 +520,12 @@ func exitWithError(err error) {
479520
os.Exit(1)
480521
}
481522

482-
func printTmps(tmps []string, humanReadable *bool) {
523+
func printFiles(files []string, humanReadable *bool) {
483524
tw := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0)
484525
defer tw.Flush()
485526

486527
fmt.Fprintln(tw, "PATH\tSIZE\tDATE\t")
487-
for _, path := range tmps {
528+
for _, path := range files {
488529
f, e := os.Stat(path)
489530
if e != nil {
490531
exitWithError(e)

repair_test.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package tsdb
22

33
import (
44
"os"
5+
"path/filepath"
56
"reflect"
67
"testing"
78

@@ -53,9 +54,9 @@ func TestRepairBadIndexVersion(t *testing.T) {
5354
t.Fatal("error expected but got none")
5455
}
5556
// Touch chunks dir in block.
56-
os.MkdirAll(dir+"chunks", 0777)
57+
os.MkdirAll(chunkDir(dir), 0777)
5758

58-
r, err := index.NewFileReader(dir + "index")
59+
r, err := index.NewFileReader(filepath.Join(dir, indexFilename))
5960
if err != nil {
6061
t.Fatal(err)
6162
}

0 commit comments

Comments
 (0)