mirror of
https://github.com/postgres/postgres.git
synced 2025-11-09 06:21:09 +03:00
snapshot scalability: Introduce dense array of in-progress xids.
The new array contains the xids for all connected backends / in-use PGPROC entries in a dense manner (in contrast to the PGPROC/PGXACT arrays which can have unused entries interspersed). This improves performance because GetSnapshotData() always needs to scan the xids of all live procarray entries and now there's no need to go through the procArray->pgprocnos indirection anymore. As the set of running top-level xids changes rarely, compared to the number of snapshots taken, this substantially increases the likelihood of most data required for a snapshot being in l2 cache. In read-mostly workloads scanning the xids[] array will sufficient to build a snapshot, as most backends will not have an xid assigned. To keep the xid array dense ProcArrayRemove() needs to move entries behind the to-be-removed proc's one further up in the array. Obviously moving array entries cannot happen while a backend sets it xid. I.e. locking needs to prevent that array entries are moved while a backend modifies its xid. To avoid locking ProcArrayLock in GetNewTransactionId() - a fairly hot spot already - ProcArrayAdd() / ProcArrayRemove() now needs to hold XidGenLock in addition to ProcArrayLock. Adding / Removing a procarray entry is not a very frequent operation, even taking 2PC into account. Due to the above, the dense array entries can only be read or modified while holding ProcArrayLock and/or XidGenLock. This prevents a concurrent ProcArrayRemove() from shifting the dense array while it is accessed concurrently. While the new dense array is very good when needing to look at all xids it is less suitable when accessing a single backend's xid. In particular it would be problematic to have to acquire a lock to access a backend's own xid. Therefore a backend's xid is not just stored in the dense array, but also in PGPROC. This also allows a backend to only access the shared xid value when the backend had acquired an xid. The infrastructure added in this commit will be used for the remaining PGXACT fields in subsequent commits. They are kept separate to make review easier. Author: Andres Freund <andres@anarazel.de> Reviewed-By: Robert Haas <robertmhaas@gmail.com> Reviewed-By: Thomas Munro <thomas.munro@gmail.com> Reviewed-By: David Rowley <dgrowleyml@gmail.com> Discussion: https://postgr.es/m/20200301083601.ews6hz5dduc3w2se@alap3.anarazel.de
This commit is contained in:
@@ -351,7 +351,7 @@ AtAbort_Twophase(void)
|
||||
|
||||
/*
|
||||
* This is called after we have finished transferring state to the prepared
|
||||
* PGXACT entry.
|
||||
* PGPROC entry.
|
||||
*/
|
||||
void
|
||||
PostPrepare_Twophase(void)
|
||||
@@ -463,7 +463,7 @@ MarkAsPreparingGuts(GlobalTransaction gxact, TransactionId xid, const char *gid,
|
||||
proc->waitStatus = PROC_WAIT_STATUS_OK;
|
||||
/* We set up the gxact's VXID as InvalidBackendId/XID */
|
||||
proc->lxid = (LocalTransactionId) xid;
|
||||
pgxact->xid = xid;
|
||||
proc->xid = xid;
|
||||
Assert(proc->xmin == InvalidTransactionId);
|
||||
proc->delayChkpt = false;
|
||||
pgxact->vacuumFlags = 0;
|
||||
@@ -768,7 +768,6 @@ pg_prepared_xact(PG_FUNCTION_ARGS)
|
||||
{
|
||||
GlobalTransaction gxact = &status->array[status->currIdx++];
|
||||
PGPROC *proc = &ProcGlobal->allProcs[gxact->pgprocno];
|
||||
PGXACT *pgxact = &ProcGlobal->allPgXact[gxact->pgprocno];
|
||||
Datum values[5];
|
||||
bool nulls[5];
|
||||
HeapTuple tuple;
|
||||
@@ -783,7 +782,7 @@ pg_prepared_xact(PG_FUNCTION_ARGS)
|
||||
MemSet(values, 0, sizeof(values));
|
||||
MemSet(nulls, 0, sizeof(nulls));
|
||||
|
||||
values[0] = TransactionIdGetDatum(pgxact->xid);
|
||||
values[0] = TransactionIdGetDatum(proc->xid);
|
||||
values[1] = CStringGetTextDatum(gxact->gid);
|
||||
values[2] = TimestampTzGetDatum(gxact->prepared_at);
|
||||
values[3] = ObjectIdGetDatum(gxact->owner);
|
||||
@@ -829,9 +828,8 @@ TwoPhaseGetGXact(TransactionId xid, bool lock_held)
|
||||
for (i = 0; i < TwoPhaseState->numPrepXacts; i++)
|
||||
{
|
||||
GlobalTransaction gxact = TwoPhaseState->prepXacts[i];
|
||||
PGXACT *pgxact = &ProcGlobal->allPgXact[gxact->pgprocno];
|
||||
|
||||
if (pgxact->xid == xid)
|
||||
if (gxact->xid == xid)
|
||||
{
|
||||
result = gxact;
|
||||
break;
|
||||
@@ -987,8 +985,7 @@ void
|
||||
StartPrepare(GlobalTransaction gxact)
|
||||
{
|
||||
PGPROC *proc = &ProcGlobal->allProcs[gxact->pgprocno];
|
||||
PGXACT *pgxact = &ProcGlobal->allPgXact[gxact->pgprocno];
|
||||
TransactionId xid = pgxact->xid;
|
||||
TransactionId xid = gxact->xid;
|
||||
TwoPhaseFileHeader hdr;
|
||||
TransactionId *children;
|
||||
RelFileNode *commitrels;
|
||||
@@ -1140,15 +1137,15 @@ EndPrepare(GlobalTransaction gxact)
|
||||
|
||||
/*
|
||||
* Mark the prepared transaction as valid. As soon as xact.c marks
|
||||
* MyPgXact as not running our XID (which it will do immediately after
|
||||
* MyProc as not running our XID (which it will do immediately after
|
||||
* this function returns), others can commit/rollback the xact.
|
||||
*
|
||||
* NB: a side effect of this is to make a dummy ProcArray entry for the
|
||||
* prepared XID. This must happen before we clear the XID from MyPgXact,
|
||||
* else there is a window where the XID is not running according to
|
||||
* TransactionIdIsInProgress, and onlookers would be entitled to assume
|
||||
* the xact crashed. Instead we have a window where the same XID appears
|
||||
* twice in ProcArray, which is OK.
|
||||
* prepared XID. This must happen before we clear the XID from MyProc /
|
||||
* ProcGlobal->xids[], else there is a window where the XID is not running
|
||||
* according to TransactionIdIsInProgress, and onlookers would be entitled
|
||||
* to assume the xact crashed. Instead we have a window where the same
|
||||
* XID appears twice in ProcArray, which is OK.
|
||||
*/
|
||||
MarkAsPrepared(gxact, false);
|
||||
|
||||
@@ -1404,7 +1401,6 @@ FinishPreparedTransaction(const char *gid, bool isCommit)
|
||||
{
|
||||
GlobalTransaction gxact;
|
||||
PGPROC *proc;
|
||||
PGXACT *pgxact;
|
||||
TransactionId xid;
|
||||
char *buf;
|
||||
char *bufptr;
|
||||
@@ -1423,8 +1419,7 @@ FinishPreparedTransaction(const char *gid, bool isCommit)
|
||||
*/
|
||||
gxact = LockGXact(gid, GetUserId());
|
||||
proc = &ProcGlobal->allProcs[gxact->pgprocno];
|
||||
pgxact = &ProcGlobal->allPgXact[gxact->pgprocno];
|
||||
xid = pgxact->xid;
|
||||
xid = gxact->xid;
|
||||
|
||||
/*
|
||||
* Read and validate 2PC state data. State data will typically be stored
|
||||
@@ -1726,7 +1721,7 @@ CheckPointTwoPhase(XLogRecPtr redo_horizon)
|
||||
for (i = 0; i < TwoPhaseState->numPrepXacts; i++)
|
||||
{
|
||||
/*
|
||||
* Note that we are using gxact not pgxact so this works in recovery
|
||||
* Note that we are using gxact not PGPROC so this works in recovery
|
||||
* also
|
||||
*/
|
||||
GlobalTransaction gxact = TwoPhaseState->prepXacts[i];
|
||||
|
||||
Reference in New Issue
Block a user