@@ -88,7 +88,7 @@ use self::eviction_task::EvictionTaskTimelineState;
88
88
use self :: logical_size:: LogicalSize ;
89
89
use self :: walreceiver:: { WalReceiver , WalReceiverConf } ;
90
90
use super :: remote_timeline_client:: RemoteTimelineClient ;
91
- use super :: remote_timeline_client:: index:: { GcCompactionState , IndexPart } ;
91
+ use super :: remote_timeline_client:: index:: { GcCompactionState , IndexPart , LayerFileMetadata } ;
92
92
use super :: secondary:: heatmap:: HeatMapLayer ;
93
93
use super :: storage_layer:: { LayerFringe , LayerVisibilityHint , ReadableLayer } ;
94
94
use super :: tasks:: log_compaction_error;
@@ -4205,6 +4205,12 @@ impl Timeline {
4205
4205
let desc: PersistentLayerDesc = hl. name . clone ( ) . into ( ) ;
4206
4206
let layer = guard. try_get_from_key ( & desc. key ( ) ) ?;
4207
4207
4208
+ // Make sure the layer in the old heatmap is the same generation one as in the layer
4209
+ // map otherwise we can in some edge case keep old obsolete layers in the heatmap.
4210
+ if layer. metadata ( ) . generation != hl. metadata . generation {
4211
+ return None ;
4212
+ }
4213
+
4208
4214
if layer. visibility ( ) == LayerVisibilityHint :: Covered {
4209
4215
return None ;
4210
4216
}
@@ -6474,6 +6480,7 @@ pub struct DeltaLayerTestDesc {
6474
6480
pub lsn_range : Range < Lsn > ,
6475
6481
pub key_range : Range < Key > ,
6476
6482
pub data : Vec < ( Key , Lsn , Value ) > ,
6483
+ pub resident : bool ,
6477
6484
}
6478
6485
6479
6486
#[ cfg( test) ]
@@ -6491,19 +6498,30 @@ impl DeltaLayerTestDesc {
6491
6498
lsn_range,
6492
6499
key_range,
6493
6500
data,
6501
+ // Default test code creates resident layers.
6502
+ resident : true ,
6494
6503
}
6495
6504
}
6496
6505
6497
6506
pub fn new_with_inferred_key_range (
6498
6507
lsn_range : Range < Lsn > ,
6499
6508
data : Vec < ( Key , Lsn , Value ) > ,
6509
+ ) -> Self {
6510
+ Self :: new_with_inferred_key_range_and_resident_state ( lsn_range, data, true )
6511
+ }
6512
+
6513
+ pub fn new_with_inferred_key_range_and_resident_state (
6514
+ lsn_range : Range < Lsn > ,
6515
+ data : Vec < ( Key , Lsn , Value ) > ,
6516
+ resident : bool ,
6500
6517
) -> Self {
6501
6518
let key_min = data. iter ( ) . map ( |( key, _, _) | key) . min ( ) . unwrap ( ) ;
6502
6519
let key_max = data. iter ( ) . map ( |( key, _, _) | key) . max ( ) . unwrap ( ) ;
6503
6520
Self {
6504
6521
key_range : ( * key_min) ..( key_max. next ( ) ) ,
6505
6522
lsn_range,
6506
6523
data,
6524
+ resident
6507
6525
}
6508
6526
}
6509
6527
@@ -7505,6 +7523,30 @@ impl Timeline {
7505
7523
check_start_lsn : Option < Lsn > ,
7506
7524
ctx : & RequestContext ,
7507
7525
) -> anyhow:: Result < ( ) > {
7526
+
7527
+ if !deltas. resident {
7528
+ // Don't need to bother creating an on-disk file, we just want the metadata for a non-resident layer.
7529
+ let delta_layer = Layer :: for_evicted (
7530
+ self . conf ,
7531
+ self ,
7532
+ deltas. layer_name ( ) ,
7533
+ LayerFileMetadata {
7534
+ generation : self . generation ,
7535
+ shard : self . get_shard_index ( ) ,
7536
+ file_size : 1024 ,
7537
+ } ,
7538
+ ) ;
7539
+ info ! ( "force created non-resident delta layer {}" , deltas. layer_name( ) ) ;
7540
+ {
7541
+ let mut guard = self . layers . write ( LayerManagerLockHolder :: Testing ) . await ;
7542
+ guard
7543
+ . open_mut ( )
7544
+ . unwrap ( )
7545
+ . force_insert_optionally_resident_layer ( delta_layer) ;
7546
+ }
7547
+ return Ok ( ( ) ) ;
7548
+ }
7549
+
7508
7550
let last_record_lsn = self . get_last_record_lsn ( ) ;
7509
7551
deltas
7510
7552
. data
@@ -8263,6 +8305,148 @@ mod tests {
8263
8305
) ) ;
8264
8306
}
8265
8307
8308
+ #[ tokio:: test]
8309
+ async fn test_heatmap_generation_removes_layers_from_old_generation ( ) {
8310
+ use std:: time:: SystemTime ;
8311
+ use utils:: generation:: Generation ;
8312
+ use crate :: tenant:: remote_timeline_client:: index:: LayerFileMetadata ;
8313
+ use crate :: tenant:: secondary:: heatmap:: { HeatMapLayer , HeatMapTimeline as HeatMapTimelineStruct } ;
8314
+
8315
+ let harness = TenantHarness :: create ( "heatmaheatmap_genereation_removes_layers_from_old_generationp_generation" ) . await . unwrap ( ) ;
8316
+
8317
+ // Create your existing layer descriptions
8318
+ let covered_delta = DeltaLayerTestDesc :: new_with_inferred_key_range (
8319
+ Lsn ( 0x10 ) ..Lsn ( 0x20 ) ,
8320
+ vec ! [ (
8321
+ Key :: from_hex( "620000000033333333444444445500000000" ) . unwrap( ) ,
8322
+ Lsn ( 0x11 ) ,
8323
+ Value :: Image ( test_img( "foo" ) ) ,
8324
+ ) ] ,
8325
+ ) ;
8326
+ // This visible layer is non-resident on disk. This is important to reproduce the failure as
8327
+ // a resident file will take priority over the previous heatmap even without the fix for
8328
+ // this issue.
8329
+ let visible_delta = DeltaLayerTestDesc :: new_with_inferred_key_range_and_resident_state (
8330
+ Lsn ( 0x10 ) ..Lsn ( 0x20 ) ,
8331
+ vec ! [ (
8332
+ Key :: from_hex( "720000000033333333444444445500000000" ) . unwrap( ) ,
8333
+ Lsn ( 0x11 ) ,
8334
+ Value :: Image ( test_img( "foo" ) ) ,
8335
+ ) ] ,
8336
+ false , // Non-resident
8337
+ ) ;
8338
+ let l0_delta = DeltaLayerTestDesc :: new (
8339
+ Lsn ( 0x20 ) ..Lsn ( 0x30 ) ,
8340
+ Key :: from_hex ( "000000000000000000000000000000000000" ) . unwrap ( )
8341
+ ..Key :: from_hex ( "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" ) . unwrap ( ) ,
8342
+ vec ! [ (
8343
+ Key :: from_hex( "720000000033333333444444445500000000" ) . unwrap( ) ,
8344
+ Lsn ( 0x25 ) ,
8345
+ Value :: Image ( test_img( "foo" ) ) ,
8346
+ ) ] ,
8347
+ ) ;
8348
+ let delta_layers = vec ! [
8349
+ covered_delta. clone( ) ,
8350
+ visible_delta. clone( ) ,
8351
+ l0_delta. clone( ) ,
8352
+ ] ;
8353
+
8354
+ let image_layer = (
8355
+ Lsn ( 0x40 ) ,
8356
+ vec ! [ (
8357
+ Key :: from_hex( "620000000033333333444444445500000000" ) . unwrap( ) ,
8358
+ test_img( "bar" ) ,
8359
+ ) ] ,
8360
+ ) ;
8361
+ let image_layers = vec ! [ image_layer] ;
8362
+
8363
+ let ( tenant, ctx) = harness. load ( ) . await ;
8364
+
8365
+ // Create timeline with current generation (0xdeadbeef by default)
8366
+ let timeline = tenant
8367
+ . create_test_timeline_with_layers (
8368
+ TimelineId :: generate ( ) ,
8369
+ Lsn ( 0x10 ) ,
8370
+ PgMajorVersion :: PG14 ,
8371
+ & ctx,
8372
+ Vec :: new ( ) , // in-memory layers
8373
+ delta_layers,
8374
+ image_layers,
8375
+ Lsn ( 0x100 ) ,
8376
+ )
8377
+ . await
8378
+ . unwrap ( ) ;
8379
+
8380
+ // Now create a previous heatmap with the visible_delta layer from an older generation
8381
+ let current_layer_metadata = LayerFileMetadata {
8382
+ generation : timeline. generation ,
8383
+ shard : timeline. get_shard_index ( ) ,
8384
+ file_size : 1024 ,
8385
+ } ;
8386
+ let old_generation = Generation :: new ( 0x12345678 ) ; // Older than 0xdeadbeef
8387
+ let old_layer_metadata = LayerFileMetadata {
8388
+ generation : old_generation,
8389
+ shard : timeline. get_shard_index ( ) ,
8390
+ file_size : 1024 ,
8391
+ } ;
8392
+
8393
+ // Create heatmap layers that reference the same keys but with old generation
8394
+ let prev_heatmap_layers = vec ! [
8395
+ HeatMapLayer :: new(
8396
+ covered_delta. layer_name( ) ,
8397
+ current_layer_metadata. clone( ) ,
8398
+ SystemTime :: now( ) ,
8399
+ false , // not cold
8400
+ ) ,
8401
+ HeatMapLayer :: new(
8402
+ visible_delta. layer_name( ) ,
8403
+ // Visible delta layer is from an older generation in heatmap
8404
+ old_layer_metadata. clone( ) ,
8405
+ SystemTime :: now( ) ,
8406
+ false , // not cold
8407
+ ) ,
8408
+ HeatMapLayer :: new(
8409
+ l0_delta. layer_name( ) ,
8410
+ current_layer_metadata. clone( ) ,
8411
+ SystemTime :: now( ) ,
8412
+ false , // not cold
8413
+ ) ,
8414
+ ] ;
8415
+
8416
+ // Create the previous heatmap with old generation layers
8417
+ let prev_heatmap = HeatMapTimelineStruct :: new ( timeline. timeline_id , prev_heatmap_layers) ;
8418
+
8419
+ // Set the previous heatmap on the timeline
8420
+ timeline
8421
+ . previous_heatmap
8422
+ . store ( Some ( Arc :: new ( PreviousHeatmap :: Active {
8423
+ heatmap : prev_heatmap,
8424
+ read_at : std:: time:: Instant :: now ( ) ,
8425
+ end_lsn : None ,
8426
+ } ) ) ) ;
8427
+
8428
+ // Layer visibility is an input to heatmap generation, so refresh it first
8429
+ timeline. update_layer_visibility ( ) . await . unwrap ( ) ;
8430
+
8431
+ // Generate a new heatmap - this should filter out the old generation layers
8432
+ let heatmap = timeline
8433
+ . generate_heatmap ( )
8434
+ . await
8435
+ . expect ( "Infallible while timeline is not shut down" ) ;
8436
+
8437
+ assert_eq ! ( heatmap. timeline_id, timeline. timeline_id) ;
8438
+
8439
+ // Verify that layers exist but they should be the current generation ones,
8440
+ // not the old generation ones from previous_heatmap
8441
+ for layer in heatmap. all_layers ( ) {
8442
+ assert_eq ! (
8443
+ layer. metadata. generation,
8444
+ timeline. generation,
8445
+ "Heatmap should only contain current generation layers, not old ones"
8446
+ ) ;
8447
+ }
8448
+ }
8449
+
8266
8450
#[ tokio:: test]
8267
8451
async fn two_layer_eviction_attempts_at_the_same_time ( ) {
8268
8452
let harness = TenantHarness :: create ( "two_layer_eviction_attempts_at_the_same_time" )
0 commit comments