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

Commit 54bfe94

Browse files
committed
Better testability of analyzeBlock command, start tests
- Factor out `extractBlock()` to retrieve specific block from db.Blocks() - Write tests for said function - Simple, non-output test for `analyzeBlock()` command
1 parent 92f160d commit 54bfe94

File tree

2 files changed

+114
-30
lines changed

2 files changed

+114
-30
lines changed

cmd/tsdb/main.go

Lines changed: 40 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ import (
4141

4242
const (
4343
printBlocksTableHeader = "BLOCK ULID\tMIN TIME\tMAX TIME\tNUM SAMPLES\tNUM CHUNKS\tNUM SERIES"
44+
defaultAnalyzeLimit = "20"
4445
)
4546

4647
func main() {
@@ -66,7 +67,7 @@ func execute() (err error) {
6667
analyzeCmd = cli.Command("analyze", "analyze churn, label pair cardinality.")
6768
analyzePath = analyzeCmd.Arg("db path", "database path (default is "+defaultDBPath+")").Default(defaultDBPath).String()
6869
analyzeBlockID = analyzeCmd.Arg("block id", "block to analyze (default is the last block)").String()
69-
analyzeLimit = analyzeCmd.Flag("limit", "how many items to show in each list").Default("20").Int()
70+
analyzeLimit = analyzeCmd.Flag("limit", "how many items to show in each list").Default(defaultAnalyzeLimit).Int()
7071
dumpCmd = cli.Command("dump", "dump samples from a TSDB")
7172
dumpPath = dumpCmd.Arg("db path", "database path (default is "+defaultDBPath+")").Default(defaultDBPath).String()
7273
dumpMinTime = dumpCmd.Flag("min-time", "minimum timestamp to dump").Default(strconv.FormatInt(math.MinInt64, 10)).Int64()
@@ -114,21 +115,12 @@ func execute() (err error) {
114115
if err != nil {
115116
return err
116117
}
117-
var block tsdb.BlockReader
118-
if *analyzeBlockID != "" {
119-
for _, b := range blocks {
120-
if b.Meta().ULID.String() == *analyzeBlockID {
121-
block = b
122-
break
123-
}
124-
}
125-
} else if len(blocks) > 0 {
126-
block = blocks[len(blocks)-1]
127-
}
128-
if block == nil {
129-
return fmt.Errorf("block not found")
118+
block, err := extractBlock(blocks, analyzeBlockID)
119+
if err != nil {
120+
return err
130121
}
131-
return analyzeBlock(block, *analyzeLimit)
122+
123+
return analyzeBlock(os.Stdout, block, *analyzeLimit)
132124
case dumpCmd.FullCommand():
133125
db, err := tsdb.OpenDBReadOnly(*dumpPath, nil)
134126
if err != nil {
@@ -144,6 +136,25 @@ func execute() (err error) {
144136
return nil
145137
}
146138

139+
// extractBlock takes a slice of BlockReader and returns a specific block by ID.
140+
func extractBlock(blocks []tsdb.BlockReader, analyzeBlockID *string) (tsdb.BlockReader, error) {
141+
var block tsdb.BlockReader
142+
if *analyzeBlockID != "" {
143+
for _, b := range blocks {
144+
if b.Meta().ULID.String() == *analyzeBlockID {
145+
block = b
146+
break
147+
}
148+
}
149+
} else if len(blocks) > 0 {
150+
block = blocks[len(blocks)-1]
151+
}
152+
if block == nil {
153+
return nil, fmt.Errorf("block not found")
154+
}
155+
return block, nil
156+
}
157+
147158
type writeBenchmark struct {
148159
outPath string
149160
samplesFile string
@@ -465,12 +476,12 @@ func getFormatedTime(timestamp int64, humanReadable *bool) string {
465476
return strconv.FormatInt(timestamp, 10)
466477
}
467478

468-
func analyzeBlock(b tsdb.BlockReader, limit int) error {
479+
func analyzeBlock(w io.Writer, b tsdb.BlockReader, limit int) error {
469480
meta := b.Meta()
470-
fmt.Printf("Block ID: %s\n", meta.ULID)
481+
fmt.Fprintf(w, "Block ID: %s\n", meta.ULID)
471482
// Presume 1ms resolution that Prometheus uses.
472-
fmt.Printf("Duration: %s\n", (time.Duration(meta.MaxTime-meta.MinTime) * 1e6).String())
473-
fmt.Printf("Series: %d\n", meta.Stats.NumSeries)
483+
fmt.Fprintf(w, "Duration: %s\n", (time.Duration(meta.MaxTime-meta.MinTime) * 1e6).String())
484+
fmt.Fprintf(w, "Series: %d\n", meta.Stats.NumSeries)
474485
ir, err := b.Index()
475486
if err != nil {
476487
return err
@@ -481,7 +492,7 @@ func analyzeBlock(b tsdb.BlockReader, limit int) error {
481492
if err != nil {
482493
return err
483494
}
484-
fmt.Printf("Label names: %d\n", len(allLabelNames))
495+
fmt.Fprintf(w, "Label names: %d\n", len(allLabelNames))
485496

486497
type postingInfo struct {
487498
key string
@@ -493,7 +504,7 @@ func analyzeBlock(b tsdb.BlockReader, limit int) error {
493504
sort.Slice(postingInfos, func(i, j int) bool { return postingInfos[i].metric > postingInfos[j].metric })
494505

495506
for i, pc := range postingInfos {
496-
fmt.Printf("%d %s\n", pc.metric, pc.key)
507+
fmt.Fprintf(w, "%d %s\n", pc.metric, pc.key)
497508
if i >= limit {
498509
break
499510
}
@@ -527,31 +538,31 @@ func analyzeBlock(b tsdb.BlockReader, limit int) error {
527538
if p.Err() != nil {
528539
return p.Err()
529540
}
530-
fmt.Printf("Postings (unique label pairs): %d\n", len(labelpairsUncovered))
531-
fmt.Printf("Postings entries (total label pairs): %d\n", entries)
541+
fmt.Fprintf(w, "Postings (unique label pairs): %d\n", len(labelpairsUncovered))
542+
fmt.Fprintf(w, "Postings entries (total label pairs): %d\n", entries)
532543

533544
postingInfos = postingInfos[:0]
534545
for k, m := range labelpairsUncovered {
535546
postingInfos = append(postingInfos, postingInfo{k, uint64(float64(m) / float64(meta.MaxTime-meta.MinTime))})
536547
}
537548

538-
fmt.Printf("\nLabel pairs most involved in churning:\n")
549+
fmt.Fprintf(w, "\nLabel pairs most involved in churning:\n")
539550
printInfo(postingInfos)
540551

541552
postingInfos = postingInfos[:0]
542553
for k, m := range labelsUncovered {
543554
postingInfos = append(postingInfos, postingInfo{k, uint64(float64(m) / float64(meta.MaxTime-meta.MinTime))})
544555
}
545556

546-
fmt.Printf("\nLabel names most involved in churning:\n")
557+
fmt.Fprintf(w, "\nLabel names most involved in churning:\n")
547558
printInfo(postingInfos)
548559

549560
postingInfos = postingInfos[:0]
550561
for k, m := range labelpairsCount {
551562
postingInfos = append(postingInfos, postingInfo{k, m})
552563
}
553564

554-
fmt.Printf("\nMost common label pairs:\n")
565+
fmt.Fprintf(w, "\nMost common label pairs:\n")
555566
printInfo(postingInfos)
556567

557568
postingInfos = postingInfos[:0]
@@ -575,7 +586,7 @@ func analyzeBlock(b tsdb.BlockReader, limit int) error {
575586
postingInfos = append(postingInfos, postingInfo{n, cumulativeLength})
576587
}
577588

578-
fmt.Printf("\nLabel names with highest cumulative label value length:\n")
589+
fmt.Fprintf(w, "\nLabel names with highest cumulative label value length:\n")
579590
printInfo(postingInfos)
580591

581592
postingInfos = postingInfos[:0]
@@ -586,7 +597,7 @@ func analyzeBlock(b tsdb.BlockReader, limit int) error {
586597
}
587598
postingInfos = append(postingInfos, postingInfo{n, uint64(lv.Len())})
588599
}
589-
fmt.Printf("\nHighest cardinality labels:\n")
600+
fmt.Fprintf(w, "\nHighest cardinality labels:\n")
590601
printInfo(postingInfos)
591602

592603
postingInfos = postingInfos[:0]
@@ -614,7 +625,7 @@ func analyzeBlock(b tsdb.BlockReader, limit int) error {
614625
postingInfos = append(postingInfos, postingInfo{n, uint64(count)})
615626
}
616627
}
617-
fmt.Printf("\nHighest cardinality metric names:\n")
628+
fmt.Fprintf(w, "\nHighest cardinality metric names:\n")
618629
printInfo(postingInfos)
619630
return nil
620631
}

cmd/tsdb/main_test.go

Lines changed: 74 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import (
1818
"fmt"
1919
"io/ioutil"
2020
"os"
21+
"strconv"
2122
"strings"
2223
"testing"
2324
"text/tabwriter"
@@ -92,11 +93,12 @@ func TestPrintBlocks(t *testing.T) {
9293
}
9394

9495
// Test table contents.
95-
var actualStdout bytes.Buffer
9696
blocks, err = db.Blocks()
9797
if err != nil {
9898
t.Error(err)
9999
}
100+
101+
var actualStdout bytes.Buffer
100102
printBlocks(&actualStdout, blocks, &hr)
101103

102104
actual = actualStdout.String()
@@ -113,3 +115,74 @@ func TestPrintBlocks(t *testing.T) {
113115
t.Errorf("expected (%#v) != actual (%#v)", expected, actual)
114116
}
115117
}
118+
119+
func TestExtractBlock(t *testing.T) {
120+
db, closeFn := createRoDb(t)
121+
defer closeFn()
122+
123+
blocks, err := db.Blocks()
124+
if err != nil {
125+
t.Error(err)
126+
}
127+
128+
var analyzeBlockID string
129+
130+
// Pass: analyze last block (default).
131+
block, err := extractBlock(blocks, &analyzeBlockID)
132+
if err != nil {
133+
t.Error(err)
134+
}
135+
if block == nil {
136+
t.Error("block shouldn't be nil")
137+
}
138+
139+
// Pass: analyze specific block.
140+
analyzeBlockID = block.Meta().ULID.String()
141+
block, err = extractBlock(blocks, &analyzeBlockID)
142+
if err != nil {
143+
t.Error(err)
144+
}
145+
if block == nil {
146+
t.Error("block shouldn't be nil")
147+
}
148+
149+
// Fail: analyze non-existing block
150+
analyzeBlockID = "foo"
151+
block, err = extractBlock(blocks, &analyzeBlockID)
152+
if err == nil {
153+
t.Errorf("Analyzing block %q should throw error", analyzeBlockID)
154+
}
155+
if block != nil {
156+
t.Error("block should be nil")
157+
}
158+
}
159+
160+
func TestAnalyzeBlocks(t *testing.T) {
161+
db, closeFn := createRoDb(t)
162+
defer closeFn()
163+
164+
blocks, err := db.Blocks()
165+
if err != nil {
166+
t.Error(err)
167+
}
168+
169+
var analyzeBlockID string
170+
block, err := extractBlock(blocks, &analyzeBlockID)
171+
if err != nil {
172+
t.Error(err)
173+
}
174+
if block == nil {
175+
t.Errorf("block shouldn't be nil")
176+
}
177+
178+
dal, err := strconv.Atoi(defaultAnalyzeLimit)
179+
if err != nil {
180+
t.Error(err)
181+
}
182+
183+
var actual bytes.Buffer
184+
err = analyzeBlock(&actual, block, dal)
185+
if err != nil {
186+
t.Error(err)
187+
}
188+
}

0 commit comments

Comments
 (0)