From cc877f1980d3b980aec30e2c410233c6fda039e4 Mon Sep 17 00:00:00 2001 From: anastasia Date: Fri, 4 Jun 2021 19:47:32 +0300 Subject: [PATCH] Add unit test for find_end_of_wal(). Based on previous attempt to add same test by @lubennikovaav Now WAL files are generated by initdb command. --- Cargo.lock | 1 + postgres_ffi/Cargo.toml | 1 + postgres_ffi/src/xlog_utils.rs | 70 ++++++++++++++++++++++++++++++++++ 3 files changed, 72 insertions(+) diff --git a/Cargo.lock b/Cargo.lock index cfbeba8a3c..1598fede16 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1309,6 +1309,7 @@ dependencies = [ "regex", "thiserror", "workspace_hack", + "zenith_utils", ] [[package]] diff --git a/postgres_ffi/Cargo.toml b/postgres_ffi/Cargo.toml index d9d522b5b9..0060ed94b4 100644 --- a/postgres_ffi/Cargo.toml +++ b/postgres_ffi/Cargo.toml @@ -20,6 +20,7 @@ log = "0.4.14" memoffset = "0.6.2" thiserror = "1.0" workspace_hack = { path = "../workspace_hack" } +zenith_utils = { path = "../zenith_utils" } [build-dependencies] bindgen = "0.57" diff --git a/postgres_ffi/src/xlog_utils.rs b/postgres_ffi/src/xlog_utils.rs index 4b35e2b27c..2b6297fa10 100644 --- a/postgres_ffi/src/xlog_utils.rs +++ b/postgres_ffi/src/xlog_utils.rs @@ -428,3 +428,73 @@ pub fn generate_wal_segment(pg_control: &ControlFileData) -> Bytes { seg_buf.resize(pg_constants::WAL_SEGMENT_SIZE, 0); seg_buf.freeze() } + +#[cfg(test)] +mod tests { + use super::*; + use regex::Regex; + use std::{env, process::Command, str::FromStr}; + use zenith_utils::lsn::Lsn; + + // Run find_end_of_wal against file in test_wal dir + // Ensure that it finds last record correctly + #[test] + pub fn test_find_end_of_wal() { + // 1. Run initdb to generate some WAL + let top_path = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join(".."); + let data_dir = top_path.join("test_output/test_find_end_of_wal"); + let initdb_path = top_path.join("tmp_install/bin/initdb"); + let lib_path = top_path.join("tmp_install/lib"); + if data_dir.exists() { + fs::remove_dir_all(&data_dir).unwrap(); + } + println!("Using initdb from '{}'", initdb_path.display()); + println!("Data directory '{}'", data_dir.display()); + let initdb_output = Command::new(initdb_path) + .args(&["-D", data_dir.to_str().unwrap()]) + .arg("--no-instructions") + .arg("--no-sync") + .env_clear() + .env("LD_LIBRARY_PATH", &lib_path) + .env("DYLD_LIBRARY_PATH", &lib_path) + .output() + .unwrap(); + assert!(initdb_output.status.success()); + + // 2. Pick WAL generated by initdb + let wal_dir = data_dir.join("pg_wal"); + let wal_seg_size = 16 * 1024 * 1024; + + // 3. Check end_of_wal on non-partial WAL segment (we treat it as fully populated) + let (wal_end, tli) = find_end_of_wal(&wal_dir, wal_seg_size, true); + let wal_end = Lsn(wal_end); + println!("wal_end={}, tli={}", wal_end, tli); + assert_eq!(wal_end, "0/2000000".parse::().unwrap()); + + // 4. Get the actual end of WAL by pg_waldump + let waldump_path = top_path.join("tmp_install/bin/pg_waldump"); + let waldump_output = Command::new(waldump_path) + .arg(wal_dir.join("000000010000000000000001")) + .env_clear() + .env("LD_LIBRARY_PATH", &lib_path) + .env("DYLD_LIBRARY_PATH", &lib_path) + .output() + .unwrap(); + let waldump_output = std::str::from_utf8(&waldump_output.stderr).unwrap(); + println!("waldump_output = '{}'", &waldump_output); + let re = Regex::new(r"invalid record length at (.+):").unwrap(); + let caps = re.captures(&waldump_output).unwrap(); + let waldump_wal_end = Lsn::from_str(caps.get(1).unwrap().as_str()).unwrap(); + + // 5. Rename file to partial to actually find last valid lsn + fs::rename( + wal_dir.join("000000010000000000000001"), + wal_dir.join("000000010000000000000001.partial"), + ) + .unwrap(); + let (wal_end, tli) = find_end_of_wal(&wal_dir, wal_seg_size, true); + let wal_end = Lsn(wal_end); + println!("wal_end={}, tli={}", wal_end, tli); + assert_eq!(wal_end, waldump_wal_end); + } +}