Summary
pkg/compactor tests flake in the test (amd64) / test (arm64) CI jobs (make test, i.e.
go test -race). Across eight PRs opened on 2026-08-19 the package failed 5 times, each time
with a different test, and every failure passed on rerun without a code change.
There are two distinct mechanisms, both of which are test-synchronisation bugs rather than product
bugs. Neither is a new regression — the older one dates to #1942 (2020).
This is the same class as #7565, which fixed two of these this cycle by polling on a better signal,
and is adjacent to #7607 / #7617. Filing separately because the specific tests and lines below are
not covered by those.
Observed failures
All on runs against master (a330338) plus unrelated changes. Notably #7792 touches only
pkg/util/validation and pkg/ruler, and #7790/#7791 are strict subsets of it that both passed —
so nothing in these PRs is implicated.
| PR |
Test |
Assertion |
Package runtime |
| #7788 |
TestPartitionCompactor_ShouldSkipOutOrOrderBlocks |
compactor_paritioning_test.go:1027 counter 0 vs expected 1 |
209s |
| #7788 |
TestCompactor_ShouldIterateOverUsersAndRunCompaction |
compactor_test.go:530: expected 1, got 0 |
327s |
| #7792 |
TestCompactor_ShouldIncrementCompactionErrorIfFailedToCompactASingleTenant |
compactor_test.go:395: expected 1, got 0 |
— |
| #7792 |
TestCompactor_ShouldIterateOverUsersAndRunCompaction |
compactor_test.go:530: expected 1, got 0 |
323s |
| #7792 |
TestCompactor_ShouldIncrementCompactionErrorIfFailedToCompactASingleTenant |
compactor_test.go:395: expected 1, got 0 |
293s |
Locally the package passes 3/3 with -race in isolation, including all of the above. It only
fails as part of ./..., where the package itself takes 209–327s under -race — i.e. the runner is
heavily contended.
Mechanism 1 — a one-second Poll budget for a full compaction run
compactor_test.go:395 and :530 both wait for a compaction run to finish with a 1 second budget:
// Wait until a run has completed.
cortex_testutil.Poll(t, time.Second, 1.0, func() any {
return prom_testutil.ToFloat64(c.CompactionRunsCompleted)
})
expected 1, got 0 is this Poll timing out. One second is not a meaningful budget for starting the
compactor service, discovering users, and completing a run on a contended runner under -race.
The budgets in this package are inconsistent — of 41 Poll calls:
| Budget |
Count |
20*time.Second |
17 |
time.Second / 1*time.Second |
12 |
120*time.Second |
3 |
60*time.Second |
2 |
| others (5s, 10s, 240s, 5000ms) |
7 |
The 1s form originates in #1942 (2020-01-21, "Introduce TSDB blocks compactor") and was copied into
TestCompactor_ShouldIncrementCompactionErrorIfFailedToCompactASingleTenant by #4094 (2021-04-21).
git blame misleadingly points at #7005, which only rewrote interface{} → any.
Mechanism 2 — polling one signal, then asserting on another
compactor_paritioning_test.go:1019-1031 polls for the marker file and then immediately asserts
on the counter:
cortex_testutil.Poll(t, 20*time.Second, true, func() any {
if _, err := os.Stat(path.Join(dir, "no-compact-mark.json")); err == nil {
return true
}
return false
})
assert.NoError(t, prom_testutil.GatherAndCompare(registry, strings.NewReader(`
cortex_compactor_blocks_marked_for_no_compaction_total 1
`), "cortex_compactor_blocks_marked_for_no_compaction_total"))
The counter is incremented separately from the file write, so the file can exist before the counter is
bumped. The 20s budget is fine here; the bug is that the polled signal is not the asserted one. This is
exactly what #7565 fixed elsewhere in this package.
Proposed fixes
- Mechanism 1 — raise the 12 sub-second budgets to the package's prevailing
20*time.Second.
Poll returns as soon as the condition holds, so a larger budget costs nothing on a healthy run
and only buys headroom on a loaded one.
- Mechanism 2 — poll on the value actually asserted (the counter), and keep the file check as a
precondition rather than the sole gate.
- Optionally, add a lint or review check against sub-second
Poll budgets in service-lifecycle tests,
since the pattern keeps getting copied into new tests.
Happy to send a PR for 1 and 2 if that's welcome.
TestCompactor_ShouldIterateOverUsersAndRunCompaction — #7788, run 32320647761
go test -tags "netgo slicelabels" -timeout 30m -race -count 1 ./...
...
--- FAIL: TestCompactor_ShouldIterateOverUsersAndRunCompaction (1.06s)
compactor_test.go:530: expected 1, got 0
FAIL
FAIL github.com/cortexproject/cortex/pkg/compactor 327.440s
TestPartitionCompactor_ShouldSkipOutOrOrderBlocks — #7788, run 32320647761
--- FAIL: TestPartitionCompactor_ShouldSkipOutOrOrderBlocks (1.22s)
compactor_paritioning_test.go:1027:
Error Trace: /__w/cortex/cortex/pkg/compactor/compactor_paritioning_test.go:1027
Error: Received unexpected error:
# HELP cortex_compactor_blocks_marked_for_no_compaction_total Total number of blocks marked for no compact during a compaction run.
# TYPE cortex_compactor_blocks_marked_for_no_compaction_total counter
-cortex_compactor_blocks_marked_for_no_compaction_total 0
+cortex_compactor_blocks_marked_for_no_compaction_total 1
Test: TestPartitionCompactor_ShouldSkipOutOrOrderBlocks
FAIL
FAIL github.com/cortexproject/cortex/pkg/compactor 209.875s
TestCompactor_ShouldIncrementCompactionErrorIfFailedToCompactASingleTenant — #7792, run 32320635278
compactor_test.go:395: expected 1, got 0
--- FAIL: TestCompactor_ShouldIncrementCompactionErrorIfFailedToCompactASingleTenant (1.14s)
FAIL github.com/cortexproject/cortex/pkg/compactor 293.744s
Summary
pkg/compactortests flake in thetest (amd64)/test (arm64)CI jobs (make test, i.e.go test -race). Across eight PRs opened on 2026-08-19 the package failed 5 times, each timewith a different test, and every failure passed on rerun without a code change.
There are two distinct mechanisms, both of which are test-synchronisation bugs rather than product
bugs. Neither is a new regression — the older one dates to #1942 (2020).
This is the same class as #7565, which fixed two of these this cycle by polling on a better signal,
and is adjacent to #7607 / #7617. Filing separately because the specific tests and lines below are
not covered by those.
Observed failures
All on runs against
master(a330338) plus unrelated changes. Notably #7792 touches onlypkg/util/validationandpkg/ruler, and #7790/#7791 are strict subsets of it that both passed —so nothing in these PRs is implicated.
TestPartitionCompactor_ShouldSkipOutOrOrderBlockscompactor_paritioning_test.go:1027counter0vs expected1TestCompactor_ShouldIterateOverUsersAndRunCompactioncompactor_test.go:530: expected 1, got 0TestCompactor_ShouldIncrementCompactionErrorIfFailedToCompactASingleTenantcompactor_test.go:395: expected 1, got 0TestCompactor_ShouldIterateOverUsersAndRunCompactioncompactor_test.go:530: expected 1, got 0TestCompactor_ShouldIncrementCompactionErrorIfFailedToCompactASingleTenantcompactor_test.go:395: expected 1, got 0Locally the package passes 3/3 with
-racein isolation, including all of the above. It onlyfails as part of
./..., where the package itself takes 209–327s under-race— i.e. the runner isheavily contended.
Mechanism 1 — a one-second
Pollbudget for a full compaction runcompactor_test.go:395and:530both wait for a compaction run to finish with a 1 second budget:expected 1, got 0is thisPolltiming out. One second is not a meaningful budget for starting thecompactor service, discovering users, and completing a run on a contended runner under
-race.The budgets in this package are inconsistent — of 41
Pollcalls:20*time.Secondtime.Second/1*time.Second120*time.Second60*time.SecondThe 1s form originates in #1942 (2020-01-21, "Introduce TSDB blocks compactor") and was copied into
TestCompactor_ShouldIncrementCompactionErrorIfFailedToCompactASingleTenantby #4094 (2021-04-21).git blamemisleadingly points at #7005, which only rewroteinterface{}→any.Mechanism 2 — polling one signal, then asserting on another
compactor_paritioning_test.go:1019-1031polls for the marker file and then immediately assertson the counter:
The counter is incremented separately from the file write, so the file can exist before the counter is
bumped. The 20s budget is fine here; the bug is that the polled signal is not the asserted one. This is
exactly what #7565 fixed elsewhere in this package.
Proposed fixes
20*time.Second.Pollreturns as soon as the condition holds, so a larger budget costs nothing on a healthy runand only buys headroom on a loaded one.
precondition rather than the sole gate.
Pollbudgets in service-lifecycle tests,since the pattern keeps getting copied into new tests.
Happy to send a PR for 1 and 2 if that's welcome.
TestCompactor_ShouldIterateOverUsersAndRunCompaction— #7788, run 32320647761TestPartitionCompactor_ShouldSkipOutOrOrderBlocks— #7788, run 32320647761TestCompactor_ShouldIncrementCompactionErrorIfFailedToCompactASingleTenant— #7792, run 32320635278