Skip to content

Flaky pkg/compactor tests: sub-second Poll budgets and polling a different signal than the assertion #7794

Description

@CharlieTLe

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

  1. 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.
  2. Mechanism 2 — poll on the value actually asserted (the counter), and keep the file check as a
    precondition rather than the sole gate.
  3. 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

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions