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

Commitcd87b6f

Browse files
committed
Fix an old bug in multixact and two-phase commit. Prepared transactions can
be part of multixacts, so allocate a slot for each prepared transaction inthe "oldest member" array in multixact.c. On PREPARE TRANSACTION, transferthe oldest member value from the current backends slot to the prepared xactslot. Also save and recover the value from the 2pc state file.The symptom of the bug was that after a transaction prepared, a shared lockstill held by the prepared transaction was sometimes ignored by othertransactions.Fix back to 8.1, where both 2PC and multixact were introduced.
1 parentc194ff2 commitcd87b6f

File tree

7 files changed

+191
-19
lines changed

7 files changed

+191
-19
lines changed

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

Lines changed: 132 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@
4242
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
4343
* Portions Copyright (c) 1994, Regents of the University of California
4444
*
45-
* $PostgreSQL: pgsql/src/backend/access/transam/multixact.c,v 1.31 2009/06/26 20:29:04 tgl Exp $
45+
* $PostgreSQL: pgsql/src/backend/access/transam/multixact.c,v 1.32 2009/11/23 09:58:36 heikki Exp $
4646
*
4747
*-------------------------------------------------------------------------
4848
*/
@@ -51,6 +51,8 @@
5151
#include"access/multixact.h"
5252
#include"access/slru.h"
5353
#include"access/transam.h"
54+
#include"access/twophase.h"
55+
#include"access/twophase_rmgr.h"
5456
#include"access/xact.h"
5557
#include"miscadmin.h"
5658
#include"pg_trace.h"
@@ -118,8 +120,11 @@ typedef struct MultiXactStateData
118120
/*
119121
* Per-backend data starts here. We have two arrays stored in the area
120122
* immediately following the MultiXactStateData struct. Each is indexed by
121-
* BackendId. (Note: valid BackendIds run from 1 to MaxBackends; element
122-
* zero of each array is never used.)
123+
* BackendId.
124+
*
125+
* In both arrays, there's a slot for all normal backends (1..MaxBackends)
126+
* followed by a slot for max_prepared_xacts prepared transactions. Valid
127+
* BackendIds start from 1; element zero of each array is never used.
123128
*
124129
* OldestMemberMXactId[k] is the oldest MultiXactId each backend's current
125130
* transaction(s) could possibly be a member of, or InvalidMultiXactId
@@ -152,6 +157,12 @@ typedef struct MultiXactStateData
152157
MultiXactIdperBackendXactIds[1];/* VARIABLE LENGTH ARRAY */
153158
}MultiXactStateData;
154159

160+
/*
161+
* Last element of OldestMemberMXactID and OldestVisibleMXactId arrays.
162+
* Valid elements are (1..MaxOldestSlot); element 0 is never used.
163+
*/
164+
#defineMaxOldestSlot(MaxBackends + max_prepared_xacts)
165+
155166
/* Pointers to the state data in shared memory */
156167
staticMultiXactStateData*MultiXactState;
157168
staticMultiXactId*OldestMemberMXactId;
@@ -539,7 +550,7 @@ MultiXactIdSetOldestVisible(void)
539550
if (oldestMXact<FirstMultiXactId)
540551
oldestMXact=FirstMultiXactId;
541552

542-
for (i=1;i <=MaxBackends;i++)
553+
for (i=1;i <=MaxOldestSlot;i++)
543554
{
544555
MultiXactIdthisoldest=OldestMemberMXactId[i];
545556

@@ -1275,6 +1286,119 @@ AtEOXact_MultiXact(void)
12751286
MXactCache=NULL;
12761287
}
12771288

