Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit3930be4

Browse files
simonat2ndQuadrantkelvich
authored andcommitted
Rework handling of subtransactions in 2PC recovery
The bug fixed by0874d4fcaused us to question and rework the handling ofsubtransactions in 2PC during and at end of recovery.Patch adds checks and tests to ensure no further bugs.This effectively removes the temporary measure put in placeby546c13e.Author: Simon RiggsReviewed-by: Tom Lane, Michael PaquierDiscussion:http://postgr.es/m/CANP8+j+vvXmruL_i2buvdhMeVv5TQu0Hm2+C5N+kdVwHJuor8w@mail.gmail.com
1 parent5c416ba commit3930be4

File tree

7 files changed

+50
-57
lines changed

7 files changed

+50
-57
lines changed

‎src/backend/access/transam/subtrans.c

Lines changed: 22 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -68,32 +68,35 @@ static intZeroSUBTRANSPage(int64 pageno);
6868

6969
/*
7070
* Record the parent of a subtransaction in the subtrans log.
71-
*
72-
* In some cases we may need to overwrite an existing value.
7371
*/
7472
void
75-
SubTransSetParent(TransactionIdxid,TransactionIdparent,booloverwriteOK)
73+
SubTransSetParent(TransactionIdxid,TransactionIdparent)
7674
{
7775
int64pageno=TransactionIdToPage(xid);
7876
intentryno=TransactionIdToEntry(xid);
7977
intslotno;
8078
TransactionId*ptr;
8179

8280
Assert(TransactionIdIsValid(parent));
81+
Assert(TransactionIdFollows(xid,parent));
8382

8483
LWLockAcquire(SubtransControlLock,LW_EXCLUSIVE);
8584

8685
slotno=SimpleLruReadPage(SubTransCtl,pageno, true,xid);
8786
ptr= (TransactionId*)SubTransCtl->shared->page_buffer[slotno];
8887
ptr+=entryno;
8988

90-
/* Current state should be 0 */
91-
Assert(*ptr==InvalidTransactionId||
92-
(*ptr==parent&&overwriteOK));
93-
94-
*ptr=parent;
95-
96-
SubTransCtl->shared->page_dirty[slotno]= true;
89+
/*
90+
* It's possible we'll try to set the parent xid multiple times
91+
* but we shouldn't ever be changing the xid from one valid xid
92+
* to another valid xid, which would corrupt the data structure.
93+
*/
94+
if (*ptr!=parent)
95+
{
96+
Assert(*ptr==InvalidTransactionId);
97+
*ptr=parent;
98+
SubTransCtl->shared->page_dirty[slotno]= true;
99+
}
97100

98101
LWLockRelease(SubtransControlLock);
99102
}
@@ -160,6 +163,15 @@ SubTransGetTopmostTransaction(TransactionId xid)
160163
if (TransactionIdPrecedes(parentXid,TransactionXmin))
161164
break;
162165
parentXid=SubTransGetParent(parentXid);
166+
167+
/*
168+
* By convention the parent xid gets allocated first, so should
169+
* always precede the child xid. Anything else points to a corrupted
170+
* data structure that could lead to an infinite loop, so exit.
171+
*/
172+
if (!TransactionIdPrecedes(parentXid,previousXid))
173+
elog(ERROR,"pg_subtrans contains invalid entry: xid %u points to parent xid %u",
174+
previousXid,parentXid);
163175
}
164176

165177
Assert(TransactionIdIsValid(previousXid));

‎src/backend/access/transam/twophase.c

Lines changed: 22 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -303,8 +303,7 @@ static void RemoveGXact(GlobalTransaction gxact);
303303
staticvoidXlogReadTwoPhaseData(XLogRecPtrlsn,char**buf,int*len);
304304
staticchar*ProcessTwoPhaseBuffer(TransactionIdxid,
305305
XLogRecPtrprepare_start_lsn,
306-
boolfromdisk,booloverwriteOK,boolsetParent,
307-
boolsetNextXid);
306+
boolfromdisk,boolsetParent,boolsetNextXid);
308307
staticvoidMarkAsPreparingGuts(GlobalTransactiongxact,TransactionIdxid,
309308
constchar*gid,TimestampTzprepared_at,Oidowner,
310309
Oiddatabaseid);
@@ -1930,8 +1929,7 @@ restoreTwoPhaseData(void)
19301929
xid= (TransactionId)strtoul(clde->d_name,NULL,16);
19311930

