Compare commits

..

2 Commits

Author SHA1 Message Date
Alex Chi Z
4834d1c888 fix test failure
Signed-off-by: Alex Chi Z <chi@neon.tech>
2025-01-27 21:50:17 +01:00
Alex Chi Z
b12a898bf9 fix(pageserver): workaround layer map limitations in gc-compaction
Signed-off-by: Alex Chi Z <chi@neon.tech>
2025-01-27 11:48:52 -05:00
8 changed files with 53 additions and 44 deletions

View File

@@ -21,10 +21,8 @@ The Neon storage engine consists of two major components:
See developer documentation in [SUMMARY.md](/docs/SUMMARY.md) for more information.
## Running a local development environment
## Running local installation
Neon can be run on a workstation for small experiments and to test code changes, by
following these instructions.
#### Installing dependencies on Linux
1. Install build dependencies and other applicable packages
@@ -240,7 +238,7 @@ postgres=# select * from t;
> cargo neon stop
```
More advanced usages can be found at [Local Development Control Plane (`neon_local`))](./control_plane/README.md).
More advanced usages can be found at [Control Plane and Neon Local](./control_plane/README.md).
#### Handling build failures

View File

@@ -1486,7 +1486,6 @@ impl ComputeNode {
// First, create control files for all availale extensions
extension_server::create_control_files(remote_extensions, &self.pgbin);
// Second, preload all remote extensions specified in the shared_preload_libraries
let library_load_start_time = Utc::now();
let remote_ext_metrics = self.prepare_preload_libraries(&pspec.spec)?;
@@ -1909,9 +1908,8 @@ LIMIT 100",
.as_ref()
.ok_or(anyhow::anyhow!("Remote extensions are not configured"))?;
let mut libs_vec = Vec::new();
info!("parse shared_preload_libraries from spec.cluster.settings");
let mut libs_vec = Vec::new();
if let Some(libs) = spec.cluster.settings.find("shared_preload_libraries") {
libs_vec = libs
.split(&[',', '\'', ' '])
@@ -1919,9 +1917,9 @@ LIMIT 100",
.map(str::to_string)
.collect();
}
// This is used in neon_local and python tests
info!("parse shared_preload_libraries from provided postgresql.conf");
// that is used in neon_local and python tests
if let Some(conf) = &spec.cluster.postgresql_conf {
let conf_lines = conf.split('\n').collect::<Vec<&str>>();
let mut shared_preload_libraries_line = "";
@@ -1945,10 +1943,7 @@ LIMIT 100",
// Assume that they are already present locally.
libs_vec.retain(|lib| remote_extensions.library_index.contains_key(lib));
info!(
"Downloading extensions specified in shared_preload_libraries: {:?}",
&libs_vec
);
info!("Downloading to shared preload libraries: {:?}", &libs_vec);
let mut download_tasks = Vec::new();
for library in &libs_vec {

View File

@@ -148,18 +148,18 @@ fn parse_pg_version(human_version: &str) -> PostgresMajorVersion {
},
_ => {}
}
panic!("Unsupported Postgres version {human_version}");
panic!("Unsuported postgres version {human_version}");
}
/// Download the archive for a given extension,
/// unzip it, and place files in the appropriate locations (share/lib)
// download the archive for a given extension,
// unzip it, and place files in the appropriate locations (share/lib)
pub async fn download_extension(
ext_name: &str,
ext_path: &RemotePath,
ext_remote_storage: &str,
pgbin: &str,
) -> Result<u64> {
info!("Downloading extension {:?} from {:?}", ext_name, ext_path);
info!("Download extension {:?} from {:?}", ext_name, ext_path);
// TODO add retry logic
let download_buffer =
@@ -200,23 +200,23 @@ pub async fn download_extension(
// move contents of the libdir / sharedir in unzipped archive to the correct local paths
for paths in [sharedir_paths, libdir_paths] {
let (zip_dir, real_dir) = paths;
info!("Moving {zip_dir:?}/* to {real_dir:?}");
info!("mv {zip_dir:?}/* {real_dir:?}");
for file in std::fs::read_dir(zip_dir)? {
let old_file = file?.path();
let new_file =
Path::new(&real_dir).join(old_file.file_name().context("error parsing file")?);
info!("Moving {old_file:?} to {new_file:?}");
info!("moving {old_file:?} to {new_file:?}");
// extension download failed: Directory not empty (os error 39)
match std::fs::rename(old_file, new_file) {
Ok(()) => info!("Move succeeded"),
Ok(()) => info!("move succeeded"),
Err(e) => {
warn!("Move failed, probably because the extension already exists: {e}")
warn!("move failed, probably because the extension already exists: {e}")
}
}
}
}
info!("Done moving extension {ext_name}");
info!("done moving extension {ext_name}");
Ok(download_size)
}
@@ -239,16 +239,10 @@ pub fn create_control_files(remote_extensions: &RemoteExtSpec, pgbin: &str) {
for (control_name, control_content) in &ext_data.control_data {
let control_path = local_sharedir.join(control_name);
if !control_path.exists() {
info!(
"Writing control file content {:?}: {:?}",
control_path, control_content
);
info!("writing file {:?}{:?}", control_path, control_content);
std::fs::write(control_path, control_content).unwrap();
} else {
warn!(
"Control file {:?} exists locally. Ignoring the version from the spec.",
control_path
);
warn!("control file {:?} exists both locally and remotely. ignoring the remote version.", control_path);
}
}
}
@@ -256,7 +250,9 @@ pub fn create_control_files(remote_extensions: &RemoteExtSpec, pgbin: &str) {
// Do request to extension storage proxy, i.e.
// curl http://pg-ext-s3-gateway/latest/v15/extensions/anon.tar.zst
// using HTTP GET and return the response body as bytes.
// using HHTP GET
// and return the response body as bytes
//
async fn download_extension_tar(ext_remote_storage: &str, ext_path: &str) -> Result<Bytes> {
let uri = format!("{}/{}", ext_remote_storage, ext_path);

View File

@@ -1,10 +1,6 @@
# Local Development Control Plane (`neon_local`)
# Control Plane and Neon Local
This crate contains tools to start a Neon development environment locally. This utility can be used with the `cargo neon` command. This is a convenience to invoke
the `neon_local` binary.
**Note**: this is a dev/test tool -- a minimal control plane suitable for testing
code changes locally, but not suitable for running production systems.
This crate contains tools to start a Neon development environment locally. This utility can be used with the `cargo neon` command.
## Example: Start with Postgres 16

View File

@@ -377,8 +377,7 @@ impl RemoteStorage for AzureBlobStorage {
let next_item = next_item?;
// Log a warning if we saw two timeouts in a row before a successful request
if timeout_try_cnt > 2 {
if timeout_try_cnt >= 2 {
tracing::warn!("Azure Blob Storage list timed out and succeeded after {} tries", timeout_try_cnt);
}
timeout_try_cnt = 1;

View File

@@ -8225,6 +8225,13 @@ mod tests {
assert_eq!(images.len(), 0); // the image layer should not contain tombstones, or it is not created
}
fn key_max_minus_1() -> Key {
Key {
field6: u32::MAX - 1,
..Key::MAX
}
}
#[tokio::test]
async fn test_simple_bottom_most_compaction_images() -> anyhow::Result<()> {
let harness = TenantHarness::create("test_simple_bottom_most_compaction_images").await?;
@@ -8405,7 +8412,7 @@ mod tests {
vec![
// Image layer at GC horizon
PersistentLayerKey {
key_range: Key::MIN..Key::MAX,
key_range: Key::MIN..key_max_minus_1(),
lsn_range: Lsn(0x30)..Lsn(0x31),
is_delta: false
},
@@ -10777,7 +10784,7 @@ mod tests {
vec![
// The compacted image layer (full key range)
PersistentLayerKey {
key_range: Key::MIN..Key::MAX,
key_range: Key::MIN..key_max_minus_1(),
lsn_range: Lsn(0x10)..Lsn(0x11),
is_delta: false,
},
@@ -11124,7 +11131,7 @@ mod tests {
vec![
// The compacted image layer (full key range)
PersistentLayerKey {
key_range: Key::MIN..Key::MAX,
key_range: Key::MIN..key_max_minus_1(),
lsn_range: Lsn(0x10)..Lsn(0x11),
is_delta: false,
},

View File

@@ -436,6 +436,16 @@ impl KeyHistoryRetention {
if dry_run {
return true;
}
if key.key_range.end == Key::MAX {
warn!(
key=%key,
"discard layer due to layer key range being Key::MAX, which should not happen",
);
if cfg!(feature = "testing") {
panic!("Key::MAX should not be part of the key space");
}
return true;
}
let layer_generation;
{
let guard = tline.layers.read().await;
@@ -2760,7 +2770,15 @@ impl Timeline {
let produced_image_layers = if let Some(writer) = image_layer_writer {
if !dry_run {
let end_key = job_desc.compaction_key_range.end;
let mut end_key = job_desc.compaction_key_range.end;
if end_key == Key::MAX {
// There's a potential bug to-be-resolved when end_key is Key::MAX, so we need to subtract 1 to workaround it.
// Note that we never write Key::MAX into the image layer, as this is not part of the key space.
end_key = Key {
field6: u32::MAX - 1,
..Key::MAX
};
}
writer
.finish_with_discard_fn(self, ctx, end_key, discard)
.await?

View File

@@ -49,7 +49,7 @@ pub(crate) fn regenerate(
};
// Express a static value for how many shards we may schedule on one node
const MAX_SHARDS: u32 = 5000;
const MAX_SHARDS: u32 = 20000;
let mut doc = PageserverUtilization {
disk_usage_bytes: used,