]> git.ipfire.org Git - thirdparty/postgresql.git/commitdiff
Add callback for I/O error messages in SLRUs
authorHeikki Linnakangas <heikki.linnakangas@iki.fi>
Fri, 13 Mar 2026 14:21:06 +0000 (16:21 +0200)
committerHeikki Linnakangas <heikki.linnakangas@iki.fi>
Fri, 13 Mar 2026 14:21:06 +0000 (16:21 +0200)
Historically, all SLRUs were addressed by transaction IDs, but that
hasn't been true for a long time. However, the error message on I/O
error still always talked about accessing a transaction ID.

This commit adds a callback that allows subsystems to construct their
own error messages, which can then correctly refer to a transaction
ID, multixid or whatever else is used to address the particular SLRU.

Author: Maxim Orlov <orlovmg@gmail.com>
Reviewed-by: Álvaro Herrera <alvherre@kurilemu.de>
Discussion: https://www.postgresql.org/message-id/CACG=ezZZfurhYV+66ceubxQAyWqv9vaUi0yoO4-t48OE5xc0DQ@mail.gmail.com

13 files changed:
src/backend/access/transam/clog.c
src/backend/access/transam/commit_ts.c
src/backend/access/transam/multixact.c
src/backend/access/transam/slru.c
src/backend/access/transam/subtrans.c
src/backend/commands/async.c
src/backend/storage/lmgr/predicate.c
src/include/access/slru.h
src/test/modules/test_slru/expected/test_slru.out
src/test/modules/test_slru/sql/test_slru.sql
src/test/modules/test_slru/test_slru--1.0.sql
src/test/modules/test_slru/test_slru.c
src/tools/pgindent/typedefs.list

index 3ee1cfdbc24939ba8eee5ad0b0ea616d5bf79b47..c654e0929b32cddbdb86e53bfd759b950365b6cd 100644 (file)
@@ -112,6 +112,7 @@ static SlruCtlData XactCtlData;
 
 
 static bool CLOGPagePrecedes(int64 page1, int64 page2);
+static int     clog_errdetail_for_io_error(const void *opaque_data);
 static void WriteTruncateXlogRec(int64 pageno, TransactionId oldestXact,
                                                                 Oid oldestXactDb);
 static void TransactionIdSetPageStatus(TransactionId xid, int nsubxids,
@@ -382,8 +383,7 @@ TransactionIdSetPageStatusInternal(TransactionId xid, int nsubxids,
         * write-busy, since we don't care if the update reaches disk sooner than
         * we think.
         */
-       slotno = SimpleLruReadPage(XactCtl, pageno, !XLogRecPtrIsValid(lsn),
-                                                          xid);
+       slotno = SimpleLruReadPage(XactCtl, pageno, !XLogRecPtrIsValid(lsn), &xid);
 
        /*
         * Set the main transaction id, if any.
@@ -744,7 +744,7 @@ TransactionIdGetStatus(TransactionId xid, XLogRecPtr *lsn)
 
        /* lock is acquired by SimpleLruReadPage_ReadOnly */
 
-       slotno = SimpleLruReadPage_ReadOnly(XactCtl, pageno, xid);
+       slotno = SimpleLruReadPage_ReadOnly(XactCtl, pageno, &xid);
        byteptr = XactCtl->shared->page_buffer[slotno] + byteno;
 
        status = (*byteptr >> bshift) & CLOG_XACT_BITMASK;
@@ -808,6 +808,7 @@ CLOGShmemInit(void)
        Assert(transaction_buffers != 0);
 
        XactCtl->PagePrecedes = CLOGPagePrecedes;
+       XactCtl->errdetail_for_io_error = clog_errdetail_for_io_error;
        SimpleLruInit(XactCtl, "transaction", CLOGShmemBuffers(), CLOG_LSNS_PER_PAGE,
                                  "pg_xact", LWTRANCHE_XACT_BUFFER,
                                  LWTRANCHE_XACT_SLRU, SYNC_HANDLER_CLOG, false);
@@ -883,7 +884,7 @@ TrimCLOG(void)
                int                     slotno;
                char       *byteptr;
 
-               slotno = SimpleLruReadPage(XactCtl, pageno, false, xid);
+               slotno = SimpleLruReadPage(XactCtl, pageno, false, &xid);
                byteptr = XactCtl->shared->page_buffer[slotno] + byteno;
 
                /* Zero so-far-unused positions in the current byte */
@@ -1034,6 +1035,14 @@ CLOGPagePrecedes(int64 page1, int64 page2)
                        TransactionIdPrecedes(xid1, xid2 + CLOG_XACTS_PER_PAGE - 1));
 }
 
