github.com/grafana/pyroscope@v1.18.0/pkg/compactor/job_sorting_test.go (about) 1 // SPDX-License-Identifier: AGPL-3.0-only 2 // Provenance-includes-location: https://github.com/grafana/mimir/blob/main/pkg/compactor/job_sorting_test.go 3 // Provenance-includes-license: Apache-2.0 4 // Provenance-includes-copyright: The Cortex Authors. 5 6 package compactor 7 8 import ( 9 "testing" 10 11 "github.com/oklog/ulid/v2" 12 "github.com/prometheus/common/model" 13 "github.com/stretchr/testify/assert" 14 15 "github.com/grafana/pyroscope/pkg/phlaredb/block" 16 ) 17 18 func TestSortJobsBySmallestRangeOldestBlocksFirst(t *testing.T) { 19 block1 := ulid.MustNew(1, nil) 20 block2 := ulid.MustNew(2, nil) 21 block3 := ulid.MustNew(3, nil) 22 block4 := ulid.MustNew(4, nil) 23 block5 := ulid.MustNew(5, nil) 24 block6 := ulid.MustNew(6, nil) 25 26 tests := map[string]struct { 27 input []*Job 28 expected []*Job 29 }{ 30 "should do nothing on empty input": { 31 input: nil, 32 expected: nil, 33 }, 34 "should sort jobs by smallest range, oldest blocks first": { 35 input: []*Job{ 36 {metasByMinTime: []*block.Meta{mockMetaWithMinMax(block5, 40, 60), mockMetaWithMinMax(block6, 40, 80)}}, 37 {metasByMinTime: []*block.Meta{mockMetaWithMinMax(block3, 10, 20), mockMetaWithMinMax(block4, 20, 30)}}, 38 {metasByMinTime: []*block.Meta{mockMetaWithMinMax(block1, 10, 20), mockMetaWithMinMax(block2, 10, 20)}}, 39 }, 40 expected: []*Job{ 41 {metasByMinTime: []*block.Meta{mockMetaWithMinMax(block1, 10, 20), mockMetaWithMinMax(block2, 10, 20)}}, 42 {metasByMinTime: []*block.Meta{mockMetaWithMinMax(block3, 10, 20), mockMetaWithMinMax(block4, 20, 30)}}, 43 {metasByMinTime: []*block.Meta{mockMetaWithMinMax(block5, 40, 60), mockMetaWithMinMax(block6, 40, 80)}}, 44 }, 45 }, 46 "split jobs are always sorted first": { 47 input: []*Job{ 48 {metasByMinTime: []*block.Meta{mockMetaWithMinMax(block5, 40, 60), mockMetaWithMinMax(block6, 40, 80)}}, 49 {metasByMinTime: []*block.Meta{mockMetaWithMinMax(block3, 10, 20), mockMetaWithMinMax(block4, 20, 30)}, useSplitting: false}, 50 {metasByMinTime: []*block.Meta{mockMetaWithMinMax(block3, 10, 20), mockMetaWithMinMax(block4, 20, 30)}, useSplitting: true}, 51 {metasByMinTime: []*block.Meta{mockMetaWithMinMax(block1, 10, 20), mockMetaWithMinMax(block2, 10, 20)}}, 52 {metasByMinTime: []*block.Meta{mockMetaWithMinMax(block4, 5, 50)}, useSplitting: true}, // Big splitting block. Should be sorted by minTime only. 53 }, 54 expected: []*Job{ 55 {metasByMinTime: []*block.Meta{mockMetaWithMinMax(block4, 5, 50)}, useSplitting: true}, 56 {metasByMinTime: []*block.Meta{mockMetaWithMinMax(block3, 10, 20), mockMetaWithMinMax(block4, 20, 30)}, useSplitting: true}, // Split job is first. 57 {metasByMinTime: []*block.Meta{mockMetaWithMinMax(block1, 10, 20), mockMetaWithMinMax(block2, 10, 20)}}, 58 {metasByMinTime: []*block.Meta{mockMetaWithMinMax(block3, 10, 20), mockMetaWithMinMax(block4, 20, 30)}, useSplitting: false}, 59 {metasByMinTime: []*block.Meta{mockMetaWithMinMax(block5, 40, 60), mockMetaWithMinMax(block6, 40, 80)}}, 60 }, 61 }, 62 } 63 64 for testName, testData := range tests { 65 t.Run(testName, func(t *testing.T) { 66 assert.Equal(t, testData.expected, sortJobsBySmallestRangeOldestBlocksFirst(testData.input)) 67 }) 68 } 69 } 70 71 func TestSortJobsByNewestBlocksFirst(t *testing.T) { 72 block1 := ulid.MustNew(1, nil) 73 block2 := ulid.MustNew(2, nil) 74 block3 := ulid.MustNew(3, nil) 75 block4 := ulid.MustNew(4, nil) 76 block5 := ulid.MustNew(5, nil) 77 block6 := ulid.MustNew(6, nil) 78 block7 := ulid.MustNew(7, nil) 79 80 tests := map[string]struct { 81 input []*Job 82 expected []*Job 83 }{ 84 "should do nothing on empty input": { 85 input: nil, 86 expected: nil, 87 }, 88 "should sort jobs by newest blocks first": { 89 input: []*Job{ 90 {metasByMinTime: []*block.Meta{mockMetaWithMinMax(block1, 10, 20), mockMetaWithMinMax(block2, 10, 20)}}, 91 {metasByMinTime: []*block.Meta{mockMetaWithMinMax(block3, 10, 20), mockMetaWithMinMax(block4, 20, 30)}}, 92 {metasByMinTime: []*block.Meta{mockMetaWithMinMax(block5, 40, 60), mockMetaWithMinMax(block6, 40, 80)}}, 93 }, 94 expected: []*Job{ 95 {metasByMinTime: []*block.Meta{mockMetaWithMinMax(block5, 40, 60), mockMetaWithMinMax(block6, 40, 80)}}, 96 {metasByMinTime: []*block.Meta{mockMetaWithMinMax(block3, 10, 20), mockMetaWithMinMax(block4, 20, 30)}}, 97 {metasByMinTime: []*block.Meta{mockMetaWithMinMax(block1, 10, 20), mockMetaWithMinMax(block2, 10, 20)}}, 98 }, 99 }, 100 "should give precedence to smaller time ranges in case of multiple jobs with the same max time": { 101 input: []*Job{ 102 {metasByMinTime: []*block.Meta{mockMetaWithMinMax(block1, 10, 20), mockMetaWithMinMax(block2, 20, 30), mockMetaWithMinMax(block3, 30, 40)}}, 103 {metasByMinTime: []*block.Meta{mockMetaWithMinMax(block4, 30, 40), mockMetaWithMinMax(block5, 30, 40)}}, 104 }, 105 expected: []*Job{ 106 {metasByMinTime: []*block.Meta{mockMetaWithMinMax(block4, 30, 40), mockMetaWithMinMax(block5, 30, 40)}}, 107 {metasByMinTime: []*block.Meta{mockMetaWithMinMax(block1, 10, 20), mockMetaWithMinMax(block2, 20, 30), mockMetaWithMinMax(block3, 30, 40)}}, 108 }, 109 }, 110 "should give precedence to newest blocks over smaller time ranges": { 111 input: []*Job{ 112 {metasByMinTime: []*block.Meta{mockMetaWithMinMax(block1, 10, 20), mockMetaWithMinMax(block2, 20, 30), mockMetaWithMinMax(block3, 30, 40)}}, 113 {metasByMinTime: []*block.Meta{mockMetaWithMinMax(block6, 10, 20), mockMetaWithMinMax(block7, 10, 20)}}, 114 {metasByMinTime: []*block.Meta{mockMetaWithMinMax(block4, 10, 30), mockMetaWithMinMax(block5, 20, 30)}}, 115 }, 116 expected: []*Job{ 117 {metasByMinTime: []*block.Meta{mockMetaWithMinMax(block1, 10, 20), mockMetaWithMinMax(block2, 20, 30), mockMetaWithMinMax(block3, 30, 40)}}, 118 {metasByMinTime: []*block.Meta{mockMetaWithMinMax(block4, 10, 30), mockMetaWithMinMax(block5, 20, 30)}}, 119 {metasByMinTime: []*block.Meta{mockMetaWithMinMax(block6, 10, 20), mockMetaWithMinMax(block7, 10, 20)}}, 120 }, 121 }, 122 } 123 124 for testName, testData := range tests { 125 t.Run(testName, func(t *testing.T) { 126 actual := sortJobsByNewestBlocksFirst(testData.input) 127 assert.Equal(t, testData.expected, actual) 128 129 // Print for debugging. 130 t.Log("sorted jobs:") 131 for _, job := range actual { 132 t.Logf("- %s", job.String()) 133 } 134 }) 135 } 136 } 137 138 func mockMetaWithMinMax(id ulid.ULID, minTime, maxTime int64) *block.Meta { 139 return &block.Meta{ 140 ULID: id, 141 MinTime: model.Time(minTime), 142 MaxTime: model.Time(maxTime), 143 } 144 }