From 2a7d5d6a660bdbb04b50515e9b09bee4e8f694d8 Mon Sep 17 00:00:00 2001 From: Michael Paquier Date: Sun, 3 Sep 2023 14:38:27 +0900 Subject: [PATCH v4 3/3] Adjust few things on top of Thomas' patch series --- src/backend/access/transam/xlogreader.c | 10 +- src/test/perl/PostgreSQL/Test/Utils.pm | 43 +++ src/test/recovery/t/038_end_of_wal.pl | 386 +++++++++++++----------- 3 files changed, 253 insertions(+), 186 deletions(-) diff --git a/src/backend/access/transam/xlogreader.c b/src/backend/access/transam/xlogreader.c index d5ed139131..5bc7639a00 100644 --- a/src/backend/access/transam/xlogreader.c +++ b/src/backend/access/transam/xlogreader.c @@ -192,6 +192,9 @@ XLogReaderFree(XLogReaderState *state) * XLOG_BLCKSZ, and make sure it's at least 5*Max(BLCKSZ, XLOG_BLCKSZ) to start * with. (That is enough for all "normal" records, but very large commit or * abort records might need more space.) + * + * Note: This routine should *never* be called for xl_tot_len until the header + * of the record has been fully validated. */ static bool allocate_recordbuf(XLogReaderState *state, uint32 reclength) @@ -201,13 +204,6 @@ allocate_recordbuf(XLogReaderState *state, uint32 reclength) newSize += XLOG_BLCKSZ - (newSize % XLOG_BLCKSZ); newSize = Max(newSize, 5 * Max(BLCKSZ, XLOG_BLCKSZ)); -#ifndef FRONTEND - - if (!AllocSizeIsValid(newSize)) - return false; - -#endif - if (state->readRecordBuf) pfree(state->readRecordBuf); state->readRecordBuf = diff --git a/src/test/perl/PostgreSQL/Test/Utils.pm b/src/test/perl/PostgreSQL/Test/Utils.pm index 617caa022f..9de34a2260 100644 --- a/src/test/perl/PostgreSQL/Test/Utils.pm +++ b/src/test/perl/PostgreSQL/Test/Utils.pm @@ -71,6 +71,7 @@ our @EXPORT = qw( chmod_recursive check_pg_config dir_symlink + scan_server_header system_or_bail system_log run_log @@ -702,6 +703,48 @@ sub chmod_recursive =pod +=item scan_server_header(header_path, regexp) + +Returns an array that stores all the matches of the given regular expression +within the PostgreSQL installation's C. This can be used to +retrieve specific value patterns from the installation's header files. + +=cut + +sub scan_server_header +{ + my ($header_path, $regexp) = @_; + + my ($stdout, $stderr); + my $result = IPC::Run::run [ 'pg_config', '--includedir-server' ], '>', + \$stdout, '2>', \$stderr + or die "could not execute pg_config"; + chomp($stdout); + $stdout =~ s/\r$//; + + open my $header_h, '<', "$stdout/$header_path" or die "$!"; + + my @match = undef; + while (<$header_h>) + { + my $line = $_; + + if ($line =~ /^$regexp/) + { + # Found match, so store all the results. + @match = @{^CAPTURE}; + last; + } + } + + close $header_h; + die "could not find match in header $header_path\n" + unless @match; + return @match; +} + +=pod + =item check_pg_config(regexp) Return the number of matches of the given regular expression diff --git a/src/test/recovery/t/038_end_of_wal.pl b/src/test/recovery/t/038_end_of_wal.pl index fefb5b6034..aa5eec7c03 100644 --- a/src/test/recovery/t/038_end_of_wal.pl +++ b/src/test/recovery/t/038_end_of_wal.pl @@ -1,6 +1,7 @@ # Copyright (c) 2023, PostgreSQL Global Development Group # -# Test ways of detecting end of WAL. +# Test detecting end of WAL replay. This test suite generates fake +# WAL records able to trigger various failure scenarios at replay. use strict; use warnings; @@ -9,42 +10,44 @@ use PostgreSQL::Test::Utils; use Test::More; use Fcntl qw(SEEK_SET); -use integer; # causes / operator to use integer math +use integer; # causes / operator to use integer math -# Values that could be extracted from the source tree +# Header size of record header. my $RECORD_HEADER_SIZE = 24; -my $XLP_PAGE_MAGIC = 0xd113; -my $XLP_FIRST_IS_CONTRECORD = 0x1; + +# Fields retrieved from code headers. +my @scan_result = scan_server_header('access/xlog_internal.h', + '#define\s+XLOG_PAGE_MAGIC\s+(\w+)'); +my $XLP_PAGE_MAGIC = hex($scan_result[0]); +@scan_result = scan_server_header('access/xlog_internal.h', + '#define\s+XLP_FIRST_IS_CONTRECORD\s+(\w+)'); +my $XLP_FIRST_IS_CONTRECORD = hex($scan_result[0]); # Values queried from the server my $WAL_SEGMENT_SIZE; my $WAL_BLOCK_SIZE; +my $TLI; +# Build path of a WAL segment. sub wal_segment_path { my $node = shift; my $tli = shift; my $segment = shift; - my $wal_path = sprintf("%s/pg_wal/%08X%08X%08X", - $node->data_dir, - $tli, - 0, - $segment); + my $wal_path = + sprintf("%s/pg_wal/%08X%08X%08X", $node->data_dir, $tli, 0, $segment); return $wal_path; } +# Calculate from a LSN (in bytes) its segment number and its offset. sub lsn_to_segment_and_offset { my $lsn = shift; return ($lsn / $WAL_SEGMENT_SIZE, $lsn % $WAL_SEGMENT_SIZE); } -sub get_log_size -{ - my $node = shift; - return (stat $node->logfile)[7]; -} - +# Write some arbitrary data in WAL for the given segment at offset. +# This should be called while the cluster is offline. sub write_wal { my $node = shift; @@ -61,30 +64,37 @@ sub write_wal close $fh; } +# Emit a WAL record of arbitrary size. Returns the end LSN of the +# record inserted, in bytes. sub emit_message { my $node = shift; my $size = shift; - return int($node->safe_psql('postgres', "select pg_logical_emit_message(true, '', repeat('a', $size)) - '0/0'")); + return int( + $node->safe_psql( + 'postgres', + "SELECT pg_logical_emit_message(true, '', repeat('a', $size)) - '0/0'" + )); } +# Get the current insert LSN of a node, in bytes. sub get_insert_lsn { my $node = shift; - return int($node->safe_psql('postgres', "select pg_current_wal_insert_lsn() - '0/0'")); + return int( + $node->safe_psql( + 'postgres', "SELECT pg_current_wal_insert_lsn() - '0/0'")); } +# Get GUC value, converted to an int. sub get_int_setting { my $node = shift; my $name = shift; - return int($node->safe_psql('postgres', "select setting from pg_settings where name = '$name'")); -} - -sub format_lsn -{ - my $lsn = shift; - return sprintf("%X/%X", $lsn >> 32, $lsn & 0xffffffff); + return int( + $node->safe_psql( + 'postgres', + "SELECT setting FROM pg_settings WHERE name = '$name'")); } sub start_of_page @@ -99,7 +109,10 @@ sub start_of_next_page return start_of_page($lsn) + $WAL_BLOCK_SIZE; } -sub record_header +# Build a fake WAL record header based on the data given by the caller. +# This needs to follow the format of the C structure XLogRecord. To +# be inserted with write_wal(). +sub build_record_header { my $xl_tot_len = shift; my $xl_xid = shift || 0; @@ -108,19 +121,22 @@ sub record_header my $xl_rmid = shift || 0; my $xl_crc = shift || 0; - # struct XLogRecord + # This needs to follow the structure XLogRecord: + # I for xl_tot_len + # I for xl_xid + # Q for xl_prev + # C for xl_info + # C for xl_rmid + # BB for two bytes of padding + # I for xl_crc return pack("IIQCCBBI", - $xl_tot_len, - $xl_xid, - $xl_prev, - $xl_info, - $xl_rmid, - 0, - 0, - $xl_crc); + $xl_tot_len, $xl_xid, $xl_prev, $xl_info, $xl_rmid, 0, 0, $xl_crc); } -sub page_header +# Build a fake WAL page header, based on the data given by the caller +# This needs to follow the format of the C structure XLogPageHeaderData. +# To be inserted with write_wal(). +sub build_page_header { my $xlp_magic = shift; my $xlp_info = shift || 0; @@ -128,32 +144,37 @@ sub page_header my $xlp_pageaddr = shift || 0; my $xlp_rem_len = shift || 0; - # struct XLogPageHeaderData + # This needs to follow the structure XLogPageHeaderData: + # S for xlp_magic + # S for xlp_info + # I for xlp_tli + # Q for xlp_pageaddr + # I for xlp_rem_len return pack("SSIQI", - $xlp_magic, - $xlp_info, - $xlp_tli, - $xlp_pageaddr, - $xlp_rem_len); + $xlp_magic, $xlp_info, $xlp_tli, $xlp_pageaddr, $xlp_rem_len); } # Make sure we are far away enough from the end of a page that we could insert -# a couple of small records. +# a couple of small records. This inserts a few records of a fixed size, until +# the threshold gets close enough to the end of the WAL page inserting records +# to. sub advance_out_of_record_splitting_zone { my $node = shift; + my $page_threshold = $WAL_BLOCK_SIZE / 4; my $end_lsn = get_insert_lsn($node); my $page_offset = $end_lsn % $WAL_BLOCK_SIZE; - while ($page_offset >= $WAL_BLOCK_SIZE - 2000) + while ($page_offset >= $WAL_BLOCK_SIZE - $page_threshold) { - $end_lsn = emit_message($node, 2000); + $end_lsn = emit_message($node, $page_threshold); $page_offset = $end_lsn % $WAL_BLOCK_SIZE; } return $end_lsn; } -# Advance so close to the end of a page that an XLogRecordHeader wouldn't fit. +# Advance so close to the end of a page that an XLogRecordHeader would not +# fit on it. sub advance_to_record_splitting_zone { my $node = shift; @@ -161,15 +182,15 @@ sub advance_to_record_splitting_zone my $end_lsn = get_insert_lsn($node); my $page_offset = $end_lsn % $WAL_BLOCK_SIZE; - # get fairly close to the end of a page in big steps + # Get fairly close to the end of a page in big steps while ($page_offset <= $WAL_BLOCK_SIZE - 512) { $end_lsn = emit_message($node, $WAL_BLOCK_SIZE - $page_offset - 256); $page_offset = $end_lsn % $WAL_BLOCK_SIZE; } - # calibrate our message size so that we can get closer 8 bytes at - # a time (XXX something better is surely possible) + # Calibrate our message size so that we can get closer 8 bytes at + # a time. my $message_size = $WAL_BLOCK_SIZE - 80; while ($page_offset <= $WAL_BLOCK_SIZE - $RECORD_HEADER_SIZE) { @@ -178,8 +199,8 @@ sub advance_to_record_splitting_zone my $old_offset = $page_offset; $page_offset = $end_lsn % $WAL_BLOCK_SIZE; - # adjust the message size until it causes 8 bytes changes in - # offset + # Adjust the message size until it causes 8 bytes changes in + # offset, enough to be able to split a record header. my $delta = $page_offset - $old_offset; if ($delta > 8) { @@ -193,223 +214,230 @@ sub advance_to_record_splitting_zone return $end_lsn; } - +# Setup a new node. The configuration chosen here minimizes the number +# of arbitrary records that could get generated in a cluster. Enlarging +# checkpoint_timeout avoids noise with checkpoint activity. wal_level +# set to "minimal" avoids random standby snapshot records. Autovacuum +# could also trigger randomly, generating random WAL activity of its own. my $node = PostgreSQL::Test::Cluster->new("node"); $node->init; -$node->append_conf('postgresql.conf', - q[wal_level = minimal +$node->append_conf( + 'postgresql.conf', + q[wal_level = minimal autovacuum = off checkpoint_timeout = '30min' ]); $node->start; -$node->safe_psql('postgres', "create table t as select 42"); +$node->safe_psql('postgres', "CREATE TABLE t AS SELECT 42"); $WAL_SEGMENT_SIZE = get_int_setting($node, 'wal_segment_size'); $WAL_BLOCK_SIZE = get_int_setting($node, 'wal_block_size'); - +$TLI = $node->safe_psql('postgres', + "SELECT timeline_id FROM pg_control_checkpoint();"); my $end_lsn; my $prev_lsn; -my $log_size; ########################################################################### note "Single-page end-of-WAL detection"; ########################################################################### -# xl_tot_len is 0 (a common case, we hit trailing zeroes) +# xl_tot_len is 0 (a common case, we hit trailing zeroes). $end_lsn = advance_out_of_record_splitting_zone($node); $node->stop('immediate'); -$log_size = get_log_size($node); +my $log_size = -s $node->logfile; $node->start; -ok($node->log_contains("invalid record length at .*: expected at least 24, got 0", - $log_size), - "xl_tot_len zero"); +ok( $node->log_contains( + "invalid record length at .*: expected at least 24, got 0", $log_size + ), + "xl_tot_len zero"); -# xl_tot_len is < 24 (presumably recycled garbage) +# xl_tot_len is < 24 (presumably recycled garbage). $end_lsn = advance_out_of_record_splitting_zone($node); $node->stop('immediate'); -write_wal($node, 1, $end_lsn, record_header(23)); -$log_size = get_log_size($node); +write_wal($node, $TLI, $end_lsn, build_record_header(23)); +$log_size = -s $node->logfile; $node->start; -ok($node->log_contains("invalid record length at .*: expected at least 24, got 23", - $log_size), - "xl_tot_len short"); +ok( $node->log_contains( + "invalid record length at .*: expected at least 24, got 23", + $log_size), + "xl_tot_len short"); -# need more pages, but xl_prev check fails first +# Need more pages, but xl_prev check fails first. $end_lsn = advance_out_of_record_splitting_zone($node); $node->stop('immediate'); -write_wal($node, 1, $end_lsn, record_header(2 * 1024 * 1024 * 1024, - 0, - 0xdeadbeef)); -$log_size = get_log_size($node); +write_wal($node, $TLI, $end_lsn, + build_record_header(2 * 1024 * 1024 * 1024, 0, 0xdeadbeef)); +$log_size = -s $node->logfile; $node->start; -ok($node->log_contains("record with incorrect prev-link 0/DEADBEEF at .*", - $log_size), - "xl_prev bad"); +ok( $node->log_contains( + "record with incorrect prev-link 0/DEADBEEF at .*", $log_size), + "xl_prev bad"); -# xl_crc check fails +# xl_crc check fails. advance_out_of_record_splitting_zone($node); $end_lsn = emit_message($node, 10); $node->stop('immediate'); -write_wal($node, 1, $end_lsn - 8, '!'); # corrupt a byte in that record -$log_size = get_log_size($node); +# Corrupt a byte in that record, breaking its CRC. +write_wal($node, $TLI, $end_lsn - 8, '!'); +$log_size = -s $node->logfile; $node->start; -ok($node->log_contains("incorrect resource manager data checksum in record at .*", - $log_size), - "xl_crc bad"); +ok( $node->log_contains( + "incorrect resource manager data checksum in record at .*", $log_size + ), + "xl_crc bad"); ########################################################################### note "Multi-page end-of-WAL detection, header is not split"; ########################################################################### -# good xl_prev, we hit zero page next (zero magic) +# This series of tests requires a valid xl_prev set in the record header +# written to WAL. + +# Good xl_prev, we hit zero page next (zero magic). $prev_lsn = advance_out_of_record_splitting_zone($node); -#my $prev_lsn = emit_message($node, 0); $end_lsn = emit_message($node, 0); $node->stop('immediate'); -write_wal($node, 1, $end_lsn, record_header(2 * 1024 * 1024 * 1024, - 0, - $prev_lsn)); -$log_size = get_log_size($node); +write_wal($node, $TLI, $end_lsn, + build_record_header(2 * 1024 * 1024 * 1024, 0, $prev_lsn)); +$log_size = -s $node->logfile; $node->start; ok($node->log_contains("invalid magic number 0000 .* LSN .*", $log_size), - "xlp_magic zero"); + "xlp_magic zero"); -# good xl_prev, we hit garbage page next (bad magic) +# Good xl_prev, we hit garbage page next (bad magic). $prev_lsn = advance_out_of_record_splitting_zone($node); $end_lsn = emit_message($node, 0); $node->stop('immediate'); -write_wal($node, 1, $end_lsn, record_header(2 * 1024 * 1024 * 1024, - 0, - $prev_lsn)); -write_wal($node, 1, start_of_next_page($end_lsn), - page_header(0xcafe, - 0, - 1, - 0)); -$log_size = get_log_size($node); +write_wal($node, $TLI, $end_lsn, + build_record_header(2 * 1024 * 1024 * 1024, 0, $prev_lsn)); +write_wal( + $node, $TLI, + start_of_next_page($end_lsn), + build_page_header(0xcafe, 0, 1, 0)); +$log_size = -s $node->logfile; $node->start; ok($node->log_contains("invalid magic number CAFE .* LSN .*", $log_size), - "xlp_magic bad"); + "xlp_magic bad"); -# good xl_prev, we hit typical recycled page (good xlp_magic, bad xlp_pageaddr) +# Good xl_prev, we hit typical recycled page (good xlp_magic, bad +# xlp_pageaddr). $prev_lsn = advance_out_of_record_splitting_zone($node); $end_lsn = emit_message($node, 0); $node->stop('immediate'); -write_wal($node, 1, $end_lsn, record_header(2 * 1024 * 1024 * 1024, - 0, - $prev_lsn)); -write_wal($node, 1, start_of_next_page($end_lsn), - page_header($XLP_PAGE_MAGIC, - 0, - 1, - 0xbaaaaaad)); -$log_size = get_log_size($node); +write_wal($node, $TLI, $end_lsn, + build_record_header(2 * 1024 * 1024 * 1024, 0, $prev_lsn)); +write_wal( + $node, $TLI, + start_of_next_page($end_lsn), + build_page_header($XLP_PAGE_MAGIC, 0, 1, 0xbaaaaaad)); +$log_size = -s $node->logfile; $node->start; -ok($node->log_contains("unexpected pageaddr 0/BAAAAAAD in .*, LSN .*,", $log_size), - "xlp_pageaddr bad"); +ok( $node->log_contains( + "unexpected pageaddr 0/BAAAAAAD in .*, LSN .*,", $log_size), + "xlp_pageaddr bad"); -# good xl_prev, xlp_magic, xlp_pageaddr, but bogus xlp_info +# Good xl_prev, xlp_magic, xlp_pageaddr, but bogus xlp_info. $prev_lsn = advance_out_of_record_splitting_zone($node); $end_lsn = emit_message($node, 0); $node->stop('immediate'); -write_wal($node, 1, $end_lsn, record_header(2 * 1024 * 1024 * 1024, - 42, - $prev_lsn)); -write_wal($node, 1, start_of_next_page($end_lsn), - page_header($XLP_PAGE_MAGIC, - 0x1234, - 1, - start_of_next_page($end_lsn))); -$log_size = get_log_size($node); +write_wal($node, $TLI, $end_lsn, + build_record_header(2 * 1024 * 1024 * 1024, 42, $prev_lsn)); +write_wal( + $node, $TLI, + start_of_next_page($end_lsn), + build_page_header( + $XLP_PAGE_MAGIC, 0x1234, 1, start_of_next_page($end_lsn))); +$log_size = -s $node->logfile; $node->start; ok($node->log_contains("invalid info bits 1234 in .*, LSN .*,", $log_size), - "xlp_info bad"); + "xlp_info bad"); -# good xl_prev, xlp_magic, xlp_pageaddr, but xlp_info doesn't mention cont record +# Good xl_prev, xlp_magic, xlp_pageaddr, but xlp_info doesn't mention +# continuation record. $prev_lsn = advance_out_of_record_splitting_zone($node); $end_lsn = emit_message($node, 0); $node->stop('immediate'); -write_wal($node, 1, $end_lsn, record_header(2 * 1024 * 1024 * 1024, - 42, - $prev_lsn)); -write_wal($node, 1, start_of_next_page($end_lsn), - page_header($XLP_PAGE_MAGIC, - 0, - 1, - start_of_next_page($end_lsn))); -$log_size = get_log_size($node); +write_wal($node, $TLI, $end_lsn, + build_record_header(2 * 1024 * 1024 * 1024, 42, $prev_lsn)); +write_wal( + $node, $TLI, + start_of_next_page($end_lsn), + build_page_header($XLP_PAGE_MAGIC, 0, 1, start_of_next_page($end_lsn))); +$log_size = -s $node->logfile; $node->start; ok($node->log_contains("there is no contrecord flag at .*", $log_size), - "xlp_info lacks XLP_FIRST_IS_CONTRECORD"); + "xlp_info lacks XLP_FIRST_IS_CONTRECORD"); -# good xl_prev, xlp_magic, xlp_pageaddr, xlp_info but xlp_rem_len doesn't add up +# Good xl_prev, xlp_magic, xlp_pageaddr, xlp_info but xlp_rem_len doesn't add +# up. $prev_lsn = advance_out_of_record_splitting_zone($node); $end_lsn = emit_message($node, 0); $node->stop('immediate'); -write_wal($node, 1, $end_lsn, record_header(2 * 1024 * 1024 * 1024, - 42, - $prev_lsn)); -write_wal($node, 1, start_of_next_page($end_lsn), - page_header($XLP_PAGE_MAGIC, - $XLP_FIRST_IS_CONTRECORD, - 1, - start_of_next_page($end_lsn), - 123456)); -$log_size = get_log_size($node); +write_wal($node, $TLI, $end_lsn, + build_record_header(2 * 1024 * 1024 * 1024, 42, $prev_lsn)); +write_wal( + $node, $TLI, + start_of_next_page($end_lsn), + build_page_header( + $XLP_PAGE_MAGIC, $XLP_FIRST_IS_CONTRECORD, + 1, start_of_next_page($end_lsn), + 123456)); +$log_size = -s $node->logfile; $node->start; -ok($node->log_contains("invalid contrecord length 123456 .* at .*", $log_size), - "xlp_rem_len bad"); +ok( $node->log_contains( + "invalid contrecord length 123456 .* at .*", $log_size), + "xlp_rem_len bad"); ########################################################################### note "Multi-page, but header is split, so page checks are done first"; ########################################################################### -# xlp_prev is bad and xl_tot_len is too big, but we'll check xlp_magic first +# xlp_prev is bad and xl_tot_len is too big, but we'll check xlp_magic first. $end_lsn = advance_to_record_splitting_zone($node); $node->stop('immediate'); -write_wal($node, 1, $end_lsn, record_header(2 * 1024 * 1024 * 1024, - 0, - 0xdeadbeef)); -$log_size = get_log_size($node); +write_wal($node, $TLI, $end_lsn, + build_record_header(2 * 1024 * 1024 * 1024, 0, 0xdeadbeef)); +$log_size = -s $node->logfile; $node->start; ok($node->log_contains("invalid magic number 0000 .* LSN .*", $log_size), - "xlp_magic zero (split record header)"); + "xlp_magic zero (split record header)"); -# and we'll also check xlp_pageaddr before any header checks +# And we'll also check xlp_pageaddr before any header checks. $end_lsn = advance_to_record_splitting_zone($node); $node->stop('immediate'); -write_wal($node, 1, $end_lsn, record_header(2 * 1024 * 1024 * 1024, - 0, - 0xdeadbeef)); -write_wal($node, 1, start_of_next_page($end_lsn), - page_header($XLP_PAGE_MAGIC, - $XLP_FIRST_IS_CONTRECORD, - 1, - 0xbaaaaaad)); -$log_size = get_log_size($node); +write_wal($node, $TLI, $end_lsn, + build_record_header(2 * 1024 * 1024 * 1024, 0, 0xdeadbeef)); +write_wal( + $node, $TLI, + start_of_next_page($end_lsn), + build_page_header( + $XLP_PAGE_MAGIC, $XLP_FIRST_IS_CONTRECORD, 1, 0xbaaaaaad)); +$log_size = -s $node->logfile; $node->start; -ok($node->log_contains("unexpected pageaddr 0/BAAAAAAD in .*, LSN .*,", $log_size), - "xlp_pageaddr bad (split record header)"); +ok( $node->log_contains( + "unexpected pageaddr 0/BAAAAAAD in .*, LSN .*,", $log_size), + "xlp_pageaddr bad (split record header)"); -# we'll also discover that xlp_rem_len doesn't add up before any -# header checks +# We'll also discover that xlp_rem_len doesn't add up before any +# header checks, $end_lsn = advance_to_record_splitting_zone($node); $node->stop('immediate'); -write_wal($node, 1, $end_lsn, record_header(2 * 1024 * 1024 * 1024, - 0, - 0xdeadbeef)); -write_wal($node, 1, start_of_next_page($end_lsn), - page_header($XLP_PAGE_MAGIC, - $XLP_FIRST_IS_CONTRECORD, - 1, - start_of_next_page($end_lsn), - 123456)); -$log_size = get_log_size($node); +write_wal($node, $TLI, $end_lsn, + build_record_header(2 * 1024 * 1024 * 1024, 0, 0xdeadbeef)); +write_wal( + $node, $TLI, + start_of_next_page($end_lsn), + build_page_header( + $XLP_PAGE_MAGIC, $XLP_FIRST_IS_CONTRECORD, + 1, start_of_next_page($end_lsn), + 123456)); +$log_size = -s $node->logfile; $node->start; -ok($node->log_contains("invalid contrecord length 123456 .* at .*", $log_size), - "xlp_rem_len bad (split record header)"); +ok( $node->log_contains( + "invalid contrecord length 123456 .* at .*", $log_size), + "xlp_rem_len bad (split record header)"); done_testing(); -- 2.40.1