Skip to content

Commit bfc79ee

Browse files
simonat2ndQuadrantkelvich
authored andcommitted
Don’t push nextid too far forwards in recovery
Doing so allows various crash possibilities. Fix by avoiding having PrescanPreparedTransactions() increment ShmemVariableCache->nextXid when it has no 2PC files Bug found by Jeff Janes, diagnosis and patch by Pavan Deolasee, then patch re-designed for clarity and full accuracy by Michael Paquier. Reported-by: Jeff Janes Author: Pavan Deolasee, Michael Paquier Discussion: https://postgr.es/m/CAMkU=1zMLnH_i1-PVQ-biZzvNx7VcuatriquEnh7HNk6K8Ss3Q@mail.gmail.com
1 parent 84d688c commit bfc79ee

File tree

1 file changed

+36
-41
lines changed

1 file changed

+36
-41
lines changed

src/backend/access/transam/twophase.c

Lines changed: 36 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -304,7 +304,7 @@ static void XlogReadTwoPhaseData(XLogRecPtr lsn, char **buf, int *len);
304304
static char *ProcessTwoPhaseBuffer(TransactionId xid,
305305
XLogRecPtr prepare_start_lsn,
306306
bool fromdisk, bool overwriteOK, bool setParent,
307-
TransactionId *result, TransactionId *maxsubxid);
307+
bool setNextXid);
308308
static void MarkAsPreparingGuts(GlobalTransaction gxact, TransactionId xid,
309309
const char *gid, TimestampTz prepared_at, Oid owner,
310310
Oid databaseid);
@@ -1931,7 +1931,7 @@ restoreTwoPhaseData(void)
19311931

19321932
buf = ProcessTwoPhaseBuffer(xid, InvalidXLogRecPtr,
19331933
true, false, false,
1934-
NULL, NULL);
1934+
false);
19351935
if (buf == NULL)
19361936
continue;
19371937

@@ -1974,7 +1974,6 @@ PrescanPreparedTransactions(TransactionId **xids_p, int *nxids_p)
19741974
{
19751975
TransactionId origNextXid = ShmemVariableCache->nextXid;
19761976
TransactionId result = origNextXid;
1977-
TransactionId maxsubxid = origNextXid;
19781977
TransactionId *xids = NULL;
19791978
int nxids = 0;
19801979
int allocsize = 0;
@@ -1994,11 +1993,18 @@ PrescanPreparedTransactions(TransactionId **xids_p, int *nxids_p)
19941993
buf = ProcessTwoPhaseBuffer(xid,
19951994
gxact->prepare_start_lsn,
19961995
gxact->ondisk, false, false,
1997-
&result, &maxsubxid);
1996+
true);
19981997

19991998
if (buf == NULL)
20001999
continue;
20012000

2001+
/*
2002+
* OK, we think this file is valid. Incorporate xid into the
2003+
* running-minimum result.
2004+
*/
2005+
if (TransactionIdPrecedes(xid, result))
2006+
result = xid;
2007+
20022008
if (xids_p)
20032009
{
20042010
if (nxids == allocsize)
@@ -2027,15 +2033,6 @@ PrescanPreparedTransactions(TransactionId **xids_p, int *nxids_p)
20272033
*nxids_p = nxids;
20282034
}
20292035

2030-
/* update nextXid if needed */
2031-
if (TransactionIdFollowsOrEquals(maxsubxid, ShmemVariableCache->nextXid))
2032-
{
2033-
LWLockAcquire(XidGenLock, LW_EXCLUSIVE);
2034-
ShmemVariableCache->nextXid = maxsubxid;
2035-
TransactionIdAdvance(ShmemVariableCache->nextXid);
2036-
LWLockRelease(XidGenLock);
2037-
}
2038-
20392036
return result;
20402037
}
20412038

@@ -2072,7 +2069,7 @@ StandbyRecoverPreparedTransactions(bool overwriteOK)
20722069
buf = ProcessTwoPhaseBuffer(xid,
20732070
gxact->prepare_start_lsn,
20742071
gxact->ondisk, overwriteOK, true,
2075-
NULL, NULL);
2072+
false);
20762073
if (buf != NULL)
20772074
pfree(buf);
20782075
}
@@ -2118,7 +2115,7 @@ RecoverPreparedTransactions(void)
21182115
buf = ProcessTwoPhaseBuffer(xid,
21192116
gxact->prepare_start_lsn,
21202117
gxact->ondisk, false, false,
2121-
NULL, NULL);
2118+
false);
21222119
if (buf == NULL)
21232120
continue;
21242121