19321931
buf=ProcessTwoPhaseBuffer(xid,InvalidXLogRecPtr,
1933-
true, false, false,
1934-
false);
1932+
true, false, false);
19351933
if (buf==NULL)
19361934
continue;
19371935

@@ -1992,8 +1990,7 @@ PrescanPreparedTransactions(TransactionId **xids_p, int *nxids_p)
19921990

19931991
buf=ProcessTwoPhaseBuffer(xid,
19941992
gxact->prepare_start_lsn,
1995-
gxact->ondisk, false, false,
1996-
true);
1993+
gxact->ondisk, false, true);
19971994

19981995
if (buf==NULL)
19991996
continue;
@@ -2046,12 +2043,12 @@ PrescanPreparedTransactions(TransactionId **xids_p, int *nxids_p)
20462043
* This is never called at the end of recovery - we use
20472044
* RecoverPreparedTransactions() at that point.
20482045
*
2049-
*Currently we simply callSubTransSetParent()for any subxids of prepared
2050-
*transactions. If overwriteOK is true, it's OK if some XIDs have already
2051-
*been marked in pg_subtrans.
2046+
*The lack of calls toSubTransSetParent()calls here is by design;
2047+
*those calls are made by RecoverPreparedTransactions() at the end of recovery
2048+
*for those xacts that need this.
20522049
*/
20532050
void
2054-
StandbyRecoverPreparedTransactions(booloverwriteOK)
2051+
StandbyRecoverPreparedTransactions(void)
20552052
{
20562053
inti;
20572054

@@ -2068,8 +2065,7 @@ StandbyRecoverPreparedTransactions(bool overwriteOK)
20682065

20692066
buf=ProcessTwoPhaseBuffer(xid,
20702067
gxact->prepare_start_lsn,
2071-
gxact->ondisk,overwriteOK, true,
2072-
false);
2068+
gxact->ondisk, false, false);
20732069
if (buf!=NULL)
20742070
pfree(buf);
20752071
}
@@ -2082,8 +2078,7 @@ StandbyRecoverPreparedTransactions(bool overwriteOK)
20822078
* Scan the shared memory entries of TwoPhaseState and reload the state for
20832079
* each prepared transaction (reacquire locks, etc).
20842080
*
2085-
* This is run at the end of recovery, but before we allow backends to write
2086-
* WAL.
2081+
* This is run during database startup.
20872082
*
20882083
* At the end of recovery the way we take snapshots will change. We now need
20892084
* to mark all running transactions with their full SubTransSetParent() info
@@ -2107,15 +2102,21 @@ RecoverPreparedTransactions(void)
21072102
TwoPhaseFileHeader*hdr;
21082103
TransactionId*subxids;
21092104
constchar*gid;
2110-
booloverwriteOK= false;
2111-
inti;
21122105

21132106
xid=gxact->xid;
21142107

2108+
/*
2109+
* Reconstruct subtrans state for the transaction --- needed
2110+
* because pg_subtrans is not preserved over a restart. Note that
2111+
* we are linking all the subtransactions directly to the
2112+
* top-level XID; there may originally have been a more complex
2113+
* hierarchy, but there's no need to restore that exactly.
2114+
* It's possible that SubTransSetParent has been set before, if
2115+
* the prepared transaction generated xid assignment records.
2116+
*/
21152117
buf=ProcessTwoPhaseBuffer(xid,
21162118
gxact->prepare_start_lsn,
2117-
gxact->ondisk, false, false,
2118-
false);
2119+
gxact->ondisk, true, false);
21192120
if (buf==NULL)
21202121
continue;
21212122

@@ -2133,25 +2134,6 @@ RecoverPreparedTransactions(void)
21332134
bufptr+=MAXALIGN(hdr->nabortrels*sizeof(RelFileNode));
21342135
bufptr+=MAXALIGN(hdr->ninvalmsgs*sizeof(SharedInvalidationMessage));
21352136

