Fix selection of compactable blocks

This commit is contained in:
Fabian Reinartz 2017-08-03 18:33:13 +02:00
parent fa04455cd7
commit 3951d8cc29
3 changed files with 74 additions and 50 deletions

View file

@ -113,7 +113,7 @@ func (b *writeBenchmark) run(cmd *cobra.Command, args []string) {
st, err := tsdb.Open(dir, nil, nil, &tsdb.Options{ st, err := tsdb.Open(dir, nil, nil, &tsdb.Options{
WALFlushInterval: 200 * time.Millisecond, WALFlushInterval: 200 * time.Millisecond,
RetentionDuration: 15 * 24 * 60 * 60 * 1000, // 15 days in milliseconds RetentionDuration: 15 * 24 * 60 * 60 * 1000, // 15 days in milliseconds
BlockRanges: tsdb.ExponentialBlockRanges(3*60*60*1000, 3, 5), BlockRanges: tsdb.ExponentialBlockRanges(2*60*60*1000, 5, 3),
}) })
if err != nil { if err != nil {
exitWithError(err) exitWithError(err)

View file

@ -180,57 +180,50 @@ func (c *compactor) Plan() ([][]string, error) {
return nil, nil return nil, nil
} }
// selectDirs returns the dir metas that should be compacted into a single new block.
// If only a single block range is configured, the result is always nil.
func (c *compactor) selectDirs(ds []dirMeta) []dirMeta { func (c *compactor) selectDirs(ds []dirMeta) []dirMeta {
// The way to skip compaction is to not have blockRanges. if len(c.opts.blockRanges) < 2 || len(ds) < 1 {
if len(c.opts.blockRanges) == 1 {
return nil return nil
} }
return selectRecurse(ds, c.opts.blockRanges) highTime := ds[len(ds)-1].meta.MinTime
}
func selectRecurse(dms []dirMeta, intervals []int64) []dirMeta { for _, iv := range c.opts.blockRanges[1:] {
if len(intervals) == 0 { parts := splitByRange(ds, iv)
return dms if len(parts) == 0 {
} continue
}
// Get the blocks by the max interval for _, p := range parts {
blocks := splitByRange(dms, intervals[len(intervals)-1]) mint := p[0].meta.MinTime
dirs := []dirMeta{} maxt := p[len(p)-1].meta.MaxTime
for i := len(blocks) - 1; i >= 0; i-- { // Pick the range of blocks if it spans the full range (potentially with gaps)
// We need to choose the oldest blocks to compact. If there are a couple of blocks in // or is before the most recent block.
// the largest interval, we should compact those first. // This ensures we don't compact blocks prematurely when another one of the same
if len(blocks[i]) > 1 { // size still fits in the range.
dirs = blocks[i] if (maxt-mint == iv || maxt <= highTime) && len(p) > 1 {
break return p
}
} }
} }
// If there are too many blocks, see if a smaller interval will catch them. return nil
// i.e, if we have 0-20, 60-80, 80-100; all fall under 0-240, but we'd rather compact 60-100
// than all at once.
// Again if have 0-1d, 1d-2d, 3-6d we compact 0-1d, 1d-2d to compact it into the 0-3d block instead of compacting all three
// This is to honor the boundaries as much as possible.
if len(dirs) > 2 {
smallerDirs := selectRecurse(dirs, intervals[:len(intervals)-1])
if len(smallerDirs) > 1 {
return smallerDirs
}
}
return dirs
} }
// splitByRange splits the directories by the time range. // splitByRange splits the directories by the time range. The range sequence starts at 0.
// for example if we have blocks 0-10, 10-20, 50-60, 90-100 and want to split them into 30 interval ranges //
// splitByRange returns [0-10, 10-20], [50-60], [90-100]. // For example, if we have blocks [0-10, 10-20, 50-60, 90-100] and the split range tr is 30
// it returns [0-10, 10-20], [50-60], [90-100].
func splitByRange(ds []dirMeta, tr int64) [][]dirMeta { func splitByRange(ds []dirMeta, tr int64) [][]dirMeta {
var splitDirs [][]dirMeta var splitDirs [][]dirMeta
for i := 0; i < len(ds); { for i := 0; i < len(ds); {
var group []dirMeta var (
var t0 int64 group []dirMeta
m := ds[i].meta t0 int64
m = ds[i].meta
)
// Compute start of aligned time range of size tr closest to the current block's start. // Compute start of aligned time range of size tr closest to the current block's start.
if m.MinTime >= 0 { if m.MinTime >= 0 {
t0 = tr * (m.MinTime / tr) t0 = tr * (m.MinTime / tr)

View file

@ -49,6 +49,22 @@ func TestCompactionSelect(t *testing.T) {
planned: nil, planned: nil,
}, },
{ {
// We should wait for a third block of size 20 to appear before compacting
// the existing ones.
blocks: []dirMetaSimple{
{
dir: "1",
tr: []int64{0, 20},
},
{
dir: "2",
tr: []int64{20, 40},
},
},
planned: nil,
},
{
// Block to fill the entire parent range appeared should be compacted.
blocks: []dirMetaSimple{ blocks: []dirMetaSimple{
{ {
dir: "1", dir: "1",
@ -65,6 +81,25 @@ func TestCompactionSelect(t *testing.T) {
}, },
planned: [][]string{{"1", "2", "3"}}, planned: [][]string{{"1", "2", "3"}},
}, },
{
// Block for the next parent range appeared. Nothing will happen in the first one
// anymore and we should compact it.
blocks: []dirMetaSimple{
{
dir: "1",
tr: []int64{0, 20},
},
{
dir: "2",
tr: []int64{20, 40},
},
{
dir: "3",
tr: []int64{60, 80},
},
},
planned: [][]string{{"1", "2"}},
},
{ {
blocks: []dirMetaSimple{ blocks: []dirMetaSimple{
{ {
@ -92,18 +127,10 @@ func TestCompactionSelect(t *testing.T) {
}, },
{ {
blocks: []dirMetaSimple{ blocks: []dirMetaSimple{
{
dir: "1",
tr: []int64{0, 20},
},
{ {
dir: "2", dir: "2",
tr: []int64{20, 40}, tr: []int64{20, 40},
}, },
{
dir: "3",
tr: []int64{40, 60},
},
{ {
dir: "4", dir: "4",
tr: []int64{60, 120}, tr: []int64{60, 120},
@ -121,24 +148,28 @@ func TestCompactionSelect(t *testing.T) {
tr: []int64{1200, 1440}, tr: []int64{1200, 1440},
}, },
}, },
planned: [][]string{{"6", "7"}}, planned: [][]string{{"2", "4", "5"}},
}, },
{ {
blocks: []dirMetaSimple{ blocks: []dirMetaSimple{
{ {
dir: "1", dir: "1",
tr: []int64{0, 20}, tr: []int64{0, 60},
}, },
{ {
dir: "2", dir: "4",
tr: []int64{60, 80}, tr: []int64{60, 80},
}, },
{ {
dir: "3", dir: "5",
tr: []int64{80, 100}, tr: []int64{80, 100},
}, },
{
dir: "6",
tr: []int64{100, 120},
},
}, },
planned: [][]string{{"2", "3"}}, planned: [][]string{{"4", "5", "6"}},
}, },
} }