+static int
+clog_errdetail_for_io_error(const void *opaque_data)
+{
+       TransactionId xid = *(const TransactionId *) opaque_data;
+
+       return errdetail("Could not access commit status of transaction %u.", xid);
+}
+
 
 /*
  * Write a TRUNCATE xlog record
index 6fa2178f1dd8057d13d74cef65cc5f2a875fd977..36219dd13ccceb249d1f54d0a2b2376c2610f805 100644 (file)
@@ -115,6 +115,7 @@ static void TransactionIdSetCommitTs(TransactionId xid, TimestampTz ts,
                                                                         ReplOriginId nodeid, int slotno);
 static void error_commit_ts_disabled(void);
 static bool CommitTsPagePrecedes(int64 page1, int64 page2);
+static int     commit_ts_errdetail_for_io_error(const void *opaque_data);
 static void ActivateCommitTs(void);
 static void DeactivateCommitTs(void);
 static void WriteTruncateXlogRec(int64 pageno, TransactionId oldestXid);
@@ -227,7 +228,7 @@ SetXidCommitTsInPage(TransactionId xid, int nsubxids,
 
        LWLockAcquire(lock, LW_EXCLUSIVE);
 
-       slotno = SimpleLruReadPage(CommitTsCtl, pageno, true, xid);
+       slotno = SimpleLruReadPage(CommitTsCtl, pageno, true, &xid);
 
        TransactionIdSetCommitTs(xid, ts, nodeid, slotno);
        for (i = 0; i < nsubxids; i++)
@@ -332,7 +333,7 @@ TransactionIdGetCommitTsData(TransactionId xid, TimestampTz *ts,
        }
 
        /* lock is acquired by SimpleLruReadPage_ReadOnly */
-       slotno = SimpleLruReadPage_ReadOnly(CommitTsCtl, pageno, xid);
+       slotno = SimpleLruReadPage_ReadOnly(CommitTsCtl, pageno, &xid);
        memcpy(&entry,
                   CommitTsCtl->shared->page_buffer[slotno] +
                   SizeOfCommitTimestampEntry * entryno,
@@ -551,6 +552,7 @@ CommitTsShmemInit(void)
        Assert(commit_timestamp_buffers != 0);
 
        CommitTsCtl->PagePrecedes = CommitTsPagePrecedes;
+       CommitTsCtl->errdetail_for_io_error = commit_ts_errdetail_for_io_error;
        SimpleLruInit(CommitTsCtl, "commit_timestamp", CommitTsShmemBuffers(), 0,
                                  "pg_commit_ts", LWTRANCHE_COMMITTS_BUFFER,
                                  LWTRANCHE_COMMITTS_SLRU,
@@ -959,6 +961,13 @@ CommitTsPagePrecedes(int64 page1, int64 page2)
                        TransactionIdPrecedes(xid1, xid2 + COMMIT_TS_XACTS_PER_PAGE - 1));
 }
 
+static int
+commit_ts_errdetail_for_io_error(const void *opaque_data)
+{
+       TransactionId xid = *(const TransactionId *) opaque_data;
+
+       return errdetail("Could not access commit timestamp of transaction %u.", xid);
+}
 
 /*
  * Write a TRUNCATE xlog record
index 8a5c9818ed6192b59b3a7a497540ada075b9cbf1..34d7b586bec5914a6b7aacac12bcd6852a214d62 100644 (file)
@@ -313,8 +313,18 @@ static void mXactCachePut(MultiXactId multi, int nmembers,
                                                  MultiXactMember *members);
 
 /* management of SLRU infrastructure */
+
+/* opaque_data type for MultiXactMemberIoErrorDetail */
+typedef struct MultiXactMemberSlruReadContext
+{
+       MultiXactId multi;
+       MultiXactOffset offset;
+} MultiXactMemberSlruReadContext;
+
 static bool MultiXactOffsetPagePrecedes(int64 page1, int64 page2);
 static bool MultiXactMemberPagePrecedes(int64 page1, int64 page2);
+static int     MultiXactOffsetIoErrorDetail(const void *opaque_data);
+static int     MultiXactMemberIoErrorDetail(const void *opaque_data);
 static void ExtendMultiXactOffset(MultiXactId multi);
 static void ExtendMultiXactMember(MultiXactOffset offset, int nmembers);
 static void SetOldestOffset(void);