1289+
/*
1290+
* AtPrepare_MultiXact
1291+
*Save multixact state at 2PC tranasction prepare
1292+
*
1293+
* In this phase, we only store our OldestMemberMXactId value in the two-phase
1294+
* state file.
1295+
*/
1296+
void
1297+
AtPrepare_MultiXact(void)
1298+
{
1299+
MultiXactIdmyOldestMember=OldestMemberMXactId[MyBackendId];
1300+
1301+
if (MultiXactIdIsValid(myOldestMember))
1302+
RegisterTwoPhaseRecord(TWOPHASE_RM_MULTIXACT_ID,0,
1303+
&myOldestMember,sizeof(MultiXactId));
1304+
}
1305+
1306+
/*
1307+
* PostPrepare_MultiXact
1308+
*Clean up after successful PREPARE TRANSACTION
1309+
*/
1310+
void
1311+
PostPrepare_MultiXact(TransactionIdxid)
1312+
{
1313+
MultiXactIdmyOldestMember;
1314+
1315+
/*
1316+
* Transfer our OldestMemberMXactId value to the slot reserved for the
1317+
* prepared transaction.
1318+
*/
1319+
myOldestMember=OldestMemberMXactId[MyBackendId];
1320+
if (MultiXactIdIsValid(myOldestMember))
1321+
{
1322+
BackendIddummyBackendId=TwoPhaseGetDummyBackendId(xid);
1323+
1324+
/*
1325+
* Even though storing MultiXactId is atomic, acquire lock to make sure
1326+
* others see both changes, not just the reset of the slot of the
1327+
* current backend. Using a volatile pointer might suffice, but this
1328+
* isn't a hot spot.
1329+
*/
1330+
LWLockAcquire(MultiXactGenLock,LW_EXCLUSIVE);
1331+
1332+
OldestMemberMXactId[dummyBackendId]=myOldestMember;
1333+
OldestMemberMXactId[MyBackendId]=InvalidMultiXactId;
1334+
1335+
LWLockRelease(MultiXactGenLock);
1336+
}
1337+
1338+
/*
1339+
* We don't need to transfer OldestVisibleMXactId value, because the
1340+
* transaction is not going to be looking at any more multixacts once
1341+
* it's prepared.
1342+
*
1343+
* We assume that storing a MultiXactId is atomic and so we need not take
1344+
* MultiXactGenLock to do this.
1345+
*/
1346+
OldestVisibleMXactId[MyBackendId]=InvalidMultiXactId;
1347+
1348+
/*
1349+
* Discard the local MultiXactId cache like in AtEOX_MultiXact
1350+
*/
1351+
MXactContext=NULL;
1352+
MXactCache=NULL;
1353+
}
1354+
1355+
/*
1356+
* multixact_twophase_recover
1357+
*Recover the state of a prepared transaction at startup
1358+
*/
1359+
void
1360+
multixact_twophase_recover(TransactionIdxid,uint16info,
1361+
void*recdata,uint32len)
1362+
{
1363+
BackendIddummyBackendId=TwoPhaseGetDummyBackendId(xid);
1364+
MultiXactIdoldestMember;
1365+
1366+
/*
1367+
* Get the oldest member XID from the state file record, and set it in
1368+
* the OldestMemberMXactId slot reserved for this prepared transaction.
1369+
*/
1370+
Assert(len==sizeof(MultiXactId));
1371+
oldestMember=*((MultiXactId*)recdata);
1372+
1373+
OldestMemberMXactId[dummyBackendId]=oldestMember;
1374+
}
1375+
1376+
/*
1377+
* multixact_twophase_postcommit
1378+
*Similar to AtEOX_MultiXact but for COMMIT PREPARED
1379+
*/
1380+
void
1381+
multixact_twophase_postcommit(TransactionIdxid,uint16info,
1382+
void*recdata,uint32len)
1383+
{
1384+
BackendIddummyBackendId=TwoPhaseGetDummyBackendId(xid);
1385+
1386+
Assert(len==sizeof(MultiXactId));
1387+
1388+
OldestMemberMXactId[dummyBackendId]=InvalidMultiXactId;
1389+
}
1390+
1391+
/*
1392+
* multixact_twophase_postabort
1393+
*This is actually just the same as the COMMIT case.
1394+
*/
1395+
void
1396+
multixact_twophase_postabort(TransactionIdxid,uint16info,
1397+
void*recdata,uint32len)
1398+
{
1399+
multixact_twophase_postcommit(xid,info,recdata,len);
1400+
}
1401+
12781402
/*
12791403
* Initialization of shared memory for MultiXact. We use two SLRU areas,
12801404
* thus double memory.Also, reserve space for the shared MultiXactState
@@ -1287,7 +1411,7 @@ MultiXactShmemSize(void)
12871411

12881412
#defineSHARED_MULTIXACT_STATE_SIZE \
12891413
add_size(sizeof(MultiXactStateData), \
1290-
mul_size(sizeof(MultiXactId) * 2,MaxBackends))
1414+
mul_size(sizeof(MultiXactId) * 2,MaxOldestSlot))
12911415

12921416
size=SHARED_MULTIXACT_STATE_SIZE;
12931417
size=add_size(size,SimpleLruShmemSize(NUM_MXACTOFFSET_BUFFERS,0));
@@ -1329,10 +1453,10 @@ MultiXactShmemInit(void)
13291453

13301454
/*
13311455
* Set up array pointers. Note that perBackendXactIds[0] is wasted space
1332-
* since we only use indexes 1..MaxBackends in each array.
1456+
* since we only use indexes 1..MaxOldestSlot in each array.
13331457
*/
13341458
OldestMemberMXactId=MultiXactState->perBackendXactIds;
1335-
OldestVisibleMXactId=OldestMemberMXactId+MaxBackends;
1459+
OldestVisibleMXactId=OldestMemberMXactId+MaxOldestSlot;
13361460
}
13371461

13381462
/*
@@ -1702,7 +1826,7 @@ TruncateMultiXact(void)
17021826
nextMXact=FirstMultiXactId;
17031827

17041828
oldestMXact=nextMXact;
1705-
for (i=1;i <=MaxBackends;i++)
1829+
for (i=1;i <=MaxOldestSlot;i++)
17061830
{
17071831
MultiXactIdthisoldest;
17081832

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

Lines changed: 32 additions & 1 deletion
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/access/transam/twophase.c,v 1.55 2009/09/01 04:15:45 tgl Exp $
10+
*$PostgreSQL: pgsql/src/backend/access/transam/twophase.c,v 1.56 2009/11/23 09:58:36 heikki Exp $
1111
*
1212
* NOTES
1313
*Each global transaction is associated with a global transaction
@@ -109,6 +109,7 @@ intmax_prepared_xacts = 0;
109109
typedefstructGlobalTransactionData
110110
{
111111
PGPROCproc;/* dummy proc */
112+
BackendIddummyBackendId;/* similar to backend id for backends */
112113
TimestampTzprepared_at;/* time of preparation */
113114
XLogRecPtrprepare_lsn;/* XLOG offset of prepare record */
114115
Oidowner;/* ID of user that executed the xact */
@@ -200,6 +201,20 @@ TwoPhaseShmemInit(void)
200201
{
201202
gxacts[i].proc.links.next= (SHM_QUEUE*)TwoPhaseState->freeGXacts;
202203
TwoPhaseState->freeGXacts=&gxacts[i];
204+
205+
/*
206+
* Assign a unique ID for each dummy proc, so that the range of
207+
* dummy backend IDs immediately follows the range of normal
208+
* backend IDs. We don't dare to assign a real backend ID to
209+
* dummy procs, because prepared transactions don't take part in
210+
* cache invalidation like a real backend ID would imply, but
211+
* having a unique ID for them is nevertheless handy. This
212+
* arrangement allows you to allocate an array of size
213+
* (MaxBackends + max_prepared_xacts + 1), and have a slot for
214+
* every backend and prepared transaction. Currently multixact.c
215+
* uses that technique.
216+
*/
217+
gxacts[i].dummyBackendId=MaxBackends+1+i;
203218
}
204219
}
205220
else
@@ -647,6 +662,22 @@ pg_prepared_xact(PG_FUNCTION_ARGS)
647662
SRF_RETURN_DONE(funcctx);
648663
}
649664

665+
/*
666+
* TwoPhaseGetDummyProc
667+
*Get the dummy backend ID for prepared transaction specified by XID
668+
*
669+
* Dummy backend IDs are similar to real backend IDs of real backends.
670+
* They start at MaxBackends + 1, and are unique across all currently active
671+
* real backends and prepared transactions.
672+
*/
673+
BackendId
674+
TwoPhaseGetDummyBackendId(TransactionIdxid)
675+
{
676+
PGPROC*proc=TwoPhaseGetDummyProc(xid);
677+
678+
return ((GlobalTransaction)proc)->dummyBackendId;
679+
}
680+
650681
/*
651682
* TwoPhaseGetDummyProc
652683
*Get the PGPROC that represents a prepared transaction specified by XID

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

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,13 @@
88
*
99
*
1010
* IDENTIFICATION
11-
* $PostgreSQL: pgsql/src/backend/access/transam/twophase_rmgr.c,v 1.9 2009/09/01 02:54:51 alvherre Exp $
11+
* $PostgreSQL: pgsql/src/backend/access/transam/twophase_rmgr.c,v 1.10 2009/11/23 09:58:36 heikki Exp $
1212
*
1313
*-------------------------------------------------------------------------
1414
*/
1515
#include"postgres.h"
1616

17+
#include"access/multixact.h"
1718
#include"access/twophase_rmgr.h"
1819
#include"commands/async.h"
1920
#include"pgstat.h"
@@ -27,7 +28,8 @@ const TwoPhaseCallback twophase_recover_callbacks[TWOPHASE_RM_MAX_ID + 1] =
2728
lock_twophase_recover,/* Lock */
2829
NULL,/* Inval */
2930
NULL,/* notify/listen */
30-
NULL/* pgstat */
31+
NULL,/* pgstat */
32+
multixact_twophase_recover/* MultiXact */
3133
};
3234

3335
constTwoPhaseCallbacktwophase_postcommit_callbacks[TWOPHASE_RM_MAX_ID+1]=
@@ -36,7 +38,8 @@ const TwoPhaseCallback twophase_postcommit_callbacks[TWOPHASE_RM_MAX_ID + 1] =
3638
lock_twophase_postcommit,/* Lock */
3739
inval_twophase_postcommit,/* Inval */
3840
notify_twophase_postcommit,/* notify/listen */
39-
pgstat_twophase_postcommit/* pgstat */
41+
pgstat_twophase_postcommit,/* pgstat */
42+
multixact_twophase_postcommit/* MultiXact */
4043
};
4144

