diff --git a/src/backend/access/transam/varsup.c b/src/backend/access/transam/varsup.c index 555bb13..f53051d 100644 --- a/src/backend/access/transam/varsup.c +++ b/src/backend/access/transam/varsup.c @@ -224,6 +224,10 @@ GetNewTransactionId(bool isSubXact) else myproc->subxids.overflowed = true; } + + /* + * XXX Do we need to invalidate the shared snapshot ? + */ } LWLockRelease(XidGenLock); diff --git a/src/backend/storage/ipc/procarray.c b/src/backend/storage/ipc/procarray.c index e7593fa..91bd26d 100644 --- a/src/backend/storage/ipc/procarray.c +++ b/src/backend/storage/ipc/procarray.c @@ -158,6 +158,8 @@ static int KnownAssignedXidsGetAndSetXmin(TransactionId *xarray, TransactionId xmax); static TransactionId KnownAssignedXidsGetOldestXmin(void); static void KnownAssignedXidsDisplay(int trace_level); +static void SetSharedSnapshot(Snapshot snapshot, TransactionId globalxmin); +static bool GetSharedSnapshot(Snapshot snapshot, TransactionId *globalxmin); /* * Report shared-memory space needed by CreateSharedProcArray. @@ -379,6 +381,7 @@ ProcArrayEndTransaction(PGPROC *proc, TransactionId latestXid) latestXid)) ShmemVariableCache->latestCompletedXid = latestXid; + ResetSharedSnapshot(); LWLockRelease(ProcArrayLock); } else @@ -1111,6 +1114,60 @@ GetOldestXmin(bool allDbs, bool ignoreVacuum) return result; } +static bool +GetSharedSnapshot(Snapshot snapshot, TransactionId *globalxmin) +{ + volatile PROC_HDR *procglobal = ProcGlobal; + + if (!procglobal->sn_isvalid) + return false; + + snapshot->xmin = procglobal->sn_xmin; + snapshot->xmax = procglobal->sn_xmax; + + snapshot->xcnt = procglobal->sn_xcnt; + if (snapshot->xcnt) + memcpy(snapshot->xip, procglobal->sn_xip, snapshot->xcnt); + + snapshot->subxcnt = procglobal->sn_subxcnt; + if (snapshot->subxcnt) + memcpy(snapshot->subxip, procglobal->sn_subxip, snapshot->subxcnt); + + snapshot->suboverflowed = procglobal->sn_suboverflowed; + *globalxmin = procglobal->sn_globalxmin; + + return true; +} + +static void +SetSharedSnapshot(Snapshot snapshot, TransactionId globalxmin) +{ + volatile PROC_HDR *procglobal = ProcGlobal; + + procglobal->sn_xmin = snapshot->xmin; + procglobal->sn_xmax = snapshot->xmax; + + procglobal->sn_xcnt = snapshot->xcnt; + if (snapshot->xcnt) + memcpy(procglobal->sn_xip, snapshot->xip, snapshot->xcnt); + + procglobal->sn_subxcnt = snapshot->subxcnt; + if (snapshot->subxcnt) + memcpy(procglobal->sn_subxip, snapshot->subxip, snapshot->subxcnt); + + procglobal->sn_suboverflowed = snapshot->suboverflowed; + + procglobal->sn_isvalid = true; + procglobal->sn_globalxmin = globalxmin; +} + +void +ResetSharedSnapshot() +{ + volatile PROC_HDR *procglobal = ProcGlobal; + procglobal->sn_isvalid = false; +} + /* * GetSnapshotData -- returns information about running transactions. * @@ -1216,83 +1273,101 @@ GetSnapshotData(Snapshot snapshot) if (!snapshot->takenDuringRecovery) { - /* - * Spin over procArray checking xid, xmin, and subxids. The goal is - * to gather all active xids, find the lowest xmin, and try to record - * subxids. During recovery no xids will be assigned, so all normal - * backends can be ignored, nor are there any VACUUMs running. All - * prepared transaction xids are held in KnownAssignedXids, so these - * will be seen without needing to loop through procs here. - */ - for (index = 0; index < arrayP->numProcs; index++) + if (!GetSharedSnapshot(snapshot, &globalxmin)) { - volatile PGPROC *proc = arrayP->procs[index]; - TransactionId xid; - - /* Ignore procs running LAZY VACUUM */ - if (proc->vacuumFlags & PROC_IN_VACUUM) - continue; - - /* Update globalxmin to be the smallest valid xmin */ - xid = proc->xmin; /* fetch just once */ - if (TransactionIdIsNormal(xid) && - TransactionIdPrecedes(xid, globalxmin)) - globalxmin = xid; - - /* Fetch xid just once - see GetNewTransactionId */ - xid = proc->xid; - /* - * If the transaction has been assigned an xid < xmax we add it to - * the snapshot, and update xmin if necessary. There's no need to - * store XIDs >= xmax, since we'll treat them as running anyway. - * We don't bother to examine their subxids either. - * - * We don't include our own XID (if any) in the snapshot, but we - * must include it into xmin. + * Spin over procArray checking xid, xmin, and subxids. The goal is + * to gather all active xids, find the lowest xmin, and try to record + * subxids. During recovery no xids will be assigned, so all normal + * backends can be ignored, nor are there any VACUUMs running. All + * prepared transaction xids are held in KnownAssignedXids, so these + * will be seen without needing to loop through procs here. */ - if (TransactionIdIsNormal(xid)) + for (index = 0; index < arrayP->numProcs; index++) { - if (TransactionIdFollowsOrEquals(xid, xmax)) + volatile PGPROC *proc = arrayP->procs[index]; + TransactionId xid; + + /* Ignore procs running LAZY VACUUM */ + if (proc->vacuumFlags & PROC_IN_VACUUM) continue; - if (proc != MyProc) - snapshot->xip[count++] = xid; - if (TransactionIdPrecedes(xid, xmin)) - xmin = xid; - } - /* - * Save subtransaction XIDs if possible (if we've already - * overflowed, there's no point). Note that the subxact XIDs must - * be later than their parent, so no need to check them against - * xmin. We could filter against xmax, but it seems better not to - * do that much work while holding the ProcArrayLock. - * - * The other backend can add more subxids concurrently, but cannot - * remove any. Hence it's important to fetch nxids just once. - * Should be safe to use memcpy, though. (We needn't worry about - * missing any xids added concurrently, because they must postdate - * xmax.) - * - * Again, our own XIDs are not included in the snapshot. - */ - if (!suboverflowed && proc != MyProc) - { - if (proc->subxids.overflowed) - suboverflowed = true; - else + /* Update globalxmin to be the smallest valid xmin */ + xid = proc->xmin; /* fetch just once */ + if (TransactionIdIsNormal(xid) && + TransactionIdPrecedes(xid, globalxmin)) + globalxmin = xid; + + /* Fetch xid just once - see GetNewTransactionId */ + xid = proc->xid; + + /* + * If the transaction has been assigned an xid < xmax we add it to + * the snapshot, and update xmin if necessary. There's no need to + * store XIDs >= xmax, since we'll treat them as running anyway. + * We don't bother to examine their subxids either. + * + * We don't include our own XID (if any) in the snapshot, but we + * must include it into xmin. + */ + if (TransactionIdIsNormal(xid)) { - int nxids = proc->subxids.nxids; + if (TransactionIdFollowsOrEquals(xid, xmax)) + continue; + if (proc != MyProc) + snapshot->xip[count++] = xid; + if (TransactionIdPrecedes(xid, xmin)) + xmin = xid; + } - if (nxids > 0) + /* + * Save subtransaction XIDs if possible (if we've already + * overflowed, there's no point). Note that the subxact XIDs must + * be later than their parent, so no need to check them against + * xmin. We could filter against xmax, but it seems better not to + * do that much work while holding the ProcArrayLock. + * + * The other backend can add more subxids concurrently, but cannot + * remove any. Hence it's important to fetch nxids just once. + * Should be safe to use memcpy, though. (We needn't worry about + * missing any xids added concurrently, because they must postdate + * xmax.) + * + * Again, our own XIDs are not included in the snapshot. + */ + if (!suboverflowed && proc != MyProc) + { + if (proc->subxids.overflowed) + suboverflowed = true; + else { - memcpy(snapshot->subxip + subcount, - (void *) proc->subxids.xids, - nxids * sizeof(TransactionId)); - subcount += nxids; + int nxids = proc->subxids.nxids; + + if (nxids > 0) + { + memcpy(snapshot->subxip + subcount, + (void *) proc->subxids.xids, + nxids * sizeof(TransactionId)); + subcount += nxids; + } } } } + + snapshot->xmin = xmin; + snapshot->xmax = xmax; + snapshot->xcnt = count; + snapshot->subxcnt = subcount; + snapshot->suboverflowed = suboverflowed; + + /* + * Update globalxmin to include actual process xids. This is a slightly + * different way of computing it than GetOldestXmin uses, but should give + * the same result. + */ + if (TransactionIdPrecedes(snapshot->xmin, globalxmin)) + globalxmin = snapshot->xmin; + SetSharedSnapshot(snapshot, globalxmin); } } else @@ -1325,32 +1400,32 @@ GetSnapshotData(Snapshot snapshot) if (TransactionIdPrecedesOrEquals(xmin, procArray->lastOverflowedXid)) suboverflowed = true; + + snapshot->xmin = xmin; + snapshot->xmax = xmax; + snapshot->xcnt = count; + snapshot->subxcnt = subcount; + snapshot->suboverflowed = suboverflowed; + + /* + * Update globalxmin to include actual process xids. This is a slightly + * different way of computing it than GetOldestXmin uses, but should give + * the same result. + */ + if (TransactionIdPrecedes(snapshot->xmin, globalxmin)) + globalxmin = snapshot->xmin; } if (!TransactionIdIsValid(MyProc->xmin)) - MyProc->xmin = TransactionXmin = xmin; + MyProc->xmin = TransactionXmin = snapshot->xmin; LWLockRelease(ProcArrayLock); - /* - * Update globalxmin to include actual process xids. This is a slightly - * different way of computing it than GetOldestXmin uses, but should give - * the same result. - */ - if (TransactionIdPrecedes(xmin, globalxmin)) - globalxmin = xmin; - /* Update global variables too */ RecentGlobalXmin = globalxmin - vacuum_defer_cleanup_age; if (!TransactionIdIsNormal(RecentGlobalXmin)) RecentGlobalXmin = FirstNormalTransactionId; - RecentXmin = xmin; - - snapshot->xmin = xmin; - snapshot->xmax = xmax; - snapshot->xcnt = count; - snapshot->subxcnt = subcount; - snapshot->suboverflowed = suboverflowed; + RecentXmin = snapshot->xmin; snapshot->curcid = GetCurrentCommandId(false); diff --git a/src/backend/storage/lmgr/proc.c b/src/backend/storage/lmgr/proc.c index f4091ec..0d82561 100644 --- a/src/backend/storage/lmgr/proc.c +++ b/src/backend/storage/lmgr/proc.c @@ -109,6 +109,9 @@ ProcGlobalShmemSize(void) size = add_size(size, mul_size(NUM_AUXILIARY_PROCS, sizeof(PGPROC))); /* MyProcs, including autovacuum workers and launcher */ size = add_size(size, mul_size(MaxBackends, sizeof(PGPROC))); + /* Shared snapshot xip/subxip array */ + size = add_size(size, mul_size(2 * MaxBackends, sizeof (TransactionId))); + size = add_size(size, mul_size(2 * NUM_AUXILIARY_PROCS, sizeof (TransactionId))); /* ProcStructLock */ size = add_size(size, sizeof(slock_t)); @@ -229,6 +232,9 @@ InitProcGlobal(void) */ AuxiliaryProcs = &procs[MaxBackends]; + ProcGlobal->sn_xip = (TransactionId *) ShmemAlloc(sizeof (TransactionId) * (MaxBackends + NUM_AUXILIARY_PROCS)); + ProcGlobal->sn_subxip = (TransactionId *) ShmemAlloc(sizeof (TransactionId) * (MaxBackends + NUM_AUXILIARY_PROCS)); + /* Create ProcStructLock spinlock, too */ ProcStructLock = (slock_t *) ShmemAlloc(sizeof(slock_t)); SpinLockInit(ProcStructLock); diff --git a/src/include/storage/proc.h b/src/include/storage/proc.h index 8b09b2a..5831f5a 100644 --- a/src/include/storage/proc.h +++ b/src/include/storage/proc.h @@ -183,6 +183,18 @@ typedef struct PROC_HDR int startupProcPid; /* Buffer id of the buffer that Startup process waits for pin on, or -1 */ int startupBufferPinWaitBufId; + + /* Shared snapshot information */ + bool sn_isvalid; /* is shared snapshot still valid ? */ + TransactionId sn_xmin; /* all XID < xmin are visible to me */ + TransactionId sn_xmax; /* all XID >= xmax are invisible to me */ + uint32 sn_xcnt; /* # of xact ids in xip[] */ + TransactionId *sn_xip; /* array of xact IDs in progress */ + /* note: all ids in xip[] satisfy xmin <= xip[i] < xmax */ + int32 sn_subxcnt; /* # of xact ids in subxip[] */ + TransactionId *sn_subxip; /* array of subxact IDs in progress */ + bool sn_suboverflowed; /* has the subxip array overflowed? */ + TransactionId sn_globalxmin; } PROC_HDR; extern PROC_HDR *ProcGlobal; diff --git a/src/include/storage/procarray.h b/src/include/storage/procarray.h index 3c20fc4..ac0211b 100644 --- a/src/include/storage/procarray.h +++ b/src/include/storage/procarray.h @@ -70,5 +70,6 @@ extern bool CountOtherDBBackends(Oid databaseId, extern void XidCacheRemoveRunningXids(TransactionId xid, int nxids, const TransactionId *xids, TransactionId latestXid); +extern void ResetSharedSnapshot(void); #endif /* PROCARRAY_H */