@@ -3,6 +3,7 @@ package bucketindex
3
3
import (
4
4
"bytes"
5
5
"context"
6
+ "encoding/json"
6
7
"path"
7
8
"strings"
8
9
"testing"
@@ -21,6 +22,7 @@ import (
21
22
"github.com/thanos-io/thanos/pkg/block/metadata"
22
23
23
24
"github.com/cortexproject/cortex/pkg/storage/bucket"
25
+ "github.com/cortexproject/cortex/pkg/storage/parquet"
24
26
"github.com/cortexproject/cortex/pkg/storage/tsdb/testutil"
25
27
)
26
28
@@ -301,6 +303,150 @@ func TestUpdater_UpdateIndex_NoTenantInTheBucket(t *testing.T) {
301
303
}
302
304
}
303
305
306
+ func TestUpdater_UpdateIndex_WithParquet (t * testing.T ) {
307
+ const userID = "user-1"
308
+
309
+ bkt , _ := testutil .PrepareFilesystemBucket (t )
310
+
311
+ ctx := context .Background ()
312
+ logger := log .NewNopLogger ()
313
+
314
+ // Generate the initial index.
315
+ bkt = BucketWithGlobalMarkers (bkt )
316
+ block1 := testutil .MockStorageBlock (t , bkt , userID , 10 , 20 )
317
+ block2 := testutil .MockStorageBlock (t , bkt , userID , 20 , 30 )
318
+ block2Mark := testutil .MockStorageDeletionMark (t , bkt , userID , block2 )
319
+ // Add parquet marker to block 1.
320
+ block1ParquetMark := testutil .MockStorageParquetConverterMark (t , bkt , userID , block1 )
321
+
322
+ w := NewUpdater (bkt , userID , nil , logger ).EnableParquet ()
323
+ returnedIdx , _ , _ , err := w .UpdateIndex (ctx , nil )
324
+ require .NoError (t , err )
325
+ assertBucketIndexEqualWithParquet (t , returnedIdx , bkt , userID ,
326
+ []tsdb.BlockMeta {block1 , block2 },
327
+ []* metadata.DeletionMark {block2Mark }, map [string ]* parquet.ConverterMarkMeta {
328
+ block1 .ULID .String (): {Version : block1ParquetMark .Version },
329
+ })
330
+
331
+ // Create new blocks, and update the index.
332
+ block3 := testutil .MockStorageBlock (t , bkt , userID , 30 , 40 )
333
+ block4 := testutil .MockStorageBlock (t , bkt , userID , 40 , 50 )
334
+ block4Mark := testutil .MockStorageDeletionMark (t , bkt , userID , block4 )
335
+
336
+ returnedIdx , _ , _ , err = w .UpdateIndex (ctx , returnedIdx )
337
+ require .NoError (t , err )
338
+ assertBucketIndexEqualWithParquet (t , returnedIdx , bkt , userID ,
339
+ []tsdb.BlockMeta {block1 , block2 , block3 , block4 },
340
+ []* metadata.DeletionMark {block2Mark , block4Mark },
341
+ map [string ]* parquet.ConverterMarkMeta {
342
+ block1 .ULID .String (): {Version : block1ParquetMark .Version },
343
+ })
344
+
345
+ // Hard delete a block and update the index.
346
+ require .NoError (t , block .Delete (ctx , log .NewNopLogger (), bucket .NewUserBucketClient (userID , bkt , nil ), block2 .ULID ))
347
+
348
+ returnedIdx , _ , _ , err = w .UpdateIndex (ctx , returnedIdx )
349
+ require .NoError (t , err )
350
+ assertBucketIndexEqualWithParquet (t , returnedIdx , bkt , userID ,
351
+ []tsdb.BlockMeta {block1 , block3 , block4 },
352
+ []* metadata.DeletionMark {block4Mark }, map [string ]* parquet.ConverterMarkMeta {
353
+ block1 .ULID .String (): {Version : block1ParquetMark .Version },
354
+ })
355
+
356
+ // Upload parquet marker to an old block and update index
357
+ block3ParquetMark := testutil .MockStorageParquetConverterMark (t , bkt , userID , block3 )
358
+ returnedIdx , _ , _ , err = w .UpdateIndex (ctx , returnedIdx )
359
+ require .NoError (t , err )
360
+ assertBucketIndexEqualWithParquet (t , returnedIdx , bkt , userID ,
361
+ []tsdb.BlockMeta {block1 , block3 , block4 },
362
+ []* metadata.DeletionMark {block4Mark }, map [string ]* parquet.ConverterMarkMeta {
363
+ block1 .ULID .String (): {Version : block1ParquetMark .Version },
364
+ block3 .ULID .String (): {Version : block3ParquetMark .Version },
365
+ })
366
+ }
367
+
368
+ func TestUpdater_UpdateParquetBlockIndexEntry (t * testing.T ) {
369
+ const userID = "user-1"
370
+ ctx := context .Background ()
371
+ logger := log .NewNopLogger ()
372
+
373
+ tests := []struct {
374
+ name string
375
+ setupBucket func (t * testing.T , bkt objstore.InstrumentedBucket , blockID ulid.ULID ) objstore.InstrumentedBucket
376
+ expectedError error
377
+ expectParquet bool
378
+ expectParquetMeta * parquet.ConverterMarkMeta
379
+ }{
380
+ {
381
+ name : "should successfully read parquet marker" ,
382
+ setupBucket : func (t * testing.T , bkt objstore.InstrumentedBucket , blockID ulid.ULID ) objstore.InstrumentedBucket {
383
+ parquetMark := parquet.ConverterMarkMeta {
384
+ Version : 1 ,
385
+ }
386
+ data , err := json .Marshal (parquetMark )
387
+ require .NoError (t , err )
388
+ require .NoError (t , bkt .Upload (ctx , path .Join (userID , blockID .String (), parquet .ConverterMarkerFileName ), bytes .NewReader (data )))
389
+ return bkt
390
+ },
391
+ expectedError : nil ,
392
+ expectParquet : true ,
393
+ expectParquetMeta : & parquet.ConverterMarkMeta {Version : 1 },
394
+ },
395
+ {
396
+ name : "should handle missing parquet marker" ,
397
+ setupBucket : func (t * testing.T , bkt objstore.InstrumentedBucket , blockID ulid.ULID ) objstore.InstrumentedBucket {
398
+ // Don't upload any parquet marker
399
+ return bkt
400
+ },
401
+ expectedError : nil ,
402
+ expectParquet : false ,
403
+ },
404
+ {
405
+ name : "should handle access denied" ,
406
+ setupBucket : func (t * testing.T , bkt objstore.InstrumentedBucket , blockID ulid.ULID ) objstore.InstrumentedBucket {
407
+ return & testutil.MockBucketFailure {
408
+ Bucket : bkt ,
409
+ GetFailures : map [string ]error {
410
+ path .Join (userID , blockID .String (), parquet .ConverterMarkerFileName ): testutil .ErrKeyAccessDeniedError ,
411
+ },
412
+ }
413
+ },
414
+ expectedError : nil ,
415
+ expectParquet : false ,
416
+ },
417
+ }
418
+
419
+ for _ , tc := range tests {
420
+ t .Run (tc .name , func (t * testing.T ) {
421
+ bkt , _ := testutil .PrepareFilesystemBucket (t )
422
+ blockID := ulid .MustNew (1 , nil )
423
+ block := & Block {ID : blockID }
424
+
425
+ // Setup the bucket with test data
426
+ bkt = tc .setupBucket (t , bkt , blockID )
427
+
428
+ // Create an instrumented bucket wrapper
429
+ registry := prometheus .NewRegistry ()
430
+ ibkt := objstore .WrapWithMetrics (bkt , prometheus .WrapRegistererWithPrefix ("thanos_" , registry ), "test-bucket" )
431
+ w := NewUpdater (ibkt , userID , nil , logger )
432
+
433
+ err := w .updateParquetBlockIndexEntry (ctx , blockID , block )
434
+ if tc .expectedError != nil {
435
+ assert .True (t , errors .Is (err , tc .expectedError ))
436
+ } else {
437
+ assert .NoError (t , err )
438
+ }
439
+
440
+ if tc .expectParquet {
441
+ assert .NotNil (t , block .Parquet )
442
+ assert .Equal (t , tc .expectParquetMeta , block .Parquet )
443
+ } else {
444
+ assert .Nil (t , block .Parquet )
445
+ }
446
+ })
447
+ }
448
+ }
449
+
304
450
func getBlockUploadedAt (t testing.TB , bkt objstore.Bucket , userID string , blockID ulid.ULID ) int64 {
305
451
metaFile := path .Join (userID , blockID .String (), block .MetaFilename )
306
452
@@ -338,3 +484,36 @@ func assertBucketIndexEqual(t testing.TB, idx *Index, bkt objstore.Bucket, userI
338
484
339
485
assert .ElementsMatch (t , expectedMarkEntries , idx .BlockDeletionMarks )
340
486
}
487
+
488
+ func assertBucketIndexEqualWithParquet (t testing.TB , idx * Index , bkt objstore.Bucket , userID string , expectedBlocks []tsdb.BlockMeta , expectedDeletionMarks []* metadata.DeletionMark , parquetBlocks map [string ]* parquet.ConverterMarkMeta ) {
489
+ assert .Equal (t , IndexVersion1 , idx .Version )
490
+ assert .InDelta (t , time .Now ().Unix (), idx .UpdatedAt , 2 )
491
+
492
+ // Build the list of expected block index entries.
493
+ var expectedBlockEntries []* Block
494
+ for _ , b := range expectedBlocks {
495
+ block := & Block {
496
+ ID : b .ULID ,
497
+ MinTime : b .MinTime ,
498
+ MaxTime : b .MaxTime ,
499
+ UploadedAt : getBlockUploadedAt (t , bkt , userID , b .ULID ),
500
+ }
501
+ if meta , ok := parquetBlocks [b .ULID .String ()]; ok {
502
+ block .Parquet = meta
503
+ }
504
+ expectedBlockEntries = append (expectedBlockEntries , block )
505
+ }
506
+
507
+ assert .ElementsMatch (t , expectedBlockEntries , idx .Blocks )
508
+
509
+ // Build the list of expected block deletion mark index entries.
510
+ var expectedMarkEntries []* BlockDeletionMark
511
+ for _ , m := range expectedDeletionMarks {
512
+ expectedMarkEntries = append (expectedMarkEntries , & BlockDeletionMark {
513
+ ID : m .ID ,
514
+ DeletionTime : m .DeletionTime ,
515
+ })
516
+ }
517
+
518
+ assert .ElementsMatch (t , expectedMarkEntries , idx .BlockDeletionMarks )
519
+ }
0 commit comments