@@ -2209,20 +2206,16 @@ RecoverPreparedTransactions(void)
22092206
* If setParent is true, then use the overwriteOK parameter to set up
22102207
* subtransaction parent linkages.
22112208
*
2212-
* If result and maxsubxid are not NULL, fill them up with smallest
2213-
* running transaction id (lesser than ShmemVariableCache->nextXid)
2214-
* and largest subtransaction id for this transaction respectively.
2209+
* If setNextXid is true, set ShmemVariableCache->nextXid to the newest
2210+
* value scanned.
22152211
*/
22162212
static char *
22172213
ProcessTwoPhaseBuffer(TransactionId xid,
22182214
XLogRecPtr prepare_start_lsn,
22192215
bool fromdisk, bool overwriteOK,
2220-
bool setParent, TransactionId *result,
2221-
TransactionId *maxsubxid)
2216+
bool setParent, bool setNextXid)
22222217
{
22232218
TransactionId origNextXid = ShmemVariableCache->nextXid;
2224-
TransactionId res = InvalidTransactionId;
2225-
TransactionId maxsub = InvalidTransactionId;
22262219
TransactionId *subxids;
22272220
char *buf;
22282221
TwoPhaseFileHeader *hdr;
@@ -2233,11 +2226,6 @@ ProcessTwoPhaseBuffer(TransactionId xid,
22332226
if (!fromdisk)
22342227
Assert(prepare_start_lsn != InvalidXLogRecPtr);
22352228

2236-
if (result)
2237-
res = *result;
2238-
if (maxsubxid)
2239-
maxsub = *maxsubxid;
2240-
22412229
/* Already processed? */
22422230
if (TransactionIdDidCommit(xid) || TransactionIdDidAbort(xid))
22432231
{
@@ -2319,13 +2307,6 @@ ProcessTwoPhaseBuffer(TransactionId xid,
23192307
return NULL;
23202308
}
23212309

2322-
/*
2323-
* OK, we think this buffer is valid. Incorporate xid into the
2324-
* running-minimum result.
2325-
*/
2326-
if (TransactionIdPrecedes(xid, res))
2327-
res = xid;
2328-
23292310
/*
23302311
* Examine subtransaction XIDs ... they should all follow main
23312312
* XID, and they may force us to advance nextXid.
@@ -2338,17 +2319,31 @@ ProcessTwoPhaseBuffer(TransactionId xid,
23382319
TransactionId subxid = subxids[i];
23392320

23402321
Assert(TransactionIdFollows(subxid, xid));
2341-
if (TransactionIdFollowsOrEquals(subxid, maxsub))
2342-
maxsub = subxid;
2322+
2323+
/* update nextXid if needed */
2324+
if (setNextXid &&
2325+
TransactionIdFollowsOrEquals(subxid,
2326+
ShmemVariableCache->nextXid))
2327+
{
2328+
/*
2329+
* We don't expect anyone else to modify nextXid, hence we don't
2330+
* need to hold a lock while examining it. We still acquire the
2331+
* lock to modify it, though, so we recheck.
2332+
*/
2333+
LWLockAcquire(XidGenLock, LW_EXCLUSIVE);
2334+
if (TransactionIdFollowsOrEquals(subxid,
2335+
ShmemVariableCache->nextXid))
2336+
{
2337+
ShmemVariableCache->nextXid = subxid;
2338+
TransactionIdAdvance(ShmemVariableCache->nextXid);
2339+
}
2340+
LWLockRelease(XidGenLock);
2341+
}
2342+
23432343
if (setParent)
23442344
SubTransSetParent(xid, subxid, overwriteOK);
23452345
}
23462346

2347-
if (result)
2348-
*result = res;
2349-
if (maxsubxid)
2350-
*maxsubxid = maxsub;
2351-
23522347
return buf;
23532348
}
23542349

0 commit comments

Comments
 (0)