From 98ce94864fdb13f0679a92efebea73c64bf6e64f Mon Sep 17 00:00:00 2001
From: Rishu Bagga <bagrishu@amazon.com>
Date: Tue, 20 Jun 2023 21:38:19 +0000
Subject: [PATCH 3/4] slru_to_buf_keep_slru_use_nrels

---
 src/backend/access/transam/Makefile           |    1 +
 src/backend/access/transam/clog.c             |   23 +-
 src/backend/access/transam/commit_ts.c        |   36 +-
 src/backend/access/transam/multixact.c        |   58 +-
 src/backend/access/transam/nrel.c             |  722 +++++++
 src/backend/access/transam/slru.c             | 1739 +++++++++++++----
 src/backend/access/transam/subtrans.c         |   18 +-
 src/backend/access/transam/xact.c             |   15 +-
 src/backend/access/transam/xlog.c             |    2 +-
 src/backend/commands/async.c                  |   27 +-
 src/backend/storage/lmgr/predicate.c          |   16 +-
 src/backend/storage/smgr/smgr.c               |   22 +-
 src/backend/storage/sync/sync.c               |    5 +-
 src/backend/utils/activity/pgstat_slru.c      |   12 +-
 .../utils/activity/wait_event_names.txt       |    4 +
 src/backend/utils/adt/pgstatfuncs.c           |   15 +-
 src/include/access/nrel.h                     |  112 ++
 src/include/access/slru.h                     |  204 +-
 src/include/pgstat.h                          |    4 +-
 src/include/storage/smgr.h                    |    4 +-
 src/include/storage/sync.h                    |    3 +-
 src/include/utils/memutils.h                  |    1 -
 src/include/utils/pgstat_internal.h           |    5 +-
 23 files changed, 2449 insertions(+), 599 deletions(-)
 create mode 100644 src/backend/access/transam/nrel.c
 create mode 100644 src/include/access/nrel.h

diff --git a/src/backend/access/transam/Makefile b/src/backend/access/transam/Makefile
index 661c55a9db7..0fb2114f44e 100644
--- a/src/backend/access/transam/Makefile
+++ b/src/backend/access/transam/Makefile
@@ -17,6 +17,7 @@ OBJS = \
 	commit_ts.o \
 	generic_xlog.o \
 	multixact.o \
+	nrel.o \
 	parallel.o \
 	rmgr.o \
 	slru.o \
diff --git a/src/backend/access/transam/clog.c b/src/backend/access/transam/clog.c
index ec226ffd7e8..88a275f5823 100644
--- a/src/backend/access/transam/clog.c
+++ b/src/backend/access/transam/clog.c
@@ -34,6 +34,7 @@
 
 #include "access/clog.h"
 #include "access/slru.h"
+#include "access/nrel.h"
 #include "access/transam.h"
 #include "access/xlog.h"
 #include "access/xloginsert.h"
@@ -44,6 +45,7 @@
 #include "storage/bufmgr.h"
 #include "storage/buf_internals.h"
 #include "storage/proc.h"
+#include "storage/smgr.h"
 #include "storage/sync.h"
 
 /*
@@ -350,7 +352,7 @@ TransactionIdSetPageStatusInternal(TransactionId xid, int nsubxids,
 	 * write-busy, since we don't care if the update reaches disk sooner than
 	 * we think.
 	 */
-	buffer = ReadSlruBuffer(SLRU_CLOG_REL_ID, pageno);
+	buffer = ReadNrelBuffer(NREL_CLOG_REL_ID, pageno);
 	LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
 
 	/*
@@ -641,7 +643,7 @@ TransactionIdGetStatus(TransactionId xid, XLogRecPtr *lsn)
 	XidStatus	status;
 	Buffer		buffer;
 
-	buffer = ReadSlruBuffer(SLRU_CLOG_REL_ID, pageno);
+	buffer = ReadNrelBuffer(NREL_CLOG_REL_ID, pageno);
 	byteptr = BufferGetPage(buffer) + byteno;
 
 	status = (*byteptr >> bshift) & CLOG_XACT_BITMASK;
@@ -687,7 +689,8 @@ ZeroCLOGPage(int pageno, bool writeXlog)
 {
 	Buffer		buffer;
 
-	buffer = ZeroSlruBuffer(SLRU_CLOG_REL_ID, pageno);
+	buffer = ZeroNrelBuffer(NREL_CLOG_REL_ID, pageno);
+
 	MarkBufferDirty(buffer);
 
 	if (writeXlog)
@@ -733,7 +736,8 @@ TrimCLOG(void)
 		char	   *byteptr;
 		Buffer		buffer;
 
-		buffer = ReadSlruBuffer(SLRU_CLOG_REL_ID, pageno);
+		buffer = ReadNrelBuffer(NREL_CLOG_REL_ID, pageno);
+
 		LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
 		byteptr = BufferGetPage(buffer) + byteno;
 
@@ -798,13 +802,13 @@ TruncateCLOG(TransactionId oldestXact, Oid oldestxid_datoid)
 
 	/*
 	 * The cutoff point is the start of the segment containing oldestXact. We
-	 * pass the *page* containing oldestXact to SimpleLruTruncate.
+	 * pass the *page* containing oldestXact to NonRelTruncate.
 	 */
 	cutoffPage = TransactionIdToPage(oldestXact);
 
 	/* Check to see if there's any files that could be removed */
-	if (!SlruScanDirectory(SLRU_CLOG_REL_ID, CLOGPagePrecedes,
-						   SlruScanDirCbReportPresence, &cutoffPage))
+	if (!NrelScanDirectory(NREL_CLOG_REL_ID, CLOGPagePrecedes,
+						   NrelScanDirCbReportPresence, &cutoffPage))
 		return;					/* nothing to remove */
 
 	/*
@@ -825,10 +829,11 @@ TruncateCLOG(TransactionId oldestXact, Oid oldestxid_datoid)
 	WriteTruncateXlogRec(cutoffPage, oldestXact, oldestxid_datoid);
 
 	/* Now we can remove the old CLOG segment(s) */
-	SimpleLruTruncate(SLRU_CLOG_REL_ID, CLOGPagePrecedes, cutoffPage);
+	NonRelTruncate(NREL_CLOG_REL_ID, CLOGPagePrecedes, cutoffPage);
 }
 
 
+
 /*
  * Decide whether a CLOG page number is "older" for truncation purposes.
  *
@@ -939,7 +944,7 @@ clog_redo(XLogReaderState *record)
 
 		AdvanceOldestClogXid(xlrec.oldestXact);
 
-		SimpleLruTruncate(SLRU_CLOG_REL_ID, CLOGPagePrecedes, xlrec.pageno);
+		NonRelTruncate(NREL_CLOG_REL_ID, CLOGPagePrecedes, xlrec.pageno);
 	}
 	else
 		elog(PANIC, "clog_redo: unknown op code %u", info);
diff --git a/src/backend/access/transam/commit_ts.c b/src/backend/access/transam/commit_ts.c
index 3d8af7b9609..6cb2870e5f4 100644
--- a/src/backend/access/transam/commit_ts.c
+++ b/src/backend/access/transam/commit_ts.c
@@ -23,6 +23,7 @@
 
 #include "access/commit_ts.h"
 #include "access/htup_details.h"
+#include "access/nrel.h"
 #include "access/slru.h"
 #include "access/transam.h"
 #include "access/xloginsert.h"
@@ -51,7 +52,7 @@
 /*
  * We need 8+2 bytes per xact.  Note that enlarging this struct might mean
  * the largest possible file name is more than 5 chars long; see
- * SlruScanDirectory.
+ * NrelScanDirectory.
  */
 typedef struct CommitTimestampEntry
 {
@@ -212,7 +213,8 @@ SetXidCommitTsInPage(TransactionId xid, int nsubxids,
 	int			i;
 	Buffer		buffer;
 
-	buffer = ReadSlruBuffer(SLRU_COMMITTS_REL_ID, pageno);
+	buffer = ReadNrelBuffer(NREL_COMMITTS_REL_ID, pageno);
+
 	LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
 
 	TransactionIdSetCommitTs(xid, ts, nodeid, buffer);
@@ -314,7 +316,8 @@ TransactionIdGetCommitTsData(TransactionId xid, TimestampTz *ts,
 		return false;
 	}
 
-	buffer = ReadSlruBuffer(SLRU_COMMITTS_REL_ID, pageno);
+	buffer = ReadNrelBuffer(NREL_COMMITTS_REL_ID, pageno);
+
 	LockBuffer(buffer, BUFFER_LOCK_SHARE);
 
 	memcpy(&entry,
@@ -495,7 +498,7 @@ CommitTsShmemInit(void)
 {
 	bool		found;
 
-	SlruPagePrecedesUnitTests(CommitTsPagePrecedes, COMMIT_TS_XACTS_PER_PAGE);
+	NrelPagePrecedesUnitTests(CommitTsPagePrecedes, COMMIT_TS_XACTS_PER_PAGE);
 
 	commitTsShared = ShmemInitStruct("CommitTs shared",
 									 sizeof(CommitTimestampShared),
@@ -544,7 +547,8 @@ ZeroCommitTsPage(int pageno, bool writeXlog)
 {
 	Buffer		buffer;
 
-	buffer = ZeroSlruBuffer(SLRU_COMMITTS_REL_ID, pageno);
+	buffer = ZeroNrelBuffer(NREL_COMMITTS_REL_ID, pageno);
+
 	MarkBufferDirty(buffer);
 
 	if (writeXlog)
@@ -670,11 +674,12 @@ ActivateCommitTs(void)
 	LWLockRelease(CommitTsLock);
 
 	/* Create the current segment file, if necessary */
-	if (!SimpleLruDoesPhysicalPageExist(SLRU_COMMITTS_REL_ID, pageno))
+	if (!NonRelDoesPhysicalPageExist(NREL_COMMITTS_REL_ID, pageno))
 	{
 		Buffer		buffer;
 
-		buffer = ZeroSlruBuffer(SLRU_COMMITTS_REL_ID, pageno);
+		buffer = ZeroNrelBuffer(NREL_COMMITTS_REL_ID, pageno);
+
 		MarkBufferDirty(buffer);
 		FlushOneBuffer(buffer);
 		UnlockReleaseBuffer(buffer);
@@ -726,9 +731,9 @@ DeactivateCommitTs(void)
 	 * be overwritten anyway when we wrap around, but it seems better to be
 	 * tidy.)
 	 */
-	(void) SlruScanDirectory(SLRU_COMMITTS_REL_ID,
+	(void) NrelScanDirectory(NREL_COMMITTS_REL_ID,
 							 CommitTsPagePrecedes,
-							 SlruScanDirCbDeleteAll, NULL);
+							 NrelScanDirCbDeleteAll, NULL);
 }
 
 /*
@@ -783,14 +788,14 @@ TruncateCommitTs(TransactionId oldestXact)
 
 	/*
 	 * The cutoff point is the start of the segment containing oldestXact. We
-	 * pass the *page* containing oldestXact to SimpleLruTruncate.
+	 * pass the *page* containing oldestXact to NonRelTruncate.
 	 */
 	cutoffPage = TransactionIdToCTsPage(oldestXact);
 
 	/* Check to see if there's any files that could be removed */
-	if (!SlruScanDirectory(SLRU_COMMITTS_REL_ID,
+	if (!NrelScanDirectory(NREL_COMMITTS_REL_ID,
 						   CommitTsPagePrecedes,
-						   SlruScanDirCbReportPresence,
+						   NrelScanDirCbReportPresence,
 						   &cutoffPage))
 		return;					/* nothing to remove */
 
@@ -798,7 +803,7 @@ TruncateCommitTs(TransactionId oldestXact)
 	WriteTruncateXlogRec(cutoffPage, oldestXact);
 
 	/* Now we can remove the old CommitTs segment(s) */
-	SimpleLruTruncate(SLRU_COMMITTS_REL_ID, CommitTsPagePrecedes, cutoffPage);
+	NonRelTruncate(NREL_COMMITTS_REL_ID, CommitTsPagePrecedes, cutoffPage);
 }
 
 /*
@@ -926,7 +931,8 @@ commit_ts_redo(XLogReaderState *record)
 
 		memcpy(&pageno, XLogRecGetData(record), sizeof(int));
 
-		buffer = ZeroSlruBuffer(SLRU_COMMITTS_REL_ID, pageno);
+		buffer = ZeroNrelBuffer(NREL_COMMITTS_REL_ID, pageno);
+
 		MarkBufferDirty(buffer);
 		FlushOneBuffer(buffer);
 		UnlockReleaseBuffer(buffer);
@@ -937,7 +943,7 @@ commit_ts_redo(XLogReaderState *record)
 
 		AdvanceOldestCommitTsXid(trunc->oldestXid);
 
-		SimpleLruTruncate(SLRU_COMMITTS_REL_ID, CommitTsPagePrecedes, trunc->pageno);
+		NonRelTruncate(NREL_COMMITTS_REL_ID, CommitTsPagePrecedes, trunc->pageno);
 	}
 	else
 		elog(PANIC, "commit_ts_redo: unknown op code %u", info);
diff --git a/src/backend/access/transam/multixact.c b/src/backend/access/transam/multixact.c
index 9d7b2e1ce94..810aec99a49 100644
--- a/src/backend/access/transam/multixact.c
+++ b/src/backend/access/transam/multixact.c
@@ -69,6 +69,7 @@
 #include "postgres.h"
 
 #include "access/multixact.h"
+#include "access/nrel.h"
 #include "access/slru.h"
 #include "access/transam.h"
 #include "access/twophase.h"
@@ -873,7 +874,8 @@ RecordNewMultiXact(MultiXactId multi, MultiXactOffset offset,
 	if (offset_buf_ptr)
 		offset_buf = *offset_buf_ptr;
 	else 
-		offset_buf = ReadSlruBuffer(SLRU_MULTIXACT_OFFSET_ID, pageno);
+		offset_buf = ReadNrelBuffer(NREL_MULTIXACT_OFFSET_REL_ID, pageno);
+
 
 	LockBuffer(offset_buf, BUFFER_LOCK_EXCLUSIVE);
 						
@@ -917,7 +919,8 @@ RecordNewMultiXact(MultiXactId multi, MultiXactOffset offset,
 			if (member_bufs)
 				buffer = member_bufs[pageno - min_pageno];
 			else
-				buffer = ReadSlruBuffer(SLRU_MULTIXACT_MEMBER_ID, pageno);
+				buffer = ReadNrelBuffer(NREL_MULTIXACT_MEMBER_REL_ID, pageno);
+
 
 			LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
 			prev_pageno = pageno;
@@ -1363,7 +1366,8 @@ retry:
 	pageno = MultiXactIdToOffsetPage(multi);
 	entryno = MultiXactIdToOffsetEntry(multi);
 
-	buffer = ReadSlruBuffer(SLRU_MULTIXACT_OFFSET_REL_ID, pageno);
+	buffer = ReadNrelBuffer(NREL_MULTIXACT_OFFSET_REL_ID, pageno);
+
 	LockBuffer(buffer, BUFFER_LOCK_SHARE);
 	offptr = (MultiXactOffset *) BufferGetPage(buffer);
 	offptr += entryno;
@@ -1398,7 +1402,8 @@ retry:
 		if (pageno != prev_pageno)
 		{
 			UnlockReleaseBuffer(buffer);
-			buffer = ReadSlruBuffer(SLRU_MULTIXACT_OFFSET_REL_ID, pageno);
+			buffer = ReadNrelBuffer(NREL_MULTIXACT_OFFSET_REL_ID, pageno);
+
 			LockBuffer(buffer, BUFFER_LOCK_SHARE);
 		}
 
@@ -1439,7 +1444,8 @@ retry:
 		{
 			if (BufferIsValid(buffer))
 				UnlockReleaseBuffer(buffer);
-			buffer = ReadSlruBuffer(SLRU_MULTIXACT_MEMBER_REL_ID, pageno);
+			buffer = ReadNrelBuffer(NREL_MULTIXACT_MEMBER_REL_ID, pageno);
+
 			LockBuffer(buffer, BUFFER_LOCK_SHARE);
 			prev_pageno = pageno;
 		}
@@ -1921,7 +1927,8 @@ ZeroMultiXactOffsetPage(int pageno, bool writeXlog)
 {
 	Buffer		buffer;
 
-	buffer = ZeroSlruBuffer(SLRU_MULTIXACT_OFFSET_REL_ID, pageno);
+	buffer = ZeroNrelBuffer(NREL_MULTIXACT_OFFSET_REL_ID, pageno);
+
 	MarkBufferDirty(buffer);
 
 	if (writeXlog)
@@ -1938,7 +1945,8 @@ ZeroMultiXactMemberPage(int pageno, bool writeXlog)
 {
 	Buffer		buffer;
 
-	buffer = ZeroSlruBuffer(SLRU_MULTIXACT_MEMBER_REL_ID, pageno);
+	buffer = ZeroNrelBuffer(NREL_MULTIXACT_MEMBER_REL_ID, pageno);
+
 	MarkBufferDirty(buffer);
 
 	if (writeXlog)
@@ -1969,7 +1977,7 @@ MaybeExtendOffsetSlru(void)
 
 	pageno = MultiXactIdToOffsetPage(MultiXactState->nextMXact);
 
-	if (!SimpleLruDoesPhysicalPageExist(SLRU_MULTIXACT_OFFSET_REL_ID, pageno))
+	if (!NonRelDoesPhysicalPageExist(NREL_MULTIXACT_OFFSET_REL_ID, pageno))
 	{
 		Buffer			buffer;
 
@@ -2030,7 +2038,8 @@ TrimMultiXact(void)
 		MultiXactOffset *offptr;
 		Buffer		buffer;
 
-		buffer = ReadSlruBuffer(SLRU_MULTIXACT_OFFSET_REL_ID, pageno);
+		buffer = ReadNrelBuffer(NREL_MULTIXACT_OFFSET_REL_ID, pageno);
+
 		LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
 		offptr = (MultiXactOffset *) BufferGetPage(buffer);
 		offptr += entryno;
@@ -2055,7 +2064,8 @@ TrimMultiXact(void)
 		Buffer		buffer;
 
 		memberoff = MXOffsetToMemberOffset(offset);
-		buffer = ReadSlruBuffer(SLRU_MULTIXACT_MEMBER_REL_ID, pageno);
+		buffer = ReadNrelBuffer(NREL_MULTIXACT_MEMBER_REL_ID, pageno);
+
 		LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
 		xidptr = (TransactionId *) (BufferGetPage(buffer) + memberoff);
 
@@ -2352,7 +2362,7 @@ ExtendMultiXactOffset(MultiXactId multi, Buffer * buffer)
 		multi != FirstMultiXactId)
 	{
 		/* make a read buffer call to enlarge the resource owner */
-		*buffer = ReadSlruBuffer(SLRU_MULTIXACT_OFFSET_ID, pageno);
+		*buffer = ReadNrelBuffer(NREL_MULTIXACT_OFFSET_REL_ID, pageno);
 		return;
 	} else
 	{
@@ -2408,7 +2418,8 @@ ExtendMultiXactMember(MultiXactOffset offset, int nmembers, Buffer ** buffers)
 		} else
 		{
 			/* do a read buffer call to allocate space beforehand */
-			buf = ReadSlruBuffer(SLRU_MULTIXACT_MEMBER_ID, pageno);
+			buf = ReadNrelBuffer(NREL_MULTIXACT_MEMBER_REL_ID, pageno);
+
 		}
 
 		if (buffers)
@@ -2689,7 +2700,7 @@ find_multixact_start(MultiXactId multi, MultiXactOffset *result)
 	int			entryno;
 	MultiXactOffset *offptr;
 	Buffer		buffer;
-	RelFileLocator rlocator = SlruRelFileLocator(SLRU_MULTIXACT_OFFSET_REL_ID);
+	RelFileLocator rlocator = NrelRelFileLocator(NREL_MULTIXACT_OFFSET_REL_ID);
 
 	Assert(MultiXactState->finishedStartup);
 
@@ -2701,10 +2712,11 @@ find_multixact_start(MultiXactId multi, MultiXactOffset *result)
 	 * commit 068cfadf9).
 	 */
 	if (!BufferProbe(rlocator, MAIN_FORKNUM, pageno) &&
-		!SimpleLruDoesPhysicalPageExist(SLRU_MULTIXACT_OFFSET_REL_ID, pageno))
+		!NonRelDoesPhysicalPageExist(NREL_MULTIXACT_OFFSET_REL_ID, pageno))
 		return false;
 
-	buffer = ReadSlruBuffer(SLRU_MULTIXACT_OFFSET_REL_ID, pageno);
+	buffer = ReadNrelBuffer(NREL_MULTIXACT_OFFSET_REL_ID, pageno);
+
 	LockBuffer(buffer, BUFFER_LOCK_SHARE);
 	offptr = (MultiXactOffset *) BufferGetPage(buffer);
 	offptr += entryno;
@@ -2812,7 +2824,7 @@ typedef struct mxtruncinfo
  *		This callback determines the earliest existing page number.
  */
 static bool
-SlruScanDirCbFindEarliest(Oid rel_id, SlruPagePrecedesFunction PagePrecedes,
+NrelScanDirCbFindEarliest(Oid rel_id, NrelPagePrecedesFunction PagePrecedes,
 						  char *filename, int segpage, void *data)
 {
 	mxtruncinfo *trunc = (mxtruncinfo *) data;
@@ -2831,7 +2843,7 @@ SlruScanDirCbFindEarliest(Oid rel_id, SlruPagePrecedesFunction PagePrecedes,
  * Delete members segments [oldest, newOldest)
  *
  * The members SLRU can, in contrast to the offsets one, be filled to almost
- * the full range at once. This means SimpleLruTruncate() can't trivially be
+ * the full range at once. This means NonRelTruncate() can't trivially be
  * used - instead the to-be-deleted range is computed using the offsets
  * SLRU. C.f. TruncateMultiXact().
  */
@@ -2850,7 +2862,7 @@ PerformMembersTruncation(MultiXactOffset oldestOffset, MultiXactOffset newOldest
 	while (segment != endsegment)
 	{
 		elog(DEBUG2, "truncating multixact members segment %x", segment);
-		SlruDeleteSegment(SLRU_MULTIXACT_MEMBER_REL_ID, segment);
+		NrelDeleteSegment(NREL_MULTIXACT_MEMBER_REL_ID, segment);
 
 		/* move to next segment, handling wraparound correctly */
 		if (segment == maxsegment)
@@ -2870,10 +2882,10 @@ PerformOffsetsTruncation(MultiXactId oldestMulti, MultiXactId newOldestMulti)
 	 * We step back one multixact to avoid passing a cutoff page that hasn't
 	 * been created yet in the rare case that oldestMulti would be the first
 	 * item on a page and oldestMulti == nextMulti.  In that case, if we
-	 * didn't subtract one, we'd trigger SimpleLruTruncate's wraparound
+	 * didn't subtract one, we'd trigger NonRelTruncate's wraparound
 	 * detection.
 	 */
-	SimpleLruTruncate(SLRU_MULTIXACT_OFFSET_REL_ID,
+	NonRelTruncate(NREL_MULTIXACT_OFFSET_REL_ID,
 					  MultiXactOffsetPagePrecedes,
 					  MultiXactIdToOffsetPage(PreviousMultiXactId(newOldestMulti)));
 }
@@ -2937,7 +2949,7 @@ TruncateMultiXact(MultiXactId newOldestMulti, Oid newOldestMultiDB)
 	 * the earliest offsets page number that we can read without error.
 	 *
 	 * When nextMXact is less than one segment away from multiWrapLimit,
-	 * SlruScanDirCbFindEarliest can find some early segment other than the
+	 * NrelScanDirCbFindEarliest can find some early segment other than the
 	 * actual earliest.  (MultiXactOffsetPagePrecedes(EARLIEST, LATEST)
 	 * returns false, because not all pairs of entries have the same answer.)
 	 * That can also arise when an earlier truncation attempt failed unlink()
@@ -2948,9 +2960,9 @@ TruncateMultiXact(MultiXactId newOldestMulti, Oid newOldestMultiDB)
 	 * been truncated away, and we crashed before updating oldestMulti.
 	 */
 	trunc.earliestExistingPage = -1;
-	SlruScanDirectory(SLRU_MULTIXACT_OFFSET_REL_ID,
+	NrelScanDirectory(NREL_MULTIXACT_OFFSET_REL_ID,
 					  MultiXactOffsetPagePrecedes,
-					  SlruScanDirCbFindEarliest, &trunc);
+					  NrelScanDirCbFindEarliest, &trunc);
 	earliest = trunc.earliestExistingPage * MULTIXACT_OFFSETS_PER_PAGE;
 	if (earliest < FirstMultiXactId)
 		earliest = FirstMultiXactId;
diff --git a/src/backend/access/transam/nrel.c b/src/backend/access/transam/nrel.c
new file mode 100644
index 00000000000..bf0425ca5ee
--- /dev/null
+++ b/src/backend/access/transam/nrel.c
@@ -0,0 +1,722 @@
+/*-------------------------------------------------------------------------
+ *
+ * nrel.c
+ *		
+ * interface for non relational data to be buffered through
+ * buffercache.
+ * 
+ *
+ *
+ * Portions Copyright (c) 1996-2023, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/backend/access/transam/nrel.c
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include "access/nrel.h"
+#include "access/transam.h"
+#include "access/xlog.h"
+#include "access/xlogutils.h"
+#include "miscadmin.h"
+#include "pgstat.h"
+#include "storage/fd.h"
+#include "storage/shmem.h"
+
+#define NrelFileName(rel_id, path, seg)							\
+	snprintf(path, MAXPGPATH, "%s/%04X", defs[(rel_id)].path, seg)
+
+struct NrelDef {
+	const char *name;
+	const char *path;
+	bool synchronize;
+};
+
+static const struct NrelDef defs[] = {
+	[NREL_CLOG_REL_ID] = {
+		.name = "Xact",
+		.path = "pg_xact",
+		.synchronize = true,
+	},
+	[NREL_SUBTRANS_REL_ID] = {
+		.name = "Subtrans",
+		.path = "pg_subtrans",
+	},
+	[NREL_MULTIXACT_OFFSET_REL_ID] = {
+		.name = "MultiXactOffset",
+		.path = "pg_multixact/offsets",
+		.synchronize = true,
+	},
+	[NREL_MULTIXACT_MEMBER_REL_ID] = {
+		.name = "MultiXactMember",
+		.path = "pg_multixact/members",
+		.synchronize = true,
+	},
+	[NREL_COMMITTS_REL_ID] = {
+		.name = "CommitTs",
+		.path = "pg_commit_ts",
+		.synchronize = true,
+	},
+	[NREL_SERIAL_REL_ID] = {
+		.name = "Serial",
+		.path = "pg_serial",
+	},
+	[NREL_NOTIFY_REL_ID] = {
+		.name = "Notify",
+		.path = "pg_notify",
+	},
+};
+
+/*
+ * We'll maintain a little cache of recently seen buffers, to try to avoid the
+ * buffer mapping table on repeat access (ie the busy end of the CLOG).  One
+ * entry per NREL relation.
+ */
+struct NrelRecentBuffer {
+	int			pageno;
+	Buffer		recent_buffer;
+};
+
+static struct NrelRecentBuffer nrel_recent_buffers[lengthof(defs)];
+
+/*
+ * Populate a file tag identifying an NREL segment file.
+ */
+#define INIT_NRELFILETAG(a,xx_rel_id,xx_segno) \
+( \
+	memset(&(a), 0, sizeof(FileTag)), \
+	(a).handler = SYNC_HANDLER_NREL, \
+	(a).rlocator = NrelRelFileLocator(xx_rel_id), \
+	(a).segno = (xx_segno) \
+)
+
+static bool NrelScanDirCbDeleteCutoff(Oid rel_id,
+									  NrelPagePrecedesFunction PagePrecedes,
+									  char *filename,
+ 									  int segpage, void *data);
+
+static void NrelInternalDeleteSegment(Oid rel_id, int segno);
+
+static File nrelfile(SMgrRelation reln, BlockNumber blocknum, int mode,
+					 bool missing_ok);
+
+/*
+ * Return whether the given page exists on disk.
+ *
+ * A false return means that either the file does not exist, or that it's not
+ * large enough to contain the given page.
+ */
+bool
+NonRelDoesPhysicalPageExist(Oid rel_id, int pageno)
+{
+	int			rpageno = pageno % NREL_PAGES_PER_SEGMENT;
+	off_t		offset = rpageno * BLCKSZ;
+	off_t		size;
+	File		file;
+	RelFileLocator	rlocator = NrelRelFileLocator(rel_id);
+	SMgrRelation reln = smgropen(rlocator, InvalidBackendId);
+
+	/* update the stats counter of checked pages */
+	pgstat_count_slru_page_exists(rel_id);
+
+	file = nrelfile(reln, pageno, O_RDWR, true);
+	if (file < 0)
+	{
+		Assert(errno == ENOENT);
+		return false;
+	}
+	size = FileSize(file);
+	if (size < 0)
+		elog(ERROR, "could not get size of file \"%s\": %m",
+			 FilePathName(file));
+
+	return size >= offset + BLCKSZ;
+}
+
+/*
+ * Remove all segments before the one holding the passed page number
+ *
+ * All NRELs prevent concurrent calls to this function, either with an LWLock
+ * or by calling it only as part of a checkpoint.  Mutual exclusion must begin
+ * before computing cutoffPage.  Mutual exclusion must end after any limit
+ * update that would permit other backends to write fresh data into the
+ * segment immediately preceding the one containing cutoffPage.  Otherwise,
+ * when the NREL is quite full, NonRelTruncate() might delete that segment
+ * after it has accrued freshly-written data.
+ */
+void
+NonRelTruncate(Oid rel_id, NrelPagePrecedesFunction PagePrecedes, int cutoffPage)
+{
+
+	/* update the stats counter of truncates */
+	pgstat_count_slru_truncate(rel_id);
+	
+
+	/* Now we can remove the old segment(s) */
+	(void) NrelScanDirectory(rel_id, PagePrecedes, NrelScanDirCbDeleteCutoff,
+							 &cutoffPage);
+}
+
+/*
+ * Delete an individual NREL segment.
+ *
+ * NB: This does not touch the NREL buffers themselves, callers have to ensure
+ * they either can't yet contain anything, or have already been cleaned out.
+ */
+static void
+NrelInternalDeleteSegment(Oid rel_id, int segno)
+{
+	char		path[MAXPGPATH];
+
+	/* Forget any fsync requests queued for this segment. */
+	if (defs[rel_id].synchronize)
+	{
+		FileTag		tag;
+
+		INIT_NRELFILETAG(tag, rel_id, segno);
+		RegisterSyncRequest(&tag, SYNC_FORGET_REQUEST, true);
+	}
+
+	/* Unlink the file. */
+	NrelFileName(rel_id, path, segno);
+	ereport(DEBUG2, (errmsg_internal("removing file \"%s\"", path)));
+	unlink(path);
+}
+
+/*
+ * Delete an individual NREL segment, identified by the segment number.
+ */
+void
+NrelDeleteSegment(Oid rel_id, int segno)
+{
+	RelFileLocator rlocator = NrelRelFileLocator(rel_id);
+
+	/* Clean out any possibly existing references to the segment. */
+	for (int i = 0; i < NREL_PAGES_PER_SEGMENT; ++i)
+		DiscardBuffer(rlocator, MAIN_FORKNUM, segno * NREL_PAGES_PER_SEGMENT + i);
+
+	NrelInternalDeleteSegment(rel_id, segno);
+}
+
+/*
+ * Determine whether a segment is okay to delete.
+ *
+ * segpage is the first page of the segment, and cutoffPage is the oldest (in
+ * PagePrecedes order) page in the NREL containing still-useful data.  Since
+ * every core PagePrecedes callback implements "wrap around", check the
+ * segment's first and last pages:
+ *
+ * first<cutoff  && last<cutoff:  yes
+ * first<cutoff  && last>=cutoff: no; cutoff falls inside this segment
+ * first>=cutoff && last<cutoff:  no; wrap point falls inside this segment
+ * first>=cutoff && last>=cutoff: no; every page of this segment is too young
+ */
+static bool
+NrelMayDeleteSegment(NrelPagePrecedesFunction PagePrecedes,
+					 int segpage, int cutoffPage)
+{
+	int			seg_last_page = segpage + NREL_PAGES_PER_SEGMENT - 1;
+
+	Assert(segpage % NREL_PAGES_PER_SEGMENT == 0);
+
+	return (PagePrecedes(segpage, cutoffPage) &&
+			PagePrecedes(seg_last_page, cutoffPage));
+}
+
+#ifdef USE_ASSERT_CHECKING
+static void
+NrelPagePrecedesTestOffset(NrelPagePrecedesFunction PagePrecedes,
+						   int per_page, uint32 offset)
+{
+	TransactionId lhs,
+				rhs;
+	int			newestPage,
+				oldestPage;
+	TransactionId newestXact,
+				oldestXact;
+
+	/*
+	 * Compare an XID pair having undefined order (see RFC 1982), a pair at
+	 * "opposite ends" of the XID space.  TransactionIdPrecedes() treats each
+	 * as preceding the other.  If RHS is oldestXact, LHS is the first XID we
+	 * must not assign.
+	 */
+	lhs = per_page + offset;	/* skip first page to avoid non-normal XIDs */
+	rhs = lhs + (1U << 31);
+	Assert(TransactionIdPrecedes(lhs, rhs));
+	Assert(TransactionIdPrecedes(rhs, lhs));
+	Assert(!TransactionIdPrecedes(lhs - 1, rhs));
+	Assert(TransactionIdPrecedes(rhs, lhs - 1));
+	Assert(TransactionIdPrecedes(lhs + 1, rhs));
+	Assert(!TransactionIdPrecedes(rhs, lhs + 1));
+	Assert(!TransactionIdFollowsOrEquals(lhs, rhs));
+	Assert(!TransactionIdFollowsOrEquals(rhs, lhs));
+	Assert(!PagePrecedes(lhs / per_page, lhs / per_page));
+	Assert(!PagePrecedes(lhs / per_page, rhs / per_page));
+	Assert(!PagePrecedes(rhs / per_page, lhs / per_page));
+	Assert(!PagePrecedes((lhs - per_page) / per_page, rhs / per_page));
+	Assert(PagePrecedes(rhs / per_page, (lhs - 3 * per_page) / per_page));
+	Assert(PagePrecedes(rhs / per_page, (lhs - 2 * per_page) / per_page));
+	Assert(PagePrecedes(rhs / per_page, (lhs - 1 * per_page) / per_page)
+		   || (1U << 31) % per_page != 0);	/* See CommitTsPagePrecedes() */
+	Assert(PagePrecedes((lhs + 1 * per_page) / per_page, rhs / per_page)
+		   || (1U << 31) % per_page != 0);
+	Assert(PagePrecedes((lhs + 2 * per_page) / per_page, rhs / per_page));
+	Assert(PagePrecedes((lhs + 3 * per_page) / per_page, rhs / per_page));
+	Assert(!PagePrecedes(rhs / per_page, (lhs + per_page) / per_page));
+
+	/*
+	 * GetNewTransactionId() has assigned the last XID it can safely use, and
+	 * that XID is in the *LAST* page of the second segment.  We must not
+	 * delete that segment.
+	 */
+	newestPage = 2 * NREL_PAGES_PER_SEGMENT - 1;
+	newestXact = newestPage * per_page + offset;
+	Assert(newestXact / per_page == newestPage);
+	oldestXact = newestXact + 1;
+	oldestXact -= 1U << 31;
+	oldestPage = oldestXact / per_page;
+	Assert(!NrelMayDeleteSegment(PagePrecedes,
+								 (newestPage -
+								  newestPage % NREL_PAGES_PER_SEGMENT),
+								 oldestPage));
+
+	/*
+	 * GetNewTransactionId() has assigned the last XID it can safely use, and
+	 * that XID is in the *FIRST* page of the second segment.  We must not
+	 * delete that segment.
+	 */
+	newestPage = NREL_PAGES_PER_SEGMENT;
+	newestXact = newestPage * per_page + offset;
+	Assert(newestXact / per_page == newestPage);
+	oldestXact = newestXact + 1;
+	oldestXact -= 1U << 31;
+	oldestPage = oldestXact / per_page;
+	Assert(!NrelMayDeleteSegment(PagePrecedes,
+								 (newestPage -
+								  newestPage % NREL_PAGES_PER_SEGMENT),
+								 oldestPage));
+}
+
+/*
+ * Unit-test a PagePrecedes function.
+ *
+ * This assumes every uint32 >= FirstNormalTransactionId is a valid key.  It
+ * assumes each value occupies a contiguous, fixed-size region of NREL bytes.
+ * (MultiXactMemberCtl separates flags from XIDs.  NotifyCtl has
+ * variable-length entries, no keys, and no random access.  These unit tests
+ * do not apply to them.)
+ */
+void
+NrelPagePrecedesUnitTests(NrelPagePrecedesFunction PagePrecedes, int per_page)
+{
+	/* Test first, middle and last entries of a page. */
+	NrelPagePrecedesTestOffset(PagePrecedes, per_page, 0);
+	NrelPagePrecedesTestOffset(PagePrecedes, per_page, per_page / 2);
+	NrelPagePrecedesTestOffset(PagePrecedes, per_page, per_page - 1);
+}
+#endif
+
+/*
+ * NrelScanDirectory callback
+ *		This callback reports true if there's any segment wholly prior to the
+ *		one containing the page passed as "data".
+ */
+bool
+NrelScanDirCbReportPresence(Oid rel_id, NrelPagePrecedesFunction PagePrecedes,
+							char *filename, int segpage, void *data)
+{
+	int			cutoffPage = *(int *) data;
+
+	if (NrelMayDeleteSegment(PagePrecedes, segpage, cutoffPage))
+		return true;			/* found one; don't iterate any more */
+
+	return false;				/* keep going */
+}
+
+/*
+ * NrelScanDirectory callback.
+ *		This callback deletes segments prior to the one passed in as "data".
+ */
+static bool
+NrelScanDirCbDeleteCutoff(Oid rel_id, NrelPagePrecedesFunction PagePrecedes,
+						  char *filename, int segpage, void *data)
+{
+	RelFileLocator	rlocator = NrelRelFileLocator(rel_id);
+	int			cutoffPage = *(int *) data;
+
+	if (NrelMayDeleteSegment(PagePrecedes, segpage, cutoffPage))
+	{
+		for (int i = 0; i < NREL_PAGES_PER_SEGMENT; ++i)
+			DiscardBuffer(rlocator, MAIN_FORKNUM, segpage + i);
+		NrelInternalDeleteSegment(rel_id, segpage / NREL_PAGES_PER_SEGMENT);
+	}
+
+	return false;				/* keep going */
+}
+
+/*
+ * NrelScanDirectory callback.
+ *		This callback deletes all segments.
+ */
+bool
+NrelScanDirCbDeleteAll(Oid rel_id, NrelPagePrecedesFunction PagePrecedes,
+					   char *filename, int segpage, void *data)
+{
+	NrelInternalDeleteSegment(rel_id, segpage / NREL_PAGES_PER_SEGMENT);
+
+	return false;				/* keep going */
+}
+
+/*
+ * Scan the NonRel directory and apply a callback to each file found in it.
+ *
+ * If the callback returns true, the scan is stopped.  The last return value
+ * from the callback is returned.
+ *
+ * The callback receives the following arguments: 1. the NrelCtl struct for the
+ * nrel being truncated; 2. the filename being considered; 3. the page number
+ * for the first page of that file; 4. a pointer to the opaque data given to us
+ * by the caller.
+ *
+ * Note that the ordering in which the directory is scanned is not guaranteed.
+ *
+ * Note that no locking is applied.
+ */
+bool
+NrelScanDirectory(Oid rel_id, NrelPagePrecedesFunction PagePrecedes,
+				  NrelScanCallback callback, void *data)
+{
+	bool		retval = false;
+	DIR		   *cldir;
+	struct dirent *clde;
+	int			segno;
+	int			segpage;
+	const char *path;
+
+	path = defs[rel_id].path;
+
+	cldir = AllocateDir(path);
+	while ((clde = ReadDir(cldir, path)) != NULL)
+	{
+		size_t		len;
+
+		len = strlen(clde->d_name);
+
+		if ((len == 4 || len == 5 || len == 6) &&
+			strspn(clde->d_name, "0123456789ABCDEF") == len)
+		{
+			segno = (int) strtol(clde->d_name, NULL, 16);
+			segpage = segno * NREL_PAGES_PER_SEGMENT;
+
+			elog(DEBUG2, "NrelScanDirectory invoking callback on %s/%s",
+				 path, clde->d_name);
+			retval = callback(rel_id, PagePrecedes, clde->d_name, segpage, data);
+			if (retval)
+				break;
+		}
+	}
+	FreeDir(cldir);
+
+	return retval;
+}
+
+
+void
+CheckPointNREL(void)
+{
+	/* Ensure that directory entries for new files are on disk. */
+	for (int i = 0; i < lengthof(defs); ++i)
+	{
+		if (defs[i].synchronize)
+			fsync_fname(defs[i].path, true);
+	}
+}
+
+/*
+ * Read a buffer.  Buffer is pinned on return.
+ */
+Buffer
+ReadNrelBuffer(Oid rel_id, int pageno)
+{
+	RelFileLocator rlocator = NrelRelFileLocator(rel_id);
+	Buffer buffer;
+	bool hit;
+
+	/* Try to avoid doing a buffer mapping table lookup for repeated access. */
+	buffer = nrel_recent_buffers[rel_id].recent_buffer;
+	if (nrel_recent_buffers[rel_id].pageno == pageno &&
+		BufferIsValid(buffer) &&
+		ReadRecentBuffer(rlocator, MAIN_FORKNUM, pageno, buffer))
+	{
+		pgstat_count_slru_page_hit(rel_id);
+		return buffer;
+	}
+
+	/* Regular lookup. */
+	buffer = ReadBufferWithoutRelcacheWithHit(rlocator, MAIN_FORKNUM, pageno,
+											  RBM_NORMAL, &hit);
+
+	/* Remember where this page is for next time. */
+	nrel_recent_buffers[rel_id].pageno = pageno;
+	nrel_recent_buffers[rel_id].recent_buffer = buffer;
+
+	if (hit)
+		pgstat_count_slru_page_hit(rel_id);
+
+	return buffer;
+}
+
+/*
+ * Zero-initialize a buffer.  Buffer is pinned and exclusively locked on return.
+ */
+Buffer
+ZeroNrelBuffer(Oid rel_id, int pageno)
+{
+	RelFileLocator rlocator = NrelRelFileLocator(rel_id);
+	Buffer buffer;
+
+	buffer = ReadBufferWithoutRelcache(rlocator, MAIN_FORKNUM, pageno,
+									   RBM_ZERO_AND_LOCK, NULL, true);
+
+	/* Remember where this page is for next time. */
+	nrel_recent_buffers[rel_id].pageno = pageno;
+	nrel_recent_buffers[rel_id].recent_buffer = buffer;
+
+	pgstat_count_slru_page_zeroed(rel_id);
+
+	return buffer;
+}
+
+Oid
+NrelRelIdByName(const char *name)
+{
+	for (int i = 0; i < lengthof(defs); ++i)
+		if (strcmp(defs[i].name, name) == 0)
+			return i;
+	
+	/*return max index + 1 to denote "other" type component, using SLRU */
+	return lengthof(defs);
+}
+
+const char *
+NrelName(Oid rel_id)
+{
+	if (rel_id > NREL_NUM_RELS)
+		elog(ERROR, "invalid NREL rel ID %u", rel_id);
+
+	return defs[rel_id].name;
+}
+
+int
+nrelsyncfiletag(const FileTag *ftag, char *path)
+{
+	SMgrRelation reln;
+	File		file;
+
+	reln = smgropen(ftag->rlocator, InvalidBackendId);
+	file = nrelfile(reln, ftag->segno * NREL_PAGES_PER_SEGMENT, O_RDWR, true);
+	if (file < 0)
+	{
+		/* Path is reported here so the caller can make an error message */
+		NrelFileName(ftag->rlocator.relNumber, path, ftag->segno);
+		return -1;
+	}
+
+	pgstat_count_slru_flush(reln->smgr_rlocator.locator.relNumber);
+
+	return FileSync(file, WAIT_EVENT_NREL_SYNC);
+}
+
+static File
+nrelfile(SMgrRelation reln, BlockNumber blocknum, int mode, bool missing_ok)
+{
+	int segment = blocknum / NREL_PAGES_PER_SEGMENT;
+	char path[MAXPGPATH];
+
+	Assert(reln->smgr_rlocator.locator.dbOid == NREL_DB_ID);
+	Assert(reln->smgr_rlocator.locator.relNumber < lengthof(defs));
+	Assert(defs[reln->smgr_rlocator.locator.relNumber].path != NULL);
+
+	/* Do we have the right file open already? */
+	if (reln->nrel_file_segment == segment)
+	{
+		/* XXX How can we invalidate this if the NREL wraps around?! */
+		Assert(reln->nrel_file != -1);
+		return reln->nrel_file;
+	}
+
+	/* Close the current file, if we have one open. */
+	if (reln->nrel_file_segment != -1)
+	{
+		Assert(reln->nrel_file != -1);
+		FileClose(reln->nrel_file);
+		reln->nrel_file = -1;
+		reln->nrel_file_segment = -1;
+	}
+
+	/* Open the file we want. */
+	NrelFileName(reln->smgr_rlocator.locator.relNumber, path, segment);
+	reln->nrel_file = PathNameOpenFile(path, mode);
+	if (reln->nrel_file >= 0)
+		reln->nrel_file_segment = segment;
+	else if (!(missing_ok && errno == ENOENT))
+		ereport(ERROR,
+				(errcode_for_file_access(),
+				 errmsg("could not open file \"%s\": %m",
+						path)));
+
+	/*
+	 * XXX That error message doesn't contain the xid; then again, the
+	 * previous NREL error codes were all weird about xids anyway; maybe
+	 * clog.c, notify.c et al should set a context that has NREL-specific
+	 * context in a more natural format
+	 */
+
+	return reln->nrel_file;
+}
+
+void
+nrelopen(SMgrRelation reln)
+{
+	reln->nrel_file = -1;
+	reln->nrel_file_segment = -1;
+
+	/*
+	 * We don't want this to be closed at end of transaction, which would
+	 * otherwise happen, because it isn't owned by a Relation.
+	 */
+	dlist_delete(&reln->node);
+}
+
+void
+nrelclose(SMgrRelation reln, ForkNumber forknum)
+{
+	if (reln->nrel_file != -1)
+		FileClose(reln->nrel_file);
+	reln->nrel_file = -1;
+	reln->nrel_file_segment = -1;
+}
+
+void
+nrelwrite(SMgrRelation reln, ForkNumber forknum, BlockNumber blocknum,
+		  char *buffer, bool skipFsync)
+{
+	File file;
+	Oid	rel_id;
+	off_t offset;
+	int rc;
+
+	file = nrelfile(reln, blocknum, O_RDWR | O_CREAT, false);
+	offset = (blocknum % NREL_PAGES_PER_SEGMENT) * BLCKSZ;
+
+	rc = FileWrite(file, buffer, BLCKSZ, offset, WAIT_EVENT_NREL_WRITE);
+	if (rc < 0)
+		ereport(ERROR,
+				(errcode_for_file_access(),
+				 errmsg("could not write %d bytes to file \"%s\" at offset %d: %m",
+						BLCKSZ,
+						FilePathName(file),
+						(int) offset)));
+	if (rc < BLCKSZ)
+		ereport(ERROR,
+				(errcode_for_file_access(),
+				 errmsg("could not write %d bytes to file \"%s\" at offset %d, only %d bytes written",
+						BLCKSZ,
+						FilePathName(file),
+						(int) offset,
+						rc)));
+
+	rel_id = reln->smgr_rlocator.locator.relNumber;
+	if (defs[rel_id].synchronize)
+	{
+		FileTag			tag;
+
+		/* Tell checkpointer to synchronize this file. */
+		INIT_NRELFILETAG(tag, rel_id, blocknum / NREL_PAGES_PER_SEGMENT);
+		if (!RegisterSyncRequest(&tag, SYNC_REQUEST, false))
+		{
+			/* Queue full.  Do it synchronously. */
+			if (FileSync(file, WAIT_EVENT_NREL_SYNC) < 0)
+				ereport(data_sync_elevel(ERROR),
+						(errcode_for_file_access(),
+						 errmsg("could not synchronize file \"%s\": %m",
+								FilePathName(file))));
+		}
+	}
+
+	pgstat_count_slru_page_written(reln->smgr_rlocator.locator.relNumber);
+}
+
+void
+nrelread(SMgrRelation reln, ForkNumber forknum, BlockNumber blocknum,
+		 char *buffer)
+{
+	File file;
+	off_t offset;
+	int rc;
+
+	file = nrelfile(reln, blocknum, O_RDWR, false);
+	offset = (blocknum % NREL_PAGES_PER_SEGMENT) * BLCKSZ;
+
+	rc = FileRead(file, buffer, BLCKSZ, offset, WAIT_EVENT_NREL_READ);
+	if (rc < 0)
+		ereport(ERROR,
+				(errcode_for_file_access(),
+				 errmsg("could not read %d bytes from file \"%s\" at offset %d: %m",
+						BLCKSZ,
+						FilePathName(file),
+						(int) offset)));
+	if (rc < BLCKSZ)
+		ereport(ERROR,
+				(errcode_for_file_access(),
+				 errmsg("could not read %d bytes from file \"%s\" at offset %d, only %d bytes read",
+						BLCKSZ,
+						FilePathName(file),
+						(int) offset,
+						rc)));
+
+	pgstat_count_slru_page_read(reln->smgr_rlocator.locator.relNumber);
+}
+
+void
+nrelwriteback(SMgrRelation reln, ForkNumber forknum,
+			  BlockNumber blocknum, BlockNumber nblocks)
+{
+	Oid rel_id;
+
+	/* No point in flushing data we won't be fsyncing. */
+	rel_id = reln->smgr_rlocator.locator.relNumber;
+	if (!defs[rel_id].synchronize)
+		return;
+
+	while (nblocks > 0)
+	{
+		File		file;
+		BlockNumber blocknum_in_this_file;
+		BlockNumber nflush;
+
+		/* File range of blocks to flush in one file. */
+		blocknum_in_this_file = blocknum % NREL_PAGES_PER_SEGMENT;
+		nflush = Min(nblocks, NREL_PAGES_PER_SEGMENT - blocknum_in_this_file);
+
+		/* The file might have been unlinked already, so tolerate missing. */
+		file = nrelfile(reln, blocknum, O_RDWR, true);
+		if (file < 0)
+			return;
+
+		FileWriteback(file,
+					  BLCKSZ * blocknum_in_this_file,
+					  BLCKSZ * nflush,
+					  WAIT_EVENT_NREL_FLUSH);
+
+		nblocks -= nflush;
+		blocknum += nflush;
+	}
+}
diff --git a/src/backend/access/transam/slru.c b/src/backend/access/transam/slru.c
index 8429caaa80a..71ac70fb40c 100644
--- a/src/backend/access/transam/slru.c
+++ b/src/backend/access/transam/slru.c
@@ -1,9 +1,41 @@
 /*-------------------------------------------------------------------------
  *
  * slru.c
- *		Simple buffering for transaction status logfiles
+ *		Simple LRU buffering for transaction status logfiles
  *
- * XXX write me
+ * We use a simple least-recently-used scheme to manage a pool of page
+ * buffers.  Under ordinary circumstances we expect that write
+ * traffic will occur mostly to the latest page (and to the just-prior
+ * page, soon after a page transition).  Read traffic will probably touch
+ * a larger span of pages, but in any case a fairly small number of page
+ * buffers should be sufficient.  So, we just search the buffers using plain
+ * linear search; there's no need for a hashtable or anything fancy.
+ * The management algorithm is straight LRU except that we will never swap
+ * out the latest page (since we know it's going to be hit again eventually).
+ *
+ * We use a control LWLock to protect the shared data structures, plus
+ * per-buffer LWLocks that synchronize I/O for each buffer.  The control lock
+ * must be held to examine or modify any shared state.  A process that is
+ * reading in or writing out a page buffer does not hold the control lock,
+ * only the per-buffer lock for the buffer it is working on.
+ *
+ * "Holding the control lock" means exclusive lock in all cases except for
+ * SimpleLruReadPage_ReadOnly(); see comments for SlruRecentlyUsed() for
+ * the implications of that.
+ *
+ * When initiating I/O on a buffer, we acquire the per-buffer lock exclusively
+ * before releasing the control lock.  The per-buffer lock is released after
+ * completing the I/O, re-acquiring the control lock, and updating the shared
+ * state.  (Deadlock is not possible here, because we never try to initiate
+ * I/O when someone else is already doing I/O on the same buffer.)
+ * To wait for I/O to complete, release the control lock, acquire the
+ * per-buffer lock in shared mode, immediately release the per-buffer lock,
+ * reacquire the control lock, and then recheck state (since arbitrary things
+ * could have happened while we didn't have the lock).
+ *
+ * As with the regular buffer manager, it is possible for another process
+ * to re-dirty a page that is currently being written out.  This is handled
+ * by re-setting the page's page_dirty flag.
  *
  *
  * Portions Copyright (c) 1996-2023, PostgreSQL Global Development Group
@@ -28,82 +60,562 @@
 #include "storage/fd.h"
 #include "storage/shmem.h"
 
-#define SlruFileName(rel_id, path, seg)							\
-	snprintf(path, MAXPGPATH, "%s/%04X", defs[(rel_id)].path, seg)
-
-struct SlruDef {
-	const char *name;
-	const char *path;
-	bool synchronize;
-};
-
-static const struct SlruDef defs[] = {
-	[SLRU_CLOG_REL_ID] = {
-		.name = "Xact",
-		.path = "pg_xact",
-		.synchronize = true,
-	},
-	[SLRU_SUBTRANS_REL_ID] = {
-		.name = "Subtrans",
-		.path = "pg_subtrans",
-	},
-	[SLRU_MULTIXACT_OFFSET_REL_ID] = {
-		.name = "MultiXactOffset",
-		.path = "pg_multixact/offsets",
-		.synchronize = true,
-	},
-	[SLRU_MULTIXACT_MEMBER_REL_ID] = {
-		.name = "MultiXactMember",
-		.path = "pg_multixact/members",
-		.synchronize = true,
-	},
-	[SLRU_COMMITTS_REL_ID] = {
-		.name = "CommitTs",
-		.path = "pg_commit_ts",
-		.synchronize = true,
-	},
-	[SLRU_SERIAL_REL_ID] = {
-		.name = "Serial",
-		.path = "pg_serial",
-	},
-	[SLRU_NOTIFY_REL_ID] = {
-		.name = "Notify",
-		.path = "pg_notify",
-	},
-};
+#define SlruFileName(ctl, path, seg) \
+	snprintf(path, MAXPGPATH, "%s/%04X", (ctl)->Dir, seg)
 
 /*
- * We'll maintain a little cache of recently seen buffers, to try to avoid the
- * buffer mapping table on repeat access (ie the busy end of the CLOG).  One
- * entry per SLRU relation.
+ * During SimpleLruWriteAll(), we will usually not need to write more than one
+ * or two physical files, but we may need to write several pages per file.  We
+ * can consolidate the I/O requests by leaving files open until control returns
+ * to SimpleLruWriteAll().  This data structure remembers which files are open.
  */
-struct SlruRecentBuffer {
-	int			pageno;
-	Buffer		recent_buffer;
-};
+#define MAX_WRITEALL_BUFFERS	16
 
-static struct SlruRecentBuffer slru_recent_buffers[lengthof(defs)];
+typedef struct SlruWriteAllData
+{
+	int			num_files;		/* # files actually open */
+	int			fd[MAX_WRITEALL_BUFFERS];	/* their FD's */
+	int			segno[MAX_WRITEALL_BUFFERS];	/* their log seg#s */
+} SlruWriteAllData;
+
+typedef struct SlruWriteAllData *SlruWriteAll;
 
 /*
- * Populate a file tag identifying an SLRU segment file.
+ * Populate a file tag describing a segment file.  We only use the segment
+ * number, since we can derive everything else we need by having separate
+ * sync handler functions for clog, multixact etc.
  */
-#define INIT_SLRUFILETAG(a,xx_rel_id,xx_segno) \
+#define INIT_SLRUFILETAG(a,xx_handler,xx_segno) \
 ( \
 	memset(&(a), 0, sizeof(FileTag)), \
-	(a).handler = SYNC_HANDLER_SLRU, \
-	(a).rlocator = SlruRelFileLocator(xx_rel_id), \
+	(a).handler = (xx_handler), \
 	(a).segno = (xx_segno) \
 )
 
-static bool SlruScanDirCbDeleteCutoff(Oid rel_id,
-									  SlruPagePrecedesFunction PagePrecedes,
-									  char *filename,
- 									  int segpage, void *data);
+/*
+ * Macro to mark a buffer slot "most recently used".  Note multiple evaluation
+ * of arguments!
+ *
+ * The reason for the if-test is that there are often many consecutive
+ * accesses to the same page (particularly the latest page).  By suppressing
+ * useless increments of cur_lru_count, we reduce the probability that old
+ * pages' counts will "wrap around" and make them appear recently used.
+ *
+ * We allow this code to be executed concurrently by multiple processes within
+ * SimpleLruReadPage_ReadOnly().  As long as int reads and writes are atomic,
+ * this should not cause any completely-bogus values to enter the computation.
+ * However, it is possible for either cur_lru_count or individual
+ * page_lru_count entries to be "reset" to lower values than they should have,
+ * in case a process is delayed while it executes this macro.  With care in
+ * SlruSelectLRUPage(), this does little harm, and in any case the absolute
+ * worst possible consequence is a nonoptimal choice of page to evict.  The
+ * gain from allowing concurrent reads of SLRU pages seems worth it.
+ */
+#define SlruRecentlyUsed(shared, slotno)	\
+	do { \
+		int		new_lru_count = (shared)->cur_lru_count; \
+		if (new_lru_count != (shared)->page_lru_count[slotno]) { \
+			(shared)->cur_lru_count = ++new_lru_count; \
+			(shared)->page_lru_count[slotno] = new_lru_count; \
+		} \
+	} while (0)
+
+/* Saved info for SlruReportIOError */
+typedef enum
+{
+	SLRU_OPEN_FAILED,
+	SLRU_SEEK_FAILED,
+	SLRU_READ_FAILED,
+	SLRU_WRITE_FAILED,
+	SLRU_FSYNC_FAILED,
+	SLRU_CLOSE_FAILED
+} SlruErrorCause;
+
+static SlruErrorCause slru_errcause;
+static int	slru_errno;
+
+
+static void SimpleLruZeroLSNs(SlruCtl ctl, int slotno);
+static void SimpleLruWaitIO(SlruCtl ctl, int slotno);
+static void SlruInternalWritePage(SlruCtl ctl, int slotno, SlruWriteAll fdata);
+static bool SlruPhysicalReadPage(SlruCtl ctl, int pageno, int slotno);
+static bool SlruPhysicalWritePage(SlruCtl ctl, int pageno, int slotno,
+								  SlruWriteAll fdata);
+static void SlruReportIOError(SlruCtl ctl, int pageno, TransactionId xid);
+static int	SlruSelectLRUPage(SlruCtl ctl, int pageno);
+
+static bool SlruScanDirCbDeleteCutoff(SlruCtl ctl, char *filename,
+									  int segpage, void *data);
+static void SlruInternalDeleteSegment(SlruCtl ctl, int segno);
+
+/*
+ * Initialization of shared memory
+ */
+
+Size
+SimpleLruShmemSize(int nslots, int nlsns)
+{
+	Size		sz;
+
+	/* we assume nslots isn't so large as to risk overflow */
+	sz = MAXALIGN(sizeof(SlruSharedData));
+	sz += MAXALIGN(nslots * sizeof(char *));	/* page_buffer[] */
+	sz += MAXALIGN(nslots * sizeof(SlruPageStatus));	/* page_status[] */
+	sz += MAXALIGN(nslots * sizeof(bool));	/* page_dirty[] */
+	sz += MAXALIGN(nslots * sizeof(int));	/* page_number[] */
+	sz += MAXALIGN(nslots * sizeof(int));	/* page_lru_count[] */
+	sz += MAXALIGN(nslots * sizeof(LWLockPadded));	/* buffer_locks[] */
+
+	if (nlsns > 0)
+		sz += MAXALIGN(nslots * nlsns * sizeof(XLogRecPtr));	/* group_lsn[] */
+
+	return BUFFERALIGN(sz) + BLCKSZ * nslots;
+}
+
+/*
+ * Initialize, or attach to, a simple LRU cache in shared memory.
+ *
+ * ctl: address of local (unshared) control structure.
+ * name: name of SLRU.  (This is user-visible, pick with care!)
+ * nslots: number of page slots to use.
+ * nlsns: number of LSN groups per page (set to zero if not relevant).
+ * ctllock: LWLock to use to control access to the shared control structure.
+ * subdir: PGDATA-relative subdirectory that will contain the files.
+ * tranche_id: LWLock tranche ID to use for the SLRU's per-buffer LWLocks.
+ * sync_handler: which set of functions to use to handle sync requests
+ */
+void
+SimpleLruInit(SlruCtl ctl, const char *name, int nslots, int nlsns,
+			  LWLock *ctllock, const char *subdir, int tranche_id,
+			  SyncRequestHandler sync_handler)
+{
+	SlruShared	shared;
+	bool		found;
+
+	shared = (SlruShared) ShmemInitStruct(name,
+										  SimpleLruShmemSize(nslots, nlsns),
+										  &found);
+
+	if (!IsUnderPostmaster)
+	{
+		/* Initialize locks and shared memory area */
+		char	   *ptr;
+		Size		offset;
+		int			slotno;
+
+		Assert(!found);
+
+		memset(shared, 0, sizeof(SlruSharedData));
+
+		shared->ControlLock = ctllock;
+
+		shared->num_slots = nslots;
+		shared->lsn_groups_per_page = nlsns;
+
+		shared->cur_lru_count = 0;
+
+		/* shared->latest_page_number will be set later */
+
+		shared->slru_stats_idx = pgstat_get_slru_index(name);
+
+		ptr = (char *) shared;
+		offset = MAXALIGN(sizeof(SlruSharedData));
+		shared->page_buffer = (char **) (ptr + offset);
+		offset += MAXALIGN(nslots * sizeof(char *));
+		shared->page_status = (SlruPageStatus *) (ptr + offset);
+		offset += MAXALIGN(nslots * sizeof(SlruPageStatus));
+		shared->page_dirty = (bool *) (ptr + offset);
+		offset += MAXALIGN(nslots * sizeof(bool));
+		shared->page_number = (int *) (ptr + offset);
+		offset += MAXALIGN(nslots * sizeof(int));
+		shared->page_lru_count = (int *) (ptr + offset);
+		offset += MAXALIGN(nslots * sizeof(int));
+
+		/* Initialize LWLocks */
+		shared->buffer_locks = (LWLockPadded *) (ptr + offset);
+		offset += MAXALIGN(nslots * sizeof(LWLockPadded));
+
+		if (nlsns > 0)
+		{
+			shared->group_lsn = (XLogRecPtr *) (ptr + offset);
+			offset += MAXALIGN(nslots * nlsns * sizeof(XLogRecPtr));
+		}
+
+		ptr += BUFFERALIGN(offset);
+		for (slotno = 0; slotno < nslots; slotno++)
+		{
+			LWLockInitialize(&shared->buffer_locks[slotno].lock,
+							 tranche_id);
+
+			shared->page_buffer[slotno] = ptr;
+			shared->page_status[slotno] = SLRU_PAGE_EMPTY;
+			shared->page_dirty[slotno] = false;
+			shared->page_lru_count[slotno] = 0;
+			ptr += BLCKSZ;
+		}
+
+		/* Should fit to estimated shmem size */
+		Assert(ptr - (char *) shared <= SimpleLruShmemSize(nslots, nlsns));
+	}
+	else
+		Assert(found);
+
+	/*
+	 * Initialize the unshared control struct, including directory path. We
+	 * assume caller set PagePrecedes.
+	 */
+	ctl->shared = shared;
+	ctl->sync_handler = sync_handler;
+	strlcpy(ctl->Dir, subdir, sizeof(ctl->Dir));
+}
+
+/*
+ * Initialize (or reinitialize) a page to zeroes.
+ *
+ * The page is not actually written, just set up in shared memory.
+ * The slot number of the new page is returned.
+ *
+ * Control lock must be held at entry, and will be held at exit.
+ */
+int
+SimpleLruZeroPage(SlruCtl ctl, int pageno)
+{
+	SlruShared	shared = ctl->shared;
+	int			slotno;
+
+	/* Find a suitable buffer slot for the page */
+	slotno = SlruSelectLRUPage(ctl, pageno);
+	Assert(shared->page_status[slotno] == SLRU_PAGE_EMPTY ||
+		   (shared->page_status[slotno] == SLRU_PAGE_VALID &&
+			!shared->page_dirty[slotno]) ||
+		   shared->page_number[slotno] == pageno);
+
+	/* Mark the slot as containing this page */
+	shared->page_number[slotno] = pageno;
+	shared->page_status[slotno] = SLRU_PAGE_VALID;
+	shared->page_dirty[slotno] = true;
+	SlruRecentlyUsed(shared, slotno);
+
+	/* Set the buffer to zeroes */
+	MemSet(shared->page_buffer[slotno], 0, BLCKSZ);
+
+	/* Set the LSNs for this new page to zero */
+	SimpleLruZeroLSNs(ctl, slotno);
+
+	/* Assume this page is now the latest active page */
+	shared->latest_page_number = pageno;
+
+	/* update the stats counter of zeroed pages */
+	pgstat_count_slru_page_zeroed(shared->slru_stats_idx);
+
+	return slotno;
+}
+
+/*
+ * Zero all the LSNs we store for this slru page.
+ *
+ * This should be called each time we create a new page, and each time we read
+ * in a page from disk into an existing buffer.  (Such an old page cannot
+ * have any interesting LSNs, since we'd have flushed them before writing
+ * the page in the first place.)
+ *
+ * This assumes that InvalidXLogRecPtr is bitwise-all-0.
+ */
+static void
+SimpleLruZeroLSNs(SlruCtl ctl, int slotno)
+{
+	SlruShared	shared = ctl->shared;
+
+	if (shared->lsn_groups_per_page > 0)
+		MemSet(&shared->group_lsn[slotno * shared->lsn_groups_per_page], 0,
+			   shared->lsn_groups_per_page * sizeof(XLogRecPtr));
+}
+
+/*
+ * Wait for any active I/O on a page slot to finish.  (This does not
+ * guarantee that new I/O hasn't been started before we return, though.
+ * In fact the slot might not even contain the same page anymore.)
+ *
+ * Control lock must be held at entry, and will be held at exit.
+ */
+static void
+SimpleLruWaitIO(SlruCtl ctl, int slotno)
+{
+	SlruShared	shared = ctl->shared;
+
+	/* See notes at top of file */
+	LWLockRelease(shared->ControlLock);
+	LWLockAcquire(&shared->buffer_locks[slotno].lock, LW_SHARED);
+	LWLockRelease(&shared->buffer_locks[slotno].lock);
+	LWLockAcquire(shared->ControlLock, LW_EXCLUSIVE);
+
+	/*
+	 * If the slot is still in an io-in-progress state, then either someone
+	 * already started a new I/O on the slot, or a previous I/O failed and
+	 * neglected to reset the page state.  That shouldn't happen, really, but
+	 * it seems worth a few extra cycles to check and recover from it. We can
+	 * cheaply test for failure by seeing if the buffer lock is still held (we
+	 * assume that transaction abort would release the lock).
+	 */
+	if (shared->page_status[slotno] == SLRU_PAGE_READ_IN_PROGRESS ||
+		shared->page_status[slotno] == SLRU_PAGE_WRITE_IN_PROGRESS)
+	{
+		if (LWLockConditionalAcquire(&shared->buffer_locks[slotno].lock, LW_SHARED))
+		{
+			/* indeed, the I/O must have failed */
+			if (shared->page_status[slotno] == SLRU_PAGE_READ_IN_PROGRESS)
+				shared->page_status[slotno] = SLRU_PAGE_EMPTY;
+			else				/* write_in_progress */
+			{
+				shared->page_status[slotno] = SLRU_PAGE_VALID;
+				shared->page_dirty[slotno] = true;
+			}
+			LWLockRelease(&shared->buffer_locks[slotno].lock);
+		}
+	}
+}
+
+/*
+ * Find a page in a shared buffer, reading it in if necessary.
+ * The page number must correspond to an already-initialized page.
+ *
+ * If write_ok is true then it is OK to return a page that is in
+ * WRITE_IN_PROGRESS state; it is the caller's responsibility to be sure
+ * that modification of the page is safe.  If write_ok is false then we
+ * will not return the page until it is not undergoing active I/O.
+ *
+ * The passed-in xid is used only for error reporting, and may be
+ * InvalidTransactionId if no specific xid is associated with the action.
+ *
+ * Return value is the shared-buffer slot number now holding the page.
+ * The buffer's LRU access info is updated.
+ *
+ * Control lock must be held at entry, and will be held at exit.
+ */
+int
+SimpleLruReadPage(SlruCtl ctl, int pageno, bool write_ok,
+				  TransactionId xid)
+{
+	SlruShared	shared = ctl->shared;
+
+	/* Outer loop handles restart if we must wait for someone else's I/O */
+	for (;;)
+	{
+		int			slotno;
+		bool		ok;
+
+		/* See if page already is in memory; if not, pick victim slot */
+		slotno = SlruSelectLRUPage(ctl, pageno);
+
+		/* Did we find the page in memory? */
+		if (shared->page_number[slotno] == pageno &&
+			shared->page_status[slotno] != SLRU_PAGE_EMPTY)
+		{
+			/*
+			 * If page is still being read in, we must wait for I/O.  Likewise
+			 * if the page is being written and the caller said that's not OK.
+			 */
+			if (shared->page_status[slotno] == SLRU_PAGE_READ_IN_PROGRESS ||
+				(shared->page_status[slotno] == SLRU_PAGE_WRITE_IN_PROGRESS &&
+				 !write_ok))
+			{
+				SimpleLruWaitIO(ctl, slotno);
+				/* Now we must recheck state from the top */
+				continue;
+			}
+			/* Otherwise, it's ready to use */
+			SlruRecentlyUsed(shared, slotno);
+
+			/* update the stats counter of pages found in the SLRU */
+			pgstat_count_slru_page_hit(shared->slru_stats_idx);
+
+			return slotno;
+		}
+
+		/* We found no match; assert we selected a freeable slot */
+		Assert(shared->page_status[slotno] == SLRU_PAGE_EMPTY ||
+			   (shared->page_status[slotno] == SLRU_PAGE_VALID &&
+				!shared->page_dirty[slotno]));
+
+		/* Mark the slot read-busy */
+		shared->page_number[slotno] = pageno;
+		shared->page_status[slotno] = SLRU_PAGE_READ_IN_PROGRESS;
+		shared->page_dirty[slotno] = false;
+
+		/* Acquire per-buffer lock (cannot deadlock, see notes at top) */
+		LWLockAcquire(&shared->buffer_locks[slotno].lock, LW_EXCLUSIVE);
+
+		/* Release control lock while doing I/O */
+		LWLockRelease(shared->ControlLock);
+
+		/* Do the read */
+		ok = SlruPhysicalReadPage(ctl, pageno, slotno);
+
+		/* Set the LSNs for this newly read-in page to zero */
+		SimpleLruZeroLSNs(ctl, slotno);
+
+		/* Re-acquire control lock and update page state */
+		LWLockAcquire(shared->ControlLock, LW_EXCLUSIVE);
+
+		Assert(shared->page_number[slotno] == pageno &&
+			   shared->page_status[slotno] == SLRU_PAGE_READ_IN_PROGRESS &&
+			   !shared->page_dirty[slotno]);
+
+		shared->page_status[slotno] = ok ? SLRU_PAGE_VALID : SLRU_PAGE_EMPTY;
 
-static void SlruInternalDeleteSegment(Oid rel_id, int segno);
+		LWLockRelease(&shared->buffer_locks[slotno].lock);
 
-static File slrufile(SMgrRelation reln, BlockNumber blocknum, int mode,
-					 bool missing_ok);
+		/* Now it's okay to ereport if we failed */
+		if (!ok)
+			SlruReportIOError(ctl, pageno, xid);
+
+		SlruRecentlyUsed(shared, slotno);
+
+		/* update the stats counter of pages not found in SLRU */
+		pgstat_count_slru_page_read(shared->slru_stats_idx);
+
+		return slotno;
+	}
+}
+
+/*
+ * Find a page in a shared buffer, reading it in if necessary.
+ * The page number must correspond to an already-initialized page.
+ * The caller must intend only read-only access to the page.
+ *
+ * The passed-in xid is used only for error reporting, and may be
+ * InvalidTransactionId if no specific xid is associated with the action.
+ *
+ * Return value is the shared-buffer slot number now holding the page.
+ * The buffer's LRU access info is updated.
+ *
+ * Control lock must NOT be held at entry, but will be held at exit.
+ * It is unspecified whether the lock will be shared or exclusive.
+ */
+int
+SimpleLruReadPage_ReadOnly(SlruCtl ctl, int pageno, TransactionId xid)
+{
+	SlruShared	shared = ctl->shared;
+	int			slotno;
+
+	/* Try to find the page while holding only shared lock */
+	LWLockAcquire(shared->ControlLock, LW_SHARED);
+
+	/* See if page is already in a buffer */
+	for (slotno = 0; slotno < shared->num_slots; slotno++)
+	{
+		if (shared->page_number[slotno] == pageno &&
+			shared->page_status[slotno] != SLRU_PAGE_EMPTY &&
+			shared->page_status[slotno] != SLRU_PAGE_READ_IN_PROGRESS)
+		{
+			/* See comments for SlruRecentlyUsed macro */
+			SlruRecentlyUsed(shared, slotno);
+
+			/* update the stats counter of pages found in the SLRU */
+			pgstat_count_slru_page_hit(shared->slru_stats_idx);
+
+			return slotno;
+		}
+	}
+
+	/* No luck, so switch to normal exclusive lock and do regular read */
+	LWLockRelease(shared->ControlLock);
+	LWLockAcquire(shared->ControlLock, LW_EXCLUSIVE);
+
+	return SimpleLruReadPage(ctl, pageno, true, xid);
+}
+
+/*
+ * Write a page from a shared buffer, if necessary.
+ * Does nothing if the specified slot is not dirty.
+ *
+ * NOTE: only one write attempt is made here.  Hence, it is possible that
+ * the page is still dirty at exit (if someone else re-dirtied it during
+ * the write).  However, we *do* attempt a fresh write even if the page
+ * is already being written; this is for checkpoints.
+ *
+ * Control lock must be held at entry, and will be held at exit.
+ */
+static void
+SlruInternalWritePage(SlruCtl ctl, int slotno, SlruWriteAll fdata)
+{
+	SlruShared	shared = ctl->shared;
+	int			pageno = shared->page_number[slotno];
+	bool		ok;
+
+	/* If a write is in progress, wait for it to finish */
+	while (shared->page_status[slotno] == SLRU_PAGE_WRITE_IN_PROGRESS &&
+		   shared->page_number[slotno] == pageno)
+	{
+		SimpleLruWaitIO(ctl, slotno);
+	}
+
+	/*
+	 * Do nothing if page is not dirty, or if buffer no longer contains the
+	 * same page we were called for.
+	 */
+	if (!shared->page_dirty[slotno] ||
+		shared->page_status[slotno] != SLRU_PAGE_VALID ||
+		shared->page_number[slotno] != pageno)
+		return;
+
+	/*
+	 * Mark the slot write-busy, and clear the dirtybit.  After this point, a
+	 * transaction status update on this page will mark it dirty again.
+	 */
+	shared->page_status[slotno] = SLRU_PAGE_WRITE_IN_PROGRESS;
+	shared->page_dirty[slotno] = false;
+
+	/* Acquire per-buffer lock (cannot deadlock, see notes at top) */
+	LWLockAcquire(&shared->buffer_locks[slotno].lock, LW_EXCLUSIVE);
+
+	/* Release control lock while doing I/O */
+	LWLockRelease(shared->ControlLock);
+
+	/* Do the write */
+	ok = SlruPhysicalWritePage(ctl, pageno, slotno, fdata);
+
+	/* If we failed, and we're in a flush, better close the files */
+	if (!ok && fdata)
+	{
+		int			i;
+
+		for (i = 0; i < fdata->num_files; i++)
+			CloseTransientFile(fdata->fd[i]);
+	}
+
+	/* Re-acquire control lock and update page state */
+	LWLockAcquire(shared->ControlLock, LW_EXCLUSIVE);
+
+	Assert(shared->page_number[slotno] == pageno &&
+		   shared->page_status[slotno] == SLRU_PAGE_WRITE_IN_PROGRESS);
+
+	/* If we failed to write, mark the page dirty again */
+	if (!ok)
+		shared->page_dirty[slotno] = true;
+
+	shared->page_status[slotno] = SLRU_PAGE_VALID;
+
+	LWLockRelease(&shared->buffer_locks[slotno].lock);
+
+	/* Now it's okay to ereport if we failed */
+	if (!ok)
+		SlruReportIOError(ctl, pageno, InvalidTransactionId);
+
+	/* If part of a checkpoint, count this as a buffer written. */
+	if (fdata)
+		CheckpointStats.ckpt_bufs_written++;
+}
+
+/*
+ * Wrapper of SlruInternalWritePage, for external callers.
+ * fdata is always passed a NULL here.
+ */
+void
+SimpleLruWritePage(SlruCtl ctl, int slotno)
+{
+	SlruInternalWritePage(ctl, slotno, NULL);
+}
 
 /*
  * Return whether the given page exists on disk.
@@ -112,30 +624,592 @@ static File slrufile(SMgrRelation reln, BlockNumber blocknum, int mode,
  * large enough to contain the given page.
  */
 bool
-SimpleLruDoesPhysicalPageExist(Oid rel_id, int pageno)
+SimpleLruDoesPhysicalPageExist(SlruCtl ctl, int pageno)
 {
+	int			segno = pageno / SLRU_PAGES_PER_SEGMENT;
 	int			rpageno = pageno % SLRU_PAGES_PER_SEGMENT;
-	off_t		offset = rpageno * BLCKSZ;
-	off_t		size;
-	File		file;
-	RelFileLocator	rlocator = SlruRelFileLocator(rel_id);
-	SMgrRelation reln = smgropen(rlocator, InvalidBackendId);
+	int			offset = rpageno * BLCKSZ;
+	char		path[MAXPGPATH];
+	int			fd;
+	bool		result;
+	off_t		endpos;
 
 	/* update the stats counter of checked pages */
-	pgstat_count_slru_page_exists(rel_id);
+	pgstat_count_slru_page_exists(ctl->shared->slru_stats_idx);
+
+	SlruFileName(ctl, path, segno);
+
+	fd = OpenTransientFile(path, O_RDONLY | PG_BINARY);
+	if (fd < 0)
+	{
+		/* expected: file doesn't exist */
+		if (errno == ENOENT)
+			return false;
+
+		/* report error normally */
+		slru_errcause = SLRU_OPEN_FAILED;
+		slru_errno = errno;
+		SlruReportIOError(ctl, pageno, 0);
+	}
+
+	if ((endpos = lseek(fd, 0, SEEK_END)) < 0)
+	{
+		slru_errcause = SLRU_SEEK_FAILED;
+		slru_errno = errno;
+		SlruReportIOError(ctl, pageno, 0);
+	}
+
+	result = endpos >= (off_t) (offset + BLCKSZ);
+
+	if (CloseTransientFile(fd) != 0)
+	{
+		slru_errcause = SLRU_CLOSE_FAILED;
+		slru_errno = errno;
+		return false;
+	}
+
+	return result;
+}
+
+/*
+ * Physical read of a (previously existing) page into a buffer slot
+ *
+ * On failure, we cannot just ereport(ERROR) since caller has put state in
+ * shared memory that must be undone.  So, we return false and save enough
+ * info in static variables to let SlruReportIOError make the report.
+ *
+ * For now, assume it's not worth keeping a file pointer open across
+ * read/write operations.  We could cache one virtual file pointer ...
+ */
+static bool
+SlruPhysicalReadPage(SlruCtl ctl, int pageno, int slotno)
+{
+	SlruShared	shared = ctl->shared;
+	int			segno = pageno / SLRU_PAGES_PER_SEGMENT;
+	int			rpageno = pageno % SLRU_PAGES_PER_SEGMENT;
+	off_t		offset = rpageno * BLCKSZ;
+	char		path[MAXPGPATH];
+	int			fd;
+
+	SlruFileName(ctl, path, segno);
+
+	/*
+	 * In a crash-and-restart situation, it's possible for us to receive
+	 * commands to set the commit status of transactions whose bits are in
+	 * already-truncated segments of the commit log (see notes in
+	 * SlruPhysicalWritePage).  Hence, if we are InRecovery, allow the case
+	 * where the file doesn't exist, and return zeroes instead.
+	 */
+	fd = OpenTransientFile(path, O_RDONLY | PG_BINARY);
+	if (fd < 0)
+	{
+		if (errno != ENOENT || !InRecovery)
+		{
+			slru_errcause = SLRU_OPEN_FAILED;
+			slru_errno = errno;
+			return false;
+		}
+
+		ereport(LOG,
+				(errmsg("file \"%s\" doesn't exist, reading as zeroes",
+						path)));
+		MemSet(shared->page_buffer[slotno], 0, BLCKSZ);
+		return true;
+	}
+
+	errno = 0;
+	pgstat_report_wait_start(WAIT_EVENT_SLRU_READ);
+	if (pg_pread(fd, shared->page_buffer[slotno], BLCKSZ, offset) != BLCKSZ)
+	{
+		pgstat_report_wait_end();
+		slru_errcause = SLRU_READ_FAILED;
+		slru_errno = errno;
+		CloseTransientFile(fd);
+		return false;
+	}
+	pgstat_report_wait_end();
 
-	file = slrufile(reln, pageno, O_RDWR, true);
-	if (file < 0)
+	if (CloseTransientFile(fd) != 0)
 	{
-		Assert(errno == ENOENT);
+		slru_errcause = SLRU_CLOSE_FAILED;
+		slru_errno = errno;
 		return false;
 	}
-	size = FileSize(file);
-	if (size < 0)
-		elog(ERROR, "could not get size of file \"%s\": %m",
-			 FilePathName(file));
 
-	return size >= offset + BLCKSZ;
+	return true;
+}
+
+/*
+ * Physical write of a page from a buffer slot
+ *
+ * On failure, we cannot just ereport(ERROR) since caller has put state in
+ * shared memory that must be undone.  So, we return false and save enough
+ * info in static variables to let SlruReportIOError make the report.
+ *
+ * For now, assume it's not worth keeping a file pointer open across
+ * independent read/write operations.  We do batch operations during
+ * SimpleLruWriteAll, though.
+ *
+ * fdata is NULL for a standalone write, pointer to open-file info during
+ * SimpleLruWriteAll.
+ */
+static bool
+SlruPhysicalWritePage(SlruCtl ctl, int pageno, int slotno, SlruWriteAll fdata)
+{
+	SlruShared	shared = ctl->shared;
+	int			segno = pageno / SLRU_PAGES_PER_SEGMENT;
+	int			rpageno = pageno % SLRU_PAGES_PER_SEGMENT;
+	off_t		offset = rpageno * BLCKSZ;
+	char		path[MAXPGPATH];
+	int			fd = -1;
+
+	/* update the stats counter of written pages */
+	pgstat_count_slru_page_written(shared->slru_stats_idx);
+
+	/*
+	 * Honor the write-WAL-before-data rule, if appropriate, so that we do not
+	 * write out data before associated WAL records.  This is the same action
+	 * performed during FlushBuffer() in the main buffer manager.
+	 */
+	if (shared->group_lsn != NULL)
+	{
+		/*
+		 * We must determine the largest async-commit LSN for the page. This
+		 * is a bit tedious, but since this entire function is a slow path
+		 * anyway, it seems better to do this here than to maintain a per-page
+		 * LSN variable (which'd need an extra comparison in the
+		 * transaction-commit path).
+		 */
+		XLogRecPtr	max_lsn;
+		int			lsnindex,
+					lsnoff;
+
+		lsnindex = slotno * shared->lsn_groups_per_page;
+		max_lsn = shared->group_lsn[lsnindex++];
+		for (lsnoff = 1; lsnoff < shared->lsn_groups_per_page; lsnoff++)
+		{
+			XLogRecPtr	this_lsn = shared->group_lsn[lsnindex++];
+
+			if (max_lsn < this_lsn)
+				max_lsn = this_lsn;
+		}
+
+		if (!XLogRecPtrIsInvalid(max_lsn))
+		{
+			/*
+			 * As noted above, elog(ERROR) is not acceptable here, so if
+			 * XLogFlush were to fail, we must PANIC.  This isn't much of a
+			 * restriction because XLogFlush is just about all critical
+			 * section anyway, but let's make sure.
+			 */
+			START_CRIT_SECTION();
+			XLogFlush(max_lsn);
+			END_CRIT_SECTION();
+		}
+	}
+
+	/*
+	 * During a SimpleLruWriteAll, we may already have the desired file open.
+	 */
+	if (fdata)
+	{
+		int			i;
+
+		for (i = 0; i < fdata->num_files; i++)
+		{
+			if (fdata->segno[i] == segno)
+			{
+				fd = fdata->fd[i];
+				break;
+			}
+		}
+	}
+
+	if (fd < 0)
+	{
+		/*
+		 * If the file doesn't already exist, we should create it.  It is
+		 * possible for this to need to happen when writing a page that's not
+		 * first in its segment; we assume the OS can cope with that. (Note:
+		 * it might seem that it'd be okay to create files only when
+		 * SimpleLruZeroPage is called for the first page of a segment.
+		 * However, if after a crash and restart the REDO logic elects to
+		 * replay the log from a checkpoint before the latest one, then it's
+		 * possible that we will get commands to set transaction status of
+		 * transactions that have already been truncated from the commit log.
+		 * Easiest way to deal with that is to accept references to
+		 * nonexistent files here and in SlruPhysicalReadPage.)
+		 *
+		 * Note: it is possible for more than one backend to be executing this
+		 * code simultaneously for different pages of the same file. Hence,
+		 * don't use O_EXCL or O_TRUNC or anything like that.
+		 */
+		SlruFileName(ctl, path, segno);
+		fd = OpenTransientFile(path, O_RDWR | O_CREAT | PG_BINARY);
+		if (fd < 0)
+		{
+			slru_errcause = SLRU_OPEN_FAILED;
+			slru_errno = errno;
+			return false;
+		}
+
+		if (fdata)
+		{
+			if (fdata->num_files < MAX_WRITEALL_BUFFERS)
+			{
+				fdata->fd[fdata->num_files] = fd;
+				fdata->segno[fdata->num_files] = segno;
+				fdata->num_files++;
+			}
+			else
+			{
+				/*
+				 * In the unlikely event that we exceed MAX_WRITEALL_BUFFERS,
+				 * fall back to treating it as a standalone write.
+				 */
+				fdata = NULL;
+			}
+		}
+	}
+
+	errno = 0;
+	pgstat_report_wait_start(WAIT_EVENT_SLRU_WRITE);
+	if (pg_pwrite(fd, shared->page_buffer[slotno], BLCKSZ, offset) != BLCKSZ)
+	{
+		pgstat_report_wait_end();
+		/* if write didn't set errno, assume problem is no disk space */
+		if (errno == 0)
+			errno = ENOSPC;
+		slru_errcause = SLRU_WRITE_FAILED;
+		slru_errno = errno;
+		if (!fdata)
+			CloseTransientFile(fd);
+		return false;
+	}
+	pgstat_report_wait_end();
+
+	/* Queue up a sync request for the checkpointer. */
+	if (ctl->sync_handler != SYNC_HANDLER_NONE)
+	{
+		FileTag		tag;
+
+		INIT_SLRUFILETAG(tag, ctl->sync_handler, segno);
+		if (!RegisterSyncRequest(&tag, SYNC_REQUEST, false))
+		{
+			/* No space to enqueue sync request.  Do it synchronously. */
+			pgstat_report_wait_start(WAIT_EVENT_SLRU_SYNC);
+			if (pg_fsync(fd) != 0)
+			{
+				pgstat_report_wait_end();
+				slru_errcause = SLRU_FSYNC_FAILED;
+				slru_errno = errno;
+				CloseTransientFile(fd);
+				return false;
+			}
+			pgstat_report_wait_end();
+		}
+	}
+
+	/* Close file, unless part of flush request. */
+	if (!fdata)
+	{
+		if (CloseTransientFile(fd) != 0)
+		{
+			slru_errcause = SLRU_CLOSE_FAILED;
+			slru_errno = errno;
+			return false;
+		}
+	}
+
+	return true;
+}
+
+/*
+ * Issue the error message after failure of SlruPhysicalReadPage or
+ * SlruPhysicalWritePage.  Call this after cleaning up shared-memory state.
+ */
+static void
+SlruReportIOError(SlruCtl ctl, int pageno, TransactionId xid)
+{
+	int			segno = pageno / SLRU_PAGES_PER_SEGMENT;
+	int			rpageno = pageno % SLRU_PAGES_PER_SEGMENT;
+	int			offset = rpageno * BLCKSZ;
+	char		path[MAXPGPATH];
+
+	SlruFileName(ctl, path, segno);
+	errno = slru_errno;
+	switch (slru_errcause)
+	{
+		case SLRU_OPEN_FAILED:
+			ereport(ERROR,
+					(errcode_for_file_access(),
+					 errmsg("could not access status of transaction %u", 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),
+					 errdetail("Could not seek in file \"%s\" to offset %d: %m.",
+							   path, offset)));
+			break;
+		case SLRU_READ_FAILED:
+			if (errno)
+				ereport(ERROR,
+						(errcode_for_file_access(),
+						 errmsg("could not access status of transaction %u", 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),
+						 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),
+						 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),
+						 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),
+					 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),
+					 errdetail("Could not close file \"%s\": %m.",
+							   path)));
+			break;
+		default:
+			/* can't get here, we trust */
+			elog(ERROR, "unrecognized SimpleLru error cause: %d",
+				 (int) slru_errcause);
+			break;
+	}
+}
+
+/*
+ * Select the slot to re-use when we need a free slot.
+ *
+ * The target page number is passed because we need to consider the
+ * possibility that some other process reads in the target page while
+ * we are doing I/O to free a slot.  Hence, check or recheck to see if
+ * any slot already holds the target page, and return that slot if so.
+ * Thus, the returned slot is *either* a slot already holding the pageno
+ * (could be any state except EMPTY), *or* a freeable slot (state EMPTY
+ * or CLEAN).
+ *
+ * Control lock must be held at entry, and will be held at exit.
+ */
+static int
+SlruSelectLRUPage(SlruCtl ctl, int pageno)
+{
+	SlruShared	shared = ctl->shared;
+
+	/* Outer loop handles restart after I/O */
+	for (;;)
+	{
+		int			slotno;
+		int			cur_count;
+		int			bestvalidslot = 0;	/* keep compiler quiet */
+		int			best_valid_delta = -1;
+		int			best_valid_page_number = 0; /* keep compiler quiet */
+		int			bestinvalidslot = 0;	/* keep compiler quiet */
+		int			best_invalid_delta = -1;
+		int			best_invalid_page_number = 0;	/* keep compiler quiet */
+
+		/* See if page already has a buffer assigned */
+		for (slotno = 0; slotno < shared->num_slots; slotno++)
+		{
+			if (shared->page_number[slotno] == pageno &&
+				shared->page_status[slotno] != SLRU_PAGE_EMPTY)
+				return slotno;
+		}
+
+		/*
+		 * If we find any EMPTY slot, just select that one. Else choose a
+		 * victim page to replace.  We normally take the least recently used
+		 * valid page, but we will never take the slot containing
+		 * latest_page_number, even if it appears least recently used.  We
+		 * will select a slot that is already I/O busy only if there is no
+		 * other choice: a read-busy slot will not be least recently used once
+		 * the read finishes, and waiting for an I/O on a write-busy slot is
+		 * inferior to just picking some other slot.  Testing shows the slot
+		 * we pick instead will often be clean, allowing us to begin a read at
+		 * once.
+		 *
+		 * Normally the page_lru_count values will all be different and so
+		 * there will be a well-defined LRU page.  But since we allow
+		 * concurrent execution of SlruRecentlyUsed() within
+		 * SimpleLruReadPage_ReadOnly(), it is possible that multiple pages
+		 * acquire the same lru_count values.  In that case we break ties by
+		 * choosing the furthest-back page.
+		 *
+		 * Notice that this next line forcibly advances cur_lru_count to a
+		 * value that is certainly beyond any value that will be in the
+		 * page_lru_count array after the loop finishes.  This ensures that
+		 * the next execution of SlruRecentlyUsed will mark the page newly
+		 * used, even if it's for a page that has the current counter value.
+		 * That gets us back on the path to having good data when there are
+		 * multiple pages with the same lru_count.
+		 */
+		cur_count = (shared->cur_lru_count)++;
+		for (slotno = 0; slotno < shared->num_slots; slotno++)
+		{
+			int			this_delta;
+			int			this_page_number;
+
+			if (shared->page_status[slotno] == SLRU_PAGE_EMPTY)
+				return slotno;
+			this_delta = cur_count - shared->page_lru_count[slotno];
+			if (this_delta < 0)
+			{
+				/*
+				 * Clean up in case shared updates have caused cur_count
+				 * increments to get "lost".  We back off the page counts,
+				 * rather than trying to increase cur_count, to avoid any
+				 * question of infinite loops or failure in the presence of
+				 * wrapped-around counts.
+				 */
+				shared->page_lru_count[slotno] = cur_count;
+				this_delta = 0;
+			}
+			this_page_number = shared->page_number[slotno];
+			if (this_page_number == shared->latest_page_number)
+				continue;
+			if (shared->page_status[slotno] == SLRU_PAGE_VALID)
+			{
+				if (this_delta > best_valid_delta ||
+					(this_delta == best_valid_delta &&
+					 ctl->PagePrecedes(this_page_number,
+									   best_valid_page_number)))
+				{
+					bestvalidslot = slotno;
+					best_valid_delta = this_delta;
+					best_valid_page_number = this_page_number;
+				}
+			}
+			else
+			{
+				if (this_delta > best_invalid_delta ||
+					(this_delta == best_invalid_delta &&
+					 ctl->PagePrecedes(this_page_number,
+									   best_invalid_page_number)))
+				{
+					bestinvalidslot = slotno;
+					best_invalid_delta = this_delta;
+					best_invalid_page_number = this_page_number;
+				}
+			}
+		}
+
+		/*
+		 * If all pages (except possibly the latest one) are I/O busy, we'll
+		 * have to wait for an I/O to complete and then retry.  In that
+		 * unhappy case, we choose to wait for the I/O on the least recently
+		 * used slot, on the assumption that it was likely initiated first of
+		 * all the I/Os in progress and may therefore finish first.
+		 */
+		if (best_valid_delta < 0)
+		{
+			SimpleLruWaitIO(ctl, bestinvalidslot);
+			continue;
+		}
+
+		/*
+		 * If the selected page is clean, we're set.
+		 */
+		if (!shared->page_dirty[bestvalidslot])
+			return bestvalidslot;
+
+		/*
+		 * Write the page.
+		 */
+		SlruInternalWritePage(ctl, bestvalidslot, NULL);
+
+		/*
+		 * Now loop back and try again.  This is the easiest way of dealing
+		 * with corner cases such as the victim page being re-dirtied while we
+		 * wrote it.
+		 */
+	}
+}
+
+/*
+ * Write dirty pages to disk during checkpoint or database shutdown.  Flushing
+ * is deferred until the next call to ProcessSyncRequests(), though we do fsync
+ * the containing directory here to make sure that newly created directory
+ * entries are on disk.
+ */
+void
+SimpleLruWriteAll(SlruCtl ctl, bool allow_redirtied)
+{
+	SlruShared	shared = ctl->shared;
+	SlruWriteAllData fdata;
+	int			slotno;
+	int			pageno = 0;
+	int			i;
+	bool		ok;
+
+	/* update the stats counter of flushes */
+	pgstat_count_slru_flush(shared->slru_stats_idx);
+
+	/*
+	 * Find and write dirty pages
+	 */
+	fdata.num_files = 0;
+
+	LWLockAcquire(shared->ControlLock, LW_EXCLUSIVE);
+
+	for (slotno = 0; slotno < shared->num_slots; slotno++)
+	{
+		SlruInternalWritePage(ctl, slotno, &fdata);
+
+		/*
+		 * In some places (e.g. checkpoints), we cannot assert that the slot
+		 * is clean now, since another process might have re-dirtied it
+		 * already.  That's okay.
+		 */
+		Assert(allow_redirtied ||
+			   shared->page_status[slotno] == SLRU_PAGE_EMPTY ||
+			   (shared->page_status[slotno] == SLRU_PAGE_VALID &&
+				!shared->page_dirty[slotno]));
+	}
+
+	LWLockRelease(shared->ControlLock);
+
+	/*
+	 * Now close any files that were open
+	 */
+	ok = true;
+	for (i = 0; i < fdata.num_files; i++)
+	{
+		if (CloseTransientFile(fdata.fd[i]) != 0)
+		{
+			slru_errcause = SLRU_CLOSE_FAILED;
+			slru_errno = errno;
+			pageno = fdata.segno[i] * SLRU_PAGES_PER_SEGMENT;
+			ok = false;
+		}
+	}
+	if (!ok)
+		SlruReportIOError(ctl, pageno, InvalidTransactionId);
+
+	/* Ensure that directory entries for new files are on disk. */
+	if (ctl->sync_handler != SYNC_HANDLER_NONE)
+		fsync_fname(ctl->Dir, true);
 }
 
 /*
@@ -150,16 +1224,75 @@ SimpleLruDoesPhysicalPageExist(Oid rel_id, int pageno)
  * after it has accrued freshly-written data.
  */
 void
-SimpleLruTruncate(Oid rel_id, SlruPagePrecedesFunction PagePrecedes, int cutoffPage)
+SimpleLruTruncate(SlruCtl ctl, int cutoffPage)
 {
+	SlruShared	shared = ctl->shared;
+	int			slotno;
 
 	/* update the stats counter of truncates */
-	pgstat_count_slru_truncate(rel_id);
-	
+	pgstat_count_slru_truncate(shared->slru_stats_idx);
+
+	/*
+	 * Scan shared memory and remove any pages preceding the cutoff page, to
+	 * ensure we won't rewrite them later.  (Since this is normally called in
+	 * or just after a checkpoint, any dirty pages should have been flushed
+	 * already ... we're just being extra careful here.)
+	 */
+	LWLockAcquire(shared->ControlLock, LW_EXCLUSIVE);
+
+restart:
+
+	/*
+	 * While we are holding the lock, make an important safety check: the
+	 * current endpoint page must not be eligible for removal.
+	 */
+	if (ctl->PagePrecedes(shared->latest_page_number, cutoffPage))
+	{
+		LWLockRelease(shared->ControlLock);
+		ereport(LOG,
+				(errmsg("could not truncate directory \"%s\": apparent wraparound",
+						ctl->Dir)));
+		return;
+	}
+
+	for (slotno = 0; slotno < shared->num_slots; slotno++)
+	{
+		if (shared->page_status[slotno] == SLRU_PAGE_EMPTY)
+			continue;
+		if (!ctl->PagePrecedes(shared->page_number[slotno], cutoffPage))
+			continue;
+
+		/*
+		 * If page is clean, just change state to EMPTY (expected case).
+		 */
+		if (shared->page_status[slotno] == SLRU_PAGE_VALID &&
+			!shared->page_dirty[slotno])
+		{
+			shared->page_status[slotno] = SLRU_PAGE_EMPTY;
+			continue;
+		}
+
+		/*
+		 * Hmm, we have (or may have) I/O operations acting on the page, so
+		 * we've got to wait for them to finish and then start again. This is
+		 * the same logic as in SlruSelectLRUPage.  (XXX if page is dirty,
+		 * wouldn't it be OK to just discard it without writing it?
+		 * SlruMayDeleteSegment() uses a stricter qualification, so we might
+		 * not delete this page in the end; even if we don't delete it, we
+		 * won't have cause to read its data again.  For now, keep the logic
+		 * the same as it was.)
+		 */
+		if (shared->page_status[slotno] == SLRU_PAGE_VALID)
+			SlruInternalWritePage(ctl, slotno, NULL);
+		else
+			SimpleLruWaitIO(ctl, slotno);
+		goto restart;
+	}
+
+	LWLockRelease(shared->ControlLock);
 
 	/* Now we can remove the old segment(s) */
-	(void) SlruScanDirectory(rel_id, PagePrecedes, SlruScanDirCbDeleteCutoff,
-							 &cutoffPage);
+	(void) SlruScanDirectory(ctl, SlruScanDirCbDeleteCutoff, &cutoffPage);
 }
 
 /*
@@ -169,21 +1302,21 @@ SimpleLruTruncate(Oid rel_id, SlruPagePrecedesFunction PagePrecedes, int cutoffP
  * they either can't yet contain anything, or have already been cleaned out.
  */
 static void
-SlruInternalDeleteSegment(Oid rel_id, int segno)
+SlruInternalDeleteSegment(SlruCtl ctl, int segno)
 {
 	char		path[MAXPGPATH];
 
 	/* Forget any fsync requests queued for this segment. */
-	if (defs[rel_id].synchronize)
+	if (ctl->sync_handler != SYNC_HANDLER_NONE)
 	{
 		FileTag		tag;
 
-		INIT_SLRUFILETAG(tag, rel_id, segno);
+		INIT_SLRUFILETAG(tag, ctl->sync_handler, segno);
 		RegisterSyncRequest(&tag, SYNC_FORGET_REQUEST, true);
 	}
 
 	/* Unlink the file. */
-	SlruFileName(rel_id, path, segno);
+	SlruFileName(ctl, path, segno);
 	ereport(DEBUG2, (errmsg_internal("removing file \"%s\"", path)));
 	unlink(path);
 }
@@ -192,15 +1325,54 @@ SlruInternalDeleteSegment(Oid rel_id, int segno)
  * Delete an individual SLRU segment, identified by the segment number.
  */
 void
-SlruDeleteSegment(Oid rel_id, int segno)
+SlruDeleteSegment(SlruCtl ctl, int segno)
 {
-	RelFileLocator rlocator = SlruRelFileLocator(rel_id);
+	SlruShared	shared = ctl->shared;
+	int			slotno;
+	bool		did_write;
 
 	/* Clean out any possibly existing references to the segment. */
-	for (int i = 0; i < SLRU_PAGES_PER_SEGMENT; ++i)
-		DiscardBuffer(rlocator, MAIN_FORKNUM, segno * SLRU_PAGES_PER_SEGMENT + i);
+	LWLockAcquire(shared->ControlLock, LW_EXCLUSIVE);
+restart:
+	did_write = false;
+	for (slotno = 0; slotno < shared->num_slots; slotno++)
+	{
+		int			pagesegno = shared->page_number[slotno] / SLRU_PAGES_PER_SEGMENT;
+
+		if (shared->page_status[slotno] == SLRU_PAGE_EMPTY)
+			continue;
+
+		/* not the segment we're looking for */
+		if (pagesegno != segno)
+			continue;
+
+		/* If page is clean, just change state to EMPTY (expected case). */
+		if (shared->page_status[slotno] == SLRU_PAGE_VALID &&
+			!shared->page_dirty[slotno])
+		{
+			shared->page_status[slotno] = SLRU_PAGE_EMPTY;
+			continue;
+		}
 
-	SlruInternalDeleteSegment(rel_id, segno);
+		/* Same logic as SimpleLruTruncate() */
+		if (shared->page_status[slotno] == SLRU_PAGE_VALID)
+			SlruInternalWritePage(ctl, slotno, NULL);
+		else
+			SimpleLruWaitIO(ctl, slotno);
+
+		did_write = true;
+	}
+
+	/*
+	 * Be extra careful and re-check. The IO functions release the control
+	 * lock, so new pages could have been read in.
+	 */
+	if (did_write)
+		goto restart;
+
+	SlruInternalDeleteSegment(ctl, segno);
+
+	LWLockRelease(shared->ControlLock);
 }
 
 /*
@@ -217,21 +1389,19 @@ SlruDeleteSegment(Oid rel_id, int segno)
  * first>=cutoff && last>=cutoff: no; every page of this segment is too young
  */
 static bool
-SlruMayDeleteSegment(SlruPagePrecedesFunction PagePrecedes,
-					 int segpage, int cutoffPage)
+SlruMayDeleteSegment(SlruCtl ctl, int segpage, int cutoffPage)
 {
 	int			seg_last_page = segpage + SLRU_PAGES_PER_SEGMENT - 1;
 
 	Assert(segpage % SLRU_PAGES_PER_SEGMENT == 0);
 
-	return (PagePrecedes(segpage, cutoffPage) &&
-			PagePrecedes(seg_last_page, cutoffPage));
+	return (ctl->PagePrecedes(segpage, cutoffPage) &&
+			ctl->PagePrecedes(seg_last_page, cutoffPage));
 }
 
 #ifdef USE_ASSERT_CHECKING
 static void
-SlruPagePrecedesTestOffset(SlruPagePrecedesFunction PagePrecedes,
-						   int per_page, uint32 offset)
+SlruPagePrecedesTestOffset(SlruCtl ctl, int per_page, uint32 offset)
 {
 	TransactionId lhs,
 				rhs;
@@ -256,19 +1426,19 @@ SlruPagePrecedesTestOffset(SlruPagePrecedesFunction PagePrecedes,
 	Assert(!TransactionIdPrecedes(rhs, lhs + 1));
 	Assert(!TransactionIdFollowsOrEquals(lhs, rhs));
 	Assert(!TransactionIdFollowsOrEquals(rhs, lhs));
-	Assert(!PagePrecedes(lhs / per_page, lhs / per_page));
-	Assert(!PagePrecedes(lhs / per_page, rhs / per_page));
-	Assert(!PagePrecedes(rhs / per_page, lhs / per_page));
-	Assert(!PagePrecedes((lhs - per_page) / per_page, rhs / per_page));
-	Assert(PagePrecedes(rhs / per_page, (lhs - 3 * per_page) / per_page));
-	Assert(PagePrecedes(rhs / per_page, (lhs - 2 * per_page) / per_page));
-	Assert(PagePrecedes(rhs / per_page, (lhs - 1 * per_page) / per_page)
+	Assert(!ctl->PagePrecedes(lhs / per_page, lhs / per_page));
+	Assert(!ctl->PagePrecedes(lhs / per_page, rhs / per_page));
+	Assert(!ctl->PagePrecedes(rhs / per_page, lhs / per_page));
+	Assert(!ctl->PagePrecedes((lhs - per_page) / per_page, rhs / per_page));
+	Assert(ctl->PagePrecedes(rhs / per_page, (lhs - 3 * per_page) / per_page));
+	Assert(ctl->PagePrecedes(rhs / per_page, (lhs - 2 * per_page) / per_page));
+	Assert(ctl->PagePrecedes(rhs / per_page, (lhs - 1 * per_page) / per_page)
 		   || (1U << 31) % per_page != 0);	/* See CommitTsPagePrecedes() */
-	Assert(PagePrecedes((lhs + 1 * per_page) / per_page, rhs / per_page)
+	Assert(ctl->PagePrecedes((lhs + 1 * per_page) / per_page, rhs / per_page)
 		   || (1U << 31) % per_page != 0);
-	Assert(PagePrecedes((lhs + 2 * per_page) / per_page, rhs / per_page));
-	Assert(PagePrecedes((lhs + 3 * per_page) / per_page, rhs / per_page));
-	Assert(!PagePrecedes(rhs / per_page, (lhs + per_page) / per_page));
+	Assert(ctl->PagePrecedes((lhs + 2 * per_page) / per_page, rhs / per_page));
+	Assert(ctl->PagePrecedes((lhs + 3 * per_page) / per_page, rhs / per_page));
+	Assert(!ctl->PagePrecedes(rhs / per_page, (lhs + per_page) / per_page));
 
 	/*
 	 * GetNewTransactionId() has assigned the last XID it can safely use, and
@@ -281,7 +1451,7 @@ SlruPagePrecedesTestOffset(SlruPagePrecedesFunction PagePrecedes,
 	oldestXact = newestXact + 1;
 	oldestXact -= 1U << 31;
 	oldestPage = oldestXact / per_page;
-	Assert(!SlruMayDeleteSegment(PagePrecedes,
+	Assert(!SlruMayDeleteSegment(ctl,
 								 (newestPage -
 								  newestPage % SLRU_PAGES_PER_SEGMENT),
 								 oldestPage));
@@ -297,7 +1467,7 @@ SlruPagePrecedesTestOffset(SlruPagePrecedesFunction PagePrecedes,
 	oldestXact = newestXact + 1;
 	oldestXact -= 1U << 31;
 	oldestPage = oldestXact / per_page;
-	Assert(!SlruMayDeleteSegment(PagePrecedes,
+	Assert(!SlruMayDeleteSegment(ctl,
 								 (newestPage -
 								  newestPage % SLRU_PAGES_PER_SEGMENT),
 								 oldestPage));
@@ -313,12 +1483,12 @@ SlruPagePrecedesTestOffset(SlruPagePrecedesFunction PagePrecedes,
  * do not apply to them.)
  */
 void
-SlruPagePrecedesUnitTests(SlruPagePrecedesFunction PagePrecedes, int per_page)
+SlruPagePrecedesUnitTests(SlruCtl ctl, int per_page)
 {
 	/* Test first, middle and last entries of a page. */
-	SlruPagePrecedesTestOffset(PagePrecedes, per_page, 0);
-	SlruPagePrecedesTestOffset(PagePrecedes, per_page, per_page / 2);
-	SlruPagePrecedesTestOffset(PagePrecedes, per_page, per_page - 1);
+	SlruPagePrecedesTestOffset(ctl, per_page, 0);
+	SlruPagePrecedesTestOffset(ctl, per_page, per_page / 2);
+	SlruPagePrecedesTestOffset(ctl, per_page, per_page - 1);
 }
 #endif
 
@@ -328,12 +1498,11 @@ SlruPagePrecedesUnitTests(SlruPagePrecedesFunction PagePrecedes, int per_page)
  *		one containing the page passed as "data".
  */
 bool
-SlruScanDirCbReportPresence(Oid rel_id, SlruPagePrecedesFunction PagePrecedes,
-							char *filename, int segpage, void *data)
+SlruScanDirCbReportPresence(SlruCtl ctl, char *filename, int segpage, void *data)
 {
 	int			cutoffPage = *(int *) data;
 
-	if (SlruMayDeleteSegment(PagePrecedes, segpage, cutoffPage))
+	if (SlruMayDeleteSegment(ctl, segpage, cutoffPage))
 		return true;			/* found one; don't iterate any more */
 
 	return false;				/* keep going */
@@ -344,18 +1513,12 @@ SlruScanDirCbReportPresence(Oid rel_id, SlruPagePrecedesFunction PagePrecedes,
  *		This callback deletes segments prior to the one passed in as "data".
  */
 static bool
-SlruScanDirCbDeleteCutoff(Oid rel_id, SlruPagePrecedesFunction PagePrecedes,
-						  char *filename, int segpage, void *data)
+SlruScanDirCbDeleteCutoff(SlruCtl ctl, char *filename, int segpage, void *data)
 {
-	RelFileLocator	rlocator = SlruRelFileLocator(rel_id);
 	int			cutoffPage = *(int *) data;
 
-	if (SlruMayDeleteSegment(PagePrecedes, segpage, cutoffPage))
-	{
-		for (int i = 0; i < SLRU_PAGES_PER_SEGMENT; ++i)
-			DiscardBuffer(rlocator, MAIN_FORKNUM, segpage + i);
-		SlruInternalDeleteSegment(rel_id, segpage / SLRU_PAGES_PER_SEGMENT);
-	}
+	if (SlruMayDeleteSegment(ctl, segpage, cutoffPage))
+		SlruInternalDeleteSegment(ctl, segpage / SLRU_PAGES_PER_SEGMENT);
 
 	return false;				/* keep going */
 }
@@ -365,10 +1528,9 @@ SlruScanDirCbDeleteCutoff(Oid rel_id, SlruPagePrecedesFunction PagePrecedes,
  *		This callback deletes all segments.
  */
 bool
-SlruScanDirCbDeleteAll(Oid rel_id, SlruPagePrecedesFunction PagePrecedes,
-					   char *filename, int segpage, void *data)
+SlruScanDirCbDeleteAll(SlruCtl ctl, char *filename, int segpage, void *data)
 {
-	SlruInternalDeleteSegment(rel_id, segpage / SLRU_PAGES_PER_SEGMENT);
+	SlruInternalDeleteSegment(ctl, segpage / SLRU_PAGES_PER_SEGMENT);
 
 	return false;				/* keep going */
 }
@@ -389,20 +1551,16 @@ SlruScanDirCbDeleteAll(Oid rel_id, SlruPagePrecedesFunction PagePrecedes,
  * Note that no locking is applied.
  */
 bool
-SlruScanDirectory(Oid rel_id, SlruPagePrecedesFunction PagePrecedes,
-				  SlruScanCallback callback, void *data)
+SlruScanDirectory(SlruCtl ctl, SlruScanCallback callback, void *data)
 {
 	bool		retval = false;
 	DIR		   *cldir;
 	struct dirent *clde;
 	int			segno;
 	int			segpage;
-	const char *path;
 
-	path = defs[rel_id].path;
-
-	cldir = AllocateDir(path);
-	while ((clde = ReadDir(cldir, path)) != NULL)
+	cldir = AllocateDir(ctl->Dir);
+	while ((clde = ReadDir(cldir, ctl->Dir)) != NULL)
 	{
 		size_t		len;
 
@@ -415,8 +1573,8 @@ SlruScanDirectory(Oid rel_id, SlruPagePrecedesFunction PagePrecedes,
 			segpage = segno * SLRU_PAGES_PER_SEGMENT;
 
 			elog(DEBUG2, "SlruScanDirectory invoking callback on %s/%s",
-				 path, clde->d_name);
-			retval = callback(rel_id, PagePrecedes, clde->d_name, segpage, data);
+				 ctl->Dir, clde->d_name);
+			retval = callback(ctl, clde->d_name, segpage, data);
 			if (retval)
 				break;
 		}
@@ -426,295 +1584,32 @@ SlruScanDirectory(Oid rel_id, SlruPagePrecedesFunction PagePrecedes,
 	return retval;
 }
 
-
-void
-CheckPointSLRU(void)
-{
-	/* Ensure that directory entries for new files are on disk. */
-	for (int i = 0; i < lengthof(defs); ++i)
-	{
-		if (defs[i].synchronize)
-			fsync_fname(defs[i].path, true);
-	}
-}
-
-/*
- * Read a buffer.  Buffer is pinned on return.
- */
-Buffer
-ReadSlruBuffer(Oid rel_id, int pageno)
-{
-	RelFileLocator rlocator = SlruRelFileLocator(rel_id);
-	Buffer buffer;
-	bool hit;
-
-	/* Try to avoid doing a buffer mapping table lookup for repeated access. */
-	buffer = slru_recent_buffers[rel_id].recent_buffer;
-	if (slru_recent_buffers[rel_id].pageno == pageno &&
-		BufferIsValid(buffer) &&
-		ReadRecentBuffer(rlocator, MAIN_FORKNUM, pageno, buffer))
-	{
-		pgstat_count_slru_page_hit(rel_id);
-		return buffer;
-	}
-
-	/* Regular lookup. */
-	buffer = ReadBufferWithoutRelcacheWithHit(rlocator, MAIN_FORKNUM, pageno,
-											  RBM_NORMAL, &hit);
-
-	/* Remember where this page is for next time. */
-	slru_recent_buffers[rel_id].pageno = pageno;
-	slru_recent_buffers[rel_id].recent_buffer = buffer;
-
-	if (hit)
-		pgstat_count_slru_page_hit(rel_id);
-
-	return buffer;
-}
-
 /*
- * Zero-initialize a buffer.  Buffer is pinned and exclusively locked on return.
+ * Individual SLRUs (clog, ...) have to provide a sync.c handler function so
+ * that they can provide the correct "SlruCtl" (otherwise we don't know how to
+ * build the path), but they just forward to this common implementation that
+ * performs the fsync.
  */
-Buffer
-ZeroSlruBuffer(Oid rel_id, int pageno)
-{
-	RelFileLocator rlocator = SlruRelFileLocator(rel_id);
-	Buffer buffer;
-
-	buffer = ReadBufferWithoutRelcache(rlocator, MAIN_FORKNUM, pageno,
-									   RBM_ZERO_AND_LOCK, NULL, true);
-
-	/* Remember where this page is for next time. */
-	slru_recent_buffers[rel_id].pageno = pageno;
-	slru_recent_buffers[rel_id].recent_buffer = buffer;
-
-	pgstat_count_slru_page_zeroed(rel_id);
-
-	return buffer;
-}
-
-Oid
-SlruRelIdByName(const char *name)
-{
-	for (int i = 0; i < lengthof(defs); ++i)
-		if (strcmp(defs[i].name, name) == 0)
-			return i;
-
-	elog(ERROR, "unknown SLRU \"%s\"", name);
-}
-
-const char *
-SlruName(Oid rel_id)
-{
-	if (rel_id >= SLRU_NUM_RELS)
-		elog(ERROR, "invalid SLRU rel ID %u", rel_id);
-
-	return defs[rel_id].name;
-}
-
 int
-slrusyncfiletag(const FileTag *ftag, char *path)
-{
-	SMgrRelation reln;
-	File		file;
-
-	reln = smgropen(ftag->rlocator, InvalidBackendId);
-	file = slrufile(reln, ftag->segno * SLRU_PAGES_PER_SEGMENT, O_RDWR, true);
-	if (file < 0)
-	{
-		/* Path is reported here so the caller can make an error message */
-		SlruFileName(ftag->rlocator.relNumber, path, ftag->segno);
-		return -1;
-	}
-
-	pgstat_count_slru_flush(reln->smgr_rlocator.locator.relNumber);
-
-	return FileSync(file, WAIT_EVENT_SLRU_SYNC);
-}
-
-static File
-slrufile(SMgrRelation reln, BlockNumber blocknum, int mode, bool missing_ok)
-{
-	int segment = blocknum / SLRU_PAGES_PER_SEGMENT;
-	char path[MAXPGPATH];
-
-	Assert(reln->smgr_rlocator.locator.dbOid == SLRU_DB_ID);
-	Assert(reln->smgr_rlocator.locator.relNumber < lengthof(defs));
-	Assert(defs[reln->smgr_rlocator.locator.relNumber].path != NULL);
-
-	/* Do we have the right file open already? */
-	if (reln->slru_file_segment == segment)
-	{
-		/* XXX How can we invalidate this if the SLRU wraps around?! */
-		Assert(reln->slru_file != -1);
-		return reln->slru_file;
-	}
-
-	/* Close the current file, if we have one open. */
-	if (reln->slru_file_segment != -1)
-	{
-		Assert(reln->slru_file != -1);
-		FileClose(reln->slru_file);
-		reln->slru_file = -1;
-		reln->slru_file_segment = -1;
-	}
-
-	/* Open the file we want. */
-	SlruFileName(reln->smgr_rlocator.locator.relNumber, path, segment);
-	reln->slru_file = PathNameOpenFile(path, mode);
-	if (reln->slru_file >= 0)
-		reln->slru_file_segment = segment;
-	else if (!(missing_ok && errno == ENOENT))
-		ereport(ERROR,
-				(errcode_for_file_access(),
-				 errmsg("could not open file \"%s\": %m",
-						path)));
-
-	/*
-	 * XXX That error message doesn't contain the xid; then again, the
-	 * previous SLRU error codes were all weird about xids anyway; maybe
-	 * clog.c, notify.c et al should set a context that has SLRU-specific
-	 * context in a more natural format
-	 */
-
-	return reln->slru_file;
-}
-
-void
-slruopen(SMgrRelation reln)
+SlruSyncFileTag(SlruCtl ctl, const FileTag *ftag, char *path)
 {
-	reln->slru_file = -1;
-	reln->slru_file_segment = -1;
+	int			fd;
+	int			save_errno;
+	int			result;
 
-	/*
-	 * We don't want this to be closed at end of transaction, which would
-	 * otherwise happen, because it isn't owned by a Relation.
-	 */
-	dlist_delete(&reln->node);
-}
-
-void
-slruclose(SMgrRelation reln, ForkNumber forknum)
-{
-	if (reln->slru_file != -1)
-		FileClose(reln->slru_file);
-	reln->slru_file = -1;
-	reln->slru_file_segment = -1;
-}
-
-void
-slruwrite(SMgrRelation reln, ForkNumber forknum, BlockNumber blocknum,
-		  char *buffer, bool skipFsync)
-{
-	File file;
-	Oid	rel_id;
-	off_t offset;
-	int rc;
-
-	file = slrufile(reln, blocknum, O_RDWR | O_CREAT, false);
-	offset = (blocknum % SLRU_PAGES_PER_SEGMENT) * BLCKSZ;
-
-	rc = FileWrite(file, buffer, BLCKSZ, offset, WAIT_EVENT_SLRU_WRITE);
-	if (rc < 0)
-		ereport(ERROR,
-				(errcode_for_file_access(),
-				 errmsg("could not write %d bytes to file \"%s\" at offset %d: %m",
-						BLCKSZ,
-						FilePathName(file),
-						(int) offset)));
-	if (rc < BLCKSZ)
-		ereport(ERROR,
-				(errcode_for_file_access(),
-				 errmsg("could not write %d bytes to file \"%s\" at offset %d, only %d bytes written",
-						BLCKSZ,
-						FilePathName(file),
-						(int) offset,
-						rc)));
-
-	rel_id = reln->smgr_rlocator.locator.relNumber;
-	if (defs[rel_id].synchronize)
-	{
-		FileTag			tag;
-
-		/* Tell checkpointer to synchronize this file. */
-		INIT_SLRUFILETAG(tag, rel_id, blocknum / SLRU_PAGES_PER_SEGMENT);
-		if (!RegisterSyncRequest(&tag, SYNC_REQUEST, false))
-		{
-			/* Queue full.  Do it synchronously. */
-			if (FileSync(file, WAIT_EVENT_SLRU_SYNC) < 0)
-				ereport(data_sync_elevel(ERROR),
-						(errcode_for_file_access(),
-						 errmsg("could not synchronize file \"%s\": %m",
-								FilePathName(file))));
-		}
-	}
+	SlruFileName(ctl, path, ftag->segno);
 
-	pgstat_count_slru_page_written(reln->smgr_rlocator.locator.relNumber);
-}
-
-void
-slruread(SMgrRelation reln, ForkNumber forknum, BlockNumber blocknum,
-		 char *buffer)
-{
-	File file;
-	off_t offset;
-	int rc;
-
-	file = slrufile(reln, blocknum, O_RDWR, false);
-	offset = (blocknum % SLRU_PAGES_PER_SEGMENT) * BLCKSZ;
-
-	rc = FileRead(file, buffer, BLCKSZ, offset, WAIT_EVENT_SLRU_READ);
-	if (rc < 0)
-		ereport(ERROR,
-				(errcode_for_file_access(),
-				 errmsg("could not read %d bytes from file \"%s\" at offset %d: %m",
-						BLCKSZ,
-						FilePathName(file),
-						(int) offset)));
-	if (rc < BLCKSZ)
-		ereport(ERROR,
-				(errcode_for_file_access(),
-				 errmsg("could not read %d bytes from file \"%s\" at offset %d, only %d bytes read",
-						BLCKSZ,
-						FilePathName(file),
-						(int) offset,
-						rc)));
-
-	pgstat_count_slru_page_read(reln->smgr_rlocator.locator.relNumber);
-}
+	fd = OpenTransientFile(path, O_RDWR | PG_BINARY);
+	if (fd < 0)
+		return -1;
 
-void
-slruwriteback(SMgrRelation reln, ForkNumber forknum,
-			  BlockNumber blocknum, BlockNumber nblocks)
-{
-	Oid rel_id;
+	pgstat_report_wait_start(WAIT_EVENT_SLRU_FLUSH_SYNC);
+	result = pg_fsync(fd);
+	pgstat_report_wait_end();
+	save_errno = errno;
 
-	/* No point in flushing data we won't be fsyncing. */
-	rel_id = reln->smgr_rlocator.locator.relNumber;
-	if (!defs[rel_id].synchronize)
-		return;
+	CloseTransientFile(fd);
 
-	while (nblocks > 0)
-	{
-		File		file;
-		BlockNumber blocknum_in_this_file;
-		BlockNumber nflush;
-
-		/* File range of blocks to flush in one file. */
-		blocknum_in_this_file = blocknum % SLRU_PAGES_PER_SEGMENT;
-		nflush = Min(nblocks, SLRU_PAGES_PER_SEGMENT - blocknum_in_this_file);
-
-		/* The file might have been unlinked already, so tolerate missing. */
-		file = slrufile(reln, blocknum, O_RDWR, true);
-		if (file < 0)
-			return;
-
-		FileWriteback(file,
-					  BLCKSZ * blocknum_in_this_file,
-					  BLCKSZ * nflush,
-					  WAIT_EVENT_SLRU_FLUSH);
-
-		nblocks -= nflush;
-		blocknum += nflush;
-	}
+	errno = save_errno;
+	return result;
 }
diff --git a/src/backend/access/transam/subtrans.c b/src/backend/access/transam/subtrans.c
index 90cc1318526..635e5368cd6 100644
--- a/src/backend/access/transam/subtrans.c
+++ b/src/backend/access/transam/subtrans.c
@@ -29,6 +29,7 @@
 #include "postgres.h"
 
 #include "access/slru.h"
+#include "access/nrel.h"
 #include "access/subtrans.h"
 #include "access/transam.h"
 #include "pg_trace.h"
@@ -74,7 +75,8 @@ SubTransSetParent(TransactionId xid, TransactionId parent)
 	Assert(TransactionIdIsValid(parent));
 	Assert(TransactionIdFollows(xid, parent));
 
-	buffer = ReadSlruBuffer(SLRU_SUBTRANS_REL_ID, pageno);
+	buffer = ReadNrelBuffer(NREL_SUBTRANS_REL_ID, pageno);
+
 	LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
 	ptr = (TransactionId *) BufferGetPage(buffer);
 	ptr += entryno;
@@ -113,7 +115,8 @@ SubTransGetParent(TransactionId xid)
 	if (!TransactionIdIsNormal(xid))
 		return InvalidTransactionId;
 
-	buffer = ReadSlruBuffer(SLRU_SUBTRANS_REL_ID, pageno);
+	buffer = ReadNrelBuffer(NREL_SUBTRANS_REL_ID, pageno);
+
 
 	ptr = (TransactionId *) BufferGetPage(buffer);
 	ptr += entryno;
@@ -183,7 +186,7 @@ BootStrapSUBTRANS(void)
 {
 	Buffer		buffer;
 
-	SlruPagePrecedesUnitTests(SubTransPagePrecedes, SUBTRANS_XACTS_PER_PAGE);
+	NrelPagePrecedesUnitTests(SubTransPagePrecedes, SUBTRANS_XACTS_PER_PAGE);
 
 	/* Create and zero the first page of the subtrans log */
 	buffer = ZeroSUBTRANSPage(0);
@@ -206,7 +209,8 @@ ZeroSUBTRANSPage(int pageno)
 {
 	Buffer		buffer;
 
-	buffer = ZeroSlruBuffer(SLRU_SUBTRANS_REL_ID, pageno);
+	buffer = ZeroNrelBuffer(NREL_SUBTRANS_REL_ID, pageno);
+
 	MarkBufferDirty(buffer);
 
 	return buffer;
@@ -289,16 +293,16 @@ TruncateSUBTRANS(TransactionId oldestXact)
 
 	/*
 	 * The cutoff point is the start of the segment containing oldestXact. We
-	 * pass the *page* containing oldestXact to SimpleLruTruncate.  We step
+	 * pass the *page* containing oldestXact to NonRelTruncate.  We step
 	 * back one transaction to avoid passing a cutoff page that hasn't been
 	 * created yet in the rare case that oldestXact would be the first item on
 	 * a page and oldestXact == next XID.  In that case, if we didn't subtract
-	 * one, we'd trigger SimpleLruTruncate's wraparound detection.
+	 * one, we'd trigger NonRelTruncate's wraparound detection.
 	 */
 	TransactionIdRetreat(oldestXact);
 	cutoffPage = TransactionIdToPage(oldestXact);
 
-	SimpleLruTruncate(SLRU_SUBTRANS_REL_ID, SubTransPagePrecedes, cutoffPage);
+	NonRelTruncate(NREL_SUBTRANS_REL_ID, SubTransPagePrecedes, cutoffPage);
 }
 
 
diff --git a/src/backend/access/transam/xact.c b/src/backend/access/transam/xact.c
index 5aebd2ec20f..bd79fb0a34e 100644
--- a/src/backend/access/transam/xact.c
+++ b/src/backend/access/transam/xact.c
@@ -1394,22 +1394,21 @@ RecordTransactionCommit(void)
 		Assert((MyProc->delayChkptFlags & DELAY_CHKPT_START) == 0);
 		
 		/*
-	 	 * After moving the commit log to the buffer cache, we may need to
+		 * After moving the commit log to the buffer cache, we may need to
 		 * create a hashtable entry in the storage manager hash table for the
-		 * commit log which will require allocating memory. We want to do this
+		 * commit log which will require allocating memory. We want to do this 
 		 * before we start the critical section.
 		 */
 		Assert(VerifyClogLocatorInHashTable());
 
 
 		/*
-		* Similar to above - we may need to allocate memory for a new buffer pin
-		* for ReadBuffer while getting a clog page, so ensure we have enough memory
-		* before starting critical section.
-		*/
+		 * Similar to above - we may need to allocate memory for a new buffer pin
+		 * for ReadBuffer while getting a clog page, so ensure we have enough memory
+		 * before starting critical section.
+		 */
 		ResourceOwnerEnlargeBuffers(CurrentResourceOwner);
-
-		
+						
 		START_CRIT_SECTION();
 		MyProc->delayChkptFlags |= DELAY_CHKPT_START;
 
diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 901a42e93f1..f01a14c8b80 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -7037,7 +7037,7 @@ CheckPointGuts(XLogRecPtr checkPointRedo, int flags)
 	CheckPointLogicalRewriteHeap();
 	CheckPointReplicationOrigin();
 	CheckPointPredicate();
-	CheckPointSLRU();
+	CheckPointNREL();
 
 	/* Write out all dirty data in the buffer pool */
 	TRACE_POSTGRESQL_BUFFER_CHECKPOINT_START(flags);
diff --git a/src/backend/commands/async.c b/src/backend/commands/async.c
index c595fee8018..723cf8f6e1c 100644
--- a/src/backend/commands/async.c
+++ b/src/backend/commands/async.c
@@ -131,6 +131,7 @@
 #include <signal.h>
 
 #include "access/parallel.h"
+#include "access/nrel.h"
 #include "access/slru.h"
 #include "access/transam.h"
 #include "access/xact.h"
@@ -312,7 +313,7 @@ static AsyncQueueControl *asyncQueueControl;
 /*
  * Use segments 0000 through FFFF.  Each contains SLRU_PAGES_PER_SEGMENT pages
  * which gives us the pages from 0 to SLRU_PAGES_PER_SEGMENT * 0x10000 - 1.
- * We could use as many segments as SlruScanDirectory() allows, but this gives
+ * We could use as many segments as NrelScanDirectory() allows, but this gives
  * us so much space already that it doesn't seem worth the trouble.
  *
  * The most data we can have in the queue at a time is QUEUE_MAX_PAGE/2
@@ -563,8 +564,8 @@ AsyncShmemInit(void)
 		/*
 		 * During start or reboot, clean out the pg_notify directory.
 		 */
-		(void) SlruScanDirectory(SLRU_NOTIFY_REL_ID, asyncQueuePagePrecedes,
-								 SlruScanDirCbDeleteAll, NULL);
+		(void) NrelScanDirectory(NREL_NOTIFY_REL_ID, asyncQueuePagePrecedes,
+								 NrelScanDirCbDeleteAll, NULL);
 	}
 }
 
@@ -1423,11 +1424,12 @@ asyncQueueAddEntries(ListCell *nextNotify)
 	pageno = QUEUE_POS_PAGE(queue_head);
 	if (QUEUE_POS_IS_ZERO(queue_head))
 	{
-		buffer = ZeroSlruBuffer(SLRU_NOTIFY_REL_ID, pageno);
+		buffer = ZeroNrelBuffer(NREL_NOTIFY_REL_ID, pageno);
 	}
 	else
 	{
-		buffer = ReadSlruBuffer(SLRU_NOTIFY_REL_ID, pageno);
+		buffer = ReadNrelBuffer(NREL_NOTIFY_REL_ID, pageno);
+
 		LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
 	}
 
@@ -1474,12 +1476,12 @@ asyncQueueAddEntries(ListCell *nextNotify)
 			 * Page is full, so we're done here, but first fill the next page
 			 * with zeroes.  The reason to do this is to ensure that slru.c's
 			 * idea of the head page is always the same as ours, which avoids
-			 * boundary problems in SimpleLruTruncate.  The test in
+			 * boundary problems in NonRelTruncate.  The test in
 			 * asyncQueueIsFull() ensured that there is room to create this
 			 * page without overrunning the queue.
 			 */
 			UnlockReleaseBuffer(buffer);
-			buffer = ZeroSlruBuffer(SLRU_NOTIFY_REL_ID,
+			buffer = ZeroNrelBuffer(NREL_NOTIFY_REL_ID,
 									QUEUE_POS_PAGE(queue_head));
 			MarkBufferDirty(buffer);
 
@@ -1981,7 +1983,8 @@ asyncQueueReadAllNotifications(void)
 			 * transmitting them to our frontend.  Copy only the part of the
 			 * page we will actually inspect.
 			 */
-			buffer = ReadSlruBuffer(SLRU_NOTIFY_REL_ID, curpage);
+			buffer = ReadNrelBuffer(NREL_NOTIFY_REL_ID, curpage);
+
 			if (curpage == QUEUE_POS_PAGE(head))
 			{
 				/* we only want to read as far as head */
@@ -2148,7 +2151,7 @@ asyncQueueAdvanceTail(void)
 	int			newtailpage;
 	int			boundary;
 
-	/* Restrict task to one backend per cluster; see SimpleLruTruncate(). */
+	/* Restrict task to one backend per cluster; see NonRelTruncate(). */
 	LWLockAcquire(NotifyQueueTailLock, LW_EXCLUSIVE);
 
 	/*
@@ -2165,7 +2168,7 @@ asyncQueueAdvanceTail(void)
 	 * there are pages we can truncate but haven't yet finished doing so.
 	 *
 	 * For concurrency's sake, we don't want to hold NotifyQueueLock while
-	 * performing SimpleLruTruncate.  This is OK because no backend will try
+	 * performing NonRelTruncate.  This is OK because no backend will try
 	 * to access the pages we are in the midst of truncating.
 	 */
 	LWLockAcquire(NotifyQueueLock, LW_EXCLUSIVE);
@@ -2191,10 +2194,10 @@ asyncQueueAdvanceTail(void)
 	if (asyncQueuePagePrecedes(oldtailpage, boundary))
 	{
 		/*
-		 * SimpleLruTruncate() will ask for NotifySLRULock but will also
+		 * NonRelTruncate() will ask for NotifySLRULock but will also
 		 * release the lock again.
 		 */
-		SimpleLruTruncate(SLRU_NOTIFY_REL_ID, asyncQueuePagePrecedes, newtailpage);
+		NonRelTruncate(NREL_NOTIFY_REL_ID, asyncQueuePagePrecedes, newtailpage);
 
 		/*
 		 * Update QUEUE_STOP_PAGE.  This changes asyncQueueIsFull()'s verdict
diff --git a/src/backend/storage/lmgr/predicate.c b/src/backend/storage/lmgr/predicate.c
index 690d1d1401a..2177386ca50 100644
--- a/src/backend/storage/lmgr/predicate.c
+++ b/src/backend/storage/lmgr/predicate.c
@@ -193,6 +193,7 @@
 #include "postgres.h"
 
 #include "access/parallel.h"
+#include "access/nrel.h"
 #include "access/slru.h"
 #include "access/subtrans.h"
 #include "access/transam.h"
@@ -798,7 +799,7 @@ SerialInit(void)
 #ifdef USE_ASSERT_CHECKING
 	SerialPagePrecedesLogicallyUnitTests();
 #endif
-	SlruPagePrecedesUnitTests(SerialPagePrecedesLogically, SERIAL_ENTRIESPERPAGE);
+	NrelPagePrecedesUnitTests(SerialPagePrecedesLogically, SERIAL_ENTRIESPERPAGE);
 
 	/*
 	 * Create or attach to the SerialControl structure.
@@ -875,16 +876,18 @@ SerialAdd(TransactionId xid, SerCommitSeqNo minConflictCommitSeqNo)
 		/* Initialize intervening pages. */
 		while (firstZeroPage != targetPage)
 		{
-			buffer = ZeroSlruBuffer(SLRU_SERIAL_REL_ID, firstZeroPage);
+			buffer = ZeroNrelBuffer(NREL_SERIAL_REL_ID, firstZeroPage);
+
 			MarkBufferDirty(buffer);
 			UnlockReleaseBuffer(buffer);
 			firstZeroPage = SerialNextPage(firstZeroPage);
 		}
-		buffer = ZeroSlruBuffer(SLRU_SERIAL_REL_ID, targetPage);
+		buffer = ZeroNrelBuffer(NREL_SERIAL_REL_ID, targetPage);
 	}
 	else
 	{
-		buffer = ReadSlruBuffer(SLRU_SERIAL_REL_ID, targetPage);
+		buffer = ReadNrelBuffer(NREL_SERIAL_REL_ID, targetPage);
+
 		LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
 	}
 
@@ -928,7 +931,8 @@ SerialGetMinConflictCommitSeqNo(TransactionId xid)
 	 * The following function must be called without holding SerialSLRULock,
 	 * but will return with that lock held, which must then be released.
 	 */
-	buffer = ReadSlruBuffer(SLRU_SERIAL_REL_ID, SerialPage(xid));
+	buffer = ReadNrelBuffer(NREL_SERIAL_REL_ID, SerialPage(xid));
+
 	val = SerialValue(buffer, xid);
 	ReleaseBuffer(buffer);
 	LWLockRelease(SerialSLRULock);
@@ -1049,7 +1053,7 @@ CheckPointPredicate(void)
 	LWLockRelease(SerialSLRULock);
 
 	/* Truncate away pages that are no longer required */
-	SimpleLruTruncate(SLRU_SERIAL_REL_ID, SerialPagePrecedesLogically, tailPage);
+	NonRelTruncate(NREL_SERIAL_REL_ID, SerialPagePrecedesLogically, tailPage);
 }
 
 /*------------------------------------------------------------------------*/
diff --git a/src/backend/storage/smgr/smgr.c b/src/backend/storage/smgr/smgr.c
index 3e9f70d1766..88aeeb7b13a 100644
--- a/src/backend/storage/smgr/smgr.c
+++ b/src/backend/storage/smgr/smgr.c
@@ -17,6 +17,7 @@
  */
 #include "postgres.h"
 
+#include "access/nrel.h"
 #include "access/slru.h"
 #include "access/xlogutils.h"
 #include "lib/ilist.h"
@@ -27,6 +28,7 @@
 #include "storage/smgr.h"
 #include "utils/hsearch.h"
 #include "utils/inval.h"
+#include "miscadmin.h"
 
 
 /*
@@ -88,13 +90,13 @@ static const f_smgr smgrsw[] = {
 		.smgr_truncate = mdtruncate,
 		.smgr_immedsync = mdimmedsync,
 	},
-	/* "SLRU" storage */
+	/* "NREL" storage */
 	{
-		.smgr_open = slruopen,
-		.smgr_close = slruclose,
-		.smgr_read = slruread,
-		.smgr_write = slruwrite,
-		.smgr_writeback = slruwriteback,
+		.smgr_open = nrelopen,
+		.smgr_close = nrelclose,
+		.smgr_read = nrelread,
+		.smgr_write = nrelwrite,
+		.smgr_writeback = nrelwriteback,
 	}
 };
 
@@ -177,9 +179,11 @@ smgropen(RelFileLocator rlocator, BackendId backend)
 	/* Look up or create an entry */
 	brlocator.locator = rlocator;
 	brlocator.backend = backend;
+	
 	reln = (SMgrRelation) hash_search(SMgrRelationHash,
-									  &brlocator,
-									  HASH_ENTER, &found);
+								  &brlocator,
+								  HASH_ENTER, &found);
+
 
 	/* Initialize it if not present before */
 	if (!found)
@@ -191,7 +195,7 @@ smgropen(RelFileLocator rlocator, BackendId backend)
 			reln->smgr_cached_nblocks[i] = InvalidBlockNumber;
 
 		/* XXX find some elegant way to do this, or something better */
-		if (rlocator.dbOid == SLRU_DB_ID)
+		if (rlocator.dbOid == NREL_DB_ID)
 			reln->smgr_which = 1;	/* slru.c */
 		else
 			reln->smgr_which = 0;	/* md.c */
diff --git a/src/backend/storage/sync/sync.c b/src/backend/storage/sync/sync.c
index bbabb6b8f51..f04fb2ffbf1 100644
--- a/src/backend/storage/sync/sync.c
+++ b/src/backend/storage/sync/sync.c
@@ -18,6 +18,7 @@
 #include <fcntl.h>
 #include <sys/file.h>
 
+#include "access/nrel.h"
 #include "access/slru.h"
 #include "access/xlog.h"
 #include "access/xlogutils.h"
@@ -103,8 +104,8 @@ static const SyncOps syncsw[] = {
 		.sync_filetagmatches = mdfiletagmatches
 	},
 	/* SLRU */
-	[SYNC_HANDLER_SLRU] = {
-		.sync_syncfiletag = slrusyncfiletag
+	[SYNC_HANDLER_NREL] = {
+		.sync_syncfiletag = nrelsyncfiletag
 	}
 };
 
diff --git a/src/backend/utils/activity/pgstat_slru.c b/src/backend/utils/activity/pgstat_slru.c
index 30ccf36abe7..03532223ef2 100644
--- a/src/backend/utils/activity/pgstat_slru.c
+++ b/src/backend/utils/activity/pgstat_slru.c
@@ -31,7 +31,7 @@ static void pgstat_reset_slru_counter_internal(int index, TimestampTz ts);
  * SLRU counters are reported within critical sections so we use static memory
  * in order to avoid memory allocation.
  */
-static PgStat_SLRUStats pending_SLRUStats[SLRU_NUM_RELS];
+static PgStat_SLRUStats pending_SLRUStats[NREL_NUM_RELS + 1];
 bool		have_slrustats = false;
 
 
@@ -115,7 +115,7 @@ pgstat_fetch_slru(void)
 const char *
 pgstat_get_slru_name(int slru_idx)
 {
-	return SlruName(slru_idx);
+	return NrelName(slru_idx);
 }
 
 /*
@@ -124,7 +124,7 @@ pgstat_get_slru_name(int slru_idx)
 int
 pgstat_get_slru_index(const char *name)
 {
-	return SlruRelIdByName(name);
+	return NrelRelIdByName(name);
 }
 
 /*
@@ -150,7 +150,7 @@ pgstat_slru_flush(bool nowait)
 	else if (!LWLockConditionalAcquire(&stats_shmem->lock, LW_EXCLUSIVE))
 		return true;
 
-	for (i = 0; i < SLRU_NUM_RELS; i++)
+	for (i = 0; i < NREL_NUM_RELS + 1; i++)
 	{
 		PgStat_SLRUStats *sharedent = &stats_shmem->stats[i];
 		PgStat_SLRUStats *pendingent = &pending_SLRUStats[i];
@@ -179,7 +179,7 @@ pgstat_slru_flush(bool nowait)
 void
 pgstat_slru_reset_all_cb(TimestampTz ts)
 {
-	for (int i = 0; i < SLRU_NUM_RELS; i++)
+	for (int i = 0; i < NREL_NUM_RELS + 1; i++)
 		pgstat_reset_slru_counter_internal(i, ts);
 }
 
@@ -210,7 +210,7 @@ get_slru_entry(int slru_idx)
 	 */
 	Assert(IsUnderPostmaster || !IsPostmasterEnvironment);
 
-	Assert((slru_idx >= 0) && (slru_idx < SLRU_NUM_RELS));
+	Assert((slru_idx >= 0) && (slru_idx < NREL_NUM_RELS + 1));
 
 	have_slrustats = true;
 
diff --git a/src/backend/utils/activity/wait_event_names.txt b/src/backend/utils/activity/wait_event_names.txt
index 9668837b050..0d5a142715c 100644
--- a/src/backend/utils/activity/wait_event_names.txt
+++ b/src/backend/utils/activity/wait_event_names.txt
@@ -206,6 +206,10 @@ WAIT_EVENT_LOGICAL_REWRITE_MAPPING_WRITE	LogicalRewriteMappingWrite	"Waiting for
 WAIT_EVENT_LOGICAL_REWRITE_SYNC	LogicalRewriteSync	"Waiting for logical rewrite mappings to reach durable storage."
 WAIT_EVENT_LOGICAL_REWRITE_TRUNCATE	LogicalRewriteTruncate	"Waiting for truncate of mapping data during a logical rewrite."
 WAIT_EVENT_LOGICAL_REWRITE_WRITE	LogicalRewriteWrite	"Waiting for a write of logical rewrite mappings."
+WAIT_EVENT_NREL_FLUSH	NRELFlush	"Initiating writeback of NREL data."
+WAIT_EVENT_NREL_READ	NRELRead	"Waiting for a read of an NREL page."
+WAIT_EVENT_NREL_SYNC	NRELSync	"Waiting for NREL data to reach durable storage following a page write."
+WAIT_EVENT_NREL_WRITE	NRELWrite	"Waiting for a write of an NREL page."
 WAIT_EVENT_RELATION_MAP_READ	RelationMapRead	"Waiting for a read of the relation map file."
 WAIT_EVENT_RELATION_MAP_REPLACE	RelationMapReplace	"Waiting for durable replacement of a relation map file."
 WAIT_EVENT_RELATION_MAP_WRITE	RelationMapWrite	"Waiting for a write to the relation map file."
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index d8f383b8631..d01b913d59d 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -15,6 +15,7 @@
 #include "postgres.h"
 
 #include "access/htup_details.h"
+#include "access/nrel.h"
 #include "access/slru.h"
 #include "access/xlog.h"
 #include "access/xlogprefetcher.h"
@@ -1521,30 +1522,30 @@ pg_stat_get_wal(PG_FUNCTION_ARGS)
 }
 
 /*
- * Returns statistics of SLRU caches.
+ * Returns statistics of NREL caches.
  */
 Datum
 pg_stat_get_slru(PG_FUNCTION_ARGS)
 {
-#define PG_STAT_GET_SLRU_COLS	9
+#define PG_STAT_GET_NREL_COLS	9
 	ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
 	int			i;
 	PgStat_SLRUStats *stats;
 
 	InitMaterializedSRF(fcinfo, 0);
 
-	/* request SLRU stats from the cumulative stats system */
+	/* request NREL stats from the cumulative stats system */
 	stats = pgstat_fetch_slru();
 
-	for (i = 0; i < SLRU_NUM_RELS; i++)
+	for (i = 0; i < NREL_NUM_RELS; i++)
 	{
 		/* for each row */
-		Datum		values[PG_STAT_GET_SLRU_COLS] = {0};
-		bool		nulls[PG_STAT_GET_SLRU_COLS] = {0};
+		Datum		values[PG_STAT_GET_NREL_COLS] = {0};
+		bool		nulls[PG_STAT_GET_NREL_COLS] = {0};
 		PgStat_SLRUStats stat;
 		const char *name;
 
-		name = SlruName(i);
+		name = NrelName(i);
 
 		stat = stats[i];
 
diff --git a/src/include/access/nrel.h b/src/include/access/nrel.h
new file mode 100644
index 00000000000..3c2a6e32bdf
--- /dev/null
+++ b/src/include/access/nrel.h
@@ -0,0 +1,112 @@
+/*-------------------------------------------------------------------------
+ *
+ * nrel.h
+ *		Buffering for transaction status logfiles
+ *
+ * Portions Copyright (c) 1996-2023, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/access/nrel.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef NREL_H
+#define NREL_H
+
+#include "access/xlogdefs.h"
+#include "catalog/pg_tablespace_d.h"
+#include "storage/buf.h"
+#include "storage/bufmgr.h"
+#include "storage/lwlock.h"
+#include "storage/smgr.h"
+#include "storage/sync.h"
+
+/* Pseudo database ID used for NREL data. */
+#define NREL_DB_ID 9
+
+/* Pseudo relation IDs used by each cache. */
+#define NREL_CLOG_REL_ID 0 
+#define NREL_MULTIXACT_OFFSET_REL_ID 1 
+#define NREL_MULTIXACT_MEMBER_REL_ID 2 
+#define NREL_SUBTRANS_REL_ID 3
+#define NREL_COMMITTS_REL_ID 4 
+#define NREL_SERIAL_REL_ID 5 
+#define NREL_NOTIFY_REL_ID 6 
+#define NREL_NUM_RELS 7 
+
+typedef bool (*NrelPagePrecedesFunction) (int, int);
+
+static inline RelFileLocator
+NrelRelFileLocator(RelFileNumber relFileNumber)
+{
+	RelFileLocator rlocator = {DEFAULTTABLESPACE_OID, NREL_DB_ID, relFileNumber};
+	return rlocator;
+}
+
+
+/*
+ * Define NREL segment size.  A page is the same BLCKSZ as is used everywhere
+ * else in Postgres.  The segment size can be chosen somewhat arbitrarily;
+ * we make it 32 pages by default, or 256Kb, i.e. 1M transactions for CLOG
+ * or 64K transactions for SUBTRANS.
+ *
+ * Note: because TransactionIds are 32 bits and wrap around at 0xFFFFFFFF,
+ * page numbering also wraps around at 0xFFFFFFFF/xxxx_XACTS_PER_PAGE (where
+ * xxxx is CLOG or SUBTRANS, respectively), and segment numbering at
+ * 0xFFFFFFFF/xxxx_XACTS_PER_PAGE/NREL_PAGES_PER_SEGMENT.  We need
+ * take no explicit notice of that fact in nrel.c, except when comparing
+ * segment and page numbers in NonRelTruncate (see PagePrecedes()).
+ */
+#define NREL_PAGES_PER_SEGMENT	32
+
+#ifdef USE_ASSERT_CHECKING
+extern void NrelPagePrecedesUnitTests(NrelPagePrecedesFunction PagePrecedes,
+									  int per_page);
+#else
+#define NrelPagePrecedesUnitTests(ctl, per_page) do {} while (0)
+#endif
+extern void NonRelTruncate(Oid rel_id, NrelPagePrecedesFunction PagePrecedes,
+							  int cutoffPage);
+extern bool NonRelDoesPhysicalPageExist(Oid rel_id, int pageno);
+
+typedef bool (*NrelScanCallback) (Oid rel_id,
+								  NrelPagePrecedesFunction PagePrecedes,
+								  char *filename, int segpage,
+								  void *data);
+extern bool NrelScanDirectory(Oid rel_id, NrelPagePrecedesFunction PagePrecedes,
+							  NrelScanCallback callback, void *data);
+extern void NrelDeleteSegment(Oid rel_id, int segno);
+
+/* NrelScanDirectory public callbacks */
+extern bool NrelScanDirCbReportPresence(Oid rel_id,
+										NrelPagePrecedesFunction PagePrecedes,
+										char *filename,
+										int segpage, void *data);
+extern bool NrelScanDirCbDeleteAll(Oid rel_id, NrelPagePrecedesFunction PagePrecedes,
+								   char *filename, int segpage,
+								   void *data);
+
+extern void CheckPointNREL(void);
+
+/* Buffer access */
+extern Buffer ReadNrelBuffer(Oid rel_id, int pageno);
+extern Buffer ZeroNrelBuffer(Oid rel_id, int pageno);
+
+/* Interfaces use by stats view */
+extern Oid NrelRelIdByName(const char *name);
+extern const char *NrelName(Oid rel_id);
+
+/* Sync callback */
+extern int	nrelsyncfiletag(const FileTag *ftag, char *path);
+
+/* SMGR callbacks */
+extern void nrelopen(SMgrRelation reln);
+extern void nrelclose(SMgrRelation reln, ForkNumber forknum);
+extern void nrelread(SMgrRelation reln, ForkNumber forknum,
+					 BlockNumber blocknum, char *buffer);
+extern void nrelwrite(SMgrRelation reln, ForkNumber forknum,
+					  BlockNumber blocknum, char *buffer, bool skipFsync);
+extern void nrelwriteback(SMgrRelation reln, ForkNumber forknum,
+						  BlockNumber blocknum, BlockNumber nblocks);
+
+#endif							/* NREL_H */
diff --git a/src/include/access/slru.h b/src/include/access/slru.h
index 3b9986f5664..1a9ac3f1924 100644
--- a/src/include/access/slru.h
+++ b/src/include/access/slru.h
@@ -1,7 +1,7 @@
 /*-------------------------------------------------------------------------
  *
  * slru.h
- *		Buffering for transaction status logfiles
+ *		Simple LRU buffering for transaction status logfiles
  *
  * Portions Copyright (c) 1996-2023, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
@@ -14,34 +14,10 @@
 #define SLRU_H
 
 #include "access/xlogdefs.h"
-#include "catalog/pg_tablespace_d.h"
-#include "storage/buf.h"
+
 #include "storage/lwlock.h"
-#include "storage/smgr.h"
 #include "storage/sync.h"
 
-/* Pseudo database ID used for SLRU data. */
-#define SLRU_DB_ID 9
-
-/* Pseudo relation IDs used by each cache. */
-#define SLRU_CLOG_REL_ID 0
-#define SLRU_MULTIXACT_OFFSET_REL_ID 1
-#define SLRU_MULTIXACT_MEMBER_REL_ID 2
-#define SLRU_SUBTRANS_REL_ID 3
-#define SLRU_SERIAL_REL_ID 4
-#define SLRU_COMMITTS_REL_ID 5
-#define SLRU_NOTIFY_REL_ID 6
-#define SLRU_NUM_RELS 7
-
-typedef bool (*SlruPagePrecedesFunction) (int, int);
-
-static inline RelFileLocator
-SlruRelFileLocator(RelFileNumber relFileNumber)
-{
-	RelFileLocator rlocator = {DEFAULTTABLESPACE_OID, SLRU_DB_ID, relFileNumber};
-	return rlocator;
-}
-
 
 /*
  * Define SLRU segment size.  A page is the same BLCKSZ as is used everywhere
@@ -58,54 +34,154 @@ SlruRelFileLocator(RelFileNumber relFileNumber)
  */
 #define SLRU_PAGES_PER_SEGMENT	32
 
+
+/*
+ * After we moved the built in SLRU components to the buffer cache, we still
+ * want to support custom SLRU extensions. This internal index is used for the
+ * statistics system to differentiate between non-relational components in nrel.c
+ * and user defined SLRUs. There are currently 7 enumerated non relational components, 
+ * and we use 0 for an invalid id.
+ * hence we use the value 8.
+ */
+#define SLRU_OTHER_ID 8 
+
+/*
+ * Page status codes.  Note that these do not include the "dirty" bit.
+ * page_dirty can be true only in the VALID or WRITE_IN_PROGRESS states;
+ * in the latter case it implies that the page has been re-dirtied since
+ * the write started.
+ */
+typedef enum
+{
+	SLRU_PAGE_EMPTY,			/* buffer is not in use */
+	SLRU_PAGE_READ_IN_PROGRESS, /* page is being read in */
+	SLRU_PAGE_VALID,			/* page is valid and not being written */
+	SLRU_PAGE_WRITE_IN_PROGRESS /* page is being written out */
+} SlruPageStatus;
+
+/*
+ * Shared-memory state
+ */
+typedef struct SlruSharedData
+{
+	LWLock	   *ControlLock;
+
+	/* Number of buffers managed by this SLRU structure */
+	int			num_slots;
+
+	/*
+	 * Arrays holding info for each buffer slot.  Page number is undefined
+	 * when status is EMPTY, as is page_lru_count.
+	 */
+	char	  **page_buffer;
+	SlruPageStatus *page_status;
+	bool	   *page_dirty;
+	int		   *page_number;
+	int		   *page_lru_count;
+	LWLockPadded *buffer_locks;
+
+	/*
+	 * Optional array of WAL flush LSNs associated with entries in the SLRU
+	 * pages.  If not zero/NULL, we must flush WAL before writing pages (true
+	 * for pg_xact, false for multixact, pg_subtrans, pg_notify).  group_lsn[]
+	 * has lsn_groups_per_page entries per buffer slot, each containing the
+	 * highest LSN known for a contiguous group of SLRU entries on that slot's
+	 * page.
+	 */
+	XLogRecPtr *group_lsn;
+	int			lsn_groups_per_page;
+
+	/*----------
+	 * We mark a page "most recently used" by setting
+	 *		page_lru_count[slotno] = ++cur_lru_count;
+	 * The oldest page is therefore the one with the highest value of
+	 *		cur_lru_count - page_lru_count[slotno]
+	 * The counts will eventually wrap around, but this calculation still
+	 * works as long as no page's age exceeds INT_MAX counts.
+	 *----------
+	 */
+	int			cur_lru_count;
+
+	/*
+	 * latest_page_number is the page number of the current end of the log;
+	 * this is not critical data, since we use it only to avoid swapping out
+	 * the latest page.
+	 */
+	int			latest_page_number;
+
+	/* SLRU's index for statistics purposes (might not be unique) */
+	int			slru_stats_idx;
+} SlruSharedData;
+
+typedef SlruSharedData *SlruShared;
+
+/*
+ * SlruCtlData is an unshared structure that points to the active information
+ * in shared memory.
+ */
+typedef struct SlruCtlData
+{
+	SlruShared	shared;
+
+	/*
+	 * Which sync handler function to use when handing sync requests over to
+	 * the checkpointer.  SYNC_HANDLER_NONE to disable fsync (eg pg_notify).
+	 */
+	SyncRequestHandler sync_handler;
+
+	/*
+	 * Decide whether a page is "older" for truncation and as a hint for
+	 * evicting pages in LRU order.  Return true if every entry of the first
+	 * argument is older than every entry of the second argument.  Note that
+	 * !PagePrecedes(a,b) && !PagePrecedes(b,a) need not imply a==b; it also
+	 * arises when some entries are older and some are not.  For SLRUs using
+	 * SimpleLruTruncate(), this must use modular arithmetic.  (For others,
+	 * the behavior of this callback has no functional implications.)  Use
+	 * SlruPagePrecedesUnitTests() in SLRUs meeting its criteria.
+	 */
+	bool		(*PagePrecedes) (int, int);
+
+	/*
+	 * Dir is set during SimpleLruInit and does not change thereafter. Since
+	 * it's always the same, it doesn't need to be in shared memory.
+	 */
+	char		Dir[64];
+} SlruCtlData;
+
+typedef SlruCtlData *SlruCtl;
+
+
+extern Size SimpleLruShmemSize(int nslots, int nlsns);
+extern void SimpleLruInit(SlruCtl ctl, const char *name, int nslots, int nlsns,
+						  LWLock *ctllock, const char *subdir, int tranche_id,
+						  SyncRequestHandler sync_handler);
+extern int	SimpleLruZeroPage(SlruCtl ctl, int pageno);
+extern int	SimpleLruReadPage(SlruCtl ctl, int pageno, bool write_ok,
+							  TransactionId xid);
+extern int	SimpleLruReadPage_ReadOnly(SlruCtl ctl, int pageno,
+									   TransactionId xid);
+extern void SimpleLruWritePage(SlruCtl ctl, int slotno);
+extern void SimpleLruWriteAll(SlruCtl ctl, bool allow_redirtied);
 #ifdef USE_ASSERT_CHECKING
-extern void SlruPagePrecedesUnitTests(SlruPagePrecedesFunction PagePrecedes,
-									  int per_page);
+extern void SlruPagePrecedesUnitTests(SlruCtl ctl, int per_page);
 #else
 #define SlruPagePrecedesUnitTests(ctl, per_page) do {} while (0)
 #endif
-extern void SimpleLruTruncate(Oid rel_id, SlruPagePrecedesFunction PagePrecedes,
-							  int cutoffPage);
-extern bool SimpleLruDoesPhysicalPageExist(Oid rel_id, int pageno);
+extern void SimpleLruTruncate(SlruCtl ctl, int cutoffPage);
+extern bool SimpleLruDoesPhysicalPageExist(SlruCtl ctl, int pageno);
 
-typedef bool (*SlruScanCallback) (Oid rel_id,
-								  SlruPagePrecedesFunction PagePrecedes,
-								  char *filename, int segpage,
+typedef bool (*SlruScanCallback) (SlruCtl ctl, char *filename, int segpage,
 								  void *data);
-extern bool SlruScanDirectory(Oid rel_id, SlruPagePrecedesFunction PagePrecedes,
-							  SlruScanCallback callback, void *data);
-extern void SlruDeleteSegment(Oid rel_id, int segno);
+extern bool SlruScanDirectory(SlruCtl ctl, SlruScanCallback callback, void *data);
+extern void SlruDeleteSegment(SlruCtl ctl, int segno);
+
+extern int	SlruSyncFileTag(SlruCtl ctl, const FileTag *ftag, char *path);
 
 /* SlruScanDirectory public callbacks */
-extern bool SlruScanDirCbReportPresence(Oid rel_id,
-										SlruPagePrecedesFunction PagePrecedes,
-										char *filename,
+extern bool SlruScanDirCbReportPresence(SlruCtl ctl, char *filename,
 										int segpage, void *data);
-extern bool SlruScanDirCbDeleteAll(Oid rel_id, SlruPagePrecedesFunction PagePrecedes,
-								   char *filename, int segpage,
+extern bool SlruScanDirCbDeleteAll(SlruCtl ctl, char *filename, int segpage,
 								   void *data);
 
-extern void CheckPointSLRU(void);
-
-/* Buffer access */
-extern Buffer ReadSlruBuffer(Oid rel_id, int pageno);
-extern Buffer ZeroSlruBuffer(Oid rel_id, int pageno);
-
-/* Interfaces use by stats view */
-extern Oid SlruRelIdByName(const char *name);
-extern const char *SlruName(Oid rel_id);
-
-/* Sync callback */
-extern int	slrusyncfiletag(const FileTag *ftag, char *path);
-
-/* SMGR callbacks */
-extern void slruopen(SMgrRelation reln);
-extern void slruclose(SMgrRelation reln, ForkNumber forknum);
-extern void slruread(SMgrRelation reln, ForkNumber forknum,
-					 BlockNumber blocknum, char *buffer);
-extern void slruwrite(SMgrRelation reln, ForkNumber forknum,
-					  BlockNumber blocknum, char *buffer, bool skipFsync);
-extern void slruwriteback(SMgrRelation reln, ForkNumber forknum,
-						  BlockNumber blocknum, BlockNumber nblocks);
 
 #endif							/* SLRU_H */
diff --git a/src/include/pgstat.h b/src/include/pgstat.h
index f7ee92b238f..eee7319aa94 100644
--- a/src/include/pgstat.h
+++ b/src/include/pgstat.h
@@ -682,8 +682,8 @@ extern void pgstat_count_slru_page_read(int slru_idx);
 extern void pgstat_count_slru_page_written(int slru_idx);
 extern void pgstat_count_slru_page_exists(int slru_idx);
 extern void pgstat_count_slru_flush(int slru_idx);
-extern void pgstat_count_slru_truncate(int slru_idx);
-extern const char *pgstat_get_slru_name(int slru_idx);
+extern void pgstat_count_slru_truncate(int nrel_idx);
+extern const char *pgstat_get_nrel_name(int slru_idx);
 extern int	pgstat_get_slru_index(const char *name);
 extern PgStat_SLRUStats *pgstat_fetch_slru(void);
 
diff --git a/src/include/storage/smgr.h b/src/include/storage/smgr.h
index e36aace0bda..2c83f7b1b40 100644
--- a/src/include/storage/smgr.h
+++ b/src/include/storage/smgr.h
@@ -73,8 +73,8 @@ typedef struct SMgrRelationData
 	 * for slru.c; we'll just cache one File for now.
 	 * XXX Unionize with md stuff somehow so we don't waste space
 	 */
-	int			slru_file_segment;
-	File		slru_file;
+	int			nrel_file_segment;
+	File		nrel_file;
 
 	/* if unowned, list link in list of all unowned SMgrRelations */
 	dlist_node	node;
diff --git a/src/include/storage/sync.h b/src/include/storage/sync.h
index eac5cbf86b3..69f4035847d 100644
--- a/src/include/storage/sync.h
+++ b/src/include/storage/sync.h
@@ -35,7 +35,8 @@ typedef enum SyncRequestType
 typedef enum SyncRequestHandler
 {
 	SYNC_HANDLER_MD = 0,
-	SYNC_HANDLER_SLRU
+	SYNC_HANDLER_NREL,
+	SYNC_HANDLER_NONE
 } SyncRequestHandler;
 
 /*
diff --git a/src/include/utils/memutils.h b/src/include/utils/memutils.h
index c4b1a6f08de..21640d62a64 100644
--- a/src/include/utils/memutils.h
+++ b/src/include/utils/memutils.h
@@ -62,7 +62,6 @@ extern PGDLLIMPORT MemoryContext CacheMemoryContext;
 extern PGDLLIMPORT MemoryContext MessageContext;
 extern PGDLLIMPORT MemoryContext TopTransactionContext;
 extern PGDLLIMPORT MemoryContext CurTransactionContext;
-extern PGDLLIMPORT MemoryContext SlruMemoryContext;
 
 /* This is a transient link to the active portal's memory context: */
 extern PGDLLIMPORT MemoryContext PortalContext;
diff --git a/src/include/utils/pgstat_internal.h b/src/include/utils/pgstat_internal.h
index 425678daa3d..42f418ba6dc 100644
--- a/src/include/utils/pgstat_internal.h
+++ b/src/include/utils/pgstat_internal.h
@@ -14,6 +14,7 @@
 #define PGSTAT_INTERNAL_H
 
 
+#include "access/nrel.h"
 #include "access/slru.h"
 #include "common/hashfn.h"
 #include "lib/dshash.h"
@@ -323,7 +324,7 @@ typedef struct PgStatShared_SLRU
 {
 	/* lock protects ->stats */
 	LWLock		lock;
-	PgStat_SLRUStats stats[SLRU_NUM_RELS];
+	PgStat_SLRUStats stats[NREL_NUM_RELS];
 } PgStatShared_SLRU;
 
 typedef struct PgStatShared_Wal
@@ -435,7 +436,7 @@ typedef struct PgStat_Snapshot
 
 	PgStat_IO	io;
 
-	PgStat_SLRUStats slru[SLRU_NUM_RELS];
+	PgStat_SLRUStats slru[NREL_NUM_RELS];
 
 	PgStat_WalStats wal;
 
-- 
2.40.1

