*** pgsql/src/backend/access/transam/multixact.c 2009/06/26 20:29:04 1.31 --- pgsql/src/backend/access/transam/multixact.c 2009/11/23 09:58:36 1.32 *************** *** 42,48 **** * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * ! * $PostgreSQL: pgsql/src/backend/access/transam/multixact.c,v 1.30 2009/01/20 18:59:37 heikki Exp $ * *------------------------------------------------------------------------- */ --- 42,48 ---- * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * ! * $PostgreSQL: pgsql/src/backend/access/transam/multixact.c,v 1.31 2009/06/26 20:29:04 tgl Exp $ * *------------------------------------------------------------------------- */ *************** *** 51,56 **** --- 51,58 ---- #include "access/multixact.h" #include "access/slru.h" #include "access/transam.h" + #include "access/twophase.h" + #include "access/twophase_rmgr.h" #include "access/xact.h" #include "miscadmin.h" #include "pg_trace.h" *************** typedef struct MultiXactStateData *** 118,125 **** /* * Per-backend data starts here. We have two arrays stored in the area * immediately following the MultiXactStateData struct. Each is indexed by ! * BackendId. (Note: valid BackendIds run from 1 to MaxBackends; element ! * zero of each array is never used.) * * OldestMemberMXactId[k] is the oldest MultiXactId each backend's current * transaction(s) could possibly be a member of, or InvalidMultiXactId --- 120,130 ---- /* * Per-backend data starts here. We have two arrays stored in the area * immediately following the MultiXactStateData struct. Each is indexed by ! * BackendId. ! * ! * In both arrays, there's a slot for all normal backends (1..MaxBackends) ! * followed by a slot for max_prepared_xacts prepared transactions. Valid ! * BackendIds start from 1; element zero of each array is never used. * * OldestMemberMXactId[k] is the oldest MultiXactId each backend's current * transaction(s) could possibly be a member of, or InvalidMultiXactId *************** typedef struct MultiXactStateData *** 152,157 **** --- 157,168 ---- MultiXactId perBackendXactIds[1]; /* VARIABLE LENGTH ARRAY */ } MultiXactStateData; + /* + * Last element of OldestMemberMXactID and OldestVisibleMXactId arrays. + * Valid elements are (1..MaxOldestSlot); element 0 is never used. + */ + #define MaxOldestSlot (MaxBackends + max_prepared_xacts) + /* Pointers to the state data in shared memory */ static MultiXactStateData *MultiXactState; static MultiXactId *OldestMemberMXactId; *************** MultiXactIdSetOldestVisible(void) *** 539,545 **** if (oldestMXact < FirstMultiXactId) oldestMXact = FirstMultiXactId; ! for (i = 1; i <= MaxBackends; i++) { MultiXactId thisoldest = OldestMemberMXactId[i]; --- 550,556 ---- if (oldestMXact < FirstMultiXactId) oldestMXact = FirstMultiXactId; ! for (i = 1; i <= MaxOldestSlot; i++) { MultiXactId thisoldest = OldestMemberMXactId[i]; *************** AtEOXact_MultiXact(void) *** 1276,1281 **** --- 1287,1405 ---- } /* + * AtPrepare_MultiXact + * Save multixact state at 2PC tranasction prepare + * + * In this phase, we only store our OldestMemberMXactId value in the two-phase + * state file. + */ + void + AtPrepare_MultiXact(void) + { + MultiXactId myOldestMember = OldestMemberMXactId[MyBackendId]; + + if (MultiXactIdIsValid(myOldestMember)) + RegisterTwoPhaseRecord(TWOPHASE_RM_MULTIXACT_ID, 0, + &myOldestMember, sizeof(MultiXactId)); + } + + /* + * PostPrepare_MultiXact + * Clean up after successful PREPARE TRANSACTION + */ + void + PostPrepare_MultiXact(TransactionId xid) + { + MultiXactId myOldestMember; + + /* + * Transfer our OldestMemberMXactId value to the slot reserved for the + * prepared transaction. + */ + myOldestMember = OldestMemberMXactId[MyBackendId]; + if (MultiXactIdIsValid(myOldestMember)) + { + BackendId dummyBackendId = TwoPhaseGetDummyBackendId(xid); + + /* + * Even though storing MultiXactId is atomic, acquire lock to make sure + * others see both changes, not just the reset of the slot of the + * current backend. Using a volatile pointer might suffice, but this + * isn't a hot spot. + */ + LWLockAcquire(MultiXactGenLock, LW_EXCLUSIVE); + + OldestMemberMXactId[dummyBackendId] = myOldestMember; + OldestMemberMXactId[MyBackendId] = InvalidMultiXactId; + + LWLockRelease(MultiXactGenLock); + } + + /* + * We don't need to transfer OldestVisibleMXactId value, because the + * transaction is not going to be looking at any more multixacts once + * it's prepared. + * + * We assume that storing a MultiXactId is atomic and so we need not take + * MultiXactGenLock to do this. + */ + OldestVisibleMXactId[MyBackendId] = InvalidMultiXactId; + + /* + * Discard the local MultiXactId cache like in AtEOX_MultiXact + */ + MXactContext = NULL; + MXactCache = NULL; + } + + /* + * multixact_twophase_recover + * Recover the state of a prepared transaction at startup + */ + void + multixact_twophase_recover(TransactionId xid, uint16 info, + void *recdata, uint32 len) + { + BackendId dummyBackendId = TwoPhaseGetDummyBackendId(xid); + MultiXactId oldestMember; + + /* + * Get the oldest member XID from the state file record, and set it in + * the OldestMemberMXactId slot reserved for this prepared transaction. + */ + Assert(len == sizeof(MultiXactId)); + oldestMember = *((MultiXactId *)recdata); + + OldestMemberMXactId[dummyBackendId] = oldestMember; + } + + /* + * multixact_twophase_postcommit + * Similar to AtEOX_MultiXact but for COMMIT PREPARED + */ + void + multixact_twophase_postcommit(TransactionId xid, uint16 info, + void *recdata, uint32 len) + { + BackendId dummyBackendId = TwoPhaseGetDummyBackendId(xid); + + Assert(len == sizeof(MultiXactId)); + + OldestMemberMXactId[dummyBackendId] = InvalidMultiXactId; + } + + /* + * multixact_twophase_postabort + * This is actually just the same as the COMMIT case. + */ + void + multixact_twophase_postabort(TransactionId xid, uint16 info, + void *recdata, uint32 len) + { + multixact_twophase_postcommit(xid, info, recdata, len); + } + + /* * Initialization of shared memory for MultiXact. We use two SLRU areas, * thus double memory. Also, reserve space for the shared MultiXactState * struct and the per-backend MultiXactId arrays (two of those, too). *************** MultiXactShmemSize(void) *** 1287,1293 **** #define SHARED_MULTIXACT_STATE_SIZE \ add_size(sizeof(MultiXactStateData), \ ! mul_size(sizeof(MultiXactId) * 2, MaxBackends)) size = SHARED_MULTIXACT_STATE_SIZE; size = add_size(size, SimpleLruShmemSize(NUM_MXACTOFFSET_BUFFERS, 0)); --- 1411,1417 ---- #define SHARED_MULTIXACT_STATE_SIZE \ add_size(sizeof(MultiXactStateData), \ ! mul_size(sizeof(MultiXactId) * 2, MaxOldestSlot)) size = SHARED_MULTIXACT_STATE_SIZE; size = add_size(size, SimpleLruShmemSize(NUM_MXACTOFFSET_BUFFERS, 0)); *************** MultiXactShmemInit(void) *** 1329,1338 **** /* * Set up array pointers. Note that perBackendXactIds[0] is wasted space ! * since we only use indexes 1..MaxBackends in each array. */ OldestMemberMXactId = MultiXactState->perBackendXactIds; ! OldestVisibleMXactId = OldestMemberMXactId + MaxBackends; } /* --- 1453,1462 ---- /* * Set up array pointers. Note that perBackendXactIds[0] is wasted space ! * since we only use indexes 1..MaxOldestSlot in each array. */ OldestMemberMXactId = MultiXactState->perBackendXactIds; ! OldestVisibleMXactId = OldestMemberMXactId + MaxOldestSlot; } /* *************** TruncateMultiXact(void) *** 1702,1708 **** nextMXact = FirstMultiXactId; oldestMXact = nextMXact; ! for (i = 1; i <= MaxBackends; i++) { MultiXactId thisoldest; --- 1826,1832 ---- nextMXact = FirstMultiXactId; oldestMXact = nextMXact; ! for (i = 1; i <= MaxOldestSlot; i++) { MultiXactId thisoldest;