diff --git a/CHANGELOG.md b/CHANGELOG.md index 3f86f69e38..a5d3d9f813 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -37,6 +37,7 @@ * [BUGFIX] Compactor: Fix stale `cortex_bucket_index_last_successful_update_timestamp_seconds` metric not being cleaned up when tenant ownership changes due to ring rebalancing. This caused false alarms on bucket index update rate when a tenant moved between compactors. #7485 * [BUGFIX] Security: Fix stored XSS vulnerability in Alertmanager and Store Gateway status pages by replacing `text/template` with `html/template`. #7512 * [BUGFIX] Security: Limit decompressed gzip output in `ParseProtoReader` and OTLP ingestion path. The decompressed body is now capped by `-distributor.otlp-max-recv-msg-size`. #7515 +* [BUGFIX] Compactor: Wait for the blocks cleaner `VisitMarkerManager.HeartBeat` goroutine to exit before returning from per-user cleanup callbacks, preventing a late `MarkWithStatus(Completed)` and `DeleteVisitMarker` write from racing with test teardown and causing `unlinkat: directory not empty` flakes in `TestBlocksCleaner`. #7564 ## 1.21.0 2026-04-24 diff --git a/pkg/compactor/blocks_cleaner.go b/pkg/compactor/blocks_cleaner.go index e7633c9d67..508a34432e 100644 --- a/pkg/compactor/blocks_cleaner.go +++ b/pkg/compactor/blocks_cleaner.go @@ -357,9 +357,15 @@ func (c *BlocksCleaner) cleanUpActiveUsers(ctx context.Context, users []string, return nil } errChan := make(chan error, 1) - go visitMarkerManager.HeartBeat(ctx, errChan, c.cleanerVisitMarkerFileUpdateInterval, true) + var hbWG sync.WaitGroup + hbWG.Add(1) + go func() { + defer hbWG.Done() + visitMarkerManager.HeartBeat(ctx, errChan, c.cleanerVisitMarkerFileUpdateInterval, true) + }() defer func() { errChan <- nil + hbWG.Wait() }() return errors.Wrapf(c.cleanUser(ctx, userLogger, userBucket, userID, firstRun), "failed to delete blocks for user: %s", userID) }) @@ -392,9 +398,15 @@ func (c *BlocksCleaner) cleanDeletedUsers(ctx context.Context, users []string) e return nil } errChan := make(chan error, 1) - go visitMarkerManager.HeartBeat(ctx, errChan, c.cleanerVisitMarkerFileUpdateInterval, true) + var hbWG sync.WaitGroup + hbWG.Add(1) + go func() { + defer hbWG.Done() + visitMarkerManager.HeartBeat(ctx, errChan, c.cleanerVisitMarkerFileUpdateInterval, true) + }() defer func() { errChan <- nil + hbWG.Wait() }() return errors.Wrapf(c.deleteUserMarkedForDeletion(ctx, userLogger, userBucket, userID), "failed to delete user marked for deletion: %s", userID) })