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

Commit

Permalink
first draft
Browse files Browse the repository at this point in the history
Signed-off-by: Krasi Georgiev <kgeorgie@redhat.com>
  • Loading branch information
Krasi Georgiev committed May 9, 2018
1 parent 7c89db0 commit 000c29c
Show file tree
Hide file tree
Showing 3 changed files with 148 additions and 11 deletions.
61 changes: 59 additions & 2 deletions cmd/tsdb/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,15 +65,72 @@ func main() {
}
printBlocks(db.Blocks())
case scanCmd.FullCommand():
corrupted, err := tsdb.Scan(*scanPath)
useless, err := tsdb.Scan(*scanPath)
if err != nil {
exitWithError(err)
}
// list corrupted blocks with date ranges and prompt: "Do you want to delete corrupted blocks N,y ?"

if len(useless) == 0 {
fmt.Println("Hooray! The db has no corrupted blocks (or the scan tool is broken).\U0001f638")
return
}

for err, paths := range useless {
fmt.Println(err)
for _, path := range paths {
fmt.Println(path)
}
switch err.(type) {
case tsdb.ErrorTmp:
fmt.Println(`
Scanning found some temporary files!
These are usually caused by a crash or incomplete compaction and
are safe to delete as long as you are sure that no other application is currently using this database folder.`)
case tsdb.ErrorOpen:
fmt.Println(`
Scanning found some blocks that cannot be opened!
Deleting these will allow a clean startup, but you will loose all data in the listed time ranges.`)
case tsdb.ErrorSequence:
fmt.Println(`
Scanning found some overlapping blocks!
Deleting these will allow a clean startup, but you will loose all data in the listed time ranges.`)
}

if yes := prompt(); yes {
for _, path := range paths {
if err := os.Remove(path); err != nil {
fmt.Printf("error deleting: %v, %v", path, err)
}
fmt.Printf("%v \n", path)
}
}
}
}
flag.CommandLine.Set("log.level", "debug")
}

func prompt() bool {
fmt.Printf("Do you want to delete these (y/N)? ")
var s string
_, err := fmt.Scanln(&s)
if err != nil {
exitWithError(err)
}

s = strings.TrimSpace(s)
s = strings.ToLower(s)

if s == "y" || s == "yes" {
return true
}
if s == "n" || s == "no" {
return false
}
fmt.Printf("%v is not a valid answer \n", s)
prompt()
return false
}

type writeBenchmark struct {
outPath string
samplesFile string
Expand Down
13 changes: 4 additions & 9 deletions db.go
Original file line number Diff line number Diff line change
Expand Up @@ -250,11 +250,6 @@ func (db *DB) Dir() string {
return db.dir
}

// Scan checks the integrity of the db for a given directory and returns a list of unreadable blocks that can be deleted to allow a clean startup.
func Scan(dir string) ([]string, error) {
return nil, nil
}

func (db *DB) run() {
defer close(db.donec)

Expand Down Expand Up @@ -528,8 +523,8 @@ func (db *DB) reload(deleteable ...string) (err error) {
exist[meta.ULID] = struct{}{}
}

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

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

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

overlaps := OverlappingBlocks(metas)
if len(overlaps) > 0 {
return errors.Errorf("block time ranges overlap: %s", overlaps)
return overlaps
}

return nil
Expand Down
85 changes: 85 additions & 0 deletions scan.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
package tsdb

import (
"fmt"
"os"
"path/filepath"

"github.com/pkg/errors"
)

type ErrorOpen string

func (e ErrorOpen) Error() string {
return fmt.Sprintf("block open error: %v", e)
}

type ErrorSequence string

func (e ErrorSequence) Error() string {
return fmt.Sprintf("block sequence error %v", e)
}

type ErrorTmp struct{}

func (e ErrorTmp) Error() string {
return fmt.Sprintf("abandoned tmp files")
}

// Scan checks the integrity of the tsdb for a given directory and returns a list of files and folders that can be deleted.
// It returns a map of error to explain the reason and slice of dirs for further processing.
func Scan(dir string) (map[error][]string, error) {
useless := make(map[error][]string)

if _, err := os.Stat(dir); os.IsNotExist(err) {
return nil, err
}

db := &DB{
dir: dir,
logger: nil,
opts: &Options{},
}

dirs, err := blockDirs(db.dir)
if err != nil {
return nil, errors.Wrap(err, "find blocks")
}

// Scan for blocks that can't be opened.
var errOpen ErrorOpen
for _, dir := range dirs {
b, err := OpenBlock(dir, nil)
if err != nil {
errOpen = ErrorOpen(err.Error())
useless[errOpen] = append(useless[errOpen], dir)
break
}

db.blocks = append(db.blocks, b)
}

// Scan for overlaping blocks.
if overlaps := validateBlockSequence(db.blocks); overlaps != nil {
var dirs []string
for _, b := range db.blocks {
dirs = append(dirs, b.Dir())
}
useless[ErrorSequence(overlaps.String())] = dirs
}

// Scan for temporary files.
var tmpFiles []string
filepath.Walk(db.dir, func(path string, f os.FileInfo, _ error) error {
if !f.IsDir() {
if filepath.Ext(path) == ".tmp" {
tmpFiles = append(tmpFiles, path)
}
}
return nil
})
if len(tmpFiles) > 0 {
useless[ErrorTmp{}] = tmpFiles
}
return useless, nil
}

0 comments on commit 000c29c

Please sign in to comment.