@@ -829,14 +839,7 @@ RecordNewMultiXact(MultiXactId multi, MultiXactOffset offset,
        lock = SimpleLruGetBankLock(MultiXactOffsetCtl, pageno);
        LWLockAcquire(lock, LW_EXCLUSIVE);
 
-       /*
-        * Note: we pass the MultiXactId to SimpleLruReadPage as the "transaction"
-        * to complain about if there's any I/O error.  This is kinda bogus, but
-        * since the errors will always give the full pathname, it should be clear
-        * enough that a MultiXactId is really involved.  Perhaps someday we'll
-        * take the trouble to generalize the slru.c error reporting code.
-        */
-       slotno = SimpleLruReadPage(MultiXactOffsetCtl, pageno, true, multi);
+       slotno = SimpleLruReadPage(MultiXactOffsetCtl, pageno, true, &multi);
        offptr = (MultiXactOffset *) MultiXactOffsetCtl->shared->page_buffer[slotno];
        offptr += entryno;
 
@@ -865,7 +868,7 @@ RecordNewMultiXact(MultiXactId multi, MultiXactOffset offset,
                lock = SimpleLruGetBankLock(MultiXactOffsetCtl, next_pageno);
                LWLockAcquire(lock, LW_EXCLUSIVE);
 
-               slotno = SimpleLruReadPage(MultiXactOffsetCtl, next_pageno, true, next);
+               slotno = SimpleLruReadPage(MultiXactOffsetCtl, next_pageno, true, &next);
                next_offptr = (MultiXactOffset *) MultiXactOffsetCtl->shared->page_buffer[slotno];
                next_offptr += next_entryno;
        }
@@ -905,6 +908,8 @@ RecordNewMultiXact(MultiXactId multi, MultiXactOffset offset,
 
                if (pageno != prev_pageno)
                {
+                       MultiXactMemberSlruReadContext slru_read_context = {multi, offset};
+
                        /*
                         * MultiXactMember SLRU page is changed so check if this new page
                         * fall into the different SLRU bank then release the old bank's
@@ -919,7 +924,8 @@ RecordNewMultiXact(MultiXactId multi, MultiXactOffset offset,
                                LWLockAcquire(lock, LW_EXCLUSIVE);
                                prevlock = lock;
                        }
-                       slotno = SimpleLruReadPage(MultiXactMemberCtl, pageno, true, multi);
+                       slotno = SimpleLruReadPage(MultiXactMemberCtl, pageno, true,
+                                                                          &slru_read_context);
                        prev_pageno = pageno;
                }
 
@@ -1244,7 +1250,7 @@ GetMultiXactIdMembers(MultiXactId multi, MultiXactMember **members,
        LWLockAcquire(lock, LW_EXCLUSIVE);
 
        /* read this multi's offset */
-       slotno = SimpleLruReadPage(MultiXactOffsetCtl, pageno, true, multi);
+       slotno = SimpleLruReadPage(MultiXactOffsetCtl, pageno, true, &multi);
        offptr = (MultiXactOffset *) MultiXactOffsetCtl->shared->page_buffer[slotno];
        offptr += entryno;
        offset = *offptr;
@@ -1282,7 +1288,7 @@ GetMultiXactIdMembers(MultiXactId multi, MultiXactMember **members,
                                LWLockAcquire(newlock, LW_EXCLUSIVE);
                                lock = newlock;
                        }
-                       slotno = SimpleLruReadPage(MultiXactOffsetCtl, pageno, true, tmpMXact);
+                       slotno = SimpleLruReadPage(MultiXactOffsetCtl, pageno, true, &tmpMXact);
                }
 
                offptr = (MultiXactOffset *) MultiXactOffsetCtl->shared->page_buffer[slotno];
@@ -1331,6 +1337,7 @@ GetMultiXactIdMembers(MultiXactId multi, MultiXactMember **members,
 
                if (pageno != prev_pageno)
                {
+                       MultiXactMemberSlruReadContext slru_read_context = {multi, offset};
                        LWLock     *newlock;
 
                        /*
@@ -1346,8 +1353,8 @@ GetMultiXactIdMembers(MultiXactId multi, MultiXactMember **members,
                                LWLockAcquire(newlock, LW_EXCLUSIVE);
                                lock = newlock;
                        }
-
-                       slotno = SimpleLruReadPage(MultiXactMemberCtl, pageno, true, multi);
+                       slotno = SimpleLruReadPage(MultiXactMemberCtl, pageno, true,
+                                                                          &slru_read_context);
                        prev_pageno = pageno;
                }
 
@@ -1778,6 +1785,8 @@ MultiXactShmemInit(void)
 
        MultiXactOffsetCtl->PagePrecedes = MultiXactOffsetPagePrecedes;
        MultiXactMemberCtl->PagePrecedes = MultiXactMemberPagePrecedes;
+       MultiXactOffsetCtl->errdetail_for_io_error = MultiXactOffsetIoErrorDetail;
+       MultiXactMemberCtl->errdetail_for_io_error = MultiXactMemberIoErrorDetail;
 
        SimpleLruInit(MultiXactOffsetCtl,
                                  "multixact_offset", multixact_offset_buffers, 0,
@@ -1928,7 +1937,7 @@ TrimMultiXact(void)
                if (entryno == 0 || nextMXact == FirstMultiXactId)
                        slotno = SimpleLruZeroPage(MultiXactOffsetCtl, pageno);
                else
-                       slotno = SimpleLruReadPage(MultiXactOffsetCtl, pageno, true, nextMXact);
+                       slotno = SimpleLruReadPage(MultiXactOffsetCtl, pageno, true, &nextMXact);
                offptr = (MultiXactOffset *) MultiXactOffsetCtl->shared->page_buffer[slotno];
                offptr += entryno;
 
@@ -1956,6 +1965,7 @@ TrimMultiXact(void)
        flagsoff = MXOffsetToFlagsOffset(offset);
        if (flagsoff != 0)
        {
+               MultiXactMemberSlruReadContext slru_read_context = {InvalidMultiXactId, offset};
                int                     slotno;
                TransactionId *xidptr;
                int                     memberoff;
@@ -1963,7 +1973,7 @@ TrimMultiXact(void)
 
                LWLockAcquire(lock, LW_EXCLUSIVE);
                memberoff = MXOffsetToMemberOffset(offset);
-               slotno = SimpleLruReadPage(MultiXactMemberCtl, pageno, true, offset);
+               slotno = SimpleLruReadPage(MultiXactMemberCtl, pageno, true, &slru_read_context);
                xidptr = (TransactionId *)
                        (MultiXactMemberCtl->shared->page_buffer[slotno] + memberoff);
 
@@ -2497,7 +2507,7 @@ find_multixact_start(MultiXactId multi, MultiXactOffset *result)
                return false;
 
        /* lock is acquired by SimpleLruReadPage_ReadOnly */
-       slotno = SimpleLruReadPage_ReadOnly(MultiXactOffsetCtl, pageno, multi);
+       slotno = SimpleLruReadPage_ReadOnly(MultiXactOffsetCtl, pageno, &multi);
        offptr = (MultiXactOffset *) MultiXactOffsetCtl->shared->page_buffer[slotno];
        offptr += entryno;
        offset = *offptr;
@@ -2808,6 +2818,27 @@ MultiXactMemberPagePrecedes(int64 page1, int64 page2)
        return page1 < page2;
 }
 
+static int
+MultiXactOffsetIoErrorDetail(const void *opaque_data)
+{
+       MultiXactId multixid = *(const MultiXactId *) opaque_data;
+
+       return errdetail("Could not access offset of multixact %u.", multixid);
+}
+
+static int
+MultiXactMemberIoErrorDetail(const void *opaque_data)
+{
+       const MultiXactMemberSlruReadContext *context = opaque_data;
+
+       if (MultiXactIdIsValid(context->multi))
+               return errdetail("Could not access member of multixact %u at offset %" PRIu64 ".",
+                                                context->multi, context->offset);
+       else
+               return errdetail("Could not access multixact member at offset %" PRIu64 ".",
+                                                context->offset);
+}
+
 /*
  * Decide which of two MultiXactIds is earlier.
  *
index 556edcbf7cbd007f27355cc1c19186c7de8146cb..a2bb8fa80338c0b50e3c4079bdd069d2b87af823 100644 (file)
@@ -182,7 +182,8 @@ static void SlruInternalWritePage(SlruCtl ctl, int slotno, SlruWriteAll fdata);
 static bool SlruPhysicalReadPage(SlruCtl ctl, int64 pageno, int slotno);
 static bool SlruPhysicalWritePage(SlruCtl ctl, int64 pageno, int slotno,
                                                                  SlruWriteAll fdata);
-static void SlruReportIOError(SlruCtl ctl, int64 pageno, TransactionId xid);
+static void SlruReportIOError(SlruCtl ctl, int64 pageno,
+                                                         const void *opaque_data);
 static int     SlruSelectLRUPage(SlruCtl ctl, int64 pageno);
 
 static bool SlruScanDirCbDeleteCutoff(SlruCtl ctl, char *filename,
@@ -260,6 +261,9 @@ SimpleLruInit(SlruCtl ctl, const char *name, int nslots, int nlsns,
 
        Assert(nslots <= SLRU_MAX_ALLOWED_BUFFERS);
 
+       Assert(ctl->PagePrecedes != NULL);
+       Assert(ctl->errdetail_for_io_error != NULL);
+
        shared = (SlruShared) ShmemInitStruct(name,
                                                                                  SimpleLruShmemSize(nslots, nlsns),
                                                                                  &found);
@@ -516,8 +520,9 @@ SimpleLruWaitIO(SlruCtl ctl, int slotno)
  * 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.
+ * On error, the passed-in 'opaque_data' is passed to the
+ * 'errdetail_for_io_error' callback, to provide details on the operation that
+ * failed.  It is only used for error reporting.
  *
  * Return value is the shared-buffer slot number now holding the page.
  * The buffer's LRU access info is updated.
@@ -526,7 +531,7 @@ SimpleLruWaitIO(SlruCtl ctl, int slotno)
  */
 int
 SimpleLruReadPage(SlruCtl ctl, int64 pageno, bool write_ok,
-                                 TransactionId xid)
+                                 const void *opaque_data)
 {
        SlruShared      shared = ctl->shared;
        LWLock     *banklock = SimpleLruGetBankLock(ctl, pageno);
@@ -602,7 +607,7 @@ SimpleLruReadPage(SlruCtl ctl, int64 pageno, bool write_ok,
 
                /* Now it's okay to ereport if we failed */
                if (!ok)
-                       SlruReportIOError(ctl, pageno, xid);
+                       SlruReportIOError(ctl, pageno, opaque_data);
 
                SlruRecentlyUsed(shared, slotno);
 
@@ -618,8 +623,9 @@ SimpleLruReadPage(SlruCtl ctl, int64 pageno, bool write_ok,
  * 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.
+ * On error, the passed-in 'opaque_data' is passed to the
+ * 'errdetail_for_io_error' callback, to provide details on the operation that
+ * failed.  It is only used for error reporting.
  *
  * Return value is the shared-buffer slot number now holding the page.
  * The buffer's LRU access info is updated.
@@ -628,7 +634,7 @@ SimpleLruReadPage(SlruCtl ctl, int64 pageno, bool write_ok,
  * It is unspecified whether the lock will be shared or exclusive.
  */
 int
-SimpleLruReadPage_ReadOnly(SlruCtl ctl, int64 pageno, TransactionId xid)
+SimpleLruReadPage_ReadOnly(SlruCtl ctl, int64 pageno, const void *opaque_data)
 {
        SlruShared      shared = ctl->shared;
        LWLock     *banklock = SimpleLruGetBankLock(ctl, pageno);
@@ -660,7 +666,7 @@ SimpleLruReadPage_ReadOnly(SlruCtl ctl, int64 pageno, TransactionId xid)
        LWLockRelease(banklock);
        LWLockAcquire(banklock, LW_EXCLUSIVE);
 
-       return SimpleLruReadPage(ctl, pageno, true, xid);
+       return SimpleLruReadPage(ctl, pageno, true, opaque_data);
 }
 
 /*
@@ -740,7 +746,7 @@ SlruInternalWritePage(SlruCtl ctl, int slotno, SlruWriteAll fdata)
 
        /* Now it's okay to ereport if we failed */
        if (!ok)
-               SlruReportIOError(ctl, pageno, InvalidTransactionId);
+               SlruReportIOError(ctl, pageno, NULL);
 
        /* If part of a checkpoint, count this as a SLRU buffer written. */
        if (fdata)
@@ -794,14 +800,14 @@ SimpleLruDoesPhysicalPageExist(SlruCtl ctl, int64 pageno)
                /* report error normally */
                slru_errcause = SLRU_OPEN_FAILED;
                slru_errno = errno;
-               SlruReportIOError(ctl, pageno, 0);
+               SlruReportIOError(ctl, pageno, NULL);
        }
 
        if ((endpos = lseek(fd, 0, SEEK_END)) < 0)
        {
                slru_errcause = SLRU_SEEK_FAILED;
                slru_errno = errno;
-               SlruReportIOError(ctl, pageno, 0);
+               SlruReportIOError(ctl, pageno, NULL);
        }
 
        result = endpos >= (off_t) (offset + BLCKSZ);
@@ -1071,7 +1077,7 @@ SlruPhysicalWritePage(SlruCtl ctl, int64 pageno, int slotno, SlruWriteAll fdata)
  * SlruPhysicalWritePage.  Call this after cleaning up shared-memory state.
  */
 static void
-SlruReportIOError(SlruCtl ctl, int64 pageno, TransactionId xid)
+SlruReportIOError(SlruCtl ctl, int64 pageno, const void *opaque_data)
 {
        int64           segno = pageno / SLRU_PAGES_PER_SEGMENT;
        int                     rpageno = pageno % SLRU_PAGES_PER_SEGMENT;
@@ -1085,54 +1091,55 @@ SlruReportIOError(SlruCtl ctl, int64 pageno, TransactionId xid)
                case SLRU_OPEN_FAILED:
                        ereport(ERROR,
                                        (errcode_for_file_access(),
-                                        errmsg("could not access status of transaction %u", xid),
-                                        errdetail("Could not open file \"%s\": %m.", path)));
+                                        errmsg("could not open file \"%s\": %m", path),
+                                        opaque_data ? ctl->errdetail_for_io_error(opaque_data) : 0));
                        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)));
+                                        errmsg("could not seek in file \"%s\" to offset %d: %m",
+                                                       path, offset),
+                                        opaque_data ? ctl->errdetail_for_io_error(opaque_data) : 0));
                        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)));
+                                                errmsg("could not read from file \"%s\" at offset %d: %m",
+                                                               path, offset),
+                                                opaque_data ? ctl->errdetail_for_io_error(opaque_data) : 0));
                        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)));
+                                               (errmsg("could not read from file \"%s\" at offset %d: read too few bytes",
+                                                               path, offset),
+                                                opaque_data ? ctl->errdetail_for_io_error(opaque_data) : 0));
                        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)));
+                                                errmsg("Could not write to file \"%s\" at offset %d: %m",
+                                                               path, offset),
+                                                opaque_data ? ctl->errdetail_for_io_error(opaque_data) : 0));
                        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)));
+                                               (errmsg("Could not write to file \"%s\" at offset %d: wrote too few bytes.",
+                                                               path, offset),
+                                                opaque_data ? ctl->errdetail_for_io_error(opaque_data) : 0));
                        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)));
+                                        errmsg("could not fsync file \"%s\": %m",
+                                                       path),
+                                        opaque_data ? ctl->errdetail_for_io_error(opaque_data) : 0));
                        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)));
+                                        errmsg("could not close file \"%s\": %m",
+                                                       path),
+                                        opaque_data ? ctl->errdetail_for_io_error(opaque_data) : 0));
                        break;
                default:
                        /* can't get here, we trust */
