Skip to content

Commit f6b1891

Browse files
committed
feat(pageserver): enable reldirv2 by default in regress tests, try 2
Signed-off-by: Alex Chi Z <[email protected]>
1 parent fe7a4e1 commit f6b1891

File tree

12 files changed

+161
-67
lines changed

12 files changed

+161
-67
lines changed

control_plane/src/pageserver.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -571,6 +571,11 @@ impl PageServerNode {
571571
.map(|x| x.parse::<bool>())
572572
.transpose()
573573
.context("Failed to parse 'basebackup_cache_enabled' as bool")?,
574+
rel_size_v1_access_disabled: settings
575+
.remove("rel_size_v1_access_disabled")
576+
.map(|x| x.parse::<bool>())
577+
.transpose()
578+
.context("Failed to parse 'rel_size_v1_access_disabled' as bool")?,
574579
};
575580
if !settings.is_empty() {
576581
bail!("Unrecognized tenant settings: {settings:?}")

libs/pageserver_api/src/config.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -651,6 +651,9 @@ pub struct TenantConfigToml {
651651
// FIXME: Remove skip_serializing_if when the feature is stable.
652652
#[serde(skip_serializing_if = "std::ops::Not::not")]
653653
pub basebackup_cache_enabled: bool,
654+
655+
#[serde(skip_serializing_if = "std::ops::Not::not")]
656+
pub rel_size_v1_access_disabled: bool,
654657
}
655658

656659
pub mod defaults {
@@ -959,6 +962,7 @@ impl Default for TenantConfigToml {
959962
sampling_ratio: None,
960963
relsize_snapshot_cache_capacity: DEFAULT_RELSIZE_SNAPSHOT_CACHE_CAPACITY,
961964
basebackup_cache_enabled: false,
965+
rel_size_v1_access_disabled: false,
962966
}
963967
}
964968
}

libs/pageserver_api/src/models.rs

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -646,6 +646,8 @@ pub struct TenantConfigPatch {
646646
pub relsize_snapshot_cache_capacity: FieldPatch<usize>,
647647
#[serde(skip_serializing_if = "FieldPatch::is_noop")]
648648
pub basebackup_cache_enabled: FieldPatch<bool>,
649+
#[serde(skip_serializing_if = "FieldPatch::is_noop")]
650+
pub rel_size_v1_access_disabled: FieldPatch<bool>,
649651
}
650652

651653
/// Like [`crate::config::TenantConfigToml`], but preserves the information
@@ -783,6 +785,9 @@ pub struct TenantConfig {
783785

784786
#[serde(skip_serializing_if = "Option::is_none")]
785787
pub basebackup_cache_enabled: Option<bool>,
788+
789+
#[serde(skip_serializing_if = "Option::is_none")]
790+
pub rel_size_v1_access_disabled: Option<bool>,
786791
}
787792

788793
impl TenantConfig {
@@ -830,6 +835,7 @@ impl TenantConfig {
830835
mut sampling_ratio,
831836
mut relsize_snapshot_cache_capacity,
832837
mut basebackup_cache_enabled,
838+
mut rel_size_v1_access_disabled,
833839
} = self;
834840

835841
patch.checkpoint_distance.apply(&mut checkpoint_distance);
@@ -939,6 +945,9 @@ impl TenantConfig {
939945
patch
940946
.basebackup_cache_enabled
941947
.apply(&mut basebackup_cache_enabled);
948+
patch
949+
.rel_size_v1_access_disabled
950+
.apply(&mut rel_size_v1_access_disabled);
942951

943952
Ok(Self {
944953
checkpoint_distance,
@@ -980,6 +989,7 @@ impl TenantConfig {
980989
sampling_ratio,
981990
relsize_snapshot_cache_capacity,
982991
basebackup_cache_enabled,
992+
rel_size_v1_access_disabled,
983993
})
984994
}
985995

@@ -1094,6 +1104,9 @@ impl TenantConfig {
10941104
basebackup_cache_enabled: self
10951105
.basebackup_cache_enabled
10961106
.unwrap_or(global_conf.basebackup_cache_enabled),
1107+
rel_size_v1_access_disabled: self
1108+
.rel_size_v1_access_disabled
1109+
.unwrap_or(global_conf.rel_size_v1_access_disabled),
10971110
}
10981111
}
10991112
}
@@ -1526,12 +1539,15 @@ pub struct OffloadedTimelineInfo {
15261539
pub archived_at: chrono::DateTime<chrono::Utc>,
15271540
}
15281541