2136-
/*
2137-
* It's possible that SubTransSetParent has been set before, if
2138-
* the prepared transaction generated xid assignment records. Test
2139-
* here must match one used in AssignTransactionId().
2140-
*/
2141-
if (InHotStandby&& (hdr->nsubxacts >=PGPROC_MAX_CACHED_SUBXIDS||
2142-
XLogLogicalInfoActive()))
2143-
overwriteOK= true;
2144-
2145-
/*
2146-
* Reconstruct subtrans state for the transaction --- needed
2147-
* because pg_subtrans is not preserved over a restart. Note that
2148-
* we are linking all the subtransactions directly to the
2149-
* top-level XID; there may originally have been a more complex
2150-
* hierarchy, but there's no need to restore that exactly.
2151-
*/
2152-
for (i=0;i<hdr->nsubxacts;i++)
2153-
SubTransSetParent(subxids[i],xid, true);
2154-
21552137
/*
21562138
* Recreate its GXACT and dummy PGPROC. But, check whether
21572139
* it was added in redo and already has a shmem entry for
@@ -2203,16 +2185,15 @@ RecoverPreparedTransactions(void)
22032185
* Given a transaction id, read it either from disk or read it directly
22042186
* via shmem xlog record pointer using the provided "prepare_start_lsn".
22052187
*
2206-
* If setParent is true, then use the overwriteOK parameter to set up
2207-
* subtransaction parent linkages.
2188+
* If setParent is true, set up subtransaction parent linkages.
22082189
*
22092190
* If setNextXid is true, set ShmemVariableCache->nextXid to the newest
22102191
* value scanned.
22112192
*/
22122193
staticchar*
22132194
ProcessTwoPhaseBuffer(TransactionIdxid,
22142195
XLogRecPtrprepare_start_lsn,
2215-
boolfromdisk,booloverwriteOK,
2196+
boolfromdisk,
22162197
boolsetParent,boolsetNextXid)
22172198
{
22182199
TransactionIdorigNextXid=ShmemVariableCache->nextXid;
@@ -2341,7 +2322,7 @@ ProcessTwoPhaseBuffer(TransactionId xid,
23412322
}
23422323

23432324
if (setParent)
2344-
SubTransSetParent(subxid,xid,overwriteOK);
2325+
SubTransSetParent(subxid,xid);
23452326
}
23462327

23472328
returnbuf;

‎src/backend/access/transam/xact.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -577,7 +577,7 @@ AssignTransactionId(TransactionState s)
577577
XactTopTransactionId=s->transactionId;
578578

579579
if (isSubXact)
580-
SubTransSetParent(s->transactionId,s->parent->transactionId, false);
580+
SubTransSetParent(s->transactionId,s->parent->transactionId);
581581

582582
/*
583583
* If it's a top-level transaction, the predicate locking system needs to

‎src/backend/access/transam/xlog.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6690,7 +6690,7 @@ StartupXLOG(void)
66906690

66916691
ProcArrayApplyRecoveryInfo(&running);
66926692

6693-
StandbyRecoverPreparedTransactions(false);
6693+
StandbyRecoverPreparedTransactions();
66946694
}
66956695
}
66966696

@@ -9356,7 +9356,7 @@ xlog_redo(XLogReaderState *record)
93569356

93579357
ProcArrayApplyRecoveryInfo(&running);
93589358

9359-
StandbyRecoverPreparedTransactions(true);
9359+
StandbyRecoverPreparedTransactions();
93609360
}
93619361

93629362
/* ControlFile->checkPointCopy always tracks the latest ckpt XID */

‎src/backend/storage/ipc/procarray.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1012,7 +1012,7 @@ ProcArrayApplyXidAssignment(TransactionId topxid,
10121012
* have attempted to SubTransSetParent().
10131013
*/
10141014
for (i=0;i<nsubxids;i++)
1015-
SubTransSetParent(subxids[i],topxid, false);
1015+
SubTransSetParent(subxids[i],topxid);
10161016

10171017
/* KnownAssignedXids isn't maintained yet, so we're done for now */
10181018
if (standbyState==STANDBY_INITIALIZED)

‎src/include/access/subtrans.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
/* Number of SLRU buffers to use for subtrans */
1515
#defineNUM_SUBTRANS_BUFFERS32
1616

17-
externvoidSubTransSetParent(TransactionIdxid,TransactionIdparent,booloverwriteOK);
17+
externvoidSubTransSetParent(TransactionIdxid,TransactionIdparent);
1818
externTransactionIdSubTransGetParent(TransactionIdxid);
1919
externTransactionIdSubTransGetTopmostTransaction(TransactionIdxid);
2020

‎src/include/access/twophase.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ extern TransactionId PrescanPreparedTransactions(TransactionId **xids_p,
5757
int*nxids_p);
5858
externvoidParsePrepareRecord(uint8info,char*xlrec,
5959
xl_xact_parsed_prepare*parsed);
60-
externvoidStandbyRecoverPreparedTransactions(booloverwriteOK);
60+
externvoidStandbyRecoverPreparedTransactions(void);
6161
externvoidRecoverPreparedTransactions(void);
6262

6363
externvoidCheckPointTwoPhase(XLogRecPtrredo_horizon);

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp