Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit ab3148b

Browse files
committed
Fix bug in temporary file management with subtransactions. A cursor opened
in a subtransaction stays open even if the subtransaction is aborted, so any temporary files related to it must stay alive as well. With the patch, we use ResourceOwners to track open temporary files and don't automatically close them at subtransaction end (though in the normal case temporary files are registered with the subtransaction resource owner and will therefore be closed). At end of top transaction, we still check that there's no temporary files marked as close-at-end-of-transaction open, but that's now just a debugging cross-check as the resource owner cleanup should've closed them already.
1 parent dc58805 commit ab3148b

File tree

3 files changed

+146
-34
lines changed

3 files changed

+146
-34
lines changed

src/backend/storage/file/fd.c

Lines changed: 35 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
* Portions Copyright (c) 1994, Regents of the University of California
88
*
99
* IDENTIFICATION
10-
* $PostgreSQL: pgsql/src/backend/storage/file/fd.c,v 1.150 2009/08/05 18:01:54 heikki Exp $
10+
* $PostgreSQL: pgsql/src/backend/storage/file/fd.c,v 1.151 2009/12/03 11:03:28 heikki Exp $
1111
*
1212
* NOTES:
1313
*
@@ -55,6 +55,7 @@
5555
#include "storage/fd.h"
5656
#include "storage/ipc.h"
5757
#include "utils/guc.h"
58+
#include "utils/resowner.h"
5859

5960

6061
/*
@@ -134,7 +135,7 @@ typedef struct vfd
134135
{
135136
int fd; /* current FD, or VFD_CLOSED if none */
136137
unsigned short fdstate; /* bitflags for VFD's state */
137-
SubTransactionId create_subid; /* for TEMPORARY fds, creating subxact */
138+
ResourceOwner resowner; /* owner, for automatic cleanup */
138139
File nextFree; /* link to next free VFD, if in freelist */
139140
File lruMoreRecently; /* doubly linked recency-of-use list */
140141
File lruLessRecently;
@@ -865,6 +866,7 @@ PathNameOpenFile(FileName fileName, int fileFlags, int fileMode)
865866
vfdP->fileMode = fileMode;
866867
vfdP->seekPos = 0;
867868
vfdP->fdstate = 0x0;
869+
vfdP->resowner = NULL;
868870

869871
return file;
870872
}
@@ -876,11 +878,12 @@ PathNameOpenFile(FileName fileName, int fileFlags, int fileMode)
876878
* There's no need to pass in fileFlags or fileMode either, since only
877879
* one setting makes any sense for a temp file.
878880
*
879-
* interXact: if true, don't close the file at end-of-transaction. In
880-
* most cases, you don't want temporary files to outlive the transaction
881-
* that created them, so this should be false -- but if you need
882-
* "somewhat" temporary storage, this might be useful. In either case,
883-
* the file is removed when the File is explicitly closed.
881+
* Unless interXact is true, the file is remembered by CurrentResourceOwner
882+
* to ensure it's closed and deleted when it's no longer needed, typically at
883+
* the end-of-transaction. In most cases, you don't want temporary files to
884+
* outlive the transaction that created them, so this should be false -- but
885+
* if you need "somewhat" temporary storage, this might be useful. In either
886+
* case, the file is removed when the File is explicitly closed.
884887
*/
885888
File
886889
OpenTemporaryFile(bool interXact)
@@ -918,11 +921,14 @@ OpenTemporaryFile(bool interXact)
918921
/* Mark it for deletion at close */
919922
VfdCache[file].fdstate |= FD_TEMPORARY;
920923

921-
/* Mark it for deletion at EOXact */
924+
/* Register it with the current resource owner */
922925
if (!interXact)
923926
{
924927
VfdCache[file].fdstate |= FD_XACT_TEMPORARY;
925-
VfdCache[file].create_subid = GetCurrentSubTransactionId();
928+
929+
ResourceOwnerEnlargeFiles(CurrentResourceOwner);
930+
ResourceOwnerRememberFile(CurrentResourceOwner, file);
931+
VfdCache[file].resowner = CurrentResourceOwner;
926932

927933
/* ensure cleanup happens at eoxact */
928934
have_xact_temporary_files = true;
@@ -1051,6 +1057,10 @@ FileClose(File file)
10511057
elog(LOG, "could not unlink file \"%s\": %m", vfdP->fileName);
10521058
}
10531059

1060+
/* Unregister it from the resource owner */
1061+
if (vfdP->resowner)
1062+
ResourceOwnerForgetFile(vfdP->resowner, file);
1063+
10541064
/*
10551065
* Return the Vfd slot to the free list
10561066
*/
@@ -1695,24 +1705,6 @@ AtEOSubXact_Files(bool isCommit, SubTransactionId mySubid,
16951705
{
16961706
Index i;
16971707

1698-
if (have_xact_temporary_files)
1699-
{
1700-
Assert(FileIsNotOpen(0)); /* Make sure ring not corrupted */
1701-
for (i = 1; i < SizeVfdCache; i++)
1702-
{
1703-
unsigned short fdstate = VfdCache[i].fdstate;
1704-
1705-
if ((fdstate & FD_XACT_TEMPORARY) &&
1706-
VfdCache[i].create_subid == mySubid)
1707-
{
1708-
if (isCommit)
1709-
VfdCache[i].create_subid = parentSubid;
1710-
else if (VfdCache[i].fileName != NULL)
1711-
FileClose(i);
1712-
}
1713-
}
1714-
}
1715-
17161708
for (i = 0; i < numAllocatedDescs; i++)
17171709
{
17181710
if (allocatedDescs[i].create_subid == mySubid)
@@ -1733,9 +1725,10 @@ AtEOSubXact_Files(bool isCommit, SubTransactionId mySubid,
17331725
*
17341726
* This routine is called during transaction commit or abort (it doesn't
17351727
* particularly care which). All still-open per-transaction temporary file
1736-
* VFDs are closed, which also causes the underlying files to be
1737-
* deleted. Furthermore, all "allocated" stdio files are closed.
1738-
* We also forget any transaction-local temp tablespace list.
1728+
* VFDs are closed, which also causes the underlying files to be deleted
1729+
* (although they should've been closed already by the ResourceOwner
1730+
* cleanup). Furthermore, all "allocated" stdio files are closed. We also
1731+
* forget any transaction-local temp tablespace list.
17391732
*/
17401733
void
17411734
AtEOXact_Files(void)
@@ -1787,16 +1780,26 @@ CleanupTempFiles(bool isProcExit)
17871780
/*
17881781
* If we're in the process of exiting a backend process, close
17891782
* all temporary files. Otherwise, only close temporary files
1790-
* local to the current transaction.
1783+
* local to the current transaction. They should be closed
1784+
* by the ResourceOwner mechanism already, so this is just
1785+
* a debugging cross-check.
17911786
*/
1792-
if (isProcExit || (fdstate & FD_XACT_TEMPORARY))
1787+
if (isProcExit)
1788+
FileClose(i);
1789+
else if (fdstate & FD_XACT_TEMPORARY)
1790+
{
1791+
elog(WARNING,
1792+
"temporary file %s not closed at end-of-transaction",
1793+
VfdCache[i].fileName);
17931794
FileClose(i);
1795+
}
17941796
}
17951797
}
17961798

17971799
have_xact_temporary_files = false;
17981800
}
17991801

1802+
/* Clean up "allocated" stdio files and dirs. */
18001803
while (numAllocatedDescs > 0)
18011804
FreeDesc(&allocatedDescs[0]);
18021805
}

src/backend/utils/resowner/resowner.c

Lines changed: 102 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
*
1515
*
1616
* IDENTIFICATION
17-
* $PostgreSQL: pgsql/src/backend/utils/resowner/resowner.c,v 1.32 2009/06/11 14:49:06 momjian Exp $
17+
* $PostgreSQL: pgsql/src/backend/utils/resowner/resowner.c,v 1.33 2009/12/03 11:03:29 heikki Exp $
1818
*
1919
*-------------------------------------------------------------------------
2020
*/
@@ -72,6 +72,11 @@ typedef struct ResourceOwnerData
7272
int nsnapshots; /* number of owned snapshot references */
7373
Snapshot *snapshots; /* dynamically allocated array */
7474
int maxsnapshots; /* currently allocated array size */
75+
76+
/* We have built-in support for remembering open temporary files */
77+
int nfiles; /* number of owned temporary files */
78+
File *files; /* dynamically allocated array */
79+
int maxfiles; /* currently allocated array size */
7580
} ResourceOwnerData;
7681

7782

@@ -105,6 +110,7 @@ static void PrintRelCacheLeakWarning(Relation rel);
105110
static void PrintPlanCacheLeakWarning(CachedPlan *plan);
106111
static void PrintTupleDescLeakWarning(TupleDesc tupdesc);
107112
static void PrintSnapshotLeakWarning(Snapshot snapshot);
113+
static void PrintFileLeakWarning(File file);
108114

109115

110116
/*****************************************************************************
@@ -316,6 +322,14 @@ ResourceOwnerReleaseInternal(ResourceOwner owner,
316322
UnregisterSnapshot(owner->snapshots[owner->nsnapshots - 1]);
317323
}
318324

325+
/* Ditto for temporary files */
326+
while (owner->nfiles > 0)
327+
{
328+
if (isCommit)
329+
PrintFileLeakWarning(owner->files[owner->nfiles - 1]);
330+
FileClose(owner->files[owner->nfiles - 1]);
331+
}
332+
319333
/* Clean up index scans too */
320334
ReleaseResources_hash();
321335
}
@@ -347,6 +361,7 @@ ResourceOwnerDelete(ResourceOwner owner)
347361
Assert(owner->nplanrefs == 0);
348362
Assert(owner->ntupdescs == 0);
349363
Assert(owner->nsnapshots == 0);
364+
Assert(owner->nfiles == 0);
350365

351366
/*
352367
* Delete children. The recursive call will delink the child from me, so
@@ -377,6 +392,8 @@ ResourceOwnerDelete(ResourceOwner owner)
377392
pfree(owner->tupdescs);
378393
if (owner->snapshots)
379394
pfree(owner->snapshots);
395+
if (owner->files)
396+
pfree(owner->files);
380397

381398
pfree(owner);
382399
}
@@ -1035,3 +1052,87 @@ PrintSnapshotLeakWarning(Snapshot snapshot)
10351052
"Snapshot reference leak: Snapshot %p still referenced",
10361053
snapshot);
10371054
}
1055+
1056+
1057+
/*
1058+
* Make sure there is room for at least one more entry in a ResourceOwner's
1059+
* files reference array.
1060+
*
1061+
* This is separate from actually inserting an entry because if we run out
1062+
* of memory, it's critical to do so *before* acquiring the resource.
1063+
*/
1064+
void
1065+
ResourceOwnerEnlargeFiles(ResourceOwner owner)
1066+
{
1067+
int newmax;
1068+
1069+
if (owner->nfiles < owner->maxfiles)
1070+
return; /* nothing to do */
1071+
1072+
if (owner->files == NULL)
1073+
{
1074+
newmax = 16;
1075+
owner->files = (File *)
1076+
MemoryContextAlloc(TopMemoryContext, newmax * sizeof(File));
1077+
owner->maxfiles = newmax;
1078+
}
1079+
else
1080+
{
1081+
newmax = owner->maxfiles * 2;
1082+
owner->files = (File *)
1083+
repalloc(owner->files, newmax * sizeof(File));
1084+
owner->maxfiles = newmax;
1085+
}
1086+
}
1087+
1088+
/*
1089+
* Remember that a temporary file is owned by a ResourceOwner
1090+
*
1091+
* Caller must have previously done ResourceOwnerEnlargeFiles()
1092+
*/
1093+
void
1094+
ResourceOwnerRememberFile(ResourceOwner owner, File file)
1095+
{
1096+
Assert(owner->nfiles < owner->maxfiles);
1097+
owner->files[owner->nfiles] = file;
1098+
owner->nfiles++;
1099+
}
1100+
1101+
/*
1102+
* Forget that a temporary file is owned by a ResourceOwner
1103+
*/
1104+
void
1105+
ResourceOwnerForgetFile(ResourceOwner owner, File file)
1106+
{
1107+
File *files = owner->files;
1108+
int ns1 = owner->nfiles - 1;
1109+
int i;
1110+
1111+
for (i = ns1; i >= 0; i--)
1112+
{
1113+
if (files[i] == file)
1114+
{
1115+
while (i < ns1)
1116+
{
1117+
files[i] = files[i + 1];
1118+
i++;
1119+
}
1120+
owner->nfiles = ns1;
1121+
return;
1122+
}
1123+
}
1124+
elog(ERROR, "temporery file %d is not owned by resource owner %s",
1125+
file, owner->name);
1126+
}
1127+
1128+
1129+
/*
1130+
* Debugging subroutine
1131+
*/
1132+
static void
1133+
PrintFileLeakWarning(File file)
1134+
{
1135+
elog(WARNING,
1136+
"temporary file leak: File %d still referenced",
1137+
file);
1138+
}

src/include/utils/resowner.h

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,15 @@
1212
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
1313
* Portions Copyright (c) 1994, Regents of the University of California
1414
*
15-
* $PostgreSQL: pgsql/src/include/utils/resowner.h,v 1.17 2009/01/01 17:24:02 momjian Exp $
15+
* $PostgreSQL: pgsql/src/include/utils/resowner.h,v 1.18 2009/12/03 11:03:29 heikki Exp $
1616
*
1717
*-------------------------------------------------------------------------
1818
*/
1919
#ifndef RESOWNER_H
2020
#define RESOWNER_H
2121

2222
#include "storage/buf.h"
23+
#include "storage/fd.h"
2324
#include "utils/catcache.h"
2425
#include "utils/plancache.h"
2526
#include "utils/snapshot.h"
@@ -129,4 +130,11 @@ extern void ResourceOwnerRememberSnapshot(ResourceOwner owner,
129130
extern void ResourceOwnerForgetSnapshot(ResourceOwner owner,
130131
Snapshot snapshot);
131132

133+
/* support for temporary file management */
134+
extern void ResourceOwnerEnlargeFiles(ResourceOwner owner);
135+
extern void ResourceOwnerRememberFile(ResourceOwner owner,
136+
File file);
137+
extern void ResourceOwnerForgetFile(ResourceOwner owner,
138+
File file);
139+
132140
#endif /* RESOWNER_H */

0 commit comments

Comments
 (0)