*** a/src/backend/access/transam/clog.c --- b/src/backend/access/transam/clog.c *************** *** 606,612 **** TruncateCLOG(TransactionId oldestXact) cutoffPage = TransactionIdToPage(oldestXact); /* Check to see if there's any files that could be removed */ ! if (!SlruScanDirectory(ClogCtl, cutoffPage, false)) return; /* nothing to remove */ /* Write XLOG record and flush XLOG to disk */ --- 606,612 ---- cutoffPage = TransactionIdToPage(oldestXact); /* Check to see if there's any files that could be removed */ ! if (!SlruScanDirectory(ClogCtl, SlruScanDirCbReportPresence, &cutoffPage)) return; /* nothing to remove */ /* Write XLOG record and flush XLOG to disk */ *** a/src/backend/access/transam/slru.c --- b/src/backend/access/transam/slru.c *************** *** 132,137 **** static bool SlruPhysicalWritePage(SlruCtl ctl, int pageno, int slotno, --- 132,139 ---- 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); /* * Initialization of shared memory *************** *** 1137,1169 **** restart:; LWLockRelease(shared->ControlLock); /* Now we can remove the old segment(s) */ ! (void) SlruScanDirectory(ctl, cutoffPage, true); } /* ! * SimpleLruTruncate subroutine: scan directory for removable segments. ! * Actually remove them iff doDeletions is true. Return TRUE iff any ! * removable segments were found. Note: no locking is needed. * ! * This can be called directly from clog.c, for reasons explained there. */ bool ! SlruScanDirectory(SlruCtl ctl, int cutoffPage, bool doDeletions) { - bool found = false; DIR *cldir; struct dirent *clde; int segno; int segpage; ! char path[MAXPGPATH]; ! ! /* ! * The cutoff point is the start of the segment containing cutoffPage. ! * (This is redundant when called from SimpleLruTruncate, but not when ! * called directly from clog.c.) ! */ ! cutoffPage -= cutoffPage % SLRU_PAGES_PER_SEGMENT; ! cldir = AllocateDir(ctl->Dir); while ((clde = ReadDir(cldir, ctl->Dir)) != NULL) { --- 1139,1222 ---- LWLockRelease(shared->ControlLock); /* Now we can remove the old segment(s) */ ! (void) SlruScanDirectory(ctl, SlruScanDirCbDeleteCutoff, &cutoffPage); } /* ! * SlruScanDirectory callback ! * This callback reports true if there's any segment prior to the one ! * containing the page passed as "data". ! */ ! bool ! SlruScanDirCbReportPresence(SlruCtl ctl, char *filename, int segpage, void *data) ! { ! int cutoffPage = *(int *) data; ! ! cutoffPage -= cutoffPage % SLRU_PAGES_PER_SEGMENT; ! ! if (ctl->PagePrecedes(segpage, cutoffPage)) ! return true; /* found one; don't iterate any more */ ! ! return false; /* keep going */ ! } ! ! /* ! * SlruScanDirectory callback. ! * This callback deletes segments prior to the one passed in as "data". ! */ ! static bool ! SlruScanDirCbDeleteCutoff(SlruCtl ctl, char *filename, int segpage, void *data) ! { ! char path[MAXPGPATH]; ! int cutoffPage = *(int *) data; ! ! if (ctl->PagePrecedes(segpage, cutoffPage)) ! { ! snprintf(path, MAXPGPATH, "%s/%s", ctl->Dir, filename); ! ereport(DEBUG2, ! (errmsg("removing file \"%s\"", path))); ! unlink(path); ! } ! ! return false; /* keep going */ ! } ! ! /* ! * SlruScanDirectory callback. ! * This callback deletes all segments. ! */ ! bool ! SlruScanDirCbDeleteAll(SlruCtl ctl, char *filename, int segpage, void *data) ! { ! char path[MAXPGPATH]; ! ! snprintf(path, MAXPGPATH, "%s/%s", ctl->Dir, filename); ! ereport(DEBUG2, ! (errmsg("removing file \"%s\"", path))); ! unlink(path); ! ! return false; /* keep going */ ! } ! ! /* ! * Scan the SimpleLRU 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. * ! * Note that the ordering in which the directory is scanned is not guaranteed. ! * ! * Note that no locking is applied. */ bool ! SlruScanDirectory(SlruCtl ctl, SlruScanCallback callback, void *data) { DIR *cldir; struct dirent *clde; int segno; int segpage; ! bool retval; ! cldir = AllocateDir(ctl->Dir); while ((clde = ReadDir(cldir, ctl->Dir)) != NULL) { *************** *** 1172,1191 **** SlruScanDirectory(SlruCtl ctl, int cutoffPage, bool doDeletions) { segno = (int) strtol(clde->d_name, NULL, 16); segpage = segno * SLRU_PAGES_PER_SEGMENT; ! if (ctl->PagePrecedes(segpage, cutoffPage)) ! { ! found = true; ! if (doDeletions) ! { ! snprintf(path, MAXPGPATH, "%s/%s", ctl->Dir, clde->d_name); ! ereport(DEBUG2, ! (errmsg("removing file \"%s\"", path))); ! unlink(path); ! } ! } } } FreeDir(cldir); ! return found; } --- 1225,1239 ---- { segno = (int) strtol(clde->d_name, NULL, 16); segpage = segno * SLRU_PAGES_PER_SEGMENT; ! ! elog(DEBUG2, "SlruScanDirectory invoking callback on %s/%s", ! ctl->Dir, clde->d_name); ! retval = callback(ctl, clde->d_name, segpage, data); ! if (retval) ! break; } } FreeDir(cldir); ! return retval; } *** a/src/backend/commands/async.c --- b/src/backend/commands/async.c *************** *** 194,200 **** typedef struct QueuePosition /* choose logically smaller QueuePosition */ #define QUEUE_POS_MIN(x,y) \ ! (asyncQueuePagePrecedesLogically((x).page, (y).page) ? (x) : \ (x).page != (y).page ? (y) : \ (x).offset < (y).offset ? (x) : (y)) --- 194,200 ---- /* choose logically smaller QueuePosition */ #define QUEUE_POS_MIN(x,y) \ ! (asyncQueuePagePrecedes((x).page, (y).page) ? (x) : \ (x).page != (y).page ? (y) : \ (x).offset < (y).offset ? (x) : (y)) *************** *** 360,367 **** static bool backendHasExecutedInitialListen = false; bool Trace_notify = false; /* local function prototypes */ ! static bool asyncQueuePagePrecedesPhysically(int p, int q); ! static bool asyncQueuePagePrecedesLogically(int p, int q); static void queue_listen(ListenActionKind action, const char *channel); static void Async_UnlistenOnExit(int code, Datum arg); static void Exec_ListenPreCommit(void); --- 360,366 ---- bool Trace_notify = false; /* local function prototypes */ ! static bool asyncQueuePagePrecedes(int p, int q); static void queue_listen(ListenActionKind action, const char *channel); static void Async_UnlistenOnExit(int code, Datum arg); static void Exec_ListenPreCommit(void); *************** *** 388,412 **** static void NotifyMyFrontEnd(const char *channel, static bool AsyncExistsPendingNotify(const char *channel, const char *payload); static void ClearPendingActionsAndNotifies(void); - /* * We will work on the page range of 0..QUEUE_MAX_PAGE. - * - * asyncQueuePagePrecedesPhysically just checks numerically without any magic - * if one page precedes another one. This is wrong for normal operation but - * is helpful when clearing pg_notify/ during startup. - * - * asyncQueuePagePrecedesLogically compares using wraparound logic, as is - * required by slru.c. */ static bool ! asyncQueuePagePrecedesPhysically(int p, int q) ! { ! return p < q; ! } ! ! static bool ! asyncQueuePagePrecedesLogically(int p, int q) { int diff; --- 387,397 ---- static bool AsyncExistsPendingNotify(const char *channel, const char *payload); static void ClearPendingActionsAndNotifies(void); /* * We will work on the page range of 0..QUEUE_MAX_PAGE. */ static bool ! asyncQueuePagePrecedes(int p, int q) { int diff; *************** *** 484,490 **** AsyncShmemInit(void) /* * Set up SLRU management of the pg_notify data. */ ! AsyncCtl->PagePrecedes = asyncQueuePagePrecedesLogically; SimpleLruInit(AsyncCtl, "Async Ctl", NUM_ASYNC_BUFFERS, 0, AsyncCtlLock, "pg_notify"); /* Override default assumption that writes should be fsync'd */ --- 469,475 ---- /* * Set up SLRU management of the pg_notify data. */ ! AsyncCtl->PagePrecedes = asyncQueuePagePrecedes; SimpleLruInit(AsyncCtl, "Async Ctl", NUM_ASYNC_BUFFERS, 0, AsyncCtlLock, "pg_notify"); /* Override default assumption that writes should be fsync'd */ *************** *** 494,508 **** AsyncShmemInit(void) { /* * During start or reboot, clean out the pg_notify directory. - * - * Since we want to remove every file, we temporarily use - * asyncQueuePagePrecedesPhysically() and pass INT_MAX as the - * comparison value; every file in the directory should therefore - * appear to be less than that. */ ! AsyncCtl->PagePrecedes = asyncQueuePagePrecedesPhysically; ! (void) SlruScanDirectory(AsyncCtl, INT_MAX, true); ! AsyncCtl->PagePrecedes = asyncQueuePagePrecedesLogically; /* Now initialize page zero to empty */ LWLockAcquire(AsyncCtlLock, LW_EXCLUSIVE); --- 479,486 ---- { /* * During start or reboot, clean out the pg_notify directory. */ ! (void) SlruScanDirectory(AsyncCtl, SlruScanDirCbDeleteAll, NULL); /* Now initialize page zero to empty */ LWLockAcquire(AsyncCtlLock, LW_EXCLUSIVE); *************** *** 1223,1229 **** asyncQueueIsFull(void) nexthead = 0; /* wrap around */ boundary = QUEUE_POS_PAGE(QUEUE_TAIL); boundary -= boundary % SLRU_PAGES_PER_SEGMENT; ! return asyncQueuePagePrecedesLogically(nexthead, boundary); } /* --- 1201,1207 ---- nexthead = 0; /* wrap around */ boundary = QUEUE_POS_PAGE(QUEUE_TAIL); boundary -= boundary % SLRU_PAGES_PER_SEGMENT; ! return asyncQueuePagePrecedes(nexthead, boundary); } /* *************** *** 2074,2080 **** asyncQueueAdvanceTail(void) */ newtailpage = QUEUE_POS_PAGE(min); boundary = newtailpage - (newtailpage % SLRU_PAGES_PER_SEGMENT); ! if (asyncQueuePagePrecedesLogically(oldtailpage, boundary)) { /* * SimpleLruTruncate() will ask for AsyncCtlLock but will also release --- 2052,2058 ---- */ newtailpage = QUEUE_POS_PAGE(min); boundary = newtailpage - (newtailpage % SLRU_PAGES_PER_SEGMENT); ! if (asyncQueuePagePrecedes(oldtailpage, boundary)) { /* * SimpleLruTruncate() will ask for AsyncCtlLock but will also release *** a/src/backend/commands/vacuum.c --- b/src/backend/commands/vacuum.c *************** *** 674,680 **** vac_update_datfrozenxid(void) * Initialize the "min" calculation with GetOldestXmin, which is a * reasonable approximation to the minimum relfrozenxid for not-yet- * committed pg_class entries for new tables; see AddNewRelationTuple(). ! * Se we cannot produce a wrong minimum by starting with this. */ newFrozenXid = GetOldestXmin(true, true); --- 674,680 ---- * Initialize the "min" calculation with GetOldestXmin, which is a * reasonable approximation to the minimum relfrozenxid for not-yet- * committed pg_class entries for new tables; see AddNewRelationTuple(). ! * So we cannot produce a wrong minimum by starting with this. */ newFrozenXid = GetOldestXmin(true, true); *** a/src/include/access/slru.h --- b/src/include/access/slru.h *************** *** 145,150 **** extern int SimpleLruReadPage_ReadOnly(SlruCtl ctl, int pageno, extern void SimpleLruWritePage(SlruCtl ctl, int slotno); extern void SimpleLruFlush(SlruCtl ctl, bool checkpoint); extern void SimpleLruTruncate(SlruCtl ctl, int cutoffPage); ! extern bool SlruScanDirectory(SlruCtl ctl, int cutoffPage, bool doDeletions); #endif /* SLRU_H */ --- 145,159 ---- extern void SimpleLruWritePage(SlruCtl ctl, int slotno); extern void SimpleLruFlush(SlruCtl ctl, bool checkpoint); extern void SimpleLruTruncate(SlruCtl ctl, int cutoffPage); ! ! typedef bool (*SlruScanCallback) (SlruCtl ctl, char *filename, int segpage, ! void *data); ! extern bool SlruScanDirectory(SlruCtl ctl, SlruScanCallback callback, void *data); ! ! /* SlruScanDirectory public callbacks */ ! extern bool SlruScanDirCbReportPresence(SlruCtl ctl, char *filename, ! int segpage, void *data); ! extern bool SlruScanDirCbDeleteAll(SlruCtl ctl, char *filename, int segpage, ! void *data); #endif /* SLRU_H */