Use callbacks in SlruScanDirectory for the actual action

Previously, the code assumed that the only possible action to take was
to delete files behind a certain cutoff point.  The async notify code
was already a crock: it used a different "pagePrecedes" function for
truncation than for regular operation.  By allowing it to pass a
callback to SlruScanDirectory it can do cleanly exactly what it needs to
do.

The clog.c code also had its own use for SlruScanDirectory, which is
made a bit simpler with this.
This commit is contained in:
Alvaro Herrera 2011-09-28 11:32:38 -03:00
parent 1a00c0ef53
commit 09e196e453
4 changed files with 95 additions and 60 deletions

View File

@ -606,7 +606,7 @@ TruncateCLOG(TransactionId oldestXact)
cutoffPage = TransactionIdToPage(oldestXact); cutoffPage = TransactionIdToPage(oldestXact);
/* Check to see if there's any files that could be removed */ /* Check to see if there's any files that could be removed */
if (!SlruScanDirectory(ClogCtl, cutoffPage, false)) if (!SlruScanDirectory(ClogCtl, SlruScanDirCbReportPresence, &cutoffPage))
return; /* nothing to remove */ return; /* nothing to remove */
/* Write XLOG record and flush XLOG to disk */ /* Write XLOG record and flush XLOG to disk */

View File

