|
|
|
@ -160,7 +160,7 @@ func TestNoPanicFor0Tombstones(t *testing.T) {
|
|
|
|
|
}, |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
c, err := NewLeveledCompactor(context.Background(), nil, nil, []int64{50}, nil, nil) |
|
|
|
|
c, err := NewLeveledCompactor(context.Background(), nil, nil, []int64{50}, nil, nil, true) |
|
|
|
|
require.NoError(t, err) |
|
|
|
|
|
|
|
|
|
c.plan(metas) |
|
|
|
@ -174,7 +174,7 @@ func TestLeveledCompactor_plan(t *testing.T) {
|
|
|
|
|
180, |
|
|
|
|
540, |
|
|
|
|
1620, |
|
|
|
|
}, nil, nil) |
|
|
|
|
}, nil, nil, true) |
|
|
|
|
require.NoError(t, err) |
|
|
|
|
|
|
|
|
|
cases := map[string]struct { |
|
|
|
@ -383,7 +383,7 @@ func TestRangeWithFailedCompactionWontGetSelected(t *testing.T) {
|
|
|
|
|
240, |
|
|
|
|
720, |
|
|
|
|
2160, |
|
|
|
|
}, nil, nil) |
|
|
|
|
}, nil, nil, true) |
|
|
|
|
require.NoError(t, err) |
|
|
|
|
|
|
|
|
|
cases := []struct { |
|
|
|
@ -433,7 +433,7 @@ func TestCompactionFailWillCleanUpTempDir(t *testing.T) {
|
|
|
|
|
240, |
|
|
|
|
720, |
|
|
|
|
2160, |
|
|
|
|
}, nil, nil) |
|
|
|
|
}, nil, nil, true) |
|
|
|
|
require.NoError(t, err) |
|
|
|
|
|
|
|
|
|
tmpdir := t.TempDir() |
|
|
|
@ -527,7 +527,7 @@ func TestCompaction_CompactWithSplitting(t *testing.T) {
|
|
|
|
|
|
|
|
|
|
for _, shardCount := range shardCounts { |
|
|
|
|
t.Run(fmt.Sprintf("series=%d, shards=%d", series, shardCount), func(t *testing.T) { |
|
|
|
|
c, err := NewLeveledCompactor(context.Background(), nil, log.NewNopLogger(), []int64{0}, nil, nil) |
|
|
|
|
c, err := NewLeveledCompactor(context.Background(), nil, log.NewNopLogger(), []int64{0}, nil, nil, true) |
|
|
|
|
require.NoError(t, err) |
|
|
|
|
|
|
|
|
|
blockIDs, err := c.CompactWithSplitting(dir, blockDirs, openBlocks, shardCount) |
|
|
|
@ -661,7 +661,7 @@ func TestCompaction_CompactEmptyBlocks(t *testing.T) {
|
|
|
|
|
blockDirs = append(blockDirs, bdir) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
c, err := NewLeveledCompactor(context.Background(), nil, log.NewNopLogger(), []int64{0}, nil, nil) |
|
|
|
|
c, err := NewLeveledCompactor(context.Background(), nil, log.NewNopLogger(), []int64{0}, nil, nil, true) |
|
|
|
|
require.NoError(t, err) |
|
|
|
|
|
|
|
|
|
blockIDs, err := c.CompactWithSplitting(dir, blockDirs, nil, 5) |
|
|
|
@ -1136,7 +1136,7 @@ func TestCompaction_populateBlock(t *testing.T) {
|
|
|
|
|
blocks = append(blocks, &mockBReader{ir: ir, cr: cr, mint: mint, maxt: maxt}) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
c, err := NewLeveledCompactor(context.Background(), nil, nil, []int64{0}, nil, nil) |
|
|
|
|
c, err := NewLeveledCompactor(context.Background(), nil, nil, []int64{0}, nil, nil, true) |
|
|
|
|
require.NoError(t, err) |
|
|
|
|
|
|
|
|
|
meta := &BlockMeta{ |
|
|
|
@ -1258,7 +1258,7 @@ func BenchmarkCompaction(b *testing.B) {
|
|
|
|
|
blockDirs = append(blockDirs, block.Dir()) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
c, err := NewLeveledCompactor(context.Background(), nil, log.NewNopLogger(), []int64{0}, nil, nil) |
|
|
|
|
c, err := NewLeveledCompactor(context.Background(), nil, log.NewNopLogger(), []int64{0}, nil, nil, true) |
|
|
|
|
require.NoError(b, err) |
|
|
|
|
|
|
|
|
|
b.ResetTimer() |
|
|
|
@ -1642,3 +1642,212 @@ func TestCompactBlockMetas(t *testing.T) {
|
|
|
|
|
} |
|
|
|
|
require.Equal(t, expected, output) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func TestLeveledCompactor_plan_overlapping_disabled(t *testing.T) { |
|
|
|
|
// This mimics our default ExponentialBlockRanges with min block size equals to 20.
|
|
|
|
|
compactor, err := NewLeveledCompactor(context.Background(), nil, nil, []int64{ |
|
|
|
|
20, |
|
|
|
|
60, |
|
|
|
|
180, |
|
|
|
|
540, |
|
|
|
|
1620, |
|
|
|
|
}, nil, nil, false) |
|
|
|
|
require.NoError(t, err) |
|
|
|
|
|
|
|
|
|
cases := map[string]struct { |
|
|
|
|
metas []dirMeta |
|
|
|
|
expected []string |
|
|
|
|
}{ |
|
|
|
|
"Outside Range": { |
|
|
|
|
metas: []dirMeta{ |
|
|
|
|
metaRange("1", 0, 20, nil), |
|
|
|
|
}, |
|
|
|
|
expected: nil, |
|
|
|
|
}, |
|
|
|
|
"We should wait for four blocks of size 20 to appear before compacting.": { |
|
|
|
|
metas: []dirMeta{ |
|
|
|
|
metaRange("1", 0, 20, nil), |
|
|
|
|
metaRange("2", 20, 40, nil), |
|
|
|
|
}, |
|
|
|
|
expected: nil, |
|
|
|
|
}, |
|
|
|
|
`We should wait for a next block of size 20 to appear before compacting |
|
|
|
|
the existing ones. We have three, but we ignore the fresh one from WAl`: { |
|
|
|
|
metas: []dirMeta{ |
|
|
|
|
metaRange("1", 0, 20, nil), |
|
|
|
|
metaRange("2", 20, 40, nil), |
|
|
|
|
metaRange("3", 40, 60, nil), |
|
|
|
|
}, |
|
|
|
|
expected: nil, |
|
|
|
|
}, |
|
|
|
|
"Block to fill the entire parent range appeared – should be compacted": { |
|
|
|
|
metas: []dirMeta{ |
|
|
|
|
metaRange("1", 0, 20, nil), |
|
|
|
|
metaRange("2", 20, 40, nil), |
|
|
|
|
metaRange("3", 40, 60, nil), |
|
|
|
|
metaRange("4", 60, 80, nil), |
|
|
|
|
}, |
|
|
|
|
expected: []string{"1", "2", "3"}, |
|
|
|
|
}, |
|
|
|
|
`Block for the next parent range appeared with gap with size 20. Nothing will happen in the first one |
|
|
|
|
anymore but we ignore fresh one still, so no compaction`: { |
|
|
|
|
metas: []dirMeta{ |
|
|
|
|
metaRange("1", 0, 20, nil), |
|
|
|
|
metaRange("2", 20, 40, nil), |
|
|
|
|
metaRange("3", 60, 80, nil), |
|
|
|
|
}, |
|
|
|
|
expected: nil, |
|
|
|
|
}, |
|
|
|
|
`Block for the next parent range appeared, and we have a gap with size 20 between second and third block. |
|
|
|
|
We will not get this missed gap anymore and we should compact just these two.`: { |
|
|
|
|
metas: []dirMeta{ |
|
|
|
|
metaRange("1", 0, 20, nil), |
|
|
|
|
metaRange("2", 20, 40, nil), |
|
|
|
|
metaRange("3", 60, 80, nil), |
|
|
|
|
metaRange("4", 80, 100, nil), |
|
|
|
|
}, |
|
|
|
|
expected: []string{"1", "2"}, |
|
|
|
|
}, |
|
|
|
|
"We have 20, 20, 20, 60, 60 range blocks. '5' is marked as fresh one": { |
|
|
|
|
metas: []dirMeta{ |
|
|
|
|
metaRange("1", 0, 20, nil), |
|
|
|
|
metaRange("2", 20, 40, nil), |
|
|
|
|
metaRange("3", 40, 60, nil), |
|
|
|
|
metaRange("4", 60, 120, nil), |
|
|
|
|
metaRange("5", 120, 180, nil), |
|
|
|
|
}, |
|
|
|
|
expected: []string{"1", "2", "3"}, |
|
|
|
|
}, |
|
|
|
|
"We have 20, 60, 20, 60, 240 range blocks. We can compact 20 + 60 + 60": { |
|
|
|
|
metas: []dirMeta{ |
|
|
|
|
metaRange("2", 20, 40, nil), |
|
|
|
|
metaRange("4", 60, 120, nil), |
|
|
|
|
metaRange("5", 960, 980, nil), // Fresh one.
|
|
|
|
|
metaRange("6", 120, 180, nil), |
|
|
|
|
metaRange("7", 720, 960, nil), |
|
|
|
|
}, |
|
|
|
|
expected: []string{"2", "4", "6"}, |
|
|
|
|
}, |
|
|
|
|
"Do not select large blocks that have many tombstones when there is no fresh block": { |
|
|
|
|
metas: []dirMeta{ |
|
|
|
|
metaRange("1", 0, 540, &BlockStats{ |
|
|
|
|
NumSeries: 10, |
|
|
|
|
NumTombstones: 3, |
|
|
|
|
}), |
|
|
|
|
}, |
|
|
|
|
expected: nil, |
|
|
|
|
}, |
|
|
|
|
"Select large blocks that have many tombstones when fresh appears": { |
|
|
|
|
metas: []dirMeta{ |
|
|
|
|
metaRange("1", 0, 540, &BlockStats{ |
|
|
|
|
NumSeries: 10, |
|
|
|
|
NumTombstones: 3, |
|
|
|
|
}), |
|
|
|
|
metaRange("2", 540, 560, nil), |
|
|
|
|
}, |
|
|
|
|
expected: []string{"1"}, |
|
|
|
|
}, |
|
|
|
|
"For small blocks, do not compact tombstones, even when fresh appears.": { |
|
|
|
|
metas: []dirMeta{ |
|
|
|
|
metaRange("1", 0, 60, &BlockStats{ |
|
|
|
|
NumSeries: 10, |
|
|
|
|
NumTombstones: 3, |
|
|
|
|
}), |
|
|
|
|
metaRange("2", 60, 80, nil), |
|
|
|
|
}, |
|
|
|
|
expected: nil, |
|
|
|
|
}, |
|
|
|
|
`Regression test: we were stuck in a compact loop where we always recompacted |
|
|
|
|
the same block when tombstones and series counts were zero`: { |
|
|
|
|
metas: []dirMeta{ |
|
|
|
|
metaRange("1", 0, 540, &BlockStats{ |
|
|
|
|
NumSeries: 0, |
|
|
|
|
NumTombstones: 0, |
|
|
|
|
}), |
|
|
|
|
metaRange("2", 540, 560, nil), |
|
|
|
|
}, |
|
|
|
|
expected: nil, |
|
|
|
|
}, |
|
|
|
|
`Regression test: we were wrongly assuming that new block is fresh from WAL when its ULID is newest. |
|
|
|
|
We need to actually look on max time instead. |
|
|
|
|
With previous, wrong approach "8" block was ignored, so we were wrongly compacting 5 and 7 and introducing |
|
|
|
|
block overlaps`: { |
|
|
|
|
metas: []dirMeta{ |
|
|
|
|
metaRange("5", 0, 360, nil), |
|
|
|
|
metaRange("6", 540, 560, nil), // Fresh one.
|
|
|
|
|
metaRange("7", 360, 420, nil), |
|
|
|
|
metaRange("8", 420, 540, nil), |
|
|
|
|
}, |
|
|
|
|
expected: []string{"7", "8"}, |
|
|
|
|
}, |
|
|
|
|
// |--------------|
|
|
|
|
|
// |----------------|
|
|
|
|
|
// |--------------|
|
|
|
|
|
"Overlapping blocks 1": { |
|
|
|
|
metas: []dirMeta{ |
|
|
|
|
metaRange("1", 0, 20, nil), |
|
|
|
|
metaRange("2", 19, 40, nil), |
|
|
|
|
metaRange("3", 40, 60, nil), |
|
|
|
|
}, |
|
|
|
|
expected: nil, |
|
|
|
|
}, |
|
|
|
|
// |--------------|
|
|
|
|
|
// |--------------|
|
|
|
|
|
// |--------------|
|
|
|
|
|
"Overlapping blocks 2": { |
|
|
|
|
metas: []dirMeta{ |
|
|
|
|
metaRange("1", 0, 20, nil), |
|
|
|
|
metaRange("2", 20, 40, nil), |
|
|
|
|
metaRange("3", 30, 50, nil), |
|
|
|
|
}, |
|
|
|
|
expected: nil, |
|
|
|
|
}, |
|
|
|
|
// |--------------|
|
|
|
|
|
// |---------------------|
|
|
|
|
|
// |--------------|
|
|
|
|
|
"Overlapping blocks 3": { |
|
|
|
|
metas: []dirMeta{ |
|
|
|
|
metaRange("1", 0, 20, nil), |
|
|
|
|
metaRange("2", 10, 40, nil), |
|
|
|
|
metaRange("3", 30, 50, nil), |
|
|
|
|
}, |
|
|
|
|
expected: nil, |
|
|
|
|
}, |
|
|
|
|
// |--------------|
|
|
|
|
|
// |--------------------------------|
|
|
|
|
|
// |--------------|
|
|
|
|
|
// |--------------|
|
|
|
|
|
"Overlapping blocks 4": { |
|
|
|
|
metas: []dirMeta{ |
|
|
|
|
metaRange("5", 0, 360, nil), |
|
|
|
|
metaRange("6", 340, 560, nil), |
|
|
|
|
metaRange("7", 360, 420, nil), |
|
|
|
|
metaRange("8", 420, 540, nil), |
|
|
|
|
}, |
|
|
|
|
expected: nil, |
|
|
|
|
}, |
|
|
|
|
// |--------------|
|
|
|
|
|
// |--------------|
|
|
|
|
|
// |--------------|
|
|
|
|
|
// |--------------|
|
|
|
|
|
"Overlapping blocks 5": { |
|
|
|
|
metas: []dirMeta{ |
|
|
|
|
metaRange("1", 0, 10, nil), |
|
|
|
|
metaRange("2", 9, 20, nil), |
|
|
|
|
metaRange("3", 30, 40, nil), |
|
|
|
|
metaRange("4", 39, 50, nil), |
|
|
|
|
}, |
|
|
|
|
expected: nil, |
|
|
|
|
}, |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
for title, c := range cases { |
|
|
|
|
if !t.Run(title, func(t *testing.T) { |
|
|
|
|
res, err := compactor.plan(c.metas) |
|
|
|
|
require.NoError(t, err) |
|
|
|
|
require.Equal(t, c.expected, res) |
|
|
|
|
}) { |
|
|
|
|
return |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|