diff --git a/src/backend/access/transam/xact.c b/src/backend/access/transam/xact.c index e7a6606ec3b33d285ac032c18fb64e557abdf821..c24df3f38c2bbf2e0becfe0394cb92fd6b6566d1 100644 --- a/src/backend/access/transam/xact.c +++ b/src/backend/access/transam/xact.c @@ -1949,7 +1949,7 @@ CommitTransaction(void) AtEOXact_SPI(true); AtEOXact_on_commit_actions(true); AtEOXact_Namespace(true); - /* smgrcommit already done */ + AtEOXact_SMgr(); AtEOXact_Files(); AtEOXact_ComboCid(); AtEOXact_HashTables(true); @@ -2202,7 +2202,7 @@ PrepareTransaction(void) AtEOXact_SPI(true); AtEOXact_on_commit_actions(true); AtEOXact_Namespace(true); - /* smgrcommit already done */ + AtEOXact_SMgr(); AtEOXact_Files(); AtEOXact_ComboCid(); AtEOXact_HashTables(true); @@ -2348,6 +2348,7 @@ AbortTransaction(void) AtEOXact_SPI(false); AtEOXact_on_commit_actions(false); AtEOXact_Namespace(false); + AtEOXact_SMgr(); AtEOXact_Files(); AtEOXact_ComboCid(); AtEOXact_HashTables(false); diff --git a/src/backend/postmaster/bgwriter.c b/src/backend/postmaster/bgwriter.c index 748fd85edb0d7175e5ffd99288b92eddd8706d20..709ccf1f2561907f96f1ae2bf8ddc47e326efbb1 100644 --- a/src/backend/postmaster/bgwriter.c +++ b/src/backend/postmaster/bgwriter.c @@ -183,6 +183,7 @@ BackgroundWriterMain(void) false, true); /* we needn't bother with the other ResourceOwnerRelease phases */ AtEOXact_Buffers(false); + AtEOXact_SMgr(); AtEOXact_Files(); AtEOXact_HashTables(false); diff --git a/src/backend/postmaster/checkpointer.c b/src/backend/postmaster/checkpointer.c index c5f32059a792bc407bd01e1fab26c180148ebf3d..18e6a4e8c4be1c1e6041cd854a55f367ffd4ba6e 100644 --- a/src/backend/postmaster/checkpointer.c +++ b/src/backend/postmaster/checkpointer.c @@ -291,6 +291,7 @@ CheckpointerMain(void) false, true); /* we needn't bother with the other ResourceOwnerRelease phases */ AtEOXact_Buffers(false); + AtEOXact_SMgr(); AtEOXact_Files(); AtEOXact_HashTables(false); diff --git a/src/backend/postmaster/walwriter.c b/src/backend/postmaster/walwriter.c index 43139017c276648ec292a81a4684e605701e2205..c3e15ef7595de7373e319540691304285672c178 100644 --- a/src/backend/postmaster/walwriter.c +++ b/src/backend/postmaster/walwriter.c @@ -188,6 +188,7 @@ WalWriterMain(void) false, true); /* we needn't bother with the other ResourceOwnerRelease phases */ AtEOXact_Buffers(false); + AtEOXact_SMgr(); AtEOXact_Files(); AtEOXact_HashTables(false); diff --git a/src/backend/storage/smgr/smgr.c b/src/backend/storage/smgr/smgr.c index 6319d1e8589138be32e498e77cc4ce484211c1a1..5dff8b3702ee64c355a050aa76242b99b6eea151 100644 --- a/src/backend/storage/smgr/smgr.c +++ b/src/backend/storage/smgr/smgr.c @@ -76,11 +76,15 @@ static const int NSmgr = lengthof(smgrsw); /* * Each backend has a hashtable that stores all extant SMgrRelation objects. + * In addition, "unowned" SMgrRelation objects are chained together in a list. */ static HTAB *SMgrRelationHash = NULL; +static SMgrRelation first_unowned_reln = NULL; + /* local function prototypes */ static void smgrshutdown(int code, Datum arg); +static void remove_from_unowned_list(SMgrRelation reln); /* @@ -124,7 +128,7 @@ smgrshutdown(int code, Datum arg) /* * smgropen() -- Return an SMgrRelation object, creating it if need be. * - * This does not attempt to actually open the object. + * This does not attempt to actually open the underlying file. */ SMgrRelation smgropen(RelFileNode rnode, BackendId backend) @@ -144,6 +148,7 @@ smgropen(RelFileNode rnode, BackendId backend) ctl.hash = tag_hash; SMgrRelationHash = hash_create("smgr relation table", 400, &ctl, HASH_ELEM | HASH_FUNCTION); + first_unowned_reln = NULL; } /* Look up or create an entry */ @@ -168,6 +173,10 @@ smgropen(RelFileNode rnode, BackendId backend) /* mark it not open */ for (forknum = 0; forknum <= MAX_FORKNUM; forknum++) reln->md_fd[forknum] = NULL; + + /* place it at head of unowned list (to make smgrsetowner cheap) */ + reln->next_unowned_reln = first_unowned_reln; + first_unowned_reln = reln; } return reln; @@ -182,20 +191,60 @@ smgropen(RelFileNode rnode, BackendId backend) void smgrsetowner(SMgrRelation *owner, SMgrRelation reln) { + /* We don't currently support "disowning" an SMgrRelation here */ + Assert(owner != NULL); + /* * First, unhook any old owner. (Normally there shouldn't be any, but it * seems possible that this can happen during swap_relation_files() * depending on the order of processing. It's ok to close the old * relcache entry early in that case.) + * + * If there isn't an old owner, then the reln should be in the unowned + * list, and we need to remove it. */ if (reln->smgr_owner) *(reln->smgr_owner) = NULL; + else + remove_from_unowned_list(reln); /* Now establish the ownership relationship. */ reln->smgr_owner = owner; *owner = reln; } +/* + * remove_from_unowned_list -- unlink an SMgrRelation from the unowned list + * + * If the reln is not present in the list, nothing happens. Typically this + * would be caller error, but there seems no reason to throw an error. + * + * In the worst case this could be rather slow; but in all the cases that seem + * likely to be performance-critical, the reln being sought will actually be + * first in the list. Furthermore, the number of unowned relns touched in any + * one transaction shouldn't be all that high typically. So it doesn't seem + * worth expending the additional space and management logic needed for a + * doubly-linked list. + */ +static void +remove_from_unowned_list(SMgrRelation reln) +{ + SMgrRelation *link; + SMgrRelation cur; + + for (link = &first_unowned_reln, cur = *link; + cur != NULL; + link = &cur->next_unowned_reln, cur = *link) + { + if (cur == reln) + { + *link = cur->next_unowned_reln; + cur->next_unowned_reln = NULL; + break; + } + } +} + /* * smgrexists() -- Does the underlying file for a fork exist? */ @@ -219,6 +268,9 @@ smgrclose(SMgrRelation reln) owner = reln->smgr_owner; + if (!owner) + remove_from_unowned_list(reln); + if (hash_search(SMgrRelationHash, (void *) &(reln->smgr_rnode), HASH_REMOVE, NULL) == NULL) @@ -600,3 +652,29 @@ smgrpostckpt(void) (*(smgrsw[i].smgr_post_ckpt)) (); } } + +/* + * AtEOXact_SMgr + * + * This routine is called during transaction commit or abort (it doesn't + * particularly care which). All transient SMgrRelation objects are closed. + * + * We do this as a compromise between wanting transient SMgrRelations to + * live awhile (to amortize the costs of blind writes of multiple blocks) + * and needing them to not live forever (since we're probably holding open + * a kernel file descriptor for the underlying file, and we need to ensure + * that gets closed reasonably soon if the file gets deleted). + */ +void +AtEOXact_SMgr(void) +{ + /* + * Zap all unowned SMgrRelations. We rely on smgrclose() to remove each + * one from the list. + */ + while (first_unowned_reln != NULL) + { + Assert(first_unowned_reln->smgr_owner == NULL); + smgrclose(first_unowned_reln); + } +} diff --git a/src/include/storage/smgr.h b/src/include/storage/smgr.h index 92b9198d5428c1f51617f595d062da1bc8af5106..9eccf7671ed99be4c068e3df50a75c21c1ce9e51 100644 --- a/src/include/storage/smgr.h +++ b/src/include/storage/smgr.h @@ -33,6 +33,9 @@ * without having to make the smgr explicitly aware of relcache. There * can't be more than one "owner" pointer per SMgrRelation, but that's * all we need. + * + * SMgrRelations that do not have an "owner" are considered to be transient, + * and are deleted at end of transaction. */ typedef struct SMgrRelationData { @@ -63,6 +66,9 @@ typedef struct SMgrRelationData /* for md.c; NULL for forks that are not open */ struct _MdfdVec *md_fd[MAX_FORKNUM + 1]; + + /* if unowned, list link in list of all unowned SMgrRelations */ + struct SMgrRelationData *next_unowned_reln; } SMgrRelationData; typedef SMgrRelationData *SMgrRelation; @@ -95,6 +101,7 @@ extern void smgrimmedsync(SMgrRelation reln, ForkNumber forknum); extern void smgrpreckpt(void); extern void smgrsync(void); extern void smgrpostckpt(void); +extern void AtEOXact_SMgr(void); /* internals: move me elsewhere -- ay 7/94 */