@@ -22,6 +22,7 @@ import (
2222 "github.com/oklog/ulid/v2"
2323 "github.com/pkg/errors"
2424 "github.com/prometheus/client_golang/prometheus"
25+ "github.com/prometheus/client_golang/prometheus/promauto"
2526 prom_testutil "github.com/prometheus/client_golang/prometheus/testutil"
2627 "github.com/prometheus/prometheus/model/labels"
2728 "github.com/prometheus/prometheus/tsdb"
@@ -2449,3 +2450,127 @@ func TestCompactor_UserIndexUpdateLoop(t *testing.T) {
24492450 }
24502451 }
24512452}
2453+
2454+ func TestCompactor_ShouldRespectConcurrencyLimitAcrossCompactionLevels (t * testing.T ) {
2455+ block0hto2hUlid := ulid .MustNew (301 , nil )
2456+ block2hto4hUlid := ulid .MustNew (302 , nil )
2457+ block4hto6hUlid := ulid .MustNew (303 , nil )
2458+ block6hto8hUlid := ulid .MustNew (304 , nil )
2459+ block8hto10hUlid := ulid .MustNew (305 , nil )
2460+ block10hto12hUlid := ulid .MustNew (306 , nil )
2461+ block24hto36hUlid := ulid .MustNew (307 , nil )
2462+ block36hto48hUlid := ulid .MustNew (308 , nil )
2463+
2464+ h := time .Hour .Milliseconds ()
2465+
2466+ blocks := map [ulid.ULID ]* metadata.Meta {
2467+ block0hto2hUlid : {
2468+ BlockMeta : tsdb.BlockMeta {ULID : block0hto2hUlid , MinTime : 0 , MaxTime : 2 * h },
2469+ Thanos : metadata.Thanos {Labels : map [string ]string {"external" : "1" }},
2470+ },
2471+ block2hto4hUlid : {
2472+ BlockMeta : tsdb.BlockMeta {ULID : block2hto4hUlid , MinTime : 2 * h , MaxTime : 4 * h },
2473+ Thanos : metadata.Thanos {Labels : map [string ]string {"external" : "1" }},
2474+ },
2475+ block4hto6hUlid : {
2476+ BlockMeta : tsdb.BlockMeta {ULID : block4hto6hUlid , MinTime : 4 * h , MaxTime : 6 * h },
2477+ Thanos : metadata.Thanos {Labels : map [string ]string {"external" : "1" }},
2478+ },
2479+ block6hto8hUlid : {
2480+ BlockMeta : tsdb.BlockMeta {ULID : block6hto8hUlid , MinTime : 6 * h , MaxTime : 8 * h },
2481+ Thanos : metadata.Thanos {Labels : map [string ]string {"external" : "1" }},
2482+ },
2483+ block8hto10hUlid : {
2484+ BlockMeta : tsdb.BlockMeta {ULID : block8hto10hUlid , MinTime : 8 * h , MaxTime : 10 * h },
2485+ Thanos : metadata.Thanos {Labels : map [string ]string {"external" : "1" }},
2486+ },
2487+ block10hto12hUlid : {
2488+ BlockMeta : tsdb.BlockMeta {ULID : block10hto12hUlid , MinTime : 10 * h , MaxTime : 12 * h },
2489+ Thanos : metadata.Thanos {Labels : map [string ]string {"external" : "1" }},
2490+ },
2491+ block24hto36hUlid : {
2492+ BlockMeta : tsdb.BlockMeta {ULID : block24hto36hUlid , MinTime : 24 * h , MaxTime : 36 * h },
2493+ Thanos : metadata.Thanos {Labels : map [string ]string {"external" : "1" }},
2494+ },
2495+ block36hto48hUlid : {
2496+ BlockMeta : tsdb.BlockMeta {ULID : block36hto48hUlid , MinTime : 36 * h , MaxTime : 48 * h },
2497+ Thanos : metadata.Thanos {Labels : map [string ]string {"external" : "1" }},
2498+ },
2499+ }
2500+
2501+ compactorCfg := & Config {
2502+ BlockRanges : cortex_tsdb.DurationList {2 * time .Hour , 12 * time .Hour , 24 * time .Hour },
2503+ }
2504+
2505+ limits := & validation.Limits {}
2506+ overrides := validation .NewOverrides (* limits , nil )
2507+
2508+ rs := ring.ReplicationSet {
2509+ Instances : []ring.InstanceDesc {
2510+ {Addr : "test-addr" },
2511+ },
2512+ }
2513+ subring := & ring.RingMock {}
2514+ subring .On ("GetAllHealthy" , mock .Anything ).Return (rs , nil )
2515+
2516+ r := & ring.RingMock {}
2517+ r .On ("ShuffleShard" , mock .Anything , mock .Anything ).Return (subring , nil )
2518+
2519+ registerer := prometheus .NewPedanticRegistry ()
2520+ blockVisitMarkerReadFailed := promauto .With (registerer ).NewCounter (prometheus.CounterOpts {
2521+ Name : "cortex_compactor_block_visit_marker_read_failed" ,
2522+ })
2523+ blockVisitMarkerWriteFailed := promauto .With (registerer ).NewCounter (prometheus.CounterOpts {
2524+ Name : "cortex_compactor_block_visit_marker_write_failed" ,
2525+ })
2526+
2527+ bkt := & bucket.ClientMock {}
2528+ bkt .MockUpload (mock .Anything , nil )
2529+ bkt .MockGet (mock .Anything , "" , nil )
2530+
2531+ metrics := newCompactorMetrics (registerer )
2532+
2533+ noCompactFilter := func () map [ulid.ULID ]* metadata.NoCompactMark {
2534+ return nil
2535+ }
2536+
2537+ ctx := t .Context ()
2538+ g := NewShuffleShardingGrouper (
2539+ ctx ,
2540+ nil ,
2541+ objstore .WithNoopInstr (bkt ),
2542+ false ,
2543+ true ,
2544+ nil ,
2545+ metadata .NoneFunc ,
2546+ metrics .getSyncerMetrics ("test-user" ),
2547+ metrics ,
2548+ * compactorCfg ,
2549+ r ,
2550+ "test-addr" ,
2551+ "test-compactor" ,
2552+ overrides ,
2553+ "test-user" ,
2554+ 10 ,
2555+ 3 ,
2556+ 1 , // concurrency = 1
2557+ 5 * time .Minute ,
2558+ blockVisitMarkerReadFailed ,
2559+ blockVisitMarkerWriteFailed ,
2560+ noCompactFilter ,
2561+ )
2562+
2563+ totalGroupsAcrossCalls := 0
2564+ for i := 0 ; i < 5 ; i ++ {
2565+ result , err := g .Groups (blocks )
2566+ require .NoError (t , err )
2567+ totalGroupsAcrossCalls += len (result )
2568+ if len (result ) == 0 {
2569+ break
2570+ }
2571+ }
2572+
2573+ require .Equal (t , 1 , totalGroupsAcrossCalls ,
2574+ "with concurrency=1, the grouper should return at most 1 group total across multiple Groups() calls, " +
2575+ "but got %d (bug: BucketCompactor loop causes cascading compactions at different levels)" , totalGroupsAcrossCalls )
2576+ }
0 commit comments