From 4d27048d6d7f83f646f74d3b5fcdbbf7b4f0355c Mon Sep 17 00:00:00 2001 From: Heikki Linnakangas Date: Thu, 12 Sep 2024 20:46:01 +0300 Subject: [PATCH 1/3] Import SLRUs --- pageserver/src/pg_import.rs | 75 +++++++++++++++++++++++++++++ pageserver/src/pgdatadir_mapping.rs | 4 +- 2 files changed, 77 insertions(+), 2 deletions(-) diff --git a/pageserver/src/pg_import.rs b/pageserver/src/pg_import.rs index 34c962c5ce..720548c1b3 100644 --- a/pageserver/src/pg_import.rs +++ b/pageserver/src/pg_import.rs @@ -13,6 +13,7 @@ use utils::{id::{NodeId, TenantId, TimelineId}, shard::{ShardCount, ShardNumber, use walkdir::WalkDir; use crate::{context::{DownloadBehavior, RequestContext}, pgdatadir_mapping::{DbDirectory, RelDirectory}, task_mgr::TaskKind, tenant::storage_layer::ImageLayerWriter}; +use crate::pgdatadir_mapping::SlruSegmentDirectory; use crate::config::PageServerConf; use tokio::io::AsyncReadExt; @@ -25,8 +26,12 @@ use crate::tenant::remote_timeline_client; use crate::tenant::remote_timeline_client::LayerFileMetadata; use pageserver_api::shard::ShardIndex; use pageserver_api::key::Key; +use pageserver_api::reltag::SlruKind; +use pageserver_api::key::{slru_block_to_key, slru_dir_to_key, slru_segment_size_to_key}; use utils::bin_ser::BeSer; +use std::collections::HashSet; + pub struct PgImportEnv { ctx: RequestContext, conf: &'static PageServerConf, @@ -92,6 +97,15 @@ impl PgImportEnv { self.import_db(&mut one_big_layer, &db).await?; } + // Import SLRUs + + // pg_xact (01:00 keyspace) + self.import_slru(&mut one_big_layer, SlruKind::Clog, &pgdata_path.join("pg_xact")).await?; + // pg_multixact/members (01:01 keyspace) + self.import_slru(&mut one_big_layer, SlruKind::MultiXactMembers, &pgdata_path.join("pg_multixact/members")).await?; + // pg_multixact/offsets (01:02 keyspace) + self.import_slru(&mut one_big_layer, SlruKind::MultiXactOffsets, &pgdata_path.join("pg_multixact/offsets")).await?; + let layerdesc = one_big_layer.finish_raw(&self.ctx).await?; // should we anything about the wal? @@ -209,6 +223,67 @@ impl PgImportEnv { Ok(()) } + async fn import_slru( + &mut self, + layer_writer: &mut ImageLayerWriter, + kind: SlruKind, + path: &Utf8PathBuf, + ) -> anyhow::Result<()> { + let segments: Vec<(String, u32)> = WalkDir::new(path) + .max_depth(1) + .into_iter() + .filter_map(|entry| { + let entry = entry.ok()?; + let filename = entry.file_name(); + let filename = filename.to_string_lossy(); + let segno = u32::from_str_radix(&filename, 16).ok()?; + Some((filename.to_string(), segno)) + }).collect(); + + // Write SlruDir + let slrudir_key = slru_dir_to_key(kind); + let segnos: HashSet = segments.iter().map(|(_path, segno)| { *segno }).collect(); + let slrudir = SlruSegmentDirectory { + segments: segnos, + }; + let slrudir_buf = SlruSegmentDirectory::ser(&slrudir)?; + layer_writer.put_image(slrudir_key, slrudir_buf.into(), &self.ctx).await?; + + for (segpath, segno) in segments { + // SlruSegBlocks for each segment + let p = path.join(Utf8PathBuf::from(segpath)); + let mut reader = tokio::fs::File::open(&p).await + .context(format!("opening {}", &p))?; + + let mut rpageno = 0; + loop { + let mut buf: Vec = Vec::new(); + buf.resize(8192, 0); + let r = reader.read_exact(&mut buf).await; + match r { + Ok(_) => {}, + Err(err) if err.kind() == std::io::ErrorKind::UnexpectedEof => { + // reached EOF. That's expected + break; + } + Err(err) => { + bail!("error reading file {}: {:#}", &p, err); + } + }; + let slruseg_key = slru_block_to_key(kind, segno, rpageno); + layer_writer.put_image(slruseg_key, Bytes::from(buf), &self.ctx).await?; + rpageno += 1; + } + let npages: u32 = rpageno; + + // Followed by SlruSegSize + let segsize_key = slru_segment_size_to_key(kind, segno); + let segsize_buf = npages.to_le_bytes(); + layer_writer.put_image(segsize_key, Bytes::copy_from_slice(&segsize_buf), &self.ctx).await?; + } + Ok(()) + } + async fn create_index_part(&mut self, layers: &[PersistentLayerDesc], control_file: &ControlFileData) -> anyhow::Result<()> { let dstdir = &self.conf.workdir; diff --git a/pageserver/src/pgdatadir_mapping.rs b/pageserver/src/pgdatadir_mapping.rs index ab655bc724..1dc4b4667e 100644 --- a/pageserver/src/pgdatadir_mapping.rs +++ b/pageserver/src/pgdatadir_mapping.rs @@ -2022,9 +2022,9 @@ struct RelSizeEntry { } #[derive(Debug, Serialize, Deserialize, Default)] -struct SlruSegmentDirectory { +pub(crate) struct SlruSegmentDirectory { // Set of SLRU segments that exist. - segments: HashSet, + pub(crate) segments: HashSet, } #[derive(Copy, Clone, PartialEq, Eq, Debug, enum_map::Enum)] From 85f4e966e8470edd679252e988f0f34b3e5eba3b Mon Sep 17 00:00:00 2001 From: Heikki Linnakangas Date: Thu, 12 Sep 2024 20:54:16 +0300 Subject: [PATCH 2/3] Import dummy pg_twophase dir entry --- pageserver/src/pg_import.rs | 11 +++++++++-- pageserver/src/pgdatadir_mapping.rs | 4 ++-- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/pageserver/src/pg_import.rs b/pageserver/src/pg_import.rs index 720548c1b3..b2960f76b6 100644 --- a/pageserver/src/pg_import.rs +++ b/pageserver/src/pg_import.rs @@ -13,7 +13,7 @@ use utils::{id::{NodeId, TenantId, TimelineId}, shard::{ShardCount, ShardNumber, use walkdir::WalkDir; use crate::{context::{DownloadBehavior, RequestContext}, pgdatadir_mapping::{DbDirectory, RelDirectory}, task_mgr::TaskKind, tenant::storage_layer::ImageLayerWriter}; -use crate::pgdatadir_mapping::SlruSegmentDirectory; +use crate::pgdatadir_mapping::{SlruSegmentDirectory, TwoPhaseDirectory}; use crate::config::PageServerConf; use tokio::io::AsyncReadExt; @@ -27,7 +27,7 @@ use crate::tenant::remote_timeline_client::LayerFileMetadata; use pageserver_api::shard::ShardIndex; use pageserver_api::key::Key; use pageserver_api::reltag::SlruKind; -use pageserver_api::key::{slru_block_to_key, slru_dir_to_key, slru_segment_size_to_key}; +use pageserver_api::key::{slru_block_to_key, slru_dir_to_key, slru_segment_size_to_key, TWOPHASEDIR_KEY}; use utils::bin_ser::BeSer; use std::collections::HashSet; @@ -106,6 +106,13 @@ impl PgImportEnv { // pg_multixact/offsets (01:02 keyspace) self.import_slru(&mut one_big_layer, SlruKind::MultiXactOffsets, &pgdata_path.join("pg_multixact/offsets")).await?; + // Import pg_twophase. + // TODO: as empty + let twophasedir_buf = TwoPhaseDirectory::ser( + &TwoPhaseDirectory { xids: HashSet::new() } + )?; + one_big_layer.put_image(TWOPHASEDIR_KEY, Bytes::from(twophasedir_buf), &self.ctx).await?; + let layerdesc = one_big_layer.finish_raw(&self.ctx).await?; // should we anything about the wal? diff --git a/pageserver/src/pgdatadir_mapping.rs b/pageserver/src/pgdatadir_mapping.rs index 1dc4b4667e..4e8a2bb4a5 100644 --- a/pageserver/src/pgdatadir_mapping.rs +++ b/pageserver/src/pgdatadir_mapping.rs @@ -1988,8 +1988,8 @@ pub struct DbDirectory { } #[derive(Debug, Serialize, Deserialize)] -struct TwoPhaseDirectory { - xids: HashSet, +pub(crate) struct TwoPhaseDirectory { + pub(crate) xids: HashSet, } #[derive(Debug, Serialize, Deserialize, Default)] From 7b90ec6e1940d6367a0ba1ea6504911d60aab189 Mon Sep 17 00:00:00 2001 From: Heikki Linnakangas Date: Thu, 12 Sep 2024 21:01:04 +0300 Subject: [PATCH 3/3] Create controlfile and checkpoint entries XXX: untested, not sure if it works.. --- pageserver/src/pg_import.rs | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/pageserver/src/pg_import.rs b/pageserver/src/pg_import.rs index b2960f76b6..132f192fcd 100644 --- a/pageserver/src/pg_import.rs +++ b/pageserver/src/pg_import.rs @@ -27,7 +27,7 @@ use crate::tenant::remote_timeline_client::LayerFileMetadata; use pageserver_api::shard::ShardIndex; use pageserver_api::key::Key; use pageserver_api::reltag::SlruKind; -use pageserver_api::key::{slru_block_to_key, slru_dir_to_key, slru_segment_size_to_key, TWOPHASEDIR_KEY}; +use pageserver_api::key::{slru_block_to_key, slru_dir_to_key, slru_segment_size_to_key, TWOPHASEDIR_KEY, CONTROLFILE_KEY, CHECKPOINT_KEY}; use utils::bin_ser::BeSer; use std::collections::HashSet; @@ -70,7 +70,11 @@ impl PgImportEnv { pub async fn import_datadir(&mut self, pgdata_path: &Utf8PathBuf) -> anyhow::Result<()> { // Read control file - let control_file = self.import_controlfile(pgdata_path).await?; + let controlfile_path = pgdata_path.join("global").join("pg_control"); + let controlfile_buf = std::fs::read(&controlfile_path) + .with_context(|| format!("reading controlfile: {controlfile_path}"))?; + let control_file = ControlFileData::decode(&controlfile_buf)?; + let pgdata_lsn = Lsn(control_file.checkPoint).align(); let timeline_path = self.conf.timeline_path(&self.tsi, &self.tli); @@ -113,6 +117,12 @@ impl PgImportEnv { )?; one_big_layer.put_image(TWOPHASEDIR_KEY, Bytes::from(twophasedir_buf), &self.ctx).await?; + // Controlfile, checkpoint + one_big_layer.put_image(CONTROLFILE_KEY, Bytes::from(controlfile_buf), &self.ctx).await?; + + let checkpoint_buf = control_file.checkPointCopy.encode()?; + one_big_layer.put_image(CHECKPOINT_KEY, checkpoint_buf, &self.ctx).await?; + let layerdesc = one_big_layer.finish_raw(&self.ctx).await?; // should we anything about the wal? @@ -137,13 +147,6 @@ impl PgImportEnv { Ok(()) } - async fn import_controlfile(&mut self, pgdata_path: &Utf8Path) -> anyhow::Result { - let controlfile_path = pgdata_path.join("global").join("pg_control"); - let controlfile_buf = std::fs::read(&controlfile_path) - .with_context(|| format!("reading controlfile: {controlfile_path}"))?; - ControlFileData::decode(&controlfile_buf) - } - async fn import_db( &mut self, layer_writer: &mut ImageLayerWriter,