@@ -79,6 +79,24 @@ pub struct IndexPart {
79
79
/// when this flag is introduced.
80
80
#[ serde( skip_serializing_if = "Option::is_none" , default ) ]
81
81
pub ( crate ) last_aux_file_policy : Option < AuxFilePolicy > ,
82
+
83
+ #[ serde( skip_serializing_if = "Option::is_none" , default ) ]
84
+ pub ( crate ) rel_size_migration : Option < RelSizeMigration > ,
85
+ }
86
+
87
+ #[ derive( Debug , Clone , PartialEq , Eq , Serialize , Deserialize ) ]
88
+ #[ serde( rename_all = "camelCase" ) ]
89
+ pub enum RelSizeMigration {
90
+ /// The tenant is using the old rel_size format.
91
+ /// Note that this enum is persisted as `Option<RelSizeMigration>` in the index part, so
92
+ /// `None` is the same as `Some(RelSizeMigration::Legacy)`.
93
+ Legacy ,
94
+ /// The tenant is migrating to the new rel_size format. Both old and new rel_size format are
95
+ /// persisted in the index part. The read path will read both formats and merge them.
96
+ Migrating ,
97
+ /// The tenant has migrated to the new rel_size format. Only the new rel_size format is persisted
98
+ /// in the index part, and the read path will not read the old format.
99
+ Migrated ,
82
100
}
83
101
84
102
impl IndexPart {
@@ -97,10 +115,11 @@ impl IndexPart {
97
115
/// - 8: added `archived_at`
98
116
/// - 9: +gc_blocking
99
117
/// - 10: +import_pgdata
100
- const LATEST_VERSION : usize = 10 ;
118
+ /// - 11: +rel_size_migration
119
+ const LATEST_VERSION : usize = 11 ;
101
120
102
121
// Versions we may see when reading from a bucket.
103
- pub const KNOWN_VERSIONS : & ' static [ usize ] = & [ 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 10 ] ;
122
+ pub const KNOWN_VERSIONS : & ' static [ usize ] = & [ 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 10 , 11 ] ;
104
123
105
124
pub const FILE_NAME : & ' static str = "index_part.json" ;
106
125
@@ -116,6 +135,7 @@ impl IndexPart {
116
135
gc_blocking : None ,
117
136
last_aux_file_policy : None ,
118
137
import_pgdata : None ,
138
+ rel_size_migration : None ,
119
139
}
120
140
}
121
141
@@ -416,6 +436,7 @@ mod tests {
416
436
gc_blocking : None ,
417
437
last_aux_file_policy : None ,
418
438
import_pgdata : None ,
439
+ rel_size_migration : None ,
419
440
} ;
420
441
421
442
let part = IndexPart :: from_json_bytes ( example. as_bytes ( ) ) . unwrap ( ) ;
@@ -461,6 +482,7 @@ mod tests {
461
482
gc_blocking : None ,
462
483
last_aux_file_policy : None ,
463
484
import_pgdata : None ,
485
+ rel_size_migration : None ,
464
486
} ;
465
487
466
488
let part = IndexPart :: from_json_bytes ( example. as_bytes ( ) ) . unwrap ( ) ;
@@ -507,6 +529,7 @@ mod tests {
507
529
gc_blocking : None ,
508
530
last_aux_file_policy : None ,
509
531
import_pgdata : None ,
532
+ rel_size_migration : None ,
510
533
} ;
511
534
512
535
let part = IndexPart :: from_json_bytes ( example. as_bytes ( ) ) . unwrap ( ) ;
@@ -556,6 +579,7 @@ mod tests {
556
579
gc_blocking : None ,
557
580
last_aux_file_policy : None ,
558
581
import_pgdata : None ,
582
+ rel_size_migration : None ,
559
583
} ;
560
584
561
585
let empty_layers_parsed = IndexPart :: from_json_bytes ( empty_layers_json. as_bytes ( ) ) . unwrap ( ) ;
@@ -600,6 +624,7 @@ mod tests {
600
624
gc_blocking : None ,
601
625
last_aux_file_policy : None ,
602
626
import_pgdata : None ,
627
+ rel_size_migration : None ,
603
628
} ;
604
629
605
630
let part = IndexPart :: from_json_bytes ( example. as_bytes ( ) ) . unwrap ( ) ;
@@ -647,6 +672,7 @@ mod tests {
647
672
gc_blocking : None ,
648
673
last_aux_file_policy : None ,
649
674
import_pgdata : None ,
675
+ rel_size_migration : None ,
650
676
} ;
651
677
652
678
let part = IndexPart :: from_json_bytes ( example. as_bytes ( ) ) . unwrap ( ) ;
@@ -699,6 +725,7 @@ mod tests {
699
725
gc_blocking : None ,
700
726
last_aux_file_policy : Some ( AuxFilePolicy :: V2 ) ,
701
727
import_pgdata : None ,
728
+ rel_size_migration : None ,
702
729
} ;
703
730
704
731
let part = IndexPart :: from_json_bytes ( example. as_bytes ( ) ) . unwrap ( ) ;
@@ -756,6 +783,7 @@ mod tests {
756
783
gc_blocking : None ,
757
784
last_aux_file_policy : Default :: default ( ) ,
758
785
import_pgdata : None ,
786
+ rel_size_migration : None ,
759
787
} ;
760
788
761
789
let part = IndexPart :: from_json_bytes ( example. as_bytes ( ) ) . unwrap ( ) ;
@@ -814,6 +842,7 @@ mod tests {
814
842
gc_blocking : None ,
815
843
last_aux_file_policy : Default :: default ( ) ,
816
844
import_pgdata : None ,
845
+ rel_size_migration : None ,
817
846
} ;
818
847
819
848
let part = IndexPart :: from_json_bytes ( example. as_bytes ( ) ) . unwrap ( ) ;
@@ -877,6 +906,7 @@ mod tests {
877
906
last_aux_file_policy : Default :: default ( ) ,
878
907
archived_at : None ,
879
908
import_pgdata : None ,
909
+ rel_size_migration : None ,
880
910
} ;
881
911
882
912
let part = IndexPart :: from_json_bytes ( example. as_bytes ( ) ) . unwrap ( ) ;
@@ -952,7 +982,86 @@ mod tests {
952
982
started_at : parse_naive_datetime ( "2024-11-13T09:23:42.123000000" ) ,
953
983
finished_at : parse_naive_datetime ( "2024-11-13T09:42:23.123000000" ) ,
954
984
idempotency_key : import_pgdata:: index_part_format:: IdempotencyKey :: new ( "specified-by-client-218a5213-5044-4562-a28d-d024c5f057f5" . to_string ( ) ) ,
955
- } ) ) )
985
+ } ) ) ) ,
986
+ rel_size_migration : None ,
987
+ } ;
988
+
989
+ let part = IndexPart :: from_json_bytes ( example. as_bytes ( ) ) . unwrap ( ) ;
990
+ assert_eq ! ( part, expected) ;
991
+ }
992
+
993
+ #[ test]
994
+ fn v11_rel_size_migration_is_parsed ( ) {
995
+ let example = r#"{
996
+ "version": 11,
997
+ "layer_metadata":{
998
+ "000000000000000000000000000000000000-FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF__0000000001696070-00000000016960E9": { "file_size": 25600000 },
999
+ "000000000000000000000000000000000000-FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF__00000000016B59D8-00000000016B5A51": { "file_size": 9007199254741001 }
1000
+ },
1001
+ "disk_consistent_lsn":"0/16960E8",
1002
+ "metadata": {
1003
+ "disk_consistent_lsn": "0/16960E8",
1004
+ "prev_record_lsn": "0/1696070",
1005
+ "ancestor_timeline": "e45a7f37d3ee2ff17dc14bf4f4e3f52e",
1006
+ "ancestor_lsn": "0/0",
1007
+ "latest_gc_cutoff_lsn": "0/1696070",
1008
+ "initdb_lsn": "0/1696070",
1009
+ "pg_version": 14
1010
+ },
1011
+ "gc_blocking": {
1012
+ "started_at": "2024-07-19T09:00:00.123",
1013
+ "reasons": ["DetachAncestor"]
1014
+ },
1015
+ "import_pgdata": {
1016
+ "V1": {
1017
+ "Done": {
1018
+ "idempotency_key": "specified-by-client-218a5213-5044-4562-a28d-d024c5f057f5",
1019
+ "started_at": "2024-11-13T09:23:42.123",
1020
+ "finished_at": "2024-11-13T09:42:23.123"
1021
+ }
1022
+ }
1023
+ },
1024
+ "rel_size_migration": "legacy"
1025
+ }"# ;
1026
+
1027
+ let expected = IndexPart {
1028
+ version : 11 ,
1029
+ layer_metadata : HashMap :: from ( [
1030
+ ( "000000000000000000000000000000000000-FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF__0000000001696070-00000000016960E9" . parse ( ) . unwrap ( ) , LayerFileMetadata {
1031
+ file_size : 25600000 ,
1032
+ generation : Generation :: none ( ) ,
1033
+ shard : ShardIndex :: unsharded ( )
1034
+ } ) ,
1035
+ ( "000000000000000000000000000000000000-FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF__00000000016B59D8-00000000016B5A51" . parse ( ) . unwrap ( ) , LayerFileMetadata {
1036
+ file_size : 9007199254741001 ,
1037
+ generation : Generation :: none ( ) ,
1038
+ shard : ShardIndex :: unsharded ( )
1039
+ } )
1040
+ ] ) ,
1041
+ disk_consistent_lsn : "0/16960E8" . parse :: < Lsn > ( ) . unwrap ( ) ,
1042
+ metadata : TimelineMetadata :: new (
1043
+ Lsn :: from_str ( "0/16960E8" ) . unwrap ( ) ,
1044
+ Some ( Lsn :: from_str ( "0/1696070" ) . unwrap ( ) ) ,
1045
+ Some ( TimelineId :: from_str ( "e45a7f37d3ee2ff17dc14bf4f4e3f52e" ) . unwrap ( ) ) ,
1046
+ Lsn :: INVALID ,
1047
+ Lsn :: from_str ( "0/1696070" ) . unwrap ( ) ,
1048
+ Lsn :: from_str ( "0/1696070" ) . unwrap ( ) ,
1049
+ 14 ,
1050
+ ) . with_recalculated_checksum ( ) . unwrap ( ) ,
1051
+ deleted_at : None ,
1052
+ lineage : Default :: default ( ) ,
1053
+ gc_blocking : Some ( GcBlocking {
1054
+ started_at : parse_naive_datetime ( "2024-07-19T09:00:00.123000000" ) ,
1055
+ reasons : enumset:: EnumSet :: from_iter ( [ GcBlockingReason :: DetachAncestor ] ) ,
1056
+ } ) ,
1057
+ last_aux_file_policy : Default :: default ( ) ,
1058
+ archived_at : None ,
1059
+ import_pgdata : Some ( import_pgdata:: index_part_format:: Root :: V1 ( import_pgdata:: index_part_format:: V1 :: Done ( import_pgdata:: index_part_format:: Done {
1060
+ started_at : parse_naive_datetime ( "2024-11-13T09:23:42.123000000" ) ,
1061
+ finished_at : parse_naive_datetime ( "2024-11-13T09:42:23.123000000" ) ,
1062
+ idempotency_key : import_pgdata:: index_part_format:: IdempotencyKey :: new ( "specified-by-client-218a5213-5044-4562-a28d-d024c5f057f5" . to_string ( ) ) ,
1063
+ } ) ) ) ,
1064
+ rel_size_migration : Some ( RelSizeMigration :: Legacy ) ,
956
1065
} ;
957
1066
958
1067
let part = IndexPart :: from_json_bytes ( example. as_bytes ( ) ) . unwrap ( ) ;
0 commit comments