4245
constTwoPhaseCallbacktwophase_postabort_callbacks[TWOPHASE_RM_MAX_ID+1]=
@@ -45,5 +48,6 @@ const TwoPhaseCallback twophase_postabort_callbacks[TWOPHASE_RM_MAX_ID + 1] =
4548
lock_twophase_postabort,/* Lock */
4649
NULL,/* Inval */
4750
NULL,/* notify/listen */
48-
pgstat_twophase_postabort/* pgstat */
51+
pgstat_twophase_postabort,/* pgstat */
52+
multixact_twophase_postabort/* MultiXact */
4953
};

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

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
*
1111
*
1212
* IDENTIFICATION
13-
* $PostgreSQL: pgsql/src/backend/access/transam/xact.c,v 1.275 2009/09/01 02:54:51 alvherre Exp $
13+
* $PostgreSQL: pgsql/src/backend/access/transam/xact.c,v 1.276 2009/11/23 09:58:36 heikki Exp $
1414
*
1515
*-------------------------------------------------------------------------
1616
*/
@@ -1856,6 +1856,7 @@ PrepareTransaction(void)
18561856
AtPrepare_Inval();
18571857
AtPrepare_Locks();
18581858
AtPrepare_PgStat();
1859+
AtPrepare_MultiXact();
18591860

18601861
/*
18611862
* Here is where we really truly prepare.
@@ -1909,7 +1910,7 @@ PrepareTransaction(void)
19091910

19101911
PostPrepare_smgr();
19111912

1912-
AtEOXact_MultiXact();
1913+
PostPrepare_MultiXact(xid);
19131914

19141915
PostPrepare_Locks(xid);
19151916

‎src/include/access/multixact.h

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
77
* Portions Copyright (c) 1994, Regents of the University of California
88
*
9-
* $PostgreSQL: pgsql/src/include/access/multixact.h,v 1.14 2009/01/01 17:23:56 momjian Exp $
9+
* $PostgreSQL: pgsql/src/include/access/multixact.h,v 1.15 2009/11/23 09:58:36 heikki Exp $
1010
*/
1111
#ifndefMULTIXACT_H
1212
#defineMULTIXACT_H
@@ -52,6 +52,8 @@ extern void MultiXactIdSetOldestMember(void);
5252
externintGetMultiXactIdMembers(MultiXactIdmulti,TransactionId**xids);
5353

5454
externvoidAtEOXact_MultiXact(void);
55+
externvoidAtPrepare_MultiXact(void);
56+
externvoidPostPrepare_MultiXact(TransactionIdxid);
5557

5658
externSizeMultiXactShmemSize(void);
5759
externvoidMultiXactShmemInit(void);
@@ -67,6 +69,13 @@ extern void MultiXactSetNextMXact(MultiXactId nextMulti,
6769
externvoidMultiXactAdvanceNextMXact(MultiXactIdminMulti,
6870
MultiXactOffsetminMultiOffset);
6971

72+
externvoidmultixact_twophase_recover(TransactionIdxid,uint16info,
73+
void*recdata,uint32len);
74+
externvoidmultixact_twophase_postcommit(TransactionIdxid,uint16info,
75+
void*recdata,uint32len);
76+
externvoidmultixact_twophase_postabort(TransactionIdxid,uint16info,
77+
void*recdata,uint32len);
78+
7079
externvoidmultixact_redo(XLogRecPtrlsn,XLogRecord*record);
7180
externvoidmultixact_desc(StringInfobuf,uint8xl_info,char*rec);
7281

‎src/include/access/twophase.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,15 @@
77
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
88
* Portions Copyright (c) 1994, Regents of the University of California
99
*
10-
* $PostgreSQL: pgsql/src/include/access/twophase.h,v 1.11 2009/01/01 17:23:56 momjian Exp $
10+
* $PostgreSQL: pgsql/src/include/access/twophase.h,v 1.12 2009/11/23 09:58:36 heikki Exp $
1111
*
1212
*-------------------------------------------------------------------------
1313
*/
1414
#ifndefTWOPHASE_H
1515
#defineTWOPHASE_H
1616

1717
#include"access/xlogdefs.h"
18+
#include"storage/backendid.h"
1819
#include"storage/proc.h"
1920
#include"utils/timestamp.h"
2021

@@ -31,6 +32,7 @@ extern Size TwoPhaseShmemSize(void);
3132
externvoidTwoPhaseShmemInit(void);
3233

3334
externPGPROC*TwoPhaseGetDummyProc(TransactionIdxid);
35+
externBackendIdTwoPhaseGetDummyBackendId(TransactionIdxid);
3436

3537
externGlobalTransactionMarkAsPreparing(TransactionIdxid,constchar*gid,
3638
TimestampTzprepared_at,

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp