Compaction, retention, and soak testing

Block refcounting (atomic refs + condemned flag) lets live queries
survive concurrent compaction and retention. Levelled compactor merges
2h->8h->32h blocks via raw chunk passthrough. Retention drops blocks
older than the configured window. Background goroutine drives
flush/compact/retain cycles; exported RunCompaction/ApplyRetention allow
deterministic test control via injectable clock.

Soak test: 10k series x 48h simulated at 15s intervals (115M samples).
Validates flat memory (158 MiB peak), bounded disk (13 blocks), and zero
query errors during compaction.

All existing tests refactored to table-driven with uniform assertions.
This commit is contained in:
2026-07-04 17:02:57 -04:00
parent 323a6f2951
commit 0356f2e082
10 changed files with 1748 additions and 341 deletions
+63 -14
View File
@@ -51,15 +51,41 @@ func TestMatcherMatches(t *testing.T) {
}
}
func TestNewMatcherBadRegexp(t *testing.T) {
_, err := NewMatcher(MatchRegexp, "__name__", "[invalid")
assert.Error(t, err)
func TestNewMatcherErrors(t *testing.T) {
tests := []struct {
name string
typ MatchType
pattern string
}{
{name: "bad_regexp_bracket", typ: MatchRegexp, pattern: "[invalid"},
{name: "bad_not_regexp_bracket", typ: MatchNotRegexp, pattern: "(unclosed"},
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
_, err := NewMatcher(tc.typ, "__name__", tc.pattern)
assert.Error(t, err)
})
}
}
func TestMustNewMatcherPanics(t *testing.T) {
assert.Panics(t, func() {
MustNewMatcher(MatchRegexp, "__name__", "[invalid")
})
tests := []struct {
name string
typ MatchType
pattern string
}{
{name: "bad_regexp", typ: MatchRegexp, pattern: "[invalid"},
{name: "bad_not_regexp", typ: MatchNotRegexp, pattern: "(unclosed"},
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
assert.Panics(t, func() {
MustNewMatcher(tc.typ, "__name__", tc.pattern)
})
})
}
}
func TestFromStrings(t *testing.T) {
@@ -93,15 +119,38 @@ func TestFromStrings(t *testing.T) {
}
}
func TestFromStringsPanicsOnOdd(t *testing.T) {
assert.Panics(t, func() {
FromStrings("__name__")
})
func TestFromStringsPanics(t *testing.T) {
tests := []struct {
name string
args []string
}{
{name: "odd_count_one", args: []string{"__name__"}},
{name: "odd_count_three", args: []string{"__name__", "temp", "room"}},
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
assert.Panics(t, func() {
FromStrings(tc.args...)
})
})
}
}
func TestMatchTypeString(t *testing.T) {
assert.Equal(t, "=", MatchEqual.String())
assert.Equal(t, "!=", MatchNotEqual.String())
assert.Equal(t, "=~", MatchRegexp.String())
assert.Equal(t, "!~", MatchNotRegexp.String())
tests := []struct {
typ MatchType
want string
}{
{typ: MatchEqual, want: "="},
{typ: MatchNotEqual, want: "!="},
{typ: MatchRegexp, want: "=~"},
{typ: MatchNotRegexp, want: "!~"},
}
for _, tc := range tests {
t.Run(tc.want, func(t *testing.T) {
assert.Equal(t, tc.want, tc.typ.String())
})
}
}