1
0
mirror of https://github.com/postgres/postgres.git synced 2025-06-30 21:42:05 +03:00

Use "transient" files for blind writes, take 2

"Blind writes" are a mechanism to push buffers down to disk when
evicting them; since they may belong to different databases than the one
a backend is connected to, the backend does not necessarily have a
relation to link them to, and thus no way to blow them away.  We were
keeping those files open indefinitely, which would cause a problem if
the underlying table was deleted, because the operating system would not
be able to reclaim the disk space used by those files.

To fix, have bufmgr mark such files as transient to smgr; the lower
layer is allowed to close the file descriptor when the current
transaction ends.  We must be careful to have any other access of the
file to remove the transient markings, to prevent unnecessary expensive
system calls when evicting buffers belonging to our own database (which
files we're likely to require again soon.)

This commit fixes a bug in the previous one, which neglected to cleanly
handle the LRU ring that fd.c uses to manage open files, and caused an
unacceptable failure just before beta2 and was thus reverted.
This commit is contained in:
Alvaro Herrera
2011-06-10 13:43:02 -04:00
parent 3d114b63b2
commit fba105b109
6 changed files with 102 additions and 28 deletions

View File

@ -125,12 +125,11 @@ static int max_safe_fds = 32; /* default if not changed */
/* these are the assigned bits in fdstate below: */
#define FD_TEMPORARY (1 << 0) /* T = delete when closed */
#define FD_XACT_TEMPORARY (1 << 1) /* T = delete at eoXact */
#define FD_XACT_TRANSIENT (1 << 2) /* T = close (not delete) at aoXact,
* but keep VFD */
/*
* Flag to tell whether it's worth scanning VfdCache looking for temp files to
* close
*/
static bool have_xact_temporary_files = false;
/* Flag to tell whether there are files to close/delete at end of transaction */
static bool have_pending_fd_cleanup = false;
typedef struct vfd
{
@ -591,6 +590,7 @@ LruDelete(File file)
Vfd *vfdP;
Assert(file != 0);
Assert(!FileIsNotOpen(file));
DO_DB(elog(LOG, "LruDelete %d (%s)",
file, VfdCache[file].fileName));
@ -953,7 +953,7 @@ OpenTemporaryFile(bool interXact)
VfdCache[file].resowner = CurrentResourceOwner;
/* ensure cleanup happens at eoxact */
have_xact_temporary_files = true;
have_pending_fd_cleanup = true;
}
return file;
@ -1026,6 +1026,25 @@ OpenTemporaryFileInTablespace(Oid tblspcOid, bool rejectError)
return file;
}
/*
* Set the transient flag on a file
*
* This flag tells CleanupTempFiles to close the kernel-level file descriptor
* (but not the VFD itself) at end of transaction.
*/
void
FileSetTransient(File file)
{
Vfd *vfdP;
Assert(FileIsValid(file));
vfdP = &VfdCache[file];
vfdP->fdstate |= FD_XACT_TRANSIENT;
have_pending_fd_cleanup = true;
}
/*
* close a file when done with it
*/
@ -1778,8 +1797,9 @@ AtEOSubXact_Files(bool isCommit, SubTransactionId mySubid,
* particularly care which). All still-open per-transaction temporary file
* VFDs are closed, which also causes the underlying files to be deleted
* (although they should've been closed already by the ResourceOwner
* cleanup). Furthermore, all "allocated" stdio files are closed. We also
* forget any transaction-local temp tablespace list.
* cleanup). Transient files have their kernel file descriptors closed.
* Furthermore, all "allocated" stdio files are closed. We also forget any
* transaction-local temp tablespace list.
*/
void
AtEOXact_Files(void)
@ -1802,7 +1822,10 @@ AtProcExit_Files(int code, Datum arg)
}
/*
* Close temporary files and delete their underlying files.
* General cleanup routine for fd.c.
*
* Temporary files are closed, and their underlying files deleted.
* Transient files are closed.
*
* isProcExit: if true, this is being called as the backend process is
* exiting. If that's the case, we should remove all temporary files; if
@ -1819,35 +1842,51 @@ CleanupTempFiles(bool isProcExit)
* Careful here: at proc_exit we need extra cleanup, not just
* xact_temporary files.
*/
if (isProcExit || have_xact_temporary_files)
if (isProcExit || have_pending_fd_cleanup)
{
Assert(FileIsNotOpen(0)); /* Make sure ring not corrupted */
for (i = 1; i < SizeVfdCache; i++)
{
unsigned short fdstate = VfdCache[i].fdstate;
if ((fdstate & FD_TEMPORARY) && VfdCache[i].fileName != NULL)
if (VfdCache[i].fileName != NULL)
{
/*
* If we're in the process of exiting a backend process, close
* all temporary files. Otherwise, only close temporary files
* local to the current transaction. They should be closed by
* the ResourceOwner mechanism already, so this is just a
* debugging cross-check.
*/
if (isProcExit)
FileClose(i);
else if (fdstate & FD_XACT_TEMPORARY)
if (fdstate & FD_TEMPORARY)
{
elog(WARNING,
"temporary file %s not closed at end-of-transaction",
VfdCache[i].fileName);
FileClose(i);
/*
* If we're in the process of exiting a backend process, close
* all temporary files. Otherwise, only close temporary files
* local to the current transaction. They should be closed by
* the ResourceOwner mechanism already, so this is just a
* debugging cross-check.
*/
if (isProcExit)
FileClose(i);
else if (fdstate & FD_XACT_TEMPORARY)
{
elog(WARNING,
"temporary file %s not closed at end-of-transaction",
VfdCache[i].fileName);
FileClose(i);
}
}
else if (fdstate & FD_XACT_TRANSIENT)
{
/*
* Close the FD, and remove the entry from the LRU ring,
* but also remove the flag from the VFD. This is to
* ensure that if the VFD is reused in the future for
* non-transient access, we don't close it inappropriately
* then.
*/
if (!FileIsNotOpen(i))
LruDelete(i);
VfdCache[i].fdstate &= ~FD_XACT_TRANSIENT;
}
}
}
have_xact_temporary_files = false;
have_pending_fd_cleanup = false;
}
/* Clean up "allocated" stdio files and dirs. */