diff --git a/libs/postgres_ffi/src/waldecoder.rs b/libs/postgres_ffi/src/waldecoder.rs index 91542d268f..f7bd70653c 100644 --- a/libs/postgres_ffi/src/waldecoder.rs +++ b/libs/postgres_ffi/src/waldecoder.rs @@ -226,10 +226,10 @@ impl WalStreamDecoder { self.padlen = self.lsn.calc_padding(8u32) as u32; } - // Always align resulting LSN on 0x8 boundary -- that is important for getPage() - // and WalReceiver integration. Since this code is used both for WalReceiver and - // initial WAL import let's force alignment right here. - let result = (self.lsn.align(), recordbuf); + // We should return LSN of the next record, not the last byte of this record or + // the byte immediately after. Note that this handles both XLOG_SWITCH and usual + // records, the former "spans" until the next WAL segment (see test_xlog_switch). + let result = (self.lsn + self.padlen as u64, recordbuf); Ok(Some(result)) } } diff --git a/libs/postgres_ffi/wal_generate/src/bin/wal_generate.rs b/libs/postgres_ffi/wal_generate/src/bin/wal_generate.rs index 0da47f32c1..6ed34caf28 100644 --- a/libs/postgres_ffi/wal_generate/src/bin/wal_generate.rs +++ b/libs/postgres_ffi/wal_generate/src/bin/wal_generate.rs @@ -13,6 +13,7 @@ fn main() -> Result<()> { .help("Type of WAL to generate") .possible_values([ "simple", + "last_wal_record_xlog_switch", "last_wal_record_crossing_segment", "wal_record_crossing_segment_followed_by_small_one", ]) @@ -57,6 +58,7 @@ fn main() -> Result<()> { let wal_generate = |arg_matches: &ArgMatches, client| { let lsn = match arg_matches.value_of("type").unwrap() { "simple" => generate_simple(client)?, + "last_wal_record_xlog_switch" => generate_last_wal_record_xlog_switch(client)?, "last_wal_record_crossing_segment" => { generate_last_wal_record_crossing_segment(client)? } diff --git a/libs/postgres_ffi/wal_generate/src/lib.rs b/libs/postgres_ffi/wal_generate/src/lib.rs index 78ce320515..01639ccfff 100644 --- a/libs/postgres_ffi/wal_generate/src/lib.rs +++ b/libs/postgres_ffi/wal_generate/src/lib.rs @@ -250,6 +250,25 @@ pub fn generate_simple(client: &mut impl postgres::GenericClient) -> Result Result { + // Do not use generate_internal because here we end up with flush_lsn exactly on + // the segment boundary and insert_lsn after the initial page header, which is unusual. + ensure_server_config(client)?; + + client.execute("CREATE table t(x int)", &[])?; + let after_xlog_switch: PgLsn = client.query_one("SELECT pg_switch_wal()", &[])?.get(0); + let next_segment = PgLsn::from(0x0200_0000); + ensure!( + after_xlog_switch <= next_segment, + "XLOG_SWITCH message ended after the expected segment boundary: {} > {}", + after_xlog_switch, + next_segment + ); + Ok(next_segment) +} + fn generate_single_logical_message( client: &mut impl postgres::GenericClient, transactional: bool, diff --git a/test_runner/batch_others/test_crafted_wal_end.py b/test_runner/batch_others/test_crafted_wal_end.py index 1ddaadfae9..c4674f802e 100644 --- a/test_runner/batch_others/test_crafted_wal_end.py +++ b/test_runner/batch_others/test_crafted_wal_end.py @@ -9,6 +9,7 @@ import pytest @pytest.mark.parametrize('wal_type', [ 'simple', + 'last_wal_record_xlog_switch', 'last_wal_record_crossing_segment', 'wal_record_crossing_segment_followed_by_small_one', ])