github.com/grafana/pyroscope@v1.18.0/pkg/phlaredb/tsdb/bitprefix_test.go (about) 1 package tsdb 2 3 import ( 4 "fmt" 5 "sort" 6 "testing" 7 8 "github.com/prometheus/common/model" 9 "github.com/prometheus/prometheus/model/labels" 10 "github.com/stretchr/testify/require" 11 12 typesv1 "github.com/grafana/pyroscope/api/gen/proto/go/types/v1" 13 phlaremodel "github.com/grafana/pyroscope/pkg/model" 14 "github.com/grafana/pyroscope/pkg/phlaredb/tsdb/index" 15 "github.com/grafana/pyroscope/pkg/phlaredb/tsdb/shard" 16 ) 17 18 func Test_BitPrefixGetShards(t *testing.T) { 19 for _, tt := range []struct { 20 total uint32 21 filter bool 22 shard *shard.Annotation 23 expected []uint32 24 }{ 25 // equal factors 26 {16, false, &shard.Annotation{Shard: 0, Of: 16}, []uint32{0}}, 27 {16, false, &shard.Annotation{Shard: 4, Of: 16}, []uint32{4}}, 28 {16, false, &shard.Annotation{Shard: 15, Of: 16}, []uint32{15}}, 29 30 // idx factor a larger factor of 2 31 {32, false, &shard.Annotation{Shard: 0, Of: 16}, []uint32{0, 1}}, 32 {32, false, &shard.Annotation{Shard: 4, Of: 16}, []uint32{8, 9}}, 33 {32, false, &shard.Annotation{Shard: 15, Of: 16}, []uint32{30, 31}}, 34 {64, false, &shard.Annotation{Shard: 15, Of: 16}, []uint32{60, 61, 62, 63}}, 35 36 // // idx factor a smaller factor of 2 37 {8, true, &shard.Annotation{Shard: 0, Of: 16}, []uint32{0}}, 38 {8, true, &shard.Annotation{Shard: 4, Of: 16}, []uint32{2}}, 39 {8, true, &shard.Annotation{Shard: 15, Of: 16}, []uint32{7}}, 40 } { 41 tt := tt 42 t.Run(tt.shard.String()+fmt.Sprintf("_total_%d", tt.total), func(t *testing.T) { 43 ii, err := NewBitPrefixWithShards(tt.total) 44 require.Nil(t, err) 45 res, filter := ii.getShards(tt.shard) 46 resInt := []uint32{} 47 for _, r := range res { 48 resInt = append(resInt, r.shard) 49 } 50 require.Equal(t, tt.filter, filter) 51 require.Equal(t, tt.expected, resInt) 52 }) 53 } 54 } 55 56 func Test_BitPrefixValidateShards(t *testing.T) { 57 ii, err := NewBitPrefixWithShards(32) 58 require.Nil(t, err) 59 require.NoError(t, ii.validateShard(&shard.Annotation{Shard: 1, Of: 16})) 60 require.Error(t, ii.validateShard(&shard.Annotation{Shard: 1, Of: 15})) 61 } 62 63 func Test_BitPrefixCreation(t *testing.T) { 64 // non factor of 2 shard factor 65 _, err := NewBitPrefixWithShards(6) 66 require.Error(t, err) 67 68 // valid shard factor 69 _, err = NewBitPrefixWithShards(4) 70 require.Nil(t, err) 71 } 72 73 func Test_BitPrefixDeleteAddLoopkup(t *testing.T) { 74 index, err := NewBitPrefixWithShards(DefaultIndexShards) 75 require.Nil(t, err) 76 lbs := []*typesv1.LabelPair{ 77 {Name: "foo", Value: "foo"}, 78 {Name: "bar", Value: "bar"}, 79 {Name: "buzz", Value: "buzz"}, 80 } 81 sort.Sort(phlaremodel.Labels(lbs)) 82 83 index.Add(lbs, model.Fingerprint((phlaremodel.Labels(lbs).Hash()))) 84 index.Delete(lbs, model.Fingerprint(phlaremodel.Labels(lbs).Hash())) 85 ids, err := index.Lookup([]*labels.Matcher{ 86 labels.MustNewMatcher(labels.MatchEqual, "foo", "foo"), 87 }, nil) 88 require.NoError(t, err) 89 require.Len(t, ids, 0) 90 } 91 92 func Test_BitPrefix_hash_mapping(t *testing.T) { 93 lbs := []*typesv1.LabelPair{ 94 {Name: "compose_project", Value: "loki-boltdb-storage-s3"}, 95 {Name: "compose_service", Value: "ingester-2"}, 96 {Name: "container_name", Value: "loki-boltdb-storage-s3_ingester-2_1"}, 97 {Name: "filename", Value: "/var/log/docker/790fef4c6a587c3b386fe85c07e03f3a1613f4929ca3abaa4880e14caadb5ad1/json.log"}, 98 {Name: "host", Value: "docker-desktop"}, 99 {Name: "source", Value: "stderr"}, 100 } 101 102 // for _, shard := range []uint32{2, 4, 8, 16, 32, 64, 128} { 103 for _, shardID := range []uint32{2} { 104 t.Run(fmt.Sprintf("%d", shardID), func(t *testing.T) { 105 ii, err := NewBitPrefixWithShards(shardID) 106 require.Nil(t, err) 107 108 requestedFactor := 16 109 110 fp := model.Fingerprint(phlaremodel.Labels(lbs).Hash()) 111 ii.Add(lbs, fp) 112 113 requiredBits := index.NewShard(0, uint32(requestedFactor)).RequiredBits() 114 expShard := uint32(phlaremodel.Labels(lbs).Hash() >> (64 - requiredBits)) 115 116 res, err := ii.Lookup( 117 []*labels.Matcher{{ 118 Type: labels.MatchEqual, 119 Name: "compose_project", 120 Value: "loki-boltdb-storage-s3", 121 }}, 122 &shard.Annotation{ 123 Shard: int(expShard), 124 Of: requestedFactor, 125 }, 126 ) 127 require.NoError(t, err) 128 require.Len(t, res, 1) 129 require.Equal(t, fp, res[0]) 130 }) 131 } 132 } 133 134 func Test_BitPrefixNoMatcherLookup(t *testing.T) { 135 lbs := []*typesv1.LabelPair{ 136 {Name: "foo", Value: "bar"}, 137 {Name: "hi", Value: "hello"}, 138 } 139 // with no shard param 140 ii, err := NewBitPrefixWithShards(16) 141 require.Nil(t, err) 142 fp := model.Fingerprint(phlaremodel.Labels(lbs).Hash()) 143 ii.Add(lbs, fp) 144 ids, err := ii.Lookup(nil, nil) 145 require.Nil(t, err) 146 require.Equal(t, fp, ids[0]) 147 148 // with shard param 149 ii, err = NewBitPrefixWithShards(16) 150 require.Nil(t, err) 151 expShard := uint32(fp >> (64 - index.NewShard(0, 16).RequiredBits())) 152 ii.Add(lbs, fp) 153 ids, err = ii.Lookup(nil, &shard.Annotation{Shard: int(expShard), Of: 16}) 154 require.Nil(t, err) 155 require.Equal(t, fp, ids[0]) 156 } 157 158 func Test_BitPrefixConsistentMapping(t *testing.T) { 159 a, err := NewBitPrefixWithShards(16) 160 require.Nil(t, err) 161 b, err := NewBitPrefixWithShards(32) 162 require.Nil(t, err) 163 164 for i := 0; i < 100; i++ { 165 lbs := []*typesv1.LabelPair{ 166 {Name: "foo", Value: "bar"}, 167 {Name: "hi", Value: fmt.Sprint(i)}, 168 } 169 170 fp := model.Fingerprint(phlaremodel.Labels(lbs).Hash()) 171 a.Add(lbs, fp) 172 b.Add(lbs, fp) 173 } 174 175 shardMax := 8 176 for i := 0; i < shardMax; i++ { 177 shard := &shard.Annotation{ 178 Shard: i, 179 Of: shardMax, 180 } 181 182 aIDs, err := a.Lookup([]*labels.Matcher{ 183 labels.MustNewMatcher(labels.MatchEqual, "foo", "bar"), 184 }, shard) 185 require.Nil(t, err) 186 187 bIDs, err := b.Lookup([]*labels.Matcher{ 188 labels.MustNewMatcher(labels.MatchEqual, "foo", "bar"), 189 }, shard) 190 require.Nil(t, err) 191 192 sorter := func(xs []model.Fingerprint) { 193 sort.Slice(xs, func(i, j int) bool { 194 return xs[i] < xs[j] 195 }) 196 } 197 sorter(aIDs) 198 sorter(bIDs) 199 200 require.Equal(t, aIDs, bIDs, "incorrect shard mapping for shard %v", shard) 201 } 202 }