@ -132,6 +132,8 @@ static bool SlruPhysicalWritePage(SlruCtl ctl, int pageno, int slotno,
static void SlruReportIOError(SlruCtl ctl, int pageno, TransactionId xid); static void SlruReportIOError(SlruCtl ctl, int pageno, TransactionId xid);
static int SlruSelectLRUPage(SlruCtl ctl, int pageno); static int SlruSelectLRUPage(SlruCtl ctl, int pageno);
static bool SlruScanDirCbDeleteCutoff(SlruCtl ctl, char *filename,
int segpage, void *data);
/* /*
* Initialization of shared memory * Initialization of shared memory
@ -1137,32 +1139,83 @@ restart:;
LWLockRelease(shared->ControlLock); LWLockRelease(shared->ControlLock);
/* Now we can remove the old segment(s) */ /* Now we can remove the old segment(s) */
(void) SlruScanDirectory(ctl, cutoffPage, true); (void) SlruScanDirectory(ctl, SlruScanDirCbDeleteCutoff, &cutoffPage);
} }
/* /*
* SimpleLruTruncate subroutine: scan directory for removable segments. * SlruScanDirectory callback
* Actually remove them iff doDeletions is true. Return TRUE iff any * This callback reports true if there's any segment prior to the one
* removable segments were found. Note: no locking is needed. * containing the page passed as "data".
*
* This can be called directly from clog.c, for reasons explained there.
*/ */
bool bool
SlruScanDirectory(SlruCtl ctl, int cutoffPage, bool doDeletions) 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)
{ {
bool found = false;
DIR *cldir; DIR *cldir;
struct dirent *clde; struct dirent *clde;
int segno; int segno;
int segpage; int segpage;
char path[MAXPGPATH]; bool retval;
/*
* 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); cldir = AllocateDir(ctl->Dir);
while ((clde = ReadDir(cldir, ctl->Dir)) != NULL) while ((clde = ReadDir(cldir, ctl->Dir)) != NULL)
@ -1172,20 +1225,15 @@ SlruScanDirectory(SlruCtl ctl, int cutoffPage, bool doDeletions)
{ {
segno = (int) strtol(clde->d_name, NULL, 16); segno = (int) strtol(clde->d_name, NULL, 16);
segpage = segno * SLRU_PAGES_PER_SEGMENT; segpage = segno * SLRU_PAGES_PER_SEGMENT;
if (ctl->PagePrecedes(segpage, cutoffPage))
{ elog(DEBUG2, "SlruScanDirectory invoking callback on %s/%s",
found = true; ctl->Dir, clde->d_name);
if (doDeletions) retval = callback(ctl, clde->d_name, segpage, data);
{ if (retval)
snprintf(path, MAXPGPATH, "%s/%s", ctl->Dir, clde->d_name); break;
ereport(DEBUG2,
(errmsg("removing file \"%s\"", path)));
unlink(path);
}
}
} }
} }
FreeDir(cldir); FreeDir(cldir);
return found; return retval;
} }

View File

@ -194,7 +194,7 @@ typedef struct QueuePosition
/* choose logically smaller QueuePosition */ /* choose logically smaller QueuePosition */
#define QUEUE_POS_MIN(x,y) \ #define QUEUE_POS_MIN(x,y) \
(asyncQueuePagePrecedesLogically((x).page, (y).page) ? (x) : \ (asyncQueuePagePrecedes((x).page, (y).page) ? (x) : \
(x).page != (y).page ? (y) : \ (x).page != (y).page ? (y) : \
(x).offset < (y).offset ? (x) : (y)) (x).offset < (y).offset ? (x) : (y))
@ -360,8 +360,7 @@ static bool backendHasExecutedInitialListen = false;
bool Trace_notify = false; bool Trace_notify = false;
/* local function prototypes */ /* local function prototypes */
static bool asyncQueuePagePrecedesPhysically(int p, int q); static bool asyncQueuePagePrecedes(int p, int q);
static bool asyncQueuePagePrecedesLogically(int p, int q);
static void queue_listen(ListenActionKind action, const char *channel); static void queue_listen(ListenActionKind action, const char *channel);
static void Async_UnlistenOnExit(int code, Datum arg); static void Async_UnlistenOnExit(int code, Datum arg);
static void Exec_ListenPreCommit(void); static void Exec_ListenPreCommit(void);
@ -388,25 +387,11 @@ static void NotifyMyFrontEnd(const char *channel,
static bool AsyncExistsPendingNotify(const char *channel, const char *payload); static bool AsyncExistsPendingNotify(const char *channel, const char *payload);
static void ClearPendingActionsAndNotifies(void); static void ClearPendingActionsAndNotifies(void);
/* /*
* We will work on the page range of 0..QUEUE_MAX_PAGE. * 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 static bool
asyncQueuePagePrecedesPhysically(int p, int q) asyncQueuePagePrecedes(int p, int q)
{
return p < q;
}
static bool
asyncQueuePagePrecedesLogically(int p, int q)
{ {
int diff; int diff;
@ -484,7 +469,7 @@ AsyncShmemInit(void)
/* /*
* Set up SLRU management of the pg_notify data. * Set up SLRU management of the pg_notify data.
*/ */
AsyncCtl->PagePrecedes = asyncQueuePagePrecedesLogically; AsyncCtl->PagePrecedes = asyncQueuePagePrecedes;
SimpleLruInit(AsyncCtl, "Async Ctl", NUM_ASYNC_BUFFERS, 0, SimpleLruInit(AsyncCtl, "Async Ctl", NUM_ASYNC_BUFFERS, 0,
AsyncCtlLock, "pg_notify"); AsyncCtlLock, "pg_notify");
/* Override default assumption that writes should be fsync'd */ /* Override default assumption that writes should be fsync'd */
@ -494,15 +479,8 @@ AsyncShmemInit(void)
{ {
/* /*
* During start or reboot, clean out the pg_notify directory. * 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, SlruScanDirCbDeleteAll, NULL);
(void) SlruScanDirectory(AsyncCtl, INT_MAX, true);
AsyncCtl->PagePrecedes = asyncQueuePagePrecedesLogically;
/* Now initialize page zero to empty */ /* Now initialize page zero to empty */
LWLockAcquire(AsyncCtlLock, LW_EXCLUSIVE); LWLockAcquire(AsyncCtlLock, LW_EXCLUSIVE);
@ -1223,7 +1201,7 @@ asyncQueueIsFull(void)
nexthead = 0; /* wrap around */ nexthead = 0; /* wrap around */
boundary = QUEUE_POS_PAGE(QUEUE_TAIL); boundary = QUEUE_POS_PAGE(QUEUE_TAIL);
boundary -= boundary % SLRU_PAGES_PER_SEGMENT; boundary -= boundary % SLRU_PAGES_PER_SEGMENT;
return asyncQueuePagePrecedesLogically(nexthead, boundary); return asyncQueuePagePrecedes(nexthead, boundary);
} }
/* /*
@ -2074,7 +2052,7 @@ asyncQueueAdvanceTail(void)
*/ */
newtailpage = QUEUE_POS_PAGE(min); newtailpage = QUEUE_POS_PAGE(min);
boundary = newtailpage - (newtailpage % SLRU_PAGES_PER_SEGMENT); boundary = newtailpage - (newtailpage % SLRU_PAGES_PER_SEGMENT);
if (asyncQueuePagePrecedesLogically(oldtailpage, boundary)) if (asyncQueuePagePrecedes(oldtailpage, boundary))
{ {
/* /*
* SimpleLruTruncate() will ask for AsyncCtlLock but will also release * SimpleLruTruncate() will ask for AsyncCtlLock but will also release

View File

@ -145,6 +145,15 @@ extern int SimpleLruReadPage_ReadOnly(SlruCtl ctl, int pageno,
extern void SimpleLruWritePage(SlruCtl ctl, int slotno); extern void SimpleLruWritePage(SlruCtl ctl, int slotno);
extern void SimpleLruFlush(SlruCtl ctl, bool checkpoint); extern void SimpleLruFlush(SlruCtl ctl, bool checkpoint);
extern void SimpleLruTruncate(SlruCtl ctl, int cutoffPage); extern void SimpleLruTruncate(SlruCtl ctl, int cutoffPage);
extern bool SlruScanDirectory(SlruCtl ctl, int cutoffPage, bool doDeletions);
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 */ #endif /* SLRU_H */