| From: | Bharath Rupireddy <bharath(dot)rupireddyforpostgres(at)gmail(dot)com> |
|---|---|
| To: | Paul Kim <mok03127(at)gmail(dot)com> |
| Cc: | pgsql-hackers(at)lists(dot)postgresql(dot)org |
| Subject: | Re: Be strict when request to flush past end of WAL in WaitXLogInsertionsToFinish |
| Date: | 2026-10-19 21:05:45 |
| Message-ID: | CALj2ACVsQuwDYovODF4qFWfR9Ye4PwcqzJoinigOCkPAzjM0hg@mail.gmail.com |
| Views: | Whole Thread | Raw Message | Download mbox | Resend email |
| Thread: | |
| Lists: | pgsql-hackers |
Hi,
On Tue, Oct 13, 2026 at 2:26 AM Paul Kim <mok03127(at)gmail(dot)com> wrote:
>
> On Fri, Oct 9, 2026, Bharath Rupireddy
> <bharath(dot)rupireddyforpostgres(at)gmail(dot)com> wrote:
> > How about adjusting EndPos itself to the start of the segment (before
> > the page header) using XLogBytePosToEndRecPtr in this case, where the
> > switch WAL record falls right on the segment boundary, something like
> > the attached?
>
> Agreed, that fixes it where the bad position is produced. Let's go
> with yours and drop my XLogBackgroundFlush() change.
>
> 0001 is your patch, with you as the author and the code unchanged. I
> changed two things in the commit message:
I have been thinking about this for a while, and I still believe
fixing the EndPos calculation after writing the switch WAL records is
the right approach, unless I'm missing some other case. I also
considered whether the same issue could happen for non-switch WAL
records, but AFAICS it can't, because this particular EndPos
calculation is specific to switch WAL records.
I looked at the TAP test, and I found it a bit hard to follow given
the many hard-coded values, and I'm not sure they work for every
platform the BF animals run on. Also, starting a new server for this
may not be resource-efficient for BF and CI runs. Would something like
the below work instead [1]? The end_of_wal test ensures that no other
WAL is generated by disabling the checkpointer and autovacuum and
keeping wal_level at minimal (so there are no standby-related WAL
records).
[1]
diff --git a/src/test/recovery/t/039_end_of_wal.pl
b/src/test/recovery/t/039_end_of_wal.pl
index 5fe8835..852620d 100644
--- a/src/test/recovery/t/039_end_of_wal.pl
+++ b/src/test/recovery/t/039_end_of_wal.pl
@@ -376,4 +376,73 @@ ok( $node->log_contains(
"invalid contrecord length 123456 .* at .*", $log_size),
"xlp_rem_len bad (split record header)");
+
+###########################################################################
+note "xlog-switch record ending on a segment boundary";
+###########################################################################
+
+# A switch record that starts SizeOfXLogRecord bytes before the end of a
+# segment ends exactly on the boundary, and pg_switch_wal() has to return
+# the boundary, not a position past the next page header.
+
+# Emit a non-transactional logical message (no XID, no commit record)
+# with a payload of $size bytes, returning its end LSN in bytes.
+sub emit_message
+{
+ my ($node, $size) = @_;
+ return int(
+ $node->safe_psql(
+ 'postgres',
+ "SELECT pg_logical_emit_message(false, '', repeat('a', $size)) - '0/0'"
+ ));
+}
+
+my $SizeOfXLogRecord = 24;
+
+# Start on a fresh segment. Nothing else writes WAL on this node, so the
+# positions below are exact.
+$node->safe_psql('postgres', "SELECT pg_switch_wal()");
+my $lsn = int(
+ $node->safe_psql(
+ 'postgres', "SELECT pg_current_wal_insert_lsn() - '0/0'"));
+my $seg_end = ($lsn / $WAL_SEGMENT_SIZE + 1) * $WAL_SEGMENT_SIZE;
+
+# Measure the space a message takes beyond its payload, padding included.
+# 256 is a multiple of MAXALIGN, over 255 so the record uses the long
+# main-data header like the fill, and small enough to stay on the first page.
+my $probe_size = 256;
+my $end = emit_message($node, $probe_size);
+my $overhead = $end - $lsn - $probe_size;
+$lsn = $end;
+
+# Measure the page header size with a message crossing one page boundary.
+# The first page of a segment has a longer header, but nothing below
+# crosses a segment boundary.
+my $size = start_of_next_page($lsn) - $lsn;
+$end = emit_message($node, $size);
+my $SizeOfXLogShortPHD = $end - $lsn - $size - $overhead;
+$lsn = $end;
+
+# Fill the segment in steps, so that no record gets too big for any
+# segment size, until the rest fits in one message.
+my $step = $WAL_SEGMENT_SIZE / 16;
+$lsn = emit_message($node, $step) while $seg_end - $lsn > 2 * $step;
+
+# Size the last message to end SizeOfXLogRecord bytes before the
+# boundary, less the page headers it crosses. A record ending exactly on
+# a page boundary reports the boundary while the next one starts after
+# the page header, hence the - 1.
+my $headers =
+ ($seg_end - start_of_next_page($lsn - 1)) / $WAL_BLOCK_SIZE *
+ $SizeOfXLogShortPHD;
+$lsn = emit_message($node,
+ $seg_end - $SizeOfXLogRecord - $lsn - $headers - $overhead);
+is($lsn, $seg_end - $SizeOfXLogRecord,
+ "message ends SizeOfXLogRecord bytes before the segment end");
+
+# The switch record is only a header, so it ends on the boundary.
+is( $node->safe_psql('postgres', "SELECT pg_switch_wal() - '0/0'"),
+ $seg_end,
+ "pg_switch_wal() returns the segment boundary");
+
done_testing();
--
2.43.0
--
Bharath Rupireddy
Amazon Web Services: https://aws.amazon.com
| From | Date | Subject | |
|---|---|---|---|
| Next Message | Matthias van de Meent | 2026-10-19 21:10:13 | Re: Expanding HOT updates for expression and partial indexes |
| Previous Message | best xmg | 2026-10-19 20:54:46 | Re: Hash a ScalarArrayOpExpr whose array is fixed for one execution |