@@ -1412,7 +1419,7 @@ SimpleLruWriteAll(SlruCtl ctl, bool allow_redirtied)
                }
        }
        if (!ok)
-               SlruReportIOError(ctl, pageno, InvalidTransactionId);
+               SlruReportIOError(ctl, pageno, NULL);
 
        /* Ensure that directory entries for new files are on disk. */
        if (ctl->sync_handler != SYNC_HANDLER_NONE)
index c0987f43f11cbb5e7608eca7b73c749f6a6b8363..c6ce71fc703f5199ae9ca320377d326cefc29799 100644 (file)
@@ -75,6 +75,7 @@ static SlruCtlData SubTransCtlData;
 
 
 static bool SubTransPagePrecedes(int64 page1, int64 page2);
+static int     subtrans_errdetail_for_io_error(const void *opaque_data);
 
 
 /*
@@ -95,7 +96,7 @@ SubTransSetParent(TransactionId xid, TransactionId parent)
        lock = SimpleLruGetBankLock(SubTransCtl, pageno);
        LWLockAcquire(lock, LW_EXCLUSIVE);
 
-       slotno = SimpleLruReadPage(SubTransCtl, pageno, true, xid);
+       slotno = SimpleLruReadPage(SubTransCtl, pageno, true, &xid);
        ptr = (TransactionId *) SubTransCtl->shared->page_buffer[slotno];
        ptr += entryno;
 
@@ -135,7 +136,7 @@ SubTransGetParent(TransactionId xid)
 
        /* lock is acquired by SimpleLruReadPage_ReadOnly */
 
-       slotno = SimpleLruReadPage_ReadOnly(SubTransCtl, pageno, xid);
+       slotno = SimpleLruReadPage_ReadOnly(SubTransCtl, pageno, &xid);
        ptr = (TransactionId *) SubTransCtl->shared->page_buffer[slotno];
        ptr += entryno;
 
@@ -240,6 +241,7 @@ SUBTRANSShmemInit(void)
        Assert(subtransaction_buffers != 0);
 
        SubTransCtl->PagePrecedes = SubTransPagePrecedes;
+       SubTransCtl->errdetail_for_io_error = subtrans_errdetail_for_io_error;
        SimpleLruInit(SubTransCtl, "subtransaction", SUBTRANSShmemBuffers(), 0,
                                  "pg_subtrans", LWTRANCHE_SUBTRANS_BUFFER,
                                  LWTRANCHE_SUBTRANS_SLRU, SYNC_HANDLER_NONE, false);
@@ -419,3 +421,11 @@ SubTransPagePrecedes(int64 page1, int64 page2)
        return (TransactionIdPrecedes(xid1, xid2) &&
                        TransactionIdPrecedes(xid1, xid2 + SUBTRANS_XACTS_PER_PAGE - 1));
 }
+
+static int
+subtrans_errdetail_for_io_error(const void *opaque_data)
+{
+       TransactionId xid = *(const TransactionId *) opaque_data;
+
+       return errdetail("Could not access subtransaction status of transaction %u.", xid);
+}
index 0b6d119dad02b05ec1631123c89bfbea86ccc3cc..5c9a56c3d40782ceaf079c98d3d3acf30f0312cb 100644 (file)
@@ -570,6 +570,7 @@ bool                Trace_notify = false;
 int                    max_notify_queue_pages = 1048576;
 
 /* local function prototypes */
+static int     asyncQueueErrdetailForIoError(const void *opaque_data);
 static inline int64 asyncQueuePageDiff(int64 p, int64 q);
 static inline bool asyncQueuePagePrecedes(int64 p, int64 q);
 static inline void GlobalChannelKeyInit(GlobalChannelKey *key, Oid dboid,
@@ -610,6 +611,15 @@ static uint32 notification_hash(const void *key, Size keysize);
 static int     notification_match(const void *key1, const void *key2, Size keysize);
 static void ClearPendingActionsAndNotifies(void);
 
+static int
+asyncQueueErrdetailForIoError(const void *opaque_data)
+{
+       const QueuePosition *position = opaque_data;
+
+       return errdetail("Could not access async queue at page %" PRId64 ", offset %d.",
+                                        position->page, position->offset);
+}
+
 /*
  * Compute the difference between two queue page numbers.
  * Previously this function accounted for a wraparound.
@@ -830,6 +840,7 @@ AsyncShmemInit(void)
         * names are used in order to avoid wraparound.
         */
        NotifyCtl->PagePrecedes = asyncQueuePagePrecedes;
+       NotifyCtl->errdetail_for_io_error = asyncQueueErrdetailForIoError;
        SimpleLruInit(NotifyCtl, "notify", notify_buffers, 0,
                                  "pg_notify", LWTRANCHE_NOTIFY_BUFFER, LWTRANCHE_NOTIFY_SLRU,
                                  SYNC_HANDLER_NONE, true);
@@ -2068,8 +2079,7 @@ asyncQueueAddEntries(ListCell *nextNotify)
        if (QUEUE_POS_IS_ZERO(queue_head))
                slotno = SimpleLruZeroPage(NotifyCtl, pageno);
        else
-               slotno = SimpleLruReadPage(NotifyCtl, pageno, true,
-                                                                  InvalidTransactionId);
+               slotno = SimpleLruReadPage(NotifyCtl, pageno, true, &queue_head);
 
        /* Note we mark the page dirty before writing in it */
        NotifyCtl->shared->page_dirty[slotno] = true;
@@ -2739,8 +2749,7 @@ asyncQueueProcessPageEntries(QueuePosition *current,
        alignas(AsyncQueueEntry) char local_buf[QUEUE_PAGESIZE];
        char       *local_buf_end = local_buf;
 
-       slotno = SimpleLruReadPage_ReadOnly(NotifyCtl, curpage,
-                                                                               InvalidTransactionId);
+       slotno = SimpleLruReadPage_ReadOnly(NotifyCtl, curpage, current);
        page_buffer = NotifyCtl->shared->page_buffer[slotno];
 
        do
@@ -2998,8 +3007,7 @@ AsyncNotifyFreezeXids(TransactionId newFrozenXid)
 
                        lock = SimpleLruGetBankLock(NotifyCtl, pageno);
                        LWLockAcquire(lock, LW_EXCLUSIVE);
-                       slotno = SimpleLruReadPage(NotifyCtl, pageno, true,
-                                                                          InvalidTransactionId);
+                       slotno = SimpleLruReadPage(NotifyCtl, pageno, true, &pos);
                        page_buffer = NotifyCtl->shared->page_buffer[slotno];
                        curpage = pageno;
                }
index 548b4f66470497496d90e82281290e1c6d653d84..edabbf4ca3137884c0392ab1eb934cb85647cff4 100644 (file)
@@ -443,6 +443,7 @@ static void ReleaseRWConflict(RWConflict conflict);
 static void FlagSxactUnsafe(SERIALIZABLEXACT *sxact);
 
 static bool SerialPagePrecedesLogically(int64 page1, int64 page2);
+static int     serial_errdetail_for_io_error(const void *opaque_data);
 static void SerialInit(void);
 static void SerialAdd(TransactionId xid, SerCommitSeqNo minConflictCommitSeqNo);
 static SerCommitSeqNo SerialGetMinConflictCommitSeqNo(TransactionId xid);
@@ -743,6 +744,14 @@ SerialPagePrecedesLogically(int64 page1, int64 page2)
                        TransactionIdPrecedes(xid1, xid2 + SERIAL_ENTRIESPERPAGE - 1));
 }
 
