Commit 5fc1008e authored by Michael Paquier's avatar Michael Paquier

Clean up temporary WAL segments after an instance crash

Temporary WAL segments are created in pg_wal and named as xlogtemp.pid
before being renamed to the real deal when creating a new segment.  If
an instance crashes after the temporary segment is created and before
the rename is done, then the server would finish with unremovable data.

After an instance crash, scan pg_wal and remove any such segments.  With
repetitive unlucky crashes this would contribute to disk bloat and
presents risks of ENOSPC especially with max_wal_size close to the
maximum allowed.

Author: Michael Paquier
Reviewed-by: Yugo Nagata, Heikki Linnakangas
Discussion: https://postgr.es/m/20180514054955.GF1528@paquier.xyz
parent 5e6e2c87
...@@ -887,6 +887,7 @@ static bool WaitForWALToBecomeAvailable(XLogRecPtr RecPtr, bool randAccess, ...@@ -887,6 +887,7 @@ static bool WaitForWALToBecomeAvailable(XLogRecPtr RecPtr, bool randAccess,
static int emode_for_corrupt_record(int emode, XLogRecPtr RecPtr); static int emode_for_corrupt_record(int emode, XLogRecPtr RecPtr);
static void XLogFileClose(void); static void XLogFileClose(void);
static void PreallocXlogFiles(XLogRecPtr endptr); static void PreallocXlogFiles(XLogRecPtr endptr);
static void RemoveTempXlogFiles(void);
static void RemoveOldXlogFiles(XLogSegNo segno, XLogRecPtr PriorRedoPtr, XLogRecPtr endptr); static void RemoveOldXlogFiles(XLogSegNo segno, XLogRecPtr PriorRedoPtr, XLogRecPtr endptr);
static void RemoveXlogFile(const char *segname, XLogRecPtr PriorRedoPtr, XLogRecPtr endptr); static void RemoveXlogFile(const char *segname, XLogRecPtr PriorRedoPtr, XLogRecPtr endptr);
static void UpdateLastRemovedPtr(char *filename); static void UpdateLastRemovedPtr(char *filename);
...@@ -3863,6 +3864,35 @@ UpdateLastRemovedPtr(char *filename) ...@@ -3863,6 +3864,35 @@ UpdateLastRemovedPtr(char *filename)
SpinLockRelease(&XLogCtl->info_lck); SpinLockRelease(&XLogCtl->info_lck);
} }
/*
* Remove all temporary log files in pg_wal
*
* This is called at the beginning of recovery after a previous crash,
* at a point where no other processes write fresh WAL data.
*/
static void
RemoveTempXlogFiles(void)
{
DIR *xldir;
struct dirent *xlde;
elog(DEBUG2, "removing all temporary WAL segments");
xldir = AllocateDir(XLOGDIR);
while ((xlde = ReadDir(xldir, XLOGDIR)) != NULL)
{
char path[MAXPGPATH];
if (strncmp(xlde->d_name, "xlogtemp.", 9) != 0)
continue;
snprintf(path, MAXPGPATH, XLOGDIR "/%s", xlde->d_name);
unlink(path);
elog(DEBUG2, "removed temporary WAL segment \"%s\"", path);
}
FreeDir(xldir);
}
/* /*
* Recycle or remove all log files older or equal to passed segno. * Recycle or remove all log files older or equal to passed segno.
* *
...@@ -6379,17 +6409,25 @@ StartupXLOG(void) ...@@ -6379,17 +6409,25 @@ StartupXLOG(void)
*/ */
ValidateXLOGDirectoryStructure(); ValidateXLOGDirectoryStructure();
/* /*----------
* If we previously crashed, there might be data which we had written, * If we previously crashed, perform a couple of actions:
* intending to fsync it, but which we had not actually fsync'd yet. * - The pg_wal directory may still include some temporary WAL segments
* Therefore, a power failure in the near future might cause earlier * used when creating a new segment, so perform some clean up to not
* unflushed writes to be lost, even though more recent data written to * bloat this path. This is done first as there is no point to sync this
* disk from here on would be persisted. To avoid that, fsync the entire * temporary data.
* data directory. * - There might be data which we had written, intending to fsync it,
* but which we had not actually fsync'd yet. Therefore, a power failure
* in the near future might cause earlier unflushed writes to be lost,
* even though more recent data written to disk from here on would be
* persisted. To avoid that, fsync the entire data directory.
*---------
*/ */
if (ControlFile->state != DB_SHUTDOWNED && if (ControlFile->state != DB_SHUTDOWNED &&
ControlFile->state != DB_SHUTDOWNED_IN_RECOVERY) ControlFile->state != DB_SHUTDOWNED_IN_RECOVERY)
{
RemoveTempXlogFiles();
SyncDataDirectory(); SyncDataDirectory();
}
/* /*
* Initialize on the assumption we want to recover to the latest timeline * Initialize on the assumption we want to recover to the latest timeline
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment