@@ -12,7 +12,6 @@ import (
1212 "testing"
1313 "time"
1414
15- "github.com/efficientgo/core/testutil"
1615 "github.com/go-kit/log"
1716 "github.com/oklog/ulid"
1817 "github.com/prometheus/client_golang/prometheus"
@@ -21,6 +20,8 @@ import (
2120 "github.com/prometheus/prometheus/tsdb"
2221 "github.com/thanos-io/objstore"
2322
23+ "github.com/efficientgo/core/testutil"
24+
2425 "github.com/thanos-io/thanos/pkg/block"
2526 "github.com/thanos-io/thanos/pkg/block/metadata"
2627 "github.com/thanos-io/thanos/pkg/compact"
@@ -281,6 +282,31 @@ func TestApplyRetentionPolicyByResolution(t *testing.T) {
281282 }
282283}
283284
285+ func uploadMockBlock (t * testing.T , bkt objstore.Bucket , id string , minTime , maxTime time.Time , resolutionLevel int64 ) {
286+ t .Helper ()
287+ meta1 := metadata.Meta {
288+ BlockMeta : tsdb.BlockMeta {
289+ ULID : ulid .MustParse (id ),
290+ MinTime : minTime .Unix () * 1000 ,
291+ MaxTime : maxTime .Unix () * 1000 ,
292+ Version : 1 ,
293+ },
294+ Thanos : metadata.Thanos {
295+ Downsample : metadata.ThanosDownsample {
296+ Resolution : resolutionLevel ,
297+ },
298+ },
299+ }
300+
301+ b , err := json .Marshal (meta1 )
302+ testutil .Ok (t , err )
303+
304+ testutil .Ok (t , bkt .Upload (context .Background (), id + "/meta.json" , bytes .NewReader (b )))
305+ testutil .Ok (t , bkt .Upload (context .Background (), id + "/chunks/000001" , strings .NewReader ("@test-data@" )))
306+ testutil .Ok (t , bkt .Upload (context .Background (), id + "/chunks/000002" , strings .NewReader ("@test-data@" )))
307+ testutil .Ok (t , bkt .Upload (context .Background (), id + "/chunks/000003" , strings .NewReader ("@test-data@" )))
308+ }
309+
284310func TestParseRetentionPolicyByTenant (t * testing.T ) {
285311 t .Parallel ()
286312
@@ -298,15 +324,15 @@ func TestParseRetentionPolicyByTenant(t *testing.T) {
298324 },
299325 {
300326 "valid" ,
301- []string {"tenant-1:2021-01-01" , "tenant-2:3d " },
327+ []string {"tenant-1:2021-01-01" , "tenant-2:11d " },
302328 map [string ]compact.RetentionPolicy {
303329 "tenant-1" : {
304330 CutoffDate : time .Date (2021 , 1 , 1 , 0 , 0 , 0 , 0 , time .UTC ),
305331 RetentionDuration : time .Duration (0 ),
306332 },
307333 "tenant-2" : {
308334 CutoffDate : time.Time {},
309- RetentionDuration : 3 * 24 * time .Hour ,
335+ RetentionDuration : 11 * 24 * time .Hour ,
310336 },
311337 },
312338 false ,
@@ -329,6 +355,12 @@ func TestParseRetentionPolicyByTenant(t *testing.T) {
329355 nil ,
330356 true ,
331357 },
358+ {
359+ "invalid duration which is 0" ,
360+ []string {"tenant1:2021-01-01" , "tenant2:0d" },
361+ nil ,
362+ true ,
363+ },
332364 } {
333365 t .Run (tt .name , func (t * testing.T ) {
334366 got , err := compact .ParesRetentionPolicyByTenant (log .NewNopLogger (), tt .retentionTenants )
@@ -341,7 +373,179 @@ func TestParseRetentionPolicyByTenant(t *testing.T) {
341373 }
342374}
343375
344- func uploadMockBlock (t * testing.T , bkt objstore.Bucket , id string , minTime , maxTime time.Time , resolutionLevel int64 ) {
376+ func TestApplyRetentionPolicyByTenant (t * testing.T ) {
377+ t .Parallel ()
378+
379+ type testBlock struct {
380+ id , tenant string
381+ minTime time.Time
382+ maxTime time.Time
383+ }
384+
385+ logger := log .NewNopLogger ()
386+ ctx := context .TODO ()
387+
388+ for _ , tt := range []struct {
389+ name string
390+ blocks []testBlock
391+ retentionByTenant map [string ]compact.RetentionPolicy
392+ want []string
393+ wantErr bool
394+ }{
395+ {
396+ "empty bucket" ,
397+ []testBlock {},
398+ map [string ]compact.RetentionPolicy {},
399+ []string {},
400+ false ,
401+ },
402+ {
403+ "tenant retention disabled" ,
404+ []testBlock {
405+ {
406+ "01CPHBEX20729MJQZXE3W0BW48" ,
407+ "tenant-1" ,
408+ time .Now ().Add (- 3 * 24 * time .Hour ),
409+ time .Now ().Add (- 2 * 24 * time .Hour ),
410+ },
411+ {
412+ "01CPHBEX20729MJQZXE3W0BW49" ,
413+ "tenant-2" ,
414+ time .Now ().Add (- 3 * 24 * time .Hour ),
415+ time .Now ().Add (- 2 * 24 * time .Hour ),
416+ },
417+ },
418+ map [string ]compact.RetentionPolicy {},
419+ []string {
420+ "01CPHBEX20729MJQZXE3W0BW48/" ,
421+ "01CPHBEX20729MJQZXE3W0BW49/" ,
422+ },
423+ false ,
424+ },
425+ {
426+ "tenant retention with duration" ,
427+ []testBlock {
428+ {
429+ "01CPHBEX20729MJQZXE3W0BW48" ,
430+ "tenant-1" ,
431+ time .Now ().Add (- 3 * 24 * time .Hour ),
432+ time .Now ().Add (- 2 * 24 * time .Hour ),
433+ },
434+ {
435+ "01CPHBEX20729MJQZXE3W0BW49" ,
436+ "tenant-1" ,
437+ time .Now ().Add (- 2 * 24 * time .Hour ),
438+ time .Now ().Add (- 24 * time .Hour ),
439+ },
440+ {
441+ "01CPHBEX20729MJQZXE3W0BW50" ,
442+ "tenant-2" ,
443+ time .Now ().Add (- 24 * time .Hour ),
444+ time .Now ().Add (- 23 * time .Hour ),
445+ },
446+ {
447+ "01CPHBEX20729MJQZXE3W0BW51" ,
448+ "tenant-2" ,
449+ time .Now ().Add (- 23 * time .Hour ),
450+ time .Now ().Add (- 6 * time .Hour ),
451+ },
452+ },
453+ map [string ]compact.RetentionPolicy {
454+ "tenant-2" : {
455+ CutoffDate : time.Time {},
456+ RetentionDuration : 10 * time .Hour ,
457+ },
458+ },
459+ []string {
460+ "01CPHBEX20729MJQZXE3W0BW48/" ,
461+ "01CPHBEX20729MJQZXE3W0BW49/" ,
462+ "01CPHBEX20729MJQZXE3W0BW51/" ,
463+ },
464+ false ,
465+ },
466+ {
467+ "tenant retention with cutoff date" ,
468+ []testBlock {
469+ {
470+ "01CPHBEX20729MJQZXE3W0BW48" ,
471+ "tenant-1" ,
472+ time .Now ().Add (- 3 * 24 * time .Hour ),
473+ time .Now ().Add (- 2 * 24 * time .Hour ),
474+ },
475+ {
476+ "01CPHBEX20729MJQZXE3W0BW49" ,
477+ "tenant-1" ,
478+ time .Now ().Add (- 2 * 24 * time .Hour ),
479+ time .Now ().Add (- 24 * time .Hour ),
480+ },
481+ {
482+ "01CPHBEX20729MJQZXE3W0BW50" ,
483+ "tenant-2" ,
484+ time .Date (2024 , 11 , 1 , 0 , 0 , 0 , 0 , time .UTC ),
485+ time .Date (2024 , 11 , 1 , 0 , 0 , 0 , 0 , time .UTC ),
486+ },
487+ {
488+ "01CPHBEX20729MJQZXE3W0BW51" ,
489+ "tenant-2" ,
490+ time .Date (2024 , 1 , 1 , 0 , 0 , 0 , 0 , time .UTC ),
491+ time .Date (2024 , 1 , 1 , 0 , 0 , 0 , 0 , time .UTC ),
492+ },
493+ },
494+ map [string ]compact.RetentionPolicy {
495+ "tenant-2" : {
496+ CutoffDate : time .Date (2024 , 10 , 1 , 0 , 0 , 0 , 0 , time .UTC ),
497+ RetentionDuration : 0 ,
498+ },
499+ },
500+ []string {
501+ "01CPHBEX20729MJQZXE3W0BW48/" ,
502+ "01CPHBEX20729MJQZXE3W0BW49/" ,
503+ "01CPHBEX20729MJQZXE3W0BW50/" ,
504+ },
505+ false ,
506+ },
507+ } {
508+ t .Run (tt .name , func (t * testing.T ) {
509+ bkt := objstore .WithNoopInstr (objstore .NewInMemBucket ())
510+ for _ , b := range tt .blocks {
511+ uploadTenantBlock (t , bkt , b .id , b .tenant , b .minTime , b .maxTime )
512+ }
513+
514+ baseBlockIDsFetcher := block .NewConcurrentLister (logger , bkt )
515+ metaFetcher , err := block .NewMetaFetcher (logger , 32 , bkt , baseBlockIDsFetcher , "" , nil , nil )
516+ testutil .Ok (t , err )
517+
518+ blocksMarkedForDeletion := promauto .With (nil ).NewCounter (prometheus.CounterOpts {})
519+
520+ metas , _ , err := metaFetcher .Fetch (ctx )
521+ testutil .Ok (t , err )
522+
523+ if err := compact .ApplyRetentionPolicyByTenant (ctx , logger , bkt , metas , tt .retentionByTenant , blocksMarkedForDeletion ); (err != nil ) != tt .wantErr {
524+ t .Errorf ("ApplyRetentionPolicyByResolution() error = %v, wantErr %v" , err , tt .wantErr )
525+ }
526+
527+ got := []string {}
528+ gotMarkedBlocksCount := 0.0
529+ testutil .Ok (t , bkt .Iter (context .TODO (), "" , func (name string ) error {
530+ exists , err := bkt .Exists (ctx , filepath .Join (name , metadata .DeletionMarkFilename ))
531+ if err != nil {
532+ return err
533+ }
534+ if ! exists {
535+ got = append (got , name )
536+ return nil
537+ }
538+ gotMarkedBlocksCount += 1.0
539+ return nil
540+ }))
541+
542+ testutil .Equals (t , got , tt .want )
543+ testutil .Equals (t , gotMarkedBlocksCount , promtest .ToFloat64 (blocksMarkedForDeletion ))
544+ })
545+ }
546+ }
547+
548+ func uploadTenantBlock (t * testing.T , bkt objstore.Bucket , id , tenant string , minTime , maxTime time.Time ) {
345549 t .Helper ()
346550 meta1 := metadata.Meta {
347551 BlockMeta : tsdb.BlockMeta {
@@ -351,8 +555,8 @@ func uploadMockBlock(t *testing.T, bkt objstore.Bucket, id string, minTime, maxT
351555 Version : 1 ,
352556 },
353557 Thanos : metadata.Thanos {
354- Downsample : metadata. ThanosDownsample {
355- Resolution : resolutionLevel ,
558+ Labels : map [ string ] string {
559+ metadata . TenantLabel : tenant ,
356560 },
357561 },
358562 }
0 commit comments