Rethink PROCSIGNAL_BARRIER_SMGRRELEASE.

With sufficiently bad luck, it was possible for IssuePendingWritebacks()
to reopen a file after we'd processed PROCSIGNAL_BARRIER_SMGRRELEASE and
before the file was unlinked by some other backend.  That left a small
hole in commit 4eb21763's plan to fix all spurious errors from DROP
TABLESPACE and similar on Windows.

Fix by closing md.c's segments, instead of just closing fd.c's
descriptors, and then teaching smgrwriteback() not to open files that
aren't already open.

Reported-by: Andres Freund <andres@anarazel.de>
Reviewed-by: Robert Haas <robertmhaas@gmail.com>
Discussion: https://postgr.es/m/20220209220004.kb3dgtn2x2k2gtdm%40alap3.anarazel.de
This commit is contained in:
Thomas Munro 2022-05-07 16:19:42 +12:00
parent 701d918a42
commit b74e94dc27
4 changed files with 52 additions and 18 deletions

View File

@ -116,6 +116,8 @@ static MemoryContext MdCxt; /* context for all MdfdVec objects */
* mdnblocks().
*/
#define EXTENSION_DONT_CHECK_SIZE (1 << 4)
/* don't try to open a segment, if not already open */
#define EXTENSION_DONT_OPEN (1 << 5)
/* local routines */
@ -551,12 +553,6 @@ mdclose(SMgrRelation reln, ForkNumber forknum)
}
}
void
mdrelease(void)
{
closeAllVfds();
}
/*
* mdprefetch() -- Initiate asynchronous read of the specified block of a relation
*/
@ -605,11 +601,14 @@ mdwriteback(SMgrRelation reln, ForkNumber forknum,
segnum_end;
v = _mdfd_getseg(reln, forknum, blocknum, true /* not used */ ,
EXTENSION_RETURN_NULL);
EXTENSION_DONT_OPEN);
/*
* We might be flushing buffers of already removed relations, that's
* ok, just ignore that case.
* ok, just ignore that case. If the segment file wasn't open already
* (ie from a recent mdwrite()), then we don't want to re-open it, to
* avoid a race with PROCSIGNAL_BARRIER_SMGRRELEASE that might leave
* us with a descriptor to a file that is about to be unlinked.
*/
if (!v)
return;
@ -1202,7 +1201,8 @@ _mdfd_getseg(SMgrRelation reln, ForkNumber forknum, BlockNumber blkno,
/* some way to handle non-existent segments needs to be specified */
Assert(behavior &
(EXTENSION_FAIL | EXTENSION_CREATE | EXTENSION_RETURN_NULL));
(EXTENSION_FAIL | EXTENSION_CREATE | EXTENSION_RETURN_NULL |
EXTENSION_DONT_OPEN));
targetseg = blkno / ((BlockNumber) RELSEG_SIZE);
@ -1213,6 +1213,10 @@ _mdfd_getseg(SMgrRelation reln, ForkNumber forknum, BlockNumber blkno,
return v;
}
/* The caller only wants the segment if we already had it open. */
if (behavior & EXTENSION_DONT_OPEN)
return NULL;
/*
* The target segment is not yet open. Iterate over all the segments
* between the last opened and the target segment. This way missing

View File

@ -41,7 +41,6 @@ typedef struct f_smgr
{
void (*smgr_init) (void); /* may be NULL */
void (*smgr_shutdown) (void); /* may be NULL */
void (*smgr_release) (void); /* may be NULL */
void (*smgr_open) (SMgrRelation reln);
void (*smgr_close) (SMgrRelation reln, ForkNumber forknum);
void (*smgr_create) (SMgrRelation reln, ForkNumber forknum,
@ -70,7 +69,6 @@ static const f_smgr smgrsw[] = {
{
.smgr_init = mdinit,
.smgr_shutdown = NULL,
.smgr_release = mdrelease,
.smgr_open = mdopen,
.smgr_close = mdclose,
.smgr_create = mdcreate,
@ -281,6 +279,42 @@ smgrclose(SMgrRelation reln)
*owner = NULL;
}
/*
* smgrrelease() -- Release all resources used by this object.
*
* The object remains valid.
*/
void
smgrrelease(SMgrRelation reln)
{
for (ForkNumber forknum = 0; forknum <= MAX_FORKNUM; forknum++)
{
smgrsw[reln->smgr_which].smgr_close(reln, forknum);
reln->smgr_cached_nblocks[forknum] = InvalidBlockNumber;
}
}
/*
* smgrreleaseall() -- Release resources used by all objects.
*
* This is called for PROCSIGNAL_BARRIER_SMGRRELEASE.
*/
void
smgrreleaseall(void)
{
HASH_SEQ_STATUS status;
SMgrRelation reln;
/* Nothing to do if hashtable not set up */
if (SMgrRelationHash == NULL)
return;
hash_seq_init(&status, SMgrRelationHash);
while ((reln = (SMgrRelation) hash_seq_search(&status)) != NULL)
smgrrelease(reln);
}
/*
* smgrcloseall() -- Close all existing SMgrRelation objects.
*/
@ -698,11 +732,6 @@ AtEOXact_SMgr(void)
bool
ProcessBarrierSmgrRelease(void)
{
for (int i = 0; i < NSmgr; i++)
{
if (smgrsw[i].smgr_release)
smgrsw[i].smgr_release();
}
smgrreleaseall();
return true;
}

View File

@ -23,7 +23,6 @@
extern void mdinit(void);
extern void mdopen(SMgrRelation reln);
extern void mdclose(SMgrRelation reln, ForkNumber forknum);
extern void mdrelease(void);
extern void mdcreate(SMgrRelation reln, ForkNumber forknum, bool isRedo);
extern bool mdexists(SMgrRelation reln, ForkNumber forknum);
extern void mdunlink(RelFileNodeBackend rnode, ForkNumber forknum, bool isRedo);

View File

@ -85,6 +85,8 @@ extern void smgrclearowner(SMgrRelation *owner, SMgrRelation reln);
extern void smgrclose(SMgrRelation reln);
extern void smgrcloseall(void);
extern void smgrclosenode(RelFileNodeBackend rnode);
extern void smgrrelease(SMgrRelation reln);
extern void smgrreleaseall(void);
extern void smgrcreate(SMgrRelation reln, ForkNumber forknum, bool isRedo);
extern void smgrdosyncall(SMgrRelation *rels, int nrels);
extern void smgrdounlinkall(SMgrRelation *rels, int nrels, bool isRedo);