1529-
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
1542+
/// The state of the rel size migration. This is persisted in the DbDir key and index part. Do not change without considering
1543+
/// compatibility.
1544+
#[derive(Default, Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
15301545
#[serde(rename_all = "camelCase")]
15311546
pub enum RelSizeMigration {
15321547
/// The tenant is using the old rel_size format.
15331548
/// Note that this enum is persisted as `Option<RelSizeMigration>` in the index part, so
15341549
/// `None` is the same as `Some(RelSizeMigration::Legacy)`.
1550+
#[default]
15351551
Legacy,
15361552
/// The tenant is migrating to the new rel_size format. Both old and new rel_size format are
15371553
/// persisted in the storage. The read path will read both formats and validate them.

pageserver/src/pgdatadir_mapping.rs

Lines changed: 96 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -720,9 +720,12 @@ impl Timeline {
720720
"inconsistent v1/v2 reldir keyspace for rel {}: v1_exists={}, v2_exists={}",
721721
tag,
722722
v1_exists,
723-
v2_exists
723+
v2_exists,
724724
);
725725
}
726+
Err(e) if e.is_cancel() => {
727+
// Cancellation errors are fine to ignore, do not log.
728+
}
726729
Err(e) => {
727730
tracing::warn!("failed to get rel exists in v2: {e}");
728731
}
@@ -811,11 +814,11 @@ impl Timeline {
811814
forknum: key.field5,
812815
};
813816
if val == RelDirExists::Removed {
814-
debug_assert!(!rels.contains(&tag), "removed reltag in v2");
817+
debug_assert!(!rels.contains(&tag), "removed reltag in v2: {tag}");
815818
continue;
816819
}
817820
let did_not_contain = rels.insert(tag);
818-
debug_assert!(did_not_contain, "duplicate reltag in v2");
821+
debug_assert!(did_not_contain, "duplicate reltag in v2: {tag}");
819822
}
820823
Ok(rels)
821824
}
@@ -863,6 +866,9 @@ impl Timeline {
863866
rels_v2.len()
864867
);
865868
}
869+
Err(e) if e.is_cancel() => {
870+
// Cancellation errors are fine to ignore, do not log.
871+
}
866872
Err(e) => {
867873
tracing::warn!("failed to list rels in v2: {e}");
868874
}
@@ -1724,6 +1730,8 @@ pub struct RelDirMode {
17241730
current_status: RelSizeMigration,
17251731
// Whether we should initialize the v2 keyspace or not.
17261732
initialize: bool,
1733+
// Whether we should disable v1 access starting this LSNor not
1734+
disable_v1: bool,
17271735
}
17281736

