github.com/grafana/pyroscope@v1.18.0/pkg/storegateway/bucket_index_metadata_fetcher_test.go (about) 1 // SPDX-License-Identifier: AGPL-3.0-only 2 // Provenance-includes-location: https://github.com/cortexproject/cortex/blob/master/pkg/storegateway/bucket_index_metadata_fetcher_test.go 3 // Provenance-includes-license: Apache-2.0 4 // Provenance-includes-copyright: The Cortex Authors. 5 6 package storegateway 7 8 import ( 9 "bytes" 10 "context" 11 "path" 12 "strings" 13 "testing" 14 "time" 15 16 "github.com/go-kit/log" 17 "github.com/grafana/dskit/concurrency" 18 "github.com/oklog/ulid/v2" 19 "github.com/prometheus/client_golang/prometheus" 20 "github.com/prometheus/client_golang/prometheus/testutil" 21 "github.com/prometheus/common/model" 22 "github.com/stretchr/testify/assert" 23 "github.com/stretchr/testify/require" 24 25 "github.com/grafana/pyroscope/pkg/objstore" 26 objstore_testutil "github.com/grafana/pyroscope/pkg/objstore/testutil" 27 "github.com/grafana/pyroscope/pkg/phlaredb/block" 28 "github.com/grafana/pyroscope/pkg/phlaredb/bucketindex" 29 ) 30 31 func TestBucketIndexMetadataFetcher_Fetch(t *testing.T) { 32 const userID = "user-1" 33 34 ctx := context.Background() 35 36 bkt, _ := objstore_testutil.NewFilesystemBucket(t, ctx, t.TempDir()) 37 reg := prometheus.NewPedanticRegistry() 38 now := time.Now() 39 logs := &concurrency.SyncBuffer{} 40 logger := log.NewLogfmtLogger(logs) 41 42 // Create a bucket index. 43 block1 := &bucketindex.Block{ID: ulid.MustNew(1, nil)} 44 block2 := &bucketindex.Block{ID: ulid.MustNew(2, nil)} 45 block3 := &bucketindex.Block{ID: ulid.MustNew(3, nil)} 46 block4 := &bucketindex.Block{ID: ulid.MustNew(4, nil), MinTime: model.Time(now.Add(-30 * time.Minute).UnixMilli())} // Has most-recent data, to be ignored by minTimeMetaFilter. 47 48 mark1 := &bucketindex.BlockDeletionMark{ID: block1.ID, DeletionTime: now.Add(-time.Hour).Unix()} // Below the ignore delay threshold. 49 mark2 := &bucketindex.BlockDeletionMark{ID: block2.ID, DeletionTime: now.Add(-3 * time.Hour).Unix()} // Above the ignore delay threshold. 50 51 require.NoError(t, bucketindex.WriteIndex(ctx, bkt, userID, nil, &bucketindex.Index{ 52 Version: bucketindex.IndexVersion1, 53 Blocks: bucketindex.Blocks{block1, block2, block3, block4}, 54 BlockDeletionMarks: bucketindex.BlockDeletionMarks{mark1, mark2}, 55 UpdatedAt: now.Unix(), 56 })) 57 58 // Create a metadata fetcher with filters. 59 filters := []block.MetadataFilter{ 60 NewIgnoreDeletionMarkFilter(logger, objstore.NewTenantBucketClient(userID, bkt, nil), 2*time.Hour, 1), 61 newMinTimeMetaFilter(1 * time.Hour), 62 } 63 64 fetcher := NewBucketIndexMetadataFetcher(userID, bkt, nil, logger, reg, filters) 65 metas, partials, err := fetcher.Fetch(ctx) 66 require.NoError(t, err) 67 assert.Equal(t, map[ulid.ULID]*block.Meta{ 68 block1.ID: block1.Meta(), 69 block3.ID: block3.Meta(), 70 }, metas) 71 assert.Empty(t, partials) 72 assert.Empty(t, logs) 73 74 assert.NoError(t, testutil.GatherAndCompare(reg, bytes.NewBufferString(` 75 # HELP blocks_meta_sync_failures_total Total blocks metadata synchronization failures 76 # TYPE blocks_meta_sync_failures_total counter 77 blocks_meta_sync_failures_total 0 78 79 # HELP blocks_meta_synced Number of block metadata synced 80 # TYPE blocks_meta_synced gauge 81 blocks_meta_synced{state="corrupted-bucket-index"} 0 82 blocks_meta_synced{state="corrupted-meta-json"} 0 83 blocks_meta_synced{state="duplicate"} 0 84 blocks_meta_synced{state="failed"} 0 85 blocks_meta_synced{state="label-excluded"} 0 86 blocks_meta_synced{state="loaded"} 2 87 blocks_meta_synced{state="marked-for-deletion"} 1 88 blocks_meta_synced{state="marked-for-no-compact"} 0 89 blocks_meta_synced{state="no-bucket-index"} 0 90 blocks_meta_synced{state="no-meta-json"} 0 91 blocks_meta_synced{state="time-excluded"} 0 92 blocks_meta_synced{state="min-time-excluded"} 1 93 94 # HELP blocks_meta_syncs_total Total blocks metadata synchronization attempts 95 # TYPE blocks_meta_syncs_total counter 96 blocks_meta_syncs_total 1 97 `), 98 "blocks_meta_sync_failures_total", 99 "blocks_meta_synced", 100 "blocks_meta_syncs_total", 101 )) 102 } 103 104 func TestBucketIndexMetadataFetcher_Fetch_NoBucketIndex(t *testing.T) { 105 const userID = "user-1" 106 107 ctx := context.Background() 108 bkt, _ := objstore_testutil.NewFilesystemBucket(t, ctx, t.TempDir()) 109 reg := prometheus.NewPedanticRegistry() 110 logs := &concurrency.SyncBuffer{} 111 logger := log.NewLogfmtLogger(logs) 112 113 fetcher := NewBucketIndexMetadataFetcher(userID, bkt, nil, logger, reg, nil) 114 metas, partials, err := fetcher.Fetch(ctx) 115 require.NoError(t, err) 116 assert.Empty(t, metas) 117 assert.Empty(t, partials) 118 assert.Contains(t, logs.String(), "no bucket index found, falling back to fetching directly from bucket") 119 120 assert.NoError(t, testutil.GatherAndCompare(reg, bytes.NewBufferString(` 121 # HELP blocks_meta_sync_failures_total Total blocks metadata synchronization failures 122 # TYPE blocks_meta_sync_failures_total counter 123 blocks_meta_sync_failures_total 0 124 125 # HELP blocks_meta_synced Number of block metadata synced 126 # TYPE blocks_meta_synced gauge 127 blocks_meta_synced{state="corrupted-bucket-index"} 0 128 blocks_meta_synced{state="corrupted-meta-json"} 0 129 blocks_meta_synced{state="duplicate"} 0 130 blocks_meta_synced{state="failed"} 0 131 blocks_meta_synced{state="label-excluded"} 0 132 blocks_meta_synced{state="loaded"} 0 133 blocks_meta_synced{state="marked-for-deletion"} 0 134 blocks_meta_synced{state="marked-for-no-compact"} 0 135 blocks_meta_synced{state="no-bucket-index"} 1 136 blocks_meta_synced{state="no-meta-json"} 0 137 blocks_meta_synced{state="time-excluded"} 0 138 blocks_meta_synced{state="min-time-excluded"} 0 139 140 # HELP blocks_meta_syncs_total Total blocks metadata synchronization attempts 141 # TYPE blocks_meta_syncs_total counter 142 blocks_meta_syncs_total 1 143 `), 144 "blocks_meta_sync_failures_total", 145 "blocks_meta_synced", 146 "blocks_meta_syncs_total", 147 )) 148 } 149 150 func TestBucketIndexMetadataFetcher_Fetch_CorruptedBucketIndex(t *testing.T) { 151 const userID = "user-1" 152 153 ctx := context.Background() 154 155 bkt, _ := objstore_testutil.NewFilesystemBucket(t, ctx, t.TempDir()) 156 reg := prometheus.NewPedanticRegistry() 157 logs := &concurrency.SyncBuffer{} 158 logger := log.NewLogfmtLogger(logs) 159 160 // Upload a corrupted bucket index. 161 require.NoError(t, bkt.Upload(ctx, path.Join(userID, "phlaredb/", bucketindex.IndexCompressedFilename), strings.NewReader("invalid}!"))) 162 163 fetcher := NewBucketIndexMetadataFetcher(userID, bkt, nil, logger, reg, nil) 164 metas, partials, err := fetcher.Fetch(ctx) 165 require.NoError(t, err) 166 assert.Empty(t, metas) 167 assert.Empty(t, partials) 168 assert.Regexp(t, "corrupted bucket index found", logs) 169 170 assert.NoError(t, testutil.GatherAndCompare(reg, bytes.NewBufferString(` 171 # HELP blocks_meta_sync_failures_total Total blocks metadata synchronization failures 172 # TYPE blocks_meta_sync_failures_total counter 173 blocks_meta_sync_failures_total 0 174 175 # HELP blocks_meta_synced Number of block metadata synced 176 # TYPE blocks_meta_synced gauge 177 blocks_meta_synced{state="corrupted-bucket-index"} 1 178 blocks_meta_synced{state="corrupted-meta-json"} 0 179 blocks_meta_synced{state="duplicate"} 0 180 blocks_meta_synced{state="failed"} 0 181 blocks_meta_synced{state="label-excluded"} 0 182 blocks_meta_synced{state="loaded"} 0 183 blocks_meta_synced{state="marked-for-deletion"} 0 184 blocks_meta_synced{state="marked-for-no-compact"} 0 185 blocks_meta_synced{state="no-bucket-index"} 0 186 blocks_meta_synced{state="no-meta-json"} 0 187 blocks_meta_synced{state="time-excluded"} 0 188 blocks_meta_synced{state="min-time-excluded"} 0 189 190 # HELP blocks_meta_syncs_total Total blocks metadata synchronization attempts 191 # TYPE blocks_meta_syncs_total counter 192 blocks_meta_syncs_total 1 193 `), 194 "blocks_meta_sync_failures_total", 195 "blocks_meta_synced", 196 "blocks_meta_syncs_total", 197 )) 198 }