public inbox for [email protected]  
help / color / mirror / Atom feed
From: Maxim Orlov <[email protected]>
To: Heikki Linnakangas <[email protected]>
Cc: Alexander Lakhin <[email protected]>
Cc: Ashutosh Bapat <[email protected]>
Cc: Alvaro Herrera <[email protected]>
Cc: Alexander Korotkov <[email protected]>
Cc: wenhui qiu <[email protected]>
Cc: Postgres hackers <[email protected]>
Subject: Re: POC: make mxidoff 64 bits
Date: Wed, 25 Feb 2026 18:56:08 +0300
Message-ID: <CACG=ezYjGmkVO+J7T8uv37XxUaqkptvrkOuMf04E7MTpWPcY=A@mail.gmail.com> (raw)
In-Reply-To: <CACG=ezYGvQbtuj_V9dGjBZEveTBW5dSMpcQ5mUWvxgYPw5cgdw@mail.gmail.com>
References: <CACG=ezaWg7_nt-8ey4aKv2w9LcuLthHknwCawmBgEeTnJrJTcw@mail.gmail.com>
	<CACG=ezYbYO_KHWdeDedbDcY0tOS0JfaqBxG3=bG5+DdsDK4MpQ@mail.gmail.com>
	<CACG=ezYpZRPwoRCz_h3Qerd3XJNdpTHCpwGbZphNdy26tA4_qQ@mail.gmail.com>
	<[email protected]>
	<[email protected]>
	<CACG=ezYUJSvnuxntkURNWo_1vZ+AtmcQfqd_h6WgDzGaudfw+Q@mail.gmail.com>
	<[email protected]>
	<[email protected]>
	<CAExHW5tUEkiQrvm9hgccjKUNkWBnJ5_HDUrAwiHBTxu+Vuj29Q@mail.gmail.com>
	<[email protected]>
	<CAExHW5t3kzJiVqmoqCLyGmfkTjD4Rwa27kXH-S_XvHWLkM2fzw@mail.gmail.com>
	<CAExHW5ucnoyjd6p7UVVhQTeV7hc8-vX81ti8f7sU0COqfUWzQg@mail.gmail.com>
	<[email protected]>
	<CAExHW5s_uNeD_xYjdbSR8khMXwWJQOY9Qg=j4T+5KOfGz5-RsQ@mail.gmail.com>
	<[email protected]>
	<[email protected]>
	<[email protected]>
	<[email protected]>
	<CACG=ezbwy1zargXDNPeYXxZwRW3jXu_aD=rcG-7dc4fw7Y9Ojw@mail.gmail.com>
	<CACG=ezbccQucKvp9a-3zKmoCFjsXo1RNkYVJ9i7ZBhJH+4hJDg@mail.gmail.com>
	<CACG=ezYGvQbtuj_V9dGjBZEveTBW5dSMpcQ5mUWvxgYPw5cgdw@mail.gmail.com>

Something of this kind?
Should we start a new thread?

-- 
Best regards,
Maxim Orlov.


Attachments:

  [application/octet-stream] v1-0001-Make-SlruReportIOError-accept-64-bit-values.patch (5.6K, 3-v1-0001-Make-SlruReportIOError-accept-64-bit-values.patch)
  download | inline diff:
From 02112c6faa66ef484ed8efc5374f7a87ed27711b Mon Sep 17 00:00:00 2001
From: Maxim Orlov <[email protected]>
Date: Fri, 20 Feb 2026 11:22:18 +0300
Subject: [PATCH v1] Make SlruReportIOError accept 64-bit values

Expanding SLRU segment values to 64 bits requires an extended format
for an error output in SlruReportIOError. Previously, the values were
implicitly converted to 32 bits without preserving the epoch.
---
 src/backend/access/transam/slru.c | 33 ++++++++++++++++++++-----------
 src/include/access/slru.h         |  2 +-
 2 files changed, 23 insertions(+), 12 deletions(-)

diff --git a/src/backend/access/transam/slru.c b/src/backend/access/transam/slru.c
index 549c7e3e64b..ab4018384f9 100644
--- a/src/backend/access/transam/slru.c
+++ b/src/backend/access/transam/slru.c
@@ -181,7 +181,7 @@ static void SlruInternalWritePage(SlruCtl ctl, int slotno, SlruWriteAll fdata);
 static bool SlruPhysicalReadPage(SlruCtl ctl, int64 pageno, int slotno);
 static bool SlruPhysicalWritePage(SlruCtl ctl, int64 pageno, int slotno,
 								  SlruWriteAll fdata);