17291737
impl DatadirModification<'_> {
@@ -2085,44 +2093,82 @@ impl DatadirModification<'_> {
20852093
///
20862094
/// As this function is only used on the write path, we do not need to read the migrated_at
20872095
/// field.
2088-
pub fn maybe_enable_rel_size_v2(&mut self, is_create: bool) -> anyhow::Result<RelDirMode> {
2096+
pub(crate) fn maybe_enable_rel_size_v2(
2097+
&mut self,
2098+
lsn: Lsn,
2099+
is_create: bool,
2100+
) -> anyhow::Result<RelDirMode> {
20892101
// TODO: define the behavior of the tenant-level config flag and use feature flag to enable this feature
2102+
let expected_status = self.tline.get_rel_size_v2_expected_state();
2103+
let (persistent_status, migrated_at) = self.tline.get_rel_size_v2_status();
2104+
2105+
// migrated_at LSN means the LSN where we "enable_v2" (but not "disable_v1")
2106+
if let Some(migrated_at) = migrated_at
2107+
&& lsn <= migrated_at
2108+
&& persistent_status == RelSizeMigration::Migrating
2109+
{
2110+
// Revert the status to legacy if the write head LSN is before the migrating LSN.
2111+
self.tline
2112+
.update_rel_size_v2_status(RelSizeMigration::Legacy, None)?;
2113+
}
2114+
// TODO: what if the migration is at "migrated" state but we need to revert?
2115+
2116+
// Only initialize the v2 keyspace on new relation creation. No initialization
2117+
// during `timeline_create` (TODO: fix this, we should allow, but currently it
2118+
// hits expected consistency issues; need to ignore warnings).
2119+
let can_update = is_create && !self.is_importing_pgdata;
20902120

2091-
let (status, _) = self.tline.get_rel_size_v2_status();
2092-
let config = self.tline.get_rel_size_v2_enabled();
2093-
match (config, status) {
2094-
(false, RelSizeMigration::Legacy) => {
2121+
match (expected_status, persistent_status) {
2122+
(RelSizeMigration::Legacy, RelSizeMigration::Legacy) => {
20952123
// tenant config didn't enable it and we didn't write any reldir_v2 key yet
20962124
Ok(RelDirMode {
20972125
current_status: RelSizeMigration::Legacy,
20982126
initialize: false,
2127+
disable_v1: false,
20992128
})
21002129
}
2101-
(false, status @ RelSizeMigration::Migrating | status @ RelSizeMigration::Migrated) => {
2102-
// index_part already persisted that the timeline has enabled rel_size_v2
2130+
(
2131+
RelSizeMigration::Legacy,
2132+
current_status @ RelSizeMigration::Migrating
2133+
| current_status @ RelSizeMigration::Migrated,
2134+
) => {
2135+
// already persisted that the timeline has enabled rel_size_v2, cannot rollback
21032136
Ok(RelDirMode {
2104-
current_status: status,
2137+
current_status,
21052138
initialize: false,
2139+
disable_v1: false,
21062140
})
21072141
}
2108-
(true, RelSizeMigration::Legacy) => {
2142+
(
2143+
expected_status @ RelSizeMigration::Migrating
2144+
| expected_status @ RelSizeMigration::Migrated,
2145+
RelSizeMigration::Legacy,
2146+
) => {
21092147
// The first time we enable it, we need to persist it in `index_part.json`
21102148
// The caller should update the reldir status once the initialization is done.
2111-
//
2112-
// Only initialize the v2 keyspace on new relation creation. No initialization
2113-
// during `timeline_create` (TODO: fix this, we should allow, but currently it
2114-
// hits consistency issues).
2149+
21152150
Ok(RelDirMode {
21162151
current_status: RelSizeMigration::Legacy,
2117-
initialize: is_create && !self.is_importing_pgdata,
2152+
initialize: can_update,
2153+
disable_v1: can_update && expected_status == RelSizeMigration::Migrated,
21182154
})
21192155
}
2120-
(true, status @ RelSizeMigration::Migrating | status @ RelSizeMigration::Migrated) => {
2121-
// index_part already persisted that the timeline has enabled rel_size_v2
2122-
// and we don't need to do anything
2156+
(RelSizeMigration::Migrating, current_status @ RelSizeMigration::Migrating)
2157+
| (RelSizeMigration::Migrated, current_status @ RelSizeMigration::Migrated)
2158+
| (RelSizeMigration::Migrating, current_status @ RelSizeMigration::Migrated) => {
2159+
// Keep the current state
21232160
Ok(RelDirMode {
2124-
current_status: status,
2161+
current_status,
21252162
initialize: false,
2163+
disable_v1: false,
2164+
})
2165+
}
2166+
(RelSizeMigration::Migrated, RelSizeMigration::Migrating) => {
2167+
// Switch to v2-only mode
2168+
Ok(RelDirMode {
2169+
current_status: RelSizeMigration::Migrating,
2170+
initialize: false,
2171+
disable_v1: can_update,
21262172
})
21272173
}
21282174
}
@@ -2137,8 +2183,8 @@ impl DatadirModification<'_> {
21372183
ctx: &RequestContext,
21382184
) -> Result<(), WalIngestError> {
21392185
let v2_mode = self
2140-
.maybe_enable_rel_size_v2(false)
2141-
.map_err(WalIngestErrorKind::MaybeRelSizeV2Error)?;
2186+
.maybe_enable_rel_size_v2(self.lsn, false)
2187+
.map_err(WalIngestErrorKind::RelSizeV2Error)?;
21422188

21432189
// Add it to the directory (if it doesn't exist already)
21442190
let buf = self.get(DBDIR_KEY, ctx).await?;
@@ -2290,15 +2336,6 @@ impl DatadirModification<'_> {
22902336
sparse_rel_dir_key,
22912337
Value::Image(RelDirExists::Exists.encode()),
22922338
);
2293-
tracing::info!(
2294-
"migrated rel_size_v2: {}",
2295-
RelTag {
2296-
spcnode,
2297-
dbnode,
2298-
relnode,
2299-
forknum
2300-
}
2301-
);
23022339
rel_cnt += 1;
23032340
}
23042341
}
@@ -2307,9 +2344,6 @@ impl DatadirModification<'_> {
23072344
self.lsn,
23082345
rel_cnt
23092346
);
2310-
self.tline
2311-
.update_rel_size_v2_status(RelSizeMigration::Migrating, Some(self.lsn))
2312-
.map_err(WalIngestErrorKind::MaybeRelSizeV2Error)?;
23132347
Ok::<_, WalIngestError>(())
23142348
}
23152349

@@ -2392,35 +2426,50 @@ impl DatadirModification<'_> {
23922426
// It's possible that this is the first rel for this db in this
23932427
// tablespace. Create the reldir entry for it if so.
23942428
let mut dbdir = DbDirectory::des(&self.get(DBDIR_KEY, ctx).await?)?;
2429+
let mut is_dbdir_dirty = false;
23952430

23962431
let dbdir_exists =
23972432
if let hash_map::Entry::Vacant(e) = dbdir.dbdirs.entry((rel.spcnode, rel.dbnode)) {
23982433
// Didn't exist. Update dbdir
23992434
e.insert(false);
2400-
let buf = DbDirectory::ser(&dbdir)?;
24012435
self.pending_directory_entries.push((
24022436
DirectoryKind::Db,
24032437
MetricsUpdate::Set(dbdir.dbdirs.len() as u64),
24042438
));
2405-
self.put(DBDIR_KEY, Value::Image(buf.into()));
2439+
is_dbdir_dirty = true;
24062440
false
24072441
} else {
24082442
true
24092443
};
24102444

24112445
let mut v2_mode = self
2412-
.maybe_enable_rel_size_v2(true)
2413-
.map_err(WalIngestErrorKind::MaybeRelSizeV2Error)?;
2446+
.maybe_enable_rel_size_v2(self.lsn, true)
2447+
.map_err(WalIngestErrorKind::RelSizeV2Error)?;
24142448

24152449
if v2_mode.initialize {
24162450
if let Err(e) = self.initialize_rel_size_v2_keyspace(ctx, &dbdir).await {
24172451
tracing::warn!("error initializing rel_size_v2 keyspace: {}", e);
24182452
// TODO: circuit breaker so that it won't retry forever
24192453
} else {
24202454
v2_mode.current_status = RelSizeMigration::Migrating;
2455+
self.tline
2456+
.update_rel_size_v2_status(RelSizeMigration::Migrating, Some(self.lsn))
2457+
.map_err(WalIngestErrorKind::RelSizeV2Error)?;
24212458
}
24222459
}
24232460

2461+
if v2_mode.disable_v1 {
2462+
v2_mode.current_status = RelSizeMigration::Migrated;
2463+
self.tline
2464+
.update_rel_size_v2_status(RelSizeMigration::Migrated, Some(self.lsn))
2465+
.map_err(WalIngestErrorKind::RelSizeV2Error)?;
2466+
}
2467+
2468+
if is_dbdir_dirty {
2469+
let buf = DbDirectory::ser(&dbdir)?;
2470+
self.put(DBDIR_KEY, Value::Image(buf.into()));
2471+
}
2472+
24242473
if v2_mode.current_status != RelSizeMigration::Migrated {
24252474
self.put_rel_creation_v1(rel, dbdir_exists, ctx).await?;
24262475
}
@@ -2581,8 +2630,8 @@ impl DatadirModification<'_> {
25812630
ctx: &RequestContext,
25822631
) -> Result<(), WalIngestError> {
25832632
let v2_mode = self
2584-
.maybe_enable_rel_size_v2(false)
2585-
.map_err(WalIngestErrorKind::MaybeRelSizeV2Error)?;
2633+
.maybe_enable_rel_size_v2(self.lsn, false)
2634+
.map_err(WalIngestErrorKind::RelSizeV2Error)?;
25862635
match v2_mode.current_status {
25872636
RelSizeMigration::Legacy => {
25882637
self.put_rel_drop_v1(drop_relations, ctx).await?;
@@ -2600,6 +2649,12 @@ impl DatadirModification<'_> {
26002649
);
26012650
}
26022651
}
2652+
Err(WalIngestError {
2653+
kind: WalIngestErrorKind::Cancelled,
2654+
..
2655+
}) => {
2656+
// Cancellation errors are fine to ignore, do not log.
2657+
}
26032658
Err(e) => {
26042659
tracing::warn!("error dropping rels: {}", e);
26052660
}

pageserver/src/tenant/remote_timeline_client/index.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@ pub struct IndexPart {
8383
#[serde(skip_serializing_if = "Option::is_none", default)]
8484
pub(crate) last_aux_file_policy: Option<AuxFilePolicy>,
8585

86+
/// Deprecated: the field is not used anymore and the source of truth is now stored in the dbdir key.
8687
#[serde(skip_serializing_if = "Option::is_none", default)]
8788
pub(crate) rel_size_migration: Option<RelSizeMigration>,
8889

@@ -115,8 +116,7 @@ pub struct IndexPart {
115116
#[serde(skip_serializing_if = "Option::is_none", default)]
116117
pub(crate) marked_invisible_at: Option<NaiveDateTime>,
117118

118-
/// The LSN at which we started the rel size migration. Accesses below this LSN should be
119-
/// processed with the v1 read path. Usually this LSN should be set together with `rel_size_migration`.
119+
/// Deprecated: the field is not used anymore and the source of truth is now stored in the dbdir key.
120120
#[serde(skip_serializing_if = "Option::is_none", default)]
121121
pub(crate) rel_size_migrated_at: Option<Lsn>,
122122
}

0 commit comments

Comments
 (0)