Move temporary file cleanup to before_shmem_exit().

As reported by a few OSX buildfarm animals there exist at least one path where
temporary files exist during AtProcExit_Files() processing. As temporary file
cleanup causes pgstat reporting, the assertions added in ee3f8d3d3a caused
failures.

This is not an OSX specific issue, we were just lucky that timing on OSX
reliably triggered the problem.  The known way to cause this is a FATAL error
during perform_base_backup() with a MANIFEST used - adding an elog(FATAL)
after InitializeBackupManifest() reliably reproduces the problem in isolation.

The problem is that the temporary file created in InitializeBackupManifest()
is not cleaned up via resource owner cleanup as WalSndResourceCleanup()
currently is only used for non-FATAL errors. That then allows to reach
AtProcExit_Files() with existing temporary files, causing the assertion
failure.

To fix this problem, move temporary file cleanup to a before_shmem_exit() hook
and add assertions ensuring that no temporary files are created before / after
temporary file management has been initialized / shut down. The cleanest way
to do so seems to be to split fd.c initialization into two, one for plain file
access and one for temporary file access.

Right now there's no need to perform further fd.c cleanup during process exit,
so I just renamed AtProcExit_Files() to BeforeShmemExit_Files(). Alternatively
we could perform another pass through the files to check that no temporary
files exist, but the added assertions seem to provide enough protection
against that.

It might turn out that the assertions added in ee3f8d3d3a will cause too much
noise - in that case we'll have to downgrade them to a WARNING, at least
temporarily.

This commit is not necessarily the best approach to address this issue, but it
should resolve the buildfarm failures. We can revise later.

Author: Andres Freund <andres@anarazel.de>
Discussion: https://postgr.es/m/20210807190131.2bm24acbebl4wl6i@alap3.anarazel.de
This commit is contained in:
Andres Freund 2021-08-07 19:14:20 -07:00
parent 256909c6c1
commit 675c945394
3 changed files with 65 additions and 8 deletions

View File

@ -231,6 +231,11 @@ static bool have_xact_temporary_files = false;
*/
static uint64 temporary_files_size = 0;
/* Temporary file access initialized and not yet shut down? */
#ifdef USE_ASSERT_CHECKING
static bool temporary_files_allowed = false;
#endif
/*
* List of OS handles opened with AllocateFile, AllocateDir and
* OpenTransientFile.
@ -327,7 +332,7 @@ static File OpenTemporaryFileInTablespace(Oid tblspcOid, bool rejectError);
static bool reserveAllocatedDesc(void);
static int FreeDesc(AllocateDesc *desc);
static void AtProcExit_Files(int code, Datum arg);
static void BeforeShmemExit_Files(int code, Datum arg);
static void CleanupTempFiles(bool isCommit, bool isProcExit);
static void RemovePgTempRelationFiles(const char *tsdirname);
static void RemovePgTempRelationFilesInDbspace(const char *dbspacedirname);
@ -868,6 +873,9 @@ durable_rename_excl(const char *oldfile, const char *newfile, int elevel)
*
* This is called during either normal or standalone backend start.
* It is *not* called in the postmaster.
*
* Note that this does not initialize temporary file access, that is
* separately initialized via InitTemporaryFileAccess().
*/
void
InitFileAccess(void)
@ -885,9 +893,35 @@ InitFileAccess(void)
VfdCache->fd = VFD_CLOSED;
SizeVfdCache = 1;
}
/* register proc-exit hook to ensure temp files are dropped at exit */
on_proc_exit(AtProcExit_Files, 0);
/*
* InitTemporaryFileAccess --- initialize temporary file access during startup
*
* This is called during either normal or standalone backend start.
* It is *not* called in the postmaster.
*
* This is separate from InitFileAccess() because temporary file cleanup can
* cause pgstat reporting. As pgstat is shut down during before_shmem_exit(),
* our reporting has to happen before that. Low level file access should be
* available for longer, hence the separate initialization / shutdown of
* temporary file handling.
*/
void
InitTemporaryFileAccess(void)
{
Assert(SizeVfdCache != 0); /* InitFileAccess() needs to have run*/
Assert(!temporary_files_allowed); /* call me only once */
/*
* Register before-shmem-exit hook to ensure temp files are dropped while
* we can still report stats.
*/
before_shmem_exit(BeforeShmemExit_Files, 0);
#ifdef USE_ASSERT_CHECKING
temporary_files_allowed = true;
#endif
}
/*
@ -1670,6 +1704,8 @@ OpenTemporaryFile(bool interXact)
{
File file = 0;
Assert(temporary_files_allowed); /* check temp file access is up */
/*
* Make sure the current resource owner has space for this File before we
* open it, if we'll be registering it below.
@ -1805,6 +1841,8 @@ PathNameCreateTemporaryFile(const char *path, bool error_on_failure)
{
File file;
Assert(temporary_files_allowed); /* check temp file access is up */
ResourceOwnerEnlargeFiles(CurrentResourceOwner);
/*
@ -1843,6 +1881,8 @@ PathNameOpenTemporaryFile(const char *path, int mode)
{
File file;
Assert(temporary_files_allowed); /* check temp file access is up */
ResourceOwnerEnlargeFiles(CurrentResourceOwner);
file = PathNameOpenFile(path, mode | PG_BINARY);
@ -3004,15 +3044,20 @@ AtEOXact_Files(bool isCommit)
}
/*
* AtProcExit_Files
* BeforeShmemExit_Files
*
* on_proc_exit hook to clean up temp files during backend shutdown.
* before_shmem_access hook to clean up temp files during backend shutdown.
* Here, we want to clean up *all* temp files including interXact ones.
*/
static void
AtProcExit_Files(int code, Datum arg)
BeforeShmemExit_Files(int code, Datum arg)
{
CleanupTempFiles(false, true);
/* prevent further temp files from being created */
#ifdef USE_ASSERT_CHECKING
temporary_files_allowed = false;
#endif
}
/*

View File

@ -517,6 +517,12 @@ BaseInit(void)
*/
DebugFileOpen();
/*
* Initialize file access. Done early so other subsystems can access
* files.
*/
InitFileAccess();
/*
* Initialize statistics reporting. This needs to happen early to ensure
* that pgstat's shutdown callback runs after the shutdown callbacks of
@ -525,11 +531,16 @@ BaseInit(void)
*/
pgstat_initialize();
/* Do local initialization of file, storage and buffer managers */
InitFileAccess();
/* Do local initialization of storage and buffer managers */
InitSync();
smgrinit();
InitBufferPoolAccess();
/*
* Initialize temporary file access after pgstat, so that the temorary
* file shutdown hook can report temporary file statistics.
*/
InitTemporaryFileAccess();
}

View File

@ -158,6 +158,7 @@ extern int MakePGDirectory(const char *directoryName);
/* Miscellaneous support routines */
extern void InitFileAccess(void);
extern void InitTemporaryFileAccess(void);
extern void set_max_safe_fds(void);
extern void closeAllVfds(void);
extern void SetTempTablespaces(Oid *tableSpaces, int numSpaces);