Skip to content

Commit 2de2b26

Browse files
authored
feat(pageserver): add reldir migration configs (#10439)
## Problem Part of #9516 per RFC at #10412 ## Summary of changes Adding the necessary config items and index_part items for the large relation count work. --------- Signed-off-by: Alex Chi Z <chi@neon.tech>
1 parent e781cf6 commit 2de2b26

File tree

7 files changed

+138
-3
lines changed

7 files changed

+138
-3
lines changed

control_plane/src/pageserver.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -418,6 +418,11 @@ impl PageServerNode {
418418
.map(serde_json::from_str)
419419
.transpose()
420420
.context("parse `wal_receiver_protocol_override` from json")?,
421+
rel_size_v2_enabled: settings
422+
.remove("rel_size_v2_enabled")
423+
.map(|x| x.parse::<bool>())
424+
.transpose()
425+
.context("Failed to parse 'rel_size_v2_enabled' as bool")?,
421426
};
422427
if !settings.is_empty() {
423428
bail!("Unrecognized tenant settings: {settings:?}")

libs/pageserver_api/src/config.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -301,6 +301,10 @@ pub struct TenantConfigToml {
301301
pub timeline_offloading: bool,
302302

303303
pub wal_receiver_protocol_override: Option<PostgresClientProtocol>,
304+
305+
/// Enable rel_size_v2 for this tenant. Once enabled, the tenant will persist this information into
306+
/// `index_part.json`, and it cannot be reversed.
307+
pub rel_size_v2_enabled: Option<bool>,
304308
}
305309

306310
pub mod defaults {
@@ -538,6 +542,7 @@ impl Default for TenantConfigToml {
538542
lsn_lease_length_for_ts: LsnLease::DEFAULT_LENGTH_FOR_TS,
539543
timeline_offloading: false,
540544
wal_receiver_protocol_override: None,
545+
rel_size_v2_enabled: None,
541546
}
542547
}
543548
}

libs/pageserver_api/src/models.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -497,6 +497,8 @@ pub struct TenantConfigPatch {
497497
pub timeline_offloading: FieldPatch<bool>,
498498
#[serde(skip_serializing_if = "FieldPatch::is_noop")]
499499
pub wal_receiver_protocol_override: FieldPatch<PostgresClientProtocol>,
500+
#[serde(skip_serializing_if = "FieldPatch::is_noop")]
501+
pub rel_size_v2_enabled: FieldPatch<bool>,
500502
}
501503

502504
/// An alternative representation of `pageserver::tenant::TenantConf` with
@@ -528,6 +530,7 @@ pub struct TenantConfig {
528530
pub lsn_lease_length_for_ts: Option<String>,
529531
pub timeline_offloading: Option<bool>,
530532
pub wal_receiver_protocol_override: Option<PostgresClientProtocol>,
533+
pub rel_size_v2_enabled: Option<bool>,
531534
}
532535

533536
impl TenantConfig {
@@ -557,6 +560,7 @@ impl TenantConfig {
557560
mut lsn_lease_length_for_ts,
558561
mut timeline_offloading,
559562
mut wal_receiver_protocol_override,
563+
mut rel_size_v2_enabled,
560564
} = self;
561565

562566
patch.checkpoint_distance.apply(&mut checkpoint_distance);
@@ -601,6 +605,7 @@ impl TenantConfig {
601605
patch
602606
.wal_receiver_protocol_override
603607
.apply(&mut wal_receiver_protocol_override);
608+
patch.rel_size_v2_enabled.apply(&mut rel_size_v2_enabled);
604609

605610
Self {
606611
checkpoint_distance,
@@ -627,6 +632,7 @@ impl TenantConfig {
627632
lsn_lease_length_for_ts,
628633
timeline_offloading,
629634
wal_receiver_protocol_override,
635+
rel_size_v2_enabled,
630636
}
631637
}
632638
}

pageserver/src/tenant.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5475,6 +5475,7 @@ pub(crate) mod harness {
54755475
lsn_lease_length_for_ts: Some(tenant_conf.lsn_lease_length_for_ts),
54765476
timeline_offloading: Some(tenant_conf.timeline_offloading),
54775477
wal_receiver_protocol_override: tenant_conf.wal_receiver_protocol_override,
5478+
rel_size_v2_enabled: tenant_conf.rel_size_v2_enabled,
54785479
}
54795480
}
54805481
}

pageserver/src/tenant/config.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -357,6 +357,9 @@ pub struct TenantConfOpt {
357357

358358
#[serde(skip_serializing_if = "Option::is_none")]
359359
pub wal_receiver_protocol_override: Option<PostgresClientProtocol>,
360+
361+
#[serde(skip_serializing_if = "Option::is_none")]
362+
pub rel_size_v2_enabled: Option<bool>,
360363
}
361364

362365
impl TenantConfOpt {
@@ -425,6 +428,7 @@ impl TenantConfOpt {
425428
wal_receiver_protocol_override: self
426429
.wal_receiver_protocol_override
427430
.or(global_conf.wal_receiver_protocol_override),
431+
rel_size_v2_enabled: self.rel_size_v2_enabled.or(global_conf.rel_size_v2_enabled),
428432
}
429433
}
430434

@@ -454,6 +458,7 @@ impl TenantConfOpt {
454458
mut lsn_lease_length_for_ts,
455459
mut timeline_offloading,
456460
mut wal_receiver_protocol_override,
461+
mut rel_size_v2_enabled,
457462
} = self;
458463

459464
patch.checkpoint_distance.apply(&mut checkpoint_distance);
@@ -522,6 +527,7 @@ impl TenantConfOpt {
522527
patch
523528
.wal_receiver_protocol_override
524529
.apply(&mut wal_receiver_protocol_override);
530+
patch.rel_size_v2_enabled.apply(&mut rel_size_v2_enabled);
525531

526532
Ok(Self {
527533
checkpoint_distance,
@@ -548,6 +554,7 @@ impl TenantConfOpt {
548554
lsn_lease_length_for_ts,
549555
timeline_offloading,
550556
wal_receiver_protocol_override,
557+
rel_size_v2_enabled,
551558
})
552559
}
553560
}
@@ -603,6 +610,7 @@ impl From<TenantConfOpt> for models::TenantConfig {
603610
lsn_lease_length_for_ts: value.lsn_lease_length_for_ts.map(humantime),
604611
timeline_offloading: value.timeline_offloading,
605612
wal_receiver_protocol_override: value.wal_receiver_protocol_override,
613+
rel_size_v2_enabled: value.rel_size_v2_enabled,
606614
}
607615
}
608616
}

pageserver/src/tenant/remote_timeline_client/index.rs

Lines changed: 112 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,24 @@ pub struct IndexPart {
7979
/// when this flag is introduced.
8080
#[serde(skip_serializing_if = "Option::is_none", default)]
8181
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,
82100
}
83101

84102
impl IndexPart {
@@ -97,10 +115,11 @@ impl IndexPart {
97115
/// - 8: added `archived_at`
98116
/// - 9: +gc_blocking
99117
/// - 10: +import_pgdata
100-
const LATEST_VERSION: usize = 10;
118+
/// - 11: +rel_size_migration
119+
const LATEST_VERSION: usize = 11;
101120

102121
// 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];
104123

105124
pub const FILE_NAME: &'static str = "index_part.json";
106125

@@ -116,6 +135,7 @@ impl IndexPart {
116135
gc_blocking: None,
117136
last_aux_file_policy: None,
118137
import_pgdata: None,
138+
rel_size_migration: None,
119139
}
120140
}
121141

@@ -416,6 +436,7 @@ mod tests {
416436
gc_blocking: None,
417437
last_aux_file_policy: None,
418438
import_pgdata: None,
439+
rel_size_migration: None,
419440
};
420441

421442
let part = IndexPart::from_json_bytes(example.as_bytes()).unwrap();
@@ -461,6 +482,7 @@ mod tests {
461482
gc_blocking: None,
462483
last_aux_file_policy: None,
463484
import_pgdata: None,
485+
rel_size_migration: None,
464486
};
465487

466488
let part = IndexPart::from_json_bytes(example.as_bytes()).unwrap();
@@ -507,6 +529,7 @@ mod tests {
507529
gc_blocking: None,
508530
last_aux_file_policy: None,
509531
import_pgdata: None,
532+
rel_size_migration: None,
510533
};
511534

512535
let part = IndexPart::from_json_bytes(example.as_bytes()).unwrap();
@@ -556,6 +579,7 @@ mod tests {
556579
gc_blocking: None,
557580
last_aux_file_policy: None,
558581
import_pgdata: None,
582+
rel_size_migration: None,
559583
};
560584

561585
let empty_layers_parsed = IndexPart::from_json_bytes(empty_layers_json.as_bytes()).unwrap();
@@ -600,6 +624,7 @@ mod tests {
600624
gc_blocking: None,
601625
last_aux_file_policy: None,
602626
import_pgdata: None,
627+
rel_size_migration: None,
603628
};
604629

605630
let part = IndexPart::from_json_bytes(example.as_bytes()).unwrap();
@@ -647,6 +672,7 @@ mod tests {
647672
gc_blocking: None,
648673
last_aux_file_policy: None,
649674
import_pgdata: None,
675+
rel_size_migration: None,
650676
};
651677

652678
let part = IndexPart::from_json_bytes(example.as_bytes()).unwrap();
@@ -699,6 +725,7 @@ mod tests {
699725
gc_blocking: None,
700726
last_aux_file_policy: Some(AuxFilePolicy::V2),
701727
import_pgdata: None,
728+
rel_size_migration: None,
702729
};
703730

704731
let part = IndexPart::from_json_bytes(example.as_bytes()).unwrap();
@@ -756,6 +783,7 @@ mod tests {
756783
gc_blocking: None,
757784
last_aux_file_policy: Default::default(),
758785
import_pgdata: None,
786+
rel_size_migration: None,
759787
};
760788

761789
let part = IndexPart::from_json_bytes(example.as_bytes()).unwrap();
@@ -814,6 +842,7 @@ mod tests {
814842
gc_blocking: None,
815843
last_aux_file_policy: Default::default(),
816844
import_pgdata: None,
845+
rel_size_migration: None,
817846
};
818847

819848
let part = IndexPart::from_json_bytes(example.as_bytes()).unwrap();
@@ -877,6 +906,7 @@ mod tests {
877906
last_aux_file_policy: Default::default(),
878907
archived_at: None,
879908
import_pgdata: None,
909+
rel_size_migration: None,
880910
};
881911

882912
let part = IndexPart::from_json_bytes(example.as_bytes()).unwrap();
@@ -952,7 +982,86 @@ mod tests {
952982
started_at: parse_naive_datetime("2024-11-13T09:23:42.123000000"),
953983
finished_at: parse_naive_datetime("2024-11-13T09:42:23.123000000"),
954984
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),
9561065
};
9571066

9581067
let part = IndexPart::from_json_bytes(example.as_bytes()).unwrap();

test_runner/regress/test_attach_tenant_config.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,7 @@ def test_fully_custom_config(positive_env: NeonEnv):
176176
"type": "interpreted",
177177
"args": {"format": "bincode", "compression": {"zstd": {"level": 1}}},
178178
},
179+
"rel_size_v2_enabled": True,
179180
}
180181

181182
vps_http = env.storage_controller.pageserver_api()

0 commit comments

Comments
 (0)