+static int
+serial_errdetail_for_io_error(const void *opaque_data)
+{
+       TransactionId xid = *(const TransactionId *) opaque_data;
+
+       return errdetail("Could not access serializable CSN of transaction %u.", xid);
+}
+
 #ifdef USE_ASSERT_CHECKING
 static void
 SerialPagePrecedesLogicallyUnitTests(void)
@@ -812,6 +821,7 @@ SerialInit(void)
         * Set up SLRU management of the pg_serial data.
         */
        SerialSlruCtl->PagePrecedes = SerialPagePrecedesLogically;
+       SerialSlruCtl->errdetail_for_io_error = serial_errdetail_for_io_error;
        SimpleLruInit(SerialSlruCtl, "serializable",
                                  serializable_buffers, 0, "pg_serial",
                                  LWTRANCHE_SERIAL_BUFFER, LWTRANCHE_SERIAL_SLRU,
@@ -931,7 +941,7 @@ SerialAdd(TransactionId xid, SerCommitSeqNo minConflictCommitSeqNo)
        else
        {
                LWLockAcquire(lock, LW_EXCLUSIVE);
-               slotno = SimpleLruReadPage(SerialSlruCtl, targetPage, true, xid);
+               slotno = SimpleLruReadPage(SerialSlruCtl, targetPage, true, &xid);
        }
 
        SerialValue(slotno, xid) = minConflictCommitSeqNo;
@@ -975,7 +985,7 @@ SerialGetMinConflictCommitSeqNo(TransactionId xid)
         * but will return with that lock held, which must then be released.
         */
        slotno = SimpleLruReadPage_ReadOnly(SerialSlruCtl,
-                                                                               SerialPage(xid), xid);
+                                                                               SerialPage(xid), &xid);
        val = SerialValue(slotno, xid);
        LWLockRelease(SimpleLruGetBankLock(SerialSlruCtl, SerialPage(xid)));
        return val;
index 4cb8f478fce2847e070d9c83eafd344fb712a897..f966d0d9fe79ecff4a94ea600e977275a8946f06 100644 (file)
@@ -13,6 +13,7 @@
 #ifndef SLRU_H
 #define SLRU_H
 
+#include "access/transam.h"
 #include "access/xlogdefs.h"
 #include "storage/lwlock.h"
 #include "storage/sync.h"
@@ -141,6 +142,16 @@ typedef struct SlruCtlData
         */
        bool            (*PagePrecedes) (int64, int64);
 
+       /*
+        * Callback to provide more details on an I/O error.  This is called as
+        * part of ereport(), and the callback function is expected to call
+        * errdetail() to provide more context on the SLRU access.
+        *
+        * The opaque_data argument here is the argument that was passed to the
+        * SimpleLruReadPage() function.
+        */
+       int                     (*errdetail_for_io_error) (const void *opaque_data);
+
        /*
         * 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.
@@ -174,9 +185,9 @@ extern void SimpleLruInit(SlruCtl ctl, const char *name, int nslots, int nlsns,
 extern int     SimpleLruZeroPage(SlruCtl ctl, int64 pageno);
 extern void SimpleLruZeroAndWritePage(SlruCtl ctl, int64 pageno);
 extern int     SimpleLruReadPage(SlruCtl ctl, int64 pageno, bool write_ok,
-                                                         TransactionId xid);
+                                                         const void *opaque_data);
 extern int     SimpleLruReadPage_ReadOnly(SlruCtl ctl, int64 pageno,
-                                                                          TransactionId xid);
+                                                                          const void *opaque_data);
 extern void SimpleLruWritePage(SlruCtl ctl, int slotno);
 extern void SimpleLruWriteAll(SlruCtl ctl, bool allow_redirtied);
 #ifdef USE_ASSERT_CHECKING
index 185c56e5d628bb9cc79e9cb10babd2fac0f3233f..7ae1b0d4a9806cc1b4bd13bee3609d5965fe932d 100644 (file)
@@ -23,6 +23,10 @@ SELECT test_slru_page_exists(12345);
  t
 (1 row)
 
+-- Test read failure
+SELECT test_slru_page_read(54321, false, '123'::xid);
+ERROR:  could not open file "pg_test_slru/0000000000006A1": No such file or directory
+DETAIL:  Could not access test_slru entry 123.
 -- 48 extra pages
 SELECT count(test_slru_page_write(a, 'Test SLRU'))
   FROM generate_series(12346, 12393, 1) as a;
index b1b376581ab74d4ee1e7e914793ef671d6d13b6d..c6454c5bf82bf07b7c065f1f071c71b94c57de46 100644 (file)
@@ -5,6 +5,9 @@ SELECT test_slru_page_write(12345, 'Test SLRU');
 SELECT test_slru_page_read(12345);
 SELECT test_slru_page_exists(12345);
 
+-- Test read failure
+SELECT test_slru_page_read(54321, false, '123'::xid);
+
 -- 48 extra pages
 SELECT count(test_slru_page_write(a, 'Test SLRU'))
   FROM generate_series(12346, 12393, 1) as a;
index abecb5e21838f84c4e8ea9d12d59210f92e2cd34..2148e7204ec53375fd0882f3c6a811f2370d98f2 100644 (file)
@@ -7,7 +7,7 @@ CREATE OR REPLACE FUNCTION test_slru_page_writeall() RETURNS VOID
   AS 'MODULE_PATHNAME', 'test_slru_page_writeall' LANGUAGE C;
 CREATE OR REPLACE FUNCTION test_slru_page_sync(bigint) RETURNS VOID
   AS 'MODULE_PATHNAME', 'test_slru_page_sync' LANGUAGE C;
-CREATE OR REPLACE FUNCTION test_slru_page_read(bigint, bool DEFAULT true) RETURNS text
+CREATE OR REPLACE FUNCTION test_slru_page_read(bigint, bool DEFAULT true, xid DEFAULT '0') RETURNS text
   AS 'MODULE_PATHNAME', 'test_slru_page_read' LANGUAGE C;
 CREATE OR REPLACE FUNCTION test_slru_page_readonly(bigint) RETURNS text
   AS 'MODULE_PATHNAME', 'test_slru_page_readonly' LANGUAGE C;
index 4dc74e19620459f9b19e8fec1747b8d7d4ed9b7b..e4bd2af0bf560fff993247f55e40f2629f30e513 100644 (file)
@@ -93,14 +93,14 @@ test_slru_page_read(PG_FUNCTION_ARGS)
 {
        int64           pageno = PG_GETARG_INT64(0);
        bool            write_ok = PG_GETARG_BOOL(1);
+       TransactionId xid = PG_GETARG_TRANSACTIONID(2);
        char       *data = NULL;
        int                     slotno;
        LWLock     *lock = SimpleLruGetBankLock(TestSlruCtl, pageno);
 
        /* find page in buffers, reading it if necessary */
        LWLockAcquire(lock, LW_EXCLUSIVE);
-       slotno = SimpleLruReadPage(TestSlruCtl, pageno,
-                                                          write_ok, InvalidTransactionId);
+       slotno = SimpleLruReadPage(TestSlruCtl, pageno, write_ok, &xid);
        data = (char *) TestSlruCtl->shared->page_buffer[slotno];
        LWLockRelease(lock);
 
@@ -118,7 +118,7 @@ test_slru_page_readonly(PG_FUNCTION_ARGS)
        /* find page in buffers, reading it if necessary */
        slotno = SimpleLruReadPage_ReadOnly(TestSlruCtl,
                                                                                pageno,
-                                                                               InvalidTransactionId);
+                                                                               NULL);
        Assert(LWLockHeldByMe(lock));
        data = (char *) TestSlruCtl->shared->page_buffer[slotno];
        LWLockRelease(lock);
@@ -210,6 +210,14 @@ test_slru_page_precedes_logically(int64 page1, int64 page2)
        return page1 < page2;
 }
 
+static int
+test_slru_errdetail_for_io_error(const void *opaque_data)
+{
+       TransactionId xid = *(const TransactionId *) opaque_data;
+
+       return errdetail("Could not access test_slru entry %u.", xid);
+}
+
 static void
 test_slru_shmem_startup(void)
 {
@@ -245,6 +253,7 @@ test_slru_shmem_startup(void)
        }
 
        TestSlruCtl->PagePrecedes = test_slru_page_precedes_logically;
+       TestSlruCtl->errdetail_for_io_error = test_slru_errdetail_for_io_error;
        SimpleLruInit(TestSlruCtl, "TestSLRU",
                                  NUM_TEST_BUFFERS, 0, slru_dir_name,
                                  test_buffer_tranche_id, test_tranche_id, SYNC_HANDLER_NONE,
index 0de55183793b3e30ae875eed6f29459c3a8f1f92..41d54182887b577bab718884d7e1f2ccb098577c 100644 (file)
@@ -1747,6 +1747,7 @@ MultiSortSupport
 MultiSortSupportData
 MultiXactId
 MultiXactMember
+MultiXactMemberSlruReadContext
 MultiXactOffset
 MultiXactOffset32
 MultiXactStateData