-static void SlruReportIOError(SlruCtl ctl, int64 pageno, TransactionId xid);
+static void SlruReportIOError(SlruCtl ctl, int64 pageno, uint64 value);
 static int	SlruSelectLRUPage(SlruCtl ctl, int64 pageno);
 
 static bool SlruScanDirCbDeleteCutoff(SlruCtl ctl, char *filename,
@@ -525,7 +525,7 @@ SimpleLruWaitIO(SlruCtl ctl, int slotno)
  */
 int
 SimpleLruReadPage(SlruCtl ctl, int64 pageno, bool write_ok,
-				  TransactionId xid)
+				  uint64 xid)
 {
 	SlruShared	shared = ctl->shared;
 	LWLock	   *banklock = SimpleLruGetBankLock(ctl, pageno);
@@ -1070,12 +1070,15 @@ SlruPhysicalWritePage(SlruCtl ctl, int64 pageno, int slotno, SlruWriteAll fdata)
  * SlruPhysicalWritePage.  Call this after cleaning up shared-memory state.
  */
 static void
-SlruReportIOError(SlruCtl ctl, int64 pageno, TransactionId xid)
+SlruReportIOError(SlruCtl ctl, int64 pageno, uint64 value)
 {
 	int64		segno = pageno / SLRU_PAGES_PER_SEGMENT;
 	int			rpageno = pageno % SLRU_PAGES_PER_SEGMENT;
 	int			offset = rpageno * BLCKSZ;
 	char		path[MAXPGPATH];
+	FullTransactionId	fxid = FullTransactionIdFromU64(value);
+	TransactionId		xid = XidFromFullTransactionId(fxid);
+	uint32				epoch = EpochFromFullTransactionId(fxid);
 
 	SlruFileName(ctl, path, segno);
 	errno = slru_errno;
@@ -1084,13 +1087,15 @@ SlruReportIOError(SlruCtl ctl, int64 pageno, TransactionId xid)
 		case SLRU_OPEN_FAILED:
 			ereport(ERROR,
 					(errcode_for_file_access(),
-					 errmsg("could not access status of transaction %u", xid),
+					 errmsg("could not access status of transaction %u:%u",
+							epoch, xid),
 					 errdetail("Could not open file \"%s\": %m.", path)));
 			break;
 		case SLRU_SEEK_FAILED:
 			ereport(ERROR,
 					(errcode_for_file_access(),
-					 errmsg("could not access status of transaction %u", xid),
+					 errmsg("could not access status of transaction %u:%u",
+							epoch, xid),
 					 errdetail("Could not seek in file \"%s\" to offset %d: %m.",
 							   path, offset)));
 			break;
@@ -1098,38 +1103,44 @@ SlruReportIOError(SlruCtl ctl, int64 pageno, TransactionId xid)
 			if (errno)
 				ereport(ERROR,
 						(errcode_for_file_access(),
-						 errmsg("could not access status of transaction %u", xid),
+						 errmsg("could not access status of transaction %u:%u",
+								epoch, xid),
 						 errdetail("Could not read from file \"%s\" at offset %d: %m.",
 								   path, offset)));
 			else
 				ereport(ERROR,
-						(errmsg("could not access status of transaction %u", xid),
+						(errmsg("could not access status of transaction %u:%u",
+								epoch, xid),
 						 errdetail("Could not read from file \"%s\" at offset %d: read too few bytes.", path, offset)));
 			break;
 		case SLRU_WRITE_FAILED:
 			if (errno)
 				ereport(ERROR,
 						(errcode_for_file_access(),
-						 errmsg("could not access status of transaction %u", xid),
+						 errmsg("could not access status of transaction %u:%u",
+								epoch, xid),
 						 errdetail("Could not write to file \"%s\" at offset %d: %m.",
 								   path, offset)));
 			else
 				ereport(ERROR,
-						(errmsg("could not access status of transaction %u", xid),
+						(errmsg("could not access status of transaction %u:%u",
+								epoch, xid),
 						 errdetail("Could not write to file \"%s\" at offset %d: wrote too few bytes.",
 								   path, offset)));
 			break;
 		case SLRU_FSYNC_FAILED:
 			ereport(data_sync_elevel(ERROR),
 					(errcode_for_file_access(),
-					 errmsg("could not access status of transaction %u", xid),
+					 errmsg("could not access status of transaction %u:%u",
+							epoch, xid),
 					 errdetail("Could not fsync file \"%s\": %m.",
 							   path)));
 			break;
 		case SLRU_CLOSE_FAILED:
 			ereport(ERROR,
 					(errcode_for_file_access(),
-					 errmsg("could not access status of transaction %u", xid),
+					 errmsg("could not access status of transaction %u:%u",
+							epoch, xid),
 					 errdetail("Could not close file \"%s\": %m.",
 							   path)));
 			break;
diff --git a/src/include/access/slru.h b/src/include/access/slru.h
index 4cb8f478fce..ec5c194e7ac 100644
--- a/src/include/access/slru.h
+++ b/src/include/access/slru.h
@@ -174,7 +174,7 @@ extern void SimpleLruInit(SlruCtl ctl, const char *name, int nslots, int nlsns,
 extern int	SimpleLruZeroPage(SlruCtl ctl, int64 pageno);
 extern void SimpleLruZeroAndWritePage(SlruCtl ctl, int64 pageno);
 extern int	SimpleLruReadPage(SlruCtl ctl, int64 pageno, bool write_ok,
-							  TransactionId xid);
+							  uint64 xid);
 extern int	SimpleLruReadPage_ReadOnly(SlruCtl ctl, int64 pageno,
 									   TransactionId xid);
 extern void SimpleLruWritePage(SlruCtl ctl, int slotno);
-- 
2.43.0



  [application/octet-stream] v2-0002-Add-test-case-for-custom-SLRU-IO-error.patch (4.5K, 4-v2-0002-Add-test-case-for-custom-SLRU-IO-error.patch)
  download | inline diff:
From 343d6836e7147b1cf56df8cb04365ee214ea357f Mon Sep 17 00:00:00 2001
From: Maxim Orlov <[email protected]>
Date: Wed, 25 Feb 2026 17:48:42 +0300
Subject: [PATCH v2 2/5] Add test case for custom SLRU IO error

---
 src/test/modules/test_slru/expected/test_slru.out |  7 +++++++
 src/test/modules/test_slru/sql/test_slru.sql      |  4 ++++
 src/test/modules/test_slru/test_slru--1.0.sql     |  2 +-
 src/test/modules/test_slru/test_slru.c            | 15 +++++++++++++--
 4 files changed, 25 insertions(+), 3 deletions(-)

diff --git a/src/test/modules/test_slru/expected/test_slru.out b/src/test/modules/test_slru/expected/test_slru.out
index 185c56e5d62..0dda6b60f0b 100644
--- a/src/test/modules/test_slru/expected/test_slru.out
+++ b/src/test/modules/test_slru/expected/test_slru.out
@@ -23,6 +23,13 @@ SELECT test_slru_page_exists(12345);
  t
 (1 row)
 
+-- should fail with custom error msg
+SELECT test_slru_page_read(54321);
+ERROR:  could not access test_slru entry
+DETAIL:  Could not open file "pg_test_slru/0000000000006A1": No such file or directory.
+SELECT test_slru_page_read(54321, false, '123'::xid);
+ERROR:  could not access test_slru entry 123
+DETAIL:  Could not open file "pg_test_slru/0000000000006A1": No such file or directory.
 -- 48 extra pages
 SELECT count(test_slru_page_write(a, 'Test SLRU'))
   FROM generate_series(12346, 12393, 1) as a;
diff --git a/src/test/modules/test_slru/sql/test_slru.sql b/src/test/modules/test_slru/sql/test_slru.sql
index b1b376581ab..4f66f4207b7 100644
--- a/src/test/modules/test_slru/sql/test_slru.sql
+++ b/src/test/modules/test_slru/sql/test_slru.sql
@@ -5,6 +5,10 @@ SELECT test_slru_page_write(12345, 'Test SLRU');
 SELECT test_slru_page_read(12345);
 SELECT test_slru_page_exists(12345);
 
+-- should fail with custom error msg
+SELECT test_slru_page_read(54321);
+SELECT test_slru_page_read(54321, false, '123'::xid);
+
 -- 48 extra pages
 SELECT count(test_slru_page_write(a, 'Test SLRU'))
   FROM generate_series(12346, 12393, 1) as a;
diff --git a/src/test/modules/test_slru/test_slru--1.0.sql b/src/test/modules/test_slru/test_slru--1.0.sql
index abecb5e2183..22f4f64b988 100644
--- a/src/test/modules/test_slru/test_slru--1.0.sql
+++ b/src/test/modules/test_slru/test_slru--1.0.sql
@@ -7,7 +7,7 @@ CREATE OR REPLACE FUNCTION test_slru_page_writeall() RETURNS VOID
   AS 'MODULE_PATHNAME', 'test_slru_page_writeall' LANGUAGE C;
 CREATE OR REPLACE FUNCTION test_slru_page_sync(bigint) RETURNS VOID
   AS 'MODULE_PATHNAME', 'test_slru_page_sync' LANGUAGE C;
-CREATE OR REPLACE FUNCTION test_slru_page_read(bigint, bool DEFAULT true) RETURNS text
+CREATE OR REPLACE FUNCTION test_slru_page_read(bigint, bool DEFAULT true, xid DEFAULT NULL) RETURNS text
   AS 'MODULE_PATHNAME', 'test_slru_page_read' LANGUAGE C;
 CREATE OR REPLACE FUNCTION test_slru_page_readonly(bigint) RETURNS text
   AS 'MODULE_PATHNAME', 'test_slru_page_readonly' LANGUAGE C;
diff --git a/src/test/modules/test_slru/test_slru.c b/src/test/modules/test_slru/test_slru.c
index 59ce4900173..19c69b36646 100644
--- a/src/test/modules/test_slru/test_slru.c
+++ b/src/test/modules/test_slru/test_slru.c
@@ -93,6 +93,7 @@ test_slru_page_read(PG_FUNCTION_ARGS)
 {
 	int64		pageno = PG_GETARG_INT64(0);
 	bool		write_ok = PG_GETARG_BOOL(1);
+	TransactionId xid = PG_GETARG_TRANSACTIONID(2);
 	char	   *data = NULL;
 	int			slotno;
 	LWLock	   *lock = SimpleLruGetBankLock(TestSlruCtl, pageno);
@@ -100,7 +101,7 @@ test_slru_page_read(PG_FUNCTION_ARGS)
 	/* find page in buffers, reading it if necessary */
 	LWLockAcquire(lock, LW_EXCLUSIVE);
 	slotno = SimpleLruReadPage(TestSlruCtl, pageno,
-							   write_ok, NULL);
+							   write_ok, PG_ARGISNULL(2) ? NULL : &xid);
 	data = (char *) TestSlruCtl->shared->page_buffer[slotno];
 	LWLockRelease(lock);
 
@@ -210,6 +211,16 @@ test_slru_page_precedes_logically(int64 page1, int64 page2)
 	return page1 < page2;
 }
 
+static inline char *
+test_io_error_msg(const void *opaque_data)
+{
+	if (opaque_data)
+		return psprintf("could not access test_slru entry %u",
+						*(TransactionId *) opaque_data);
+
+	return psprintf("could not access test_slru entry");
+}
+
 static void
 test_slru_shmem_startup(void)
 {
@@ -245,7 +256,7 @@ test_slru_shmem_startup(void)
 	}
 
 	TestSlruCtl->PagePrecedes = test_slru_page_precedes_logically;
-	TestSlruCtl->IoErrorMsg = TransactionIdIoErrorMsg;
+	TestSlruCtl->IoErrorMsg = test_io_error_msg;
 	SimpleLruInit(TestSlruCtl, "TestSLRU",
 				  NUM_TEST_BUFFERS, 0, slru_dir_name,
 				  test_buffer_tranche_id, test_tranche_id, SYNC_HANDLER_NONE,
-- 
2.50.1 (Apple Git-155)



  [application/octet-stream] v2-0001-Add-a-callback-for-generating-an-I-O-message-in-t.patch (22.0K, 5-v2-0001-Add-a-callback-for-generating-an-I-O-message-in-t.patch)
  download | inline diff:
From 1599a09fce586e20d172a30bff72c5586260ba37 Mon Sep 17 00:00:00 2001
From: Maxim Orlov <[email protected]>
Date: Wed, 25 Feb 2026 16:59:38 +0300
Subject: [PATCH v2 1/5] Add a callback for generating an I/O message in the
 SLRU

Historically, the SLRU module was designed to work with transaction IDs.
But now we use it to work with different objects, and even of different
types. However, I/O errors continued to be output in the
corresponding XIDs format.

This commit adds a callback that will allow to create custom IO error
messages for modules that don't work with transaction IDs.

No user-visible behavior change is expected in this commit.
---
 src/backend/access/transam/clog.c      |  8 ++---
 src/backend/access/transam/commit_ts.c |  5 +--
 src/backend/access/transam/multixact.c | 21 +++++++------
 src/backend/access/transam/slru.c      | 43 ++++++++++++++++----------
 src/backend/access/transam/subtrans.c  |  5 +--
 src/backend/commands/async.c           | 10 +++---
 src/backend/storage/lmgr/predicate.c   |  5 +--
 src/include/access/slru.h              | 26 ++++++++++++++--
 src/test/modules/test_slru/test_slru.c |  5 +--
 9 files changed, 83 insertions(+), 45 deletions(-)

diff --git a/src/backend/access/transam/clog.c b/src/backend/access/transam/clog.c
index b5c38bbb162..1c62672d656 100644
--- a/src/backend/access/transam/clog.c
+++ b/src/backend/access/transam/clog.c
@@ -381,8 +381,7 @@ TransactionIdSetPageStatusInternal(TransactionId xid, int nsubxids,
 	 * write-busy, since we don't care if the update reaches disk sooner than
 	 * we think.
 	 */
-	slotno = SimpleLruReadPage(XactCtl, pageno, !XLogRecPtrIsValid(lsn),
-							   xid);
+	slotno = SimpleLruReadPage(XactCtl, pageno, !XLogRecPtrIsValid(lsn), &xid);
 
 	/*
 	 * Set the main transaction id, if any.
@@ -743,7 +742,7 @@ TransactionIdGetStatus(TransactionId xid, XLogRecPtr *lsn)
 
 	/* lock is acquired by SimpleLruReadPage_ReadOnly */
 
-	slotno = SimpleLruReadPage_ReadOnly(XactCtl, pageno, xid);
+	slotno = SimpleLruReadPage_ReadOnly(XactCtl, pageno, &xid);
 	byteptr = XactCtl->shared->page_buffer[slotno] + byteno;
 
 	status = (*byteptr >> bshift) & CLOG_XACT_BITMASK;
@@ -807,6 +806,7 @@ CLOGShmemInit(void)
 	Assert(transaction_buffers != 0);
 
 	XactCtl->PagePrecedes = CLOGPagePrecedes;
+	XactCtl->IoErrorMsg = TransactionIdIoErrorMsg;
 	SimpleLruInit(XactCtl, "transaction", CLOGShmemBuffers(), CLOG_LSNS_PER_PAGE,
 				  "pg_xact", LWTRANCHE_XACT_BUFFER,
 				  LWTRANCHE_XACT_SLRU, SYNC_HANDLER_CLOG, false);
@@ -882,7 +882,7 @@ TrimCLOG(void)
 		int			slotno;
 		char	   *byteptr;
 
-		slotno = SimpleLruReadPage(XactCtl, pageno, false, xid);
+		slotno = SimpleLruReadPage(XactCtl, pageno, false, &xid);
 		byteptr = XactCtl->shared->page_buffer[slotno] + byteno;
 
 		/* Zero so-far-unused positions in the current byte */
diff --git a/src/backend/access/transam/commit_ts.c b/src/backend/access/transam/commit_ts.c
index 6fa2178f1dd..96775885d39 100644
--- a/src/backend/access/transam/commit_ts.c
+++ b/src/backend/access/transam/commit_ts.c
@@ -227,7 +227,7 @@ SetXidCommitTsInPage(TransactionId xid, int nsubxids,
 
 	LWLockAcquire(lock, LW_EXCLUSIVE);
 
-	slotno = SimpleLruReadPage(CommitTsCtl, pageno, true, xid);
+	slotno = SimpleLruReadPage(CommitTsCtl, pageno, true, &xid);
 
 	TransactionIdSetCommitTs(xid, ts, nodeid, slotno);
 	for (i = 0; i < nsubxids; i++)
@@ -332,7 +332,7 @@ TransactionIdGetCommitTsData(TransactionId xid, TimestampTz *ts,
 	}
 
 	/* lock is acquired by SimpleLruReadPage_ReadOnly */
-	slotno = SimpleLruReadPage_ReadOnly(CommitTsCtl, pageno, xid);
+	slotno = SimpleLruReadPage_ReadOnly(CommitTsCtl, pageno, &xid);
 	memcpy(&entry,
 		   CommitTsCtl->shared->page_buffer[slotno] +
 		   SizeOfCommitTimestampEntry * entryno,
@@ -551,6 +551,7 @@ CommitTsShmemInit(void)
 	Assert(commit_timestamp_buffers != 0);
 
 	CommitTsCtl->PagePrecedes = CommitTsPagePrecedes;
+	CommitTsCtl->IoErrorMsg = TransactionIdIoErrorMsg;
 	SimpleLruInit(CommitTsCtl, "commit_timestamp", CommitTsShmemBuffers(), 0,
 				  "pg_commit_ts", LWTRANCHE_COMMITTS_BUFFER,
 				  LWTRANCHE_COMMITTS_SLRU,
diff --git a/src/backend/access/transam/multixact.c b/src/backend/access/transam/multixact.c
index 90ec87d9dd6..63d5256823e 100644
--- a/src/backend/access/transam/multixact.c
+++ b/src/backend/access/transam/multixact.c
@@ -798,7 +798,7 @@ RecordNewMultiXact(MultiXactId multi, MultiXactOffset offset,
 	 * enough that a MultiXactId is really involved.  Perhaps someday we'll
 	 * take the trouble to generalize the slru.c error reporting code.
 	 */
-	slotno = SimpleLruReadPage(MultiXactOffsetCtl, pageno, true, multi);
+	slotno = SimpleLruReadPage(MultiXactOffsetCtl, pageno, true, &multi);
 	offptr = (MultiXactOffset *) MultiXactOffsetCtl->shared->page_buffer[slotno];
 	offptr += entryno;
 
@@ -827,7 +827,7 @@ RecordNewMultiXact(MultiXactId multi, MultiXactOffset offset,
 		lock = SimpleLruGetBankLock(MultiXactOffsetCtl, next_pageno);
 		LWLockAcquire(lock, LW_EXCLUSIVE);
 
-		slotno = SimpleLruReadPage(MultiXactOffsetCtl, next_pageno, true, next);
+		slotno = SimpleLruReadPage(MultiXactOffsetCtl, next_pageno, true, &next);
 		next_offptr = (MultiXactOffset *) MultiXactOffsetCtl->shared->page_buffer[slotno];
 		next_offptr += next_entryno;
 	}
@@ -881,7 +881,7 @@ RecordNewMultiXact(MultiXactId multi, MultiXactOffset offset,
 				LWLockAcquire(lock, LW_EXCLUSIVE);
 				prevlock = lock;
 			}
-			slotno = SimpleLruReadPage(MultiXactMemberCtl, pageno, true, multi);
+			slotno = SimpleLruReadPage(MultiXactMemberCtl, pageno, true, &multi);
 			prev_pageno = pageno;
 		}
 
@@ -1206,7 +1206,7 @@ GetMultiXactIdMembers(MultiXactId multi, MultiXactMember **members,
 	LWLockAcquire(lock, LW_EXCLUSIVE);
 
 	/* read this multi's offset */
-	slotno = SimpleLruReadPage(MultiXactOffsetCtl, pageno, true, multi);
+	slotno = SimpleLruReadPage(MultiXactOffsetCtl, pageno, true, &multi);
 	offptr = (MultiXactOffset *) MultiXactOffsetCtl->shared->page_buffer[slotno];
 	offptr += entryno;
 	offset = *offptr;
@@ -1244,7 +1244,7 @@ GetMultiXactIdMembers(MultiXactId multi, MultiXactMember **members,
 				LWLockAcquire(newlock, LW_EXCLUSIVE);
 				lock = newlock;
 			}
-			slotno = SimpleLruReadPage(MultiXactOffsetCtl, pageno, true, tmpMXact);
+			slotno = SimpleLruReadPage(MultiXactOffsetCtl, pageno, true, &tmpMXact);
 		}
 
 		offptr = (MultiXactOffset *) MultiXactOffsetCtl->shared->page_buffer[slotno];
@@ -1309,7 +1309,7 @@ GetMultiXactIdMembers(MultiXactId multi, MultiXactMember **members,
 				lock = newlock;
 			}
 
-			slotno = SimpleLruReadPage(MultiXactMemberCtl, pageno, true, multi);
+			slotno = SimpleLruReadPage(MultiXactMemberCtl, pageno, true, &multi);
 			prev_pageno = pageno;
 		}
 
@@ -1730,6 +1730,9 @@ MultiXactShmemInit(void)
 	MultiXactOffsetCtl->PagePrecedes = MultiXactOffsetPagePrecedes;
 	MultiXactMemberCtl->PagePrecedes = MultiXactMemberPagePrecedes;
 
+	MultiXactOffsetCtl->IoErrorMsg = TransactionIdIoErrorMsg;
+	MultiXactMemberCtl->IoErrorMsg = TransactionIdIoErrorMsg;
+
 	SimpleLruInit(MultiXactOffsetCtl,
 				  "multixact_offset", multixact_offset_buffers, 0,
 				  "pg_multixact/offsets", LWTRANCHE_MULTIXACTOFFSET_BUFFER,
@@ -1879,7 +1882,7 @@ TrimMultiXact(void)
 		if (entryno == 0 || nextMXact == FirstMultiXactId)
 			slotno = SimpleLruZeroPage(MultiXactOffsetCtl, pageno);
 		else
-			slotno = SimpleLruReadPage(MultiXactOffsetCtl, pageno, true, nextMXact);
+			slotno = SimpleLruReadPage(MultiXactOffsetCtl, pageno, true, &nextMXact);
 		offptr = (MultiXactOffset *) MultiXactOffsetCtl->shared->page_buffer[slotno];
 		offptr += entryno;
 
@@ -1914,7 +1917,7 @@ TrimMultiXact(void)
 
 		LWLockAcquire(lock, LW_EXCLUSIVE);
 		memberoff = MXOffsetToMemberOffset(offset);
-		slotno = SimpleLruReadPage(MultiXactMemberCtl, pageno, true, offset);
+		slotno = SimpleLruReadPage(MultiXactMemberCtl, pageno, true, &offset);
 		xidptr = (TransactionId *)
 			(MultiXactMemberCtl->shared->page_buffer[slotno] + memberoff);
 
@@ -2444,7 +2447,7 @@ find_multixact_start(MultiXactId multi, MultiXactOffset *result)
 		return false;
 
 	/* lock is acquired by SimpleLruReadPage_ReadOnly */
-	slotno = SimpleLruReadPage_ReadOnly(MultiXactOffsetCtl, pageno, multi);
+	slotno = SimpleLruReadPage_ReadOnly(MultiXactOffsetCtl, pageno, &multi);
 	offptr = (MultiXactOffset *) MultiXactOffsetCtl->shared->page_buffer[slotno];
 	offptr += entryno;
 	offset = *offptr;
diff --git a/src/backend/access/transam/slru.c b/src/backend/access/transam/slru.c
index 549c7e3e64b..a87b0fe830a 100644
--- a/src/backend/access/transam/slru.c
+++ b/src/backend/access/transam/slru.c
@@ -181,7 +181,8 @@ static void SlruInternalWritePage(SlruCtl ctl, int slotno, SlruWriteAll fdata);
 static bool SlruPhysicalReadPage(SlruCtl ctl, int64 pageno, int slotno);
 static bool SlruPhysicalWritePage(SlruCtl ctl, int64 pageno, int slotno,
 								  SlruWriteAll fdata);
-static void SlruReportIOError(SlruCtl ctl, int64 pageno, TransactionId xid);
+static void SlruReportIOError(SlruCtl ctl, int64 pageno,
+							  const void *opaque_data);
 static int	SlruSelectLRUPage(SlruCtl ctl, int64 pageno);
 
 static bool SlruScanDirCbDeleteCutoff(SlruCtl ctl, char *filename,
@@ -525,11 +526,13 @@ SimpleLruWaitIO(SlruCtl ctl, int slotno)
  */
 int
 SimpleLruReadPage(SlruCtl ctl, int64 pageno, bool write_ok,
-				  TransactionId xid)
+				  const void *opaque_data)
 {
 	SlruShared	shared = ctl->shared;
 	LWLock	   *banklock = SimpleLruGetBankLock(ctl, pageno);
 
+	Assert(ctl->IoErrorMsg != NULL);
+
 	Assert(LWLockHeldByMeInMode(banklock, LW_EXCLUSIVE));
 
 	/* Outer loop handles restart if we must wait for someone else's I/O */
@@ -601,7 +604,7 @@ SimpleLruReadPage(SlruCtl ctl, int64 pageno, bool write_ok,
 
 		/* Now it's okay to ereport if we failed */
 		if (!ok)
-			SlruReportIOError(ctl, pageno, xid);
+			SlruReportIOError(ctl, pageno, opaque_data);
 
 		SlruRecentlyUsed(shared, slotno);
 
@@ -627,7 +630,7 @@ SimpleLruReadPage(SlruCtl ctl, int64 pageno, bool write_ok,
  * It is unspecified whether the lock will be shared or exclusive.
  */
 int
-SimpleLruReadPage_ReadOnly(SlruCtl ctl, int64 pageno, TransactionId xid)
+SimpleLruReadPage_ReadOnly(SlruCtl ctl, int64 pageno, const void *opaque_data)
 {
 	SlruShared	shared = ctl->shared;
 	LWLock	   *banklock = SimpleLruGetBankLock(ctl, pageno);
@@ -659,7 +662,7 @@ SimpleLruReadPage_ReadOnly(SlruCtl ctl, int64 pageno, TransactionId xid)
 	LWLockRelease(banklock);
 	LWLockAcquire(banklock, LW_EXCLUSIVE);
 
-	return SimpleLruReadPage(ctl, pageno, true, xid);
+	return SimpleLruReadPage(ctl, pageno, true, opaque_data);
 }
 
 /*
@@ -681,7 +684,10 @@ SlruInternalWritePage(SlruCtl ctl, int slotno, SlruWriteAll fdata)
 	int			bankno = SlotGetBankNumber(slotno);
 	bool		ok;
 
+	Assert(ctl->IoErrorMsg != NULL);
+
 	Assert(shared->page_status[slotno] != SLRU_PAGE_EMPTY);
+
 	Assert(LWLockHeldByMeInMode(SimpleLruGetBankLock(ctl, pageno), LW_EXCLUSIVE));
 
 	/* If a write is in progress, wait for it to finish */
@@ -739,7 +745,7 @@ SlruInternalWritePage(SlruCtl ctl, int slotno, SlruWriteAll fdata)
 
 	/* Now it's okay to ereport if we failed */
 	if (!ok)
-		SlruReportIOError(ctl, pageno, InvalidTransactionId);
+		SlruReportIOError(ctl, pageno, NULL);
 
 	/* If part of a checkpoint, count this as a SLRU buffer written. */
 	if (fdata)
@@ -778,6 +784,8 @@ SimpleLruDoesPhysicalPageExist(SlruCtl ctl, int64 pageno)
 	bool		result;
 	off_t		endpos;
 
+	Assert(ctl->IoErrorMsg != NULL);
+
 	/* update the stats counter of checked pages */
 	pgstat_count_slru_blocks_exists(ctl->shared->slru_stats_idx);
 
@@ -1070,12 +1078,13 @@ SlruPhysicalWritePage(SlruCtl ctl, int64 pageno, int slotno, SlruWriteAll fdata)
  * SlruPhysicalWritePage.  Call this after cleaning up shared-memory state.
  */
 static void
-SlruReportIOError(SlruCtl ctl, int64 pageno, TransactionId xid)
+SlruReportIOError(SlruCtl ctl, int64 pageno, const void *opaque_data)
 {
 	int64		segno = pageno / SLRU_PAGES_PER_SEGMENT;
 	int			rpageno = pageno % SLRU_PAGES_PER_SEGMENT;
 	int			offset = rpageno * BLCKSZ;
 	char		path[MAXPGPATH];
+	char	   *msg = ctl->IoErrorMsg(opaque_data);
 
 	SlruFileName(ctl, path, segno);
 	errno = slru_errno;
@@ -1084,13 +1093,13 @@ SlruReportIOError(SlruCtl ctl, int64 pageno, TransactionId xid)
 		case SLRU_OPEN_FAILED:
 			ereport(ERROR,
 					(errcode_for_file_access(),
-					 errmsg("could not access status of transaction %u", xid),
+					 errmsg("%s", msg),
 					 errdetail("Could not open file \"%s\": %m.", path)));
 			break;
 		case SLRU_SEEK_FAILED:
 			ereport(ERROR,
 					(errcode_for_file_access(),
-					 errmsg("could not access status of transaction %u", xid),
+					 errmsg("%s", msg),
 					 errdetail("Could not seek in file \"%s\" to offset %d: %m.",
 							   path, offset)));
 			break;
@@ -1098,38 +1107,38 @@ SlruReportIOError(SlruCtl ctl, int64 pageno, TransactionId xid)
 			if (errno)
 				ereport(ERROR,
 						(errcode_for_file_access(),
-						 errmsg("could not access status of transaction %u", xid),
+						 errmsg("%s", msg),
 						 errdetail("Could not read from file \"%s\" at offset %d: %m.",
 								   path, offset)));
 			else
 				ereport(ERROR,
-						(errmsg("could not access status of transaction %u", xid),
+						(errmsg("%s", msg),
 						 errdetail("Could not read from file \"%s\" at offset %d: read too few bytes.", path, offset)));
 			break;
 		case SLRU_WRITE_FAILED:
 			if (errno)
 				ereport(ERROR,
 						(errcode_for_file_access(),
-						 errmsg("could not access status of transaction %u", xid),
+						 errmsg("%s", msg),
 						 errdetail("Could not write to file \"%s\" at offset %d: %m.",
 								   path, offset)));
 			else
 				ereport(ERROR,
-						(errmsg("could not access status of transaction %u", xid),
+						(errmsg("%s", msg),
 						 errdetail("Could not write to file \"%s\" at offset %d: wrote too few bytes.",
 								   path, offset)));
 			break;
 		case SLRU_FSYNC_FAILED:
 			ereport(data_sync_elevel(ERROR),
 					(errcode_for_file_access(),
-					 errmsg("could not access status of transaction %u", xid),
+					 errmsg("%s", msg),
 					 errdetail("Could not fsync file \"%s\": %m.",
 							   path)));
 			break;
 		case SLRU_CLOSE_FAILED:
 			ereport(ERROR,
 					(errcode_for_file_access(),
-					 errmsg("could not access status of transaction %u", xid),
+					 errmsg("%s", msg),
 					 errdetail("Could not close file \"%s\": %m.",
 							   path)));
 			break;
@@ -1352,6 +1361,8 @@ SimpleLruWriteAll(SlruCtl ctl, bool allow_redirtied)
 	int			prevbank = SlotGetBankNumber(0);
 	bool		ok;
 
+	Assert(ctl->IoErrorMsg != NULL);
+
 	/* update the stats counter of flushes */
 	pgstat_count_slru_flush(shared->slru_stats_idx);
 
@@ -1411,7 +1422,7 @@ SimpleLruWriteAll(SlruCtl ctl, bool allow_redirtied)
 		}
 	}
 	if (!ok)
-		SlruReportIOError(ctl, pageno, InvalidTransactionId);
+		SlruReportIOError(ctl, pageno, NULL);
 
 	/* Ensure that directory entries for new files are on disk. */
 	if (ctl->sync_handler != SYNC_HANDLER_NONE)
diff --git a/src/backend/access/transam/subtrans.c b/src/backend/access/transam/subtrans.c
index c0987f43f11..37f1f74ab36 100644
--- a/src/backend/access/transam/subtrans.c
+++ b/src/backend/access/transam/subtrans.c
@@ -95,7 +95,7 @@ SubTransSetParent(TransactionId xid, TransactionId parent)
 	lock = SimpleLruGetBankLock(SubTransCtl, pageno);
 	LWLockAcquire(lock, LW_EXCLUSIVE);
 
-	slotno = SimpleLruReadPage(SubTransCtl, pageno, true, xid);
+	slotno = SimpleLruReadPage(SubTransCtl, pageno, true, &xid);
 	ptr = (TransactionId *) SubTransCtl->shared->page_buffer[slotno];
 	ptr += entryno;
 
@@ -135,7 +135,7 @@ SubTransGetParent(TransactionId xid)
 
 	/* lock is acquired by SimpleLruReadPage_ReadOnly */
 
-	slotno = SimpleLruReadPage_ReadOnly(SubTransCtl, pageno, xid);
+	slotno = SimpleLruReadPage_ReadOnly(SubTransCtl, pageno, &xid);
 	ptr = (TransactionId *) SubTransCtl->shared->page_buffer[slotno];
 	ptr += entryno;
 
@@ -240,6 +240,7 @@ SUBTRANSShmemInit(void)
 	Assert(subtransaction_buffers != 0);
 
 	SubTransCtl->PagePrecedes = SubTransPagePrecedes;
+	SubTransCtl->IoErrorMsg = TransactionIdIoErrorMsg;
 	SimpleLruInit(SubTransCtl, "subtransaction", SUBTRANSShmemBuffers(), 0,
 				  "pg_subtrans", LWTRANCHE_SUBTRANS_BUFFER,
 				  LWTRANCHE_SUBTRANS_SLRU, SYNC_HANDLER_NONE, false);
diff --git a/src/backend/commands/async.c b/src/backend/commands/async.c
index 657c591618d..a347996b373 100644
--- a/src/backend/commands/async.c
+++ b/src/backend/commands/async.c
@@ -829,6 +829,7 @@ AsyncShmemInit(void)
 	 * names are used in order to avoid wraparound.
 	 */
 	NotifyCtl->PagePrecedes = asyncQueuePagePrecedes;
+	NotifyCtl->IoErrorMsg = TransactionIdIoErrorMsg;
 	SimpleLruInit(NotifyCtl, "notify", notify_buffers, 0,
 				  "pg_notify", LWTRANCHE_NOTIFY_BUFFER, LWTRANCHE_NOTIFY_SLRU,
 				  SYNC_HANDLER_NONE, true);
@@ -2067,8 +2068,7 @@ asyncQueueAddEntries(ListCell *nextNotify)
 	if (QUEUE_POS_IS_ZERO(queue_head))
 		slotno = SimpleLruZeroPage(NotifyCtl, pageno);
 	else
-		slotno = SimpleLruReadPage(NotifyCtl, pageno, true,
-								   InvalidTransactionId);
+		slotno = SimpleLruReadPage(NotifyCtl, pageno, true, NULL);
 
 	/* Note we mark the page dirty before writing in it */
 	NotifyCtl->shared->page_dirty[slotno] = true;
@@ -2738,8 +2738,7 @@ asyncQueueProcessPageEntries(QueuePosition *current,
 	alignas(AsyncQueueEntry) char local_buf[QUEUE_PAGESIZE];
 	char	   *local_buf_end = local_buf;
 
-	slotno = SimpleLruReadPage_ReadOnly(NotifyCtl, curpage,
-										InvalidTransactionId);
+	slotno = SimpleLruReadPage_ReadOnly(NotifyCtl, curpage, NULL);
 	page_buffer = NotifyCtl->shared->page_buffer[slotno];
 
 	do
@@ -2997,8 +2996,7 @@ AsyncNotifyFreezeXids(TransactionId newFrozenXid)
 
 			lock = SimpleLruGetBankLock(NotifyCtl, pageno);
 			LWLockAcquire(lock, LW_EXCLUSIVE);
-			slotno = SimpleLruReadPage(NotifyCtl, pageno, true,
-									   InvalidTransactionId);
+			slotno = SimpleLruReadPage(NotifyCtl, pageno, true, NULL);
 			page_buffer = NotifyCtl->shared->page_buffer[slotno];
 			curpage = pageno;
 		}
diff --git a/src/backend/storage/lmgr/predicate.c b/src/backend/storage/lmgr/predicate.c
index fe75ead3501..728fb3c305d 100644
--- a/src/backend/storage/lmgr/predicate.c
+++ b/src/backend/storage/lmgr/predicate.c
@@ -811,6 +811,7 @@ SerialInit(void)
 	 * Set up SLRU management of the pg_serial data.
 	 */
 	SerialSlruCtl->PagePrecedes = SerialPagePrecedesLogically;
+	SerialSlruCtl->IoErrorMsg = TransactionIdIoErrorMsg;
 	SimpleLruInit(SerialSlruCtl, "serializable",
 				  serializable_buffers, 0, "pg_serial",
 				  LWTRANCHE_SERIAL_BUFFER, LWTRANCHE_SERIAL_SLRU,
@@ -930,7 +931,7 @@ SerialAdd(TransactionId xid, SerCommitSeqNo minConflictCommitSeqNo)
 	else
 	{
 		LWLockAcquire(lock, LW_EXCLUSIVE);
-		slotno = SimpleLruReadPage(SerialSlruCtl, targetPage, true, xid);
+		slotno = SimpleLruReadPage(SerialSlruCtl, targetPage, true, &xid);
 	}
 
 	SerialValue(slotno, xid) = minConflictCommitSeqNo;
@@ -974,7 +975,7 @@ SerialGetMinConflictCommitSeqNo(TransactionId xid)
 	 * but will return with that lock held, which must then be released.
 	 */
 	slotno = SimpleLruReadPage_ReadOnly(SerialSlruCtl,
-										SerialPage(xid), xid);
+										SerialPage(xid), &xid);
 	val = SerialValue(slotno, xid);
 	LWLockRelease(SimpleLruGetBankLock(SerialSlruCtl, SerialPage(xid)));
 	return val;
diff --git a/src/include/access/slru.h b/src/include/access/slru.h
index 4cb8f478fce..67247365d16 100644
--- a/src/include/access/slru.h
+++ b/src/include/access/slru.h
@@ -13,6 +13,7 @@
 #ifndef SLRU_H
 #define SLRU_H
 
+#include "access/transam.h"
 #include "access/xlogdefs.h"
 #include "storage/lwlock.h"
 #include "storage/sync.h"
@@ -146,10 +147,31 @@ typedef struct SlruCtlData
 	 * it's always the same, it doesn't need to be in shared memory.
 	 */
 	char		Dir[64];
+
+	/*
+	 * Callback for creating an I/O error message.
+	 *
+	 * The opaque_data argument here is the same one that is passed to the
+	 * SimpleLruReadPage* calls.
+	 */
+	char	   *(*IoErrorMsg)(const void *opaque_data);
 } SlruCtlData;
 
 typedef SlruCtlData *SlruCtl;
 
+/*
+ * Historically, this module was designed for handling transaction IDs,
+ * therefore this is the most common use case. Thus, make it publicly available.
+ */
+static inline char *
+TransactionIdIoErrorMsg(const void *opaque_data)
+{
+	TransactionId xid = opaque_data ? (*(TransactionId *) opaque_data) :
+									  InvalidTransactionId;
+
+	return psprintf("could not access status of transaction %u", xid);
+}
+
 /*
  * Get the SLRU bank lock for given SlruCtl and the pageno.
  *
@@ -174,9 +196,9 @@ extern void SimpleLruInit(SlruCtl ctl, const char *name, int nslots, int nlsns,
 extern int	SimpleLruZeroPage(SlruCtl ctl, int64 pageno);
 extern void SimpleLruZeroAndWritePage(SlruCtl ctl, int64 pageno);
 extern int	SimpleLruReadPage(SlruCtl ctl, int64 pageno, bool write_ok,
-							  TransactionId xid);
+							  const void *opaque_data);
 extern int	SimpleLruReadPage_ReadOnly(SlruCtl ctl, int64 pageno,
-									   TransactionId xid);
+									   const void *opaque_data);
 extern void SimpleLruWritePage(SlruCtl ctl, int slotno);
 extern void SimpleLruWriteAll(SlruCtl ctl, bool allow_redirtied);
 #ifdef USE_ASSERT_CHECKING
diff --git a/src/test/modules/test_slru/test_slru.c b/src/test/modules/test_slru/test_slru.c
index 4dc74e19620..59ce4900173 100644
--- a/src/test/modules/test_slru/test_slru.c
+++ b/src/test/modules/test_slru/test_slru.c
@@ -100,7 +100,7 @@ test_slru_page_read(PG_FUNCTION_ARGS)
 	/* find page in buffers, reading it if necessary */
 	LWLockAcquire(lock, LW_EXCLUSIVE);
 	slotno = SimpleLruReadPage(TestSlruCtl, pageno,
-							   write_ok, InvalidTransactionId);
+							   write_ok, NULL);
 	data = (char *) TestSlruCtl->shared->page_buffer[slotno];
 	LWLockRelease(lock);
 
@@ -118,7 +118,7 @@ test_slru_page_readonly(PG_FUNCTION_ARGS)
 	/* find page in buffers, reading it if necessary */
 	slotno = SimpleLruReadPage_ReadOnly(TestSlruCtl,
 										pageno,
-										InvalidTransactionId);
+										NULL);
 	Assert(LWLockHeldByMe(lock));
 	data = (char *) TestSlruCtl->shared->page_buffer[slotno];
 	LWLockRelease(lock);
@@ -245,6 +245,7 @@ test_slru_shmem_startup(void)
 	}
 
 	TestSlruCtl->PagePrecedes = test_slru_page_precedes_logically;
+	TestSlruCtl->IoErrorMsg = TransactionIdIoErrorMsg;
 	SimpleLruInit(TestSlruCtl, "TestSLRU",
 				  NUM_TEST_BUFFERS, 0, slru_dir_name,
 				  test_buffer_tranche_id, test_tranche_id, SYNC_HANDLER_NONE,
-- 
2.50.1 (Apple Git-155)



  [application/octet-stream] v2-0004-Use-custom-SLRU-IO-error-msg-for-multixact.patch (2.2K, 6-v2-0004-Use-custom-SLRU-IO-error-msg-for-multixact.patch)
  download | inline diff:
From cde77c8ddaf2b57c39f23f2a5c1431c970e3851c Mon Sep 17 00:00:00 2001
From: Maxim Orlov <[email protected]>
Date: Wed, 25 Feb 2026 18:20:01 +0300
Subject: [PATCH v2 4/5] Use custom SLRU IO error msg for multixact

---
 src/backend/access/transam/multixact.c | 24 ++++++++++++++++++++----
 1 file changed, 20 insertions(+), 4 deletions(-)

diff --git a/src/backend/access/transam/multixact.c b/src/backend/access/transam/multixact.c
index 63d5256823e..34daacae82b 100644
--- a/src/backend/access/transam/multixact.c
+++ b/src/backend/access/transam/multixact.c
@@ -881,7 +881,8 @@ RecordNewMultiXact(MultiXactId multi, MultiXactOffset offset,
 				LWLockAcquire(lock, LW_EXCLUSIVE);
 				prevlock = lock;
 			}
-			slotno = SimpleLruReadPage(MultiXactMemberCtl, pageno, true, &multi);
+			slotno = SimpleLruReadPage(MultiXactMemberCtl, pageno, true,
+									   &offset);
 			prev_pageno = pageno;
 		}
 
@@ -1309,7 +1310,8 @@ GetMultiXactIdMembers(MultiXactId multi, MultiXactMember **members,
 				lock = newlock;
 			}
 
-			slotno = SimpleLruReadPage(MultiXactMemberCtl, pageno, true, &multi);
+			slotno = SimpleLruReadPage(MultiXactMemberCtl, pageno, true,
+									   &offset);
 			prev_pageno = pageno;
 		}
 
@@ -1720,6 +1722,20 @@ MultiXactShmemSize(void)
 	return size;
 }
 
+static inline char *
+MultiXactOffsetIoErrorMsg(const void *opaque_data)
+{
+	return psprintf("could not access status of multixact offset %u",
+					*(MultiXactId *) opaque_data);
+}
+
+static inline char *
+MultiXactMemberIoErrorMsg(const void *opaque_data)
+{
+	return psprintf("could not access status of multixact member %" PRIu64,
+					*(MultiXactOffset *) opaque_data);
+}
+
 void
 MultiXactShmemInit(void)
 {
@@ -1730,8 +1746,8 @@ MultiXactShmemInit(void)
 	MultiXactOffsetCtl->PagePrecedes = MultiXactOffsetPagePrecedes;
 	MultiXactMemberCtl->PagePrecedes = MultiXactMemberPagePrecedes;
 
-	MultiXactOffsetCtl->IoErrorMsg = TransactionIdIoErrorMsg;
-	MultiXactMemberCtl->IoErrorMsg = TransactionIdIoErrorMsg;
+	MultiXactOffsetCtl->IoErrorMsg = MultiXactOffsetIoErrorMsg;
+	MultiXactMemberCtl->IoErrorMsg = MultiXactMemberIoErrorMsg;
 
 	SimpleLruInit(MultiXactOffsetCtl,
 				  "multixact_offset", multixact_offset_buffers, 0,
-- 
2.50.1 (Apple Git-155)



  [application/octet-stream] v2-0003-Use-custom-SLRU-IO-error-msg-for-an-asynchronous-.patch (3.1K, 7-v2-0003-Use-custom-SLRU-IO-error-msg-for-an-asynchronous-.patch)
  download | inline diff:
From 35c8d78ac50fac511ff8805a1aad34369a6abb86 Mon Sep 17 00:00:00 2001
From: Maxim Orlov <[email protected]>
Date: Wed, 25 Feb 2026 18:04:13 +0300
Subject: [PATCH v2 3/5] Use custom SLRU IO error msg for an asynchronous
 notification

---
 src/backend/commands/async.c | 20 ++++++++++++++++----
 1 file changed, 16 insertions(+), 4 deletions(-)

diff --git a/src/backend/commands/async.c b/src/backend/commands/async.c
index a347996b373..302f7c52a9f 100644
--- a/src/backend/commands/async.c
+++ b/src/backend/commands/async.c
@@ -569,6 +569,7 @@ bool		Trace_notify = false;
 int			max_notify_queue_pages = 1048576;
 
 /* local function prototypes */
+static inline char *asyncQueueIoErrorMsg(const void *opaque_data);
 static inline int64 asyncQueuePageDiff(int64 p, int64 q);
 static inline bool asyncQueuePagePrecedes(int64 p, int64 q);
 static inline void GlobalChannelKeyInit(GlobalChannelKey *key, Oid dboid,
@@ -609,6 +610,17 @@ static uint32 notification_hash(const void *key, Size keysize);
 static int	notification_match(const void *key1, const void *key2, Size keysize);
 static void ClearPendingActionsAndNotifies(void);
 
+static inline char *
+asyncQueueIoErrorMsg(const void *opaque_data)
+{
+	const QueuePosition *position = opaque_data;
+
+	Assert(position != NULL);
+
+	return psprintf("could not access status of async queue position (page=%" PRId64", offset=%d)",
+					position->page, position->offset);
+}
+
 /*
  * Compute the difference between two queue page numbers.
  * Previously this function accounted for a wraparound.
@@ -829,7 +841,7 @@ AsyncShmemInit(void)
 	 * names are used in order to avoid wraparound.
 	 */
 	NotifyCtl->PagePrecedes = asyncQueuePagePrecedes;
-	NotifyCtl->IoErrorMsg = TransactionIdIoErrorMsg;
+	NotifyCtl->IoErrorMsg = asyncQueueIoErrorMsg;
 	SimpleLruInit(NotifyCtl, "notify", notify_buffers, 0,
 				  "pg_notify", LWTRANCHE_NOTIFY_BUFFER, LWTRANCHE_NOTIFY_SLRU,
 				  SYNC_HANDLER_NONE, true);
@@ -2068,7 +2080,7 @@ asyncQueueAddEntries(ListCell *nextNotify)
 	if (QUEUE_POS_IS_ZERO(queue_head))
 		slotno = SimpleLruZeroPage(NotifyCtl, pageno);
 	else
-		slotno = SimpleLruReadPage(NotifyCtl, pageno, true, NULL);
+		slotno = SimpleLruReadPage(NotifyCtl, pageno, true, &queue_head);
 
 	/* Note we mark the page dirty before writing in it */
 	NotifyCtl->shared->page_dirty[slotno] = true;
@@ -2738,7 +2750,7 @@ asyncQueueProcessPageEntries(QueuePosition *current,
 	alignas(AsyncQueueEntry) char local_buf[QUEUE_PAGESIZE];
 	char	   *local_buf_end = local_buf;
 
-	slotno = SimpleLruReadPage_ReadOnly(NotifyCtl, curpage, NULL);
+	slotno = SimpleLruReadPage_ReadOnly(NotifyCtl, curpage, current);
 	page_buffer = NotifyCtl->shared->page_buffer[slotno];
 
 	do
@@ -2996,7 +3008,7 @@ AsyncNotifyFreezeXids(TransactionId newFrozenXid)
 
 			lock = SimpleLruGetBankLock(NotifyCtl, pageno);
 			LWLockAcquire(lock, LW_EXCLUSIVE);
-			slotno = SimpleLruReadPage(NotifyCtl, pageno, true, NULL);
+			slotno = SimpleLruReadPage(NotifyCtl, pageno, true, &pos);
 			page_buffer = NotifyCtl->shared->page_buffer[slotno];
 			curpage = pageno;
 		}
-- 
2.50.1 (Apple Git-155)



  [application/octet-stream] v2-0005-Avoid-misleading-user-about-status-of-InvalidTran.patch (1.3K, 8-v2-0005-Avoid-misleading-user-about-status-of-InvalidTran.patch)
  download | inline diff:
From a9added15d182519059aae9e7598ae2cf14dd8ad Mon Sep 17 00:00:00 2001
From: Maxim Orlov <[email protected]>
Date: Wed, 25 Feb 2026 18:20:32 +0300
Subject: [PATCH v2 5/5] Avoid misleading user about status of
 InvalidTransactionId

In some cases, we use the access SLRU page without specifying the XID.
If an error occurs, you may receive a message about the inability to
obtain status of transaction 0, even though the page appears to be sane.
To avoid this, use a more general formulation in case XID is invalid.
---
 src/include/access/slru.h | 7 ++++---
 1 file changed, 4 insertions(+), 3 deletions(-)

diff --git a/src/include/access/slru.h b/src/include/access/slru.h
index 67247365d16..9a6c77101c2 100644
--- a/src/include/access/slru.h
+++ b/src/include/access/slru.h
@@ -166,10 +166,11 @@ typedef SlruCtlData *SlruCtl;
 static inline char *
 TransactionIdIoErrorMsg(const void *opaque_data)
 {
-	TransactionId xid = opaque_data ? (*(TransactionId *) opaque_data) :
-									  InvalidTransactionId;
+	if (opaque_data)
+		return psprintf("could not access status of transaction %u",
+						*(TransactionId *) opaque_data);
 
-	return psprintf("could not access status of transaction %u", xid);
+	return psprintf("could not access slru entry");	/* InvalidTransactionId */
 }
 
 /*
-- 
2.50.1 (Apple Git-155)



view thread (79+ messages)  latest in thread

reply

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Reply to all the recipients using the --to and --cc options:
  reply via email

  To: [email protected]
  Cc: [email protected], [email protected], [email protected], [email protected], [email protected], [email protected], [email protected], [email protected]
  Subject: Re: POC: make mxidoff 64 bits
  In-Reply-To: <CACG=ezYjGmkVO+J7T8uv37XxUaqkptvrkOuMf04E7MTpWPcY=A@mail.gmail.com>

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

This inbox is served by agora; see mirroring instructions
for how to clone and mirror all data and code used for this inbox