1
0
mirror of https://github.com/postgres/postgres.git synced 2025-11-06 07:49:08 +03:00

Implement lazy XID allocation: transactions that do not modify any database

rows will normally never obtain an XID at all.  We already did things this way
for subtransactions, but this patch extends the concept to top-level
transactions.  In applications where there are lots of short read-only
transactions, this should improve performance noticeably; not so much from
removal of the actual XID-assignments, as from reduction of overhead that's
driven by the rate of XID consumption.  We add a concept of a "virtual
transaction ID" so that active transactions can be uniquely identified even
if they don't have a regular XID.  This is a much lighter-weight concept:
uniqueness of VXIDs is only guaranteed over the short term, and no on-disk
record is made about them.

Florian Pflug, with some editorialization by Tom.
This commit is contained in:
Tom Lane
2007-09-05 18:10:48 +00:00
parent 2e74c53ec1
commit 295e63983d
34 changed files with 1002 additions and 754 deletions

View File

@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/storage/lmgr/lock.c,v 1.177 2007/07/16 21:09:50 tgl Exp $
* $PostgreSQL: pgsql/src/backend/storage/lmgr/lock.c,v 1.178 2007/09/05 18:10:47 tgl Exp $
*
* NOTES
* A lock table is a shared memory hash table. When
@@ -1681,20 +1681,24 @@ LockReassignCurrentOwner(void)
/*
* GetLockConflicts
* Get a list of TransactionIds of xacts currently holding locks
* Get an array of VirtualTransactionIds of xacts currently holding locks
* that would conflict with the specified lock/lockmode.
* xacts merely awaiting such a lock are NOT reported.
*
* The result array is palloc'd and is terminated with an invalid VXID.
*
* Of course, the result could be out of date by the time it's returned,
* so use of this function has to be thought about carefully.
*
* Only top-level XIDs are reported. Note we never include the current xact
* in the result list, since an xact never blocks itself.
* Note we never include the current xact's vxid in the result array,
* since an xact never blocks itself. Also, prepared transactions are
* ignored, which is a bit more debatable but is appropriate for current
* uses of the result.
*/
List *
VirtualTransactionId *
GetLockConflicts(const LOCKTAG *locktag, LOCKMODE lockmode)
{
List *result = NIL;
VirtualTransactionId *vxids;
LOCKMETHODID lockmethodid = locktag->locktag_lockmethodid;
LockMethod lockMethodTable;
LOCK *lock;
@@ -1703,6 +1707,7 @@ GetLockConflicts(const LOCKTAG *locktag, LOCKMODE lockmode)
PROCLOCK *proclock;
uint32 hashcode;
LWLockId partitionLock;
int count = 0;
if (lockmethodid <= 0 || lockmethodid >= lengthof(LockMethods))
elog(ERROR, "unrecognized lock method: %d", lockmethodid);
@@ -1710,6 +1715,14 @@ GetLockConflicts(const LOCKTAG *locktag, LOCKMODE lockmode)
if (lockmode <= 0 || lockmode > lockMethodTable->numLockModes)
elog(ERROR, "unrecognized lock mode: %d", lockmode);
/*
* Allocate memory to store results, and fill with InvalidVXID. We
* only need enough space for MaxBackends + a terminator, since
* prepared xacts don't count.
*/
vxids = (VirtualTransactionId *)
palloc0(sizeof(VirtualTransactionId) * (MaxBackends + 1));
/*
* Look up the lock object matching the tag.
*/
@@ -1730,7 +1743,7 @@ GetLockConflicts(const LOCKTAG *locktag, LOCKMODE lockmode)
* on this lockable object.
*/
LWLockRelease(partitionLock);
return NIL;
return vxids;
}
/*
@@ -1752,18 +1765,17 @@ GetLockConflicts(const LOCKTAG *locktag, LOCKMODE lockmode)
/* A backend never blocks itself */
if (proc != MyProc)
{
/* Fetch xid just once - see GetNewTransactionId */
TransactionId xid = proc->xid;
VirtualTransactionId vxid;
GET_VXID_FROM_PGPROC(vxid, *proc);
/*
* Race condition: during xact commit/abort we zero out
* PGPROC's xid before we mark its locks released. If we see
* zero in the xid field, assume the xact is in process of
* shutting down and act as though the lock is already
* released.
* If we see an invalid VXID, then either the xact has already
* committed (or aborted), or it's a prepared xact. In
* either case we may ignore it.
*/
if (TransactionIdIsValid(xid))
result = lappend_xid(result, xid);
if (VirtualTransactionIdIsValid(vxid))
vxids[count++] = vxid;
}
}
@@ -1773,7 +1785,10 @@ GetLockConflicts(const LOCKTAG *locktag, LOCKMODE lockmode)
LWLockRelease(partitionLock);
return result;
if (count > MaxBackends) /* should never happen */
elog(PANIC, "too many conflicting locks found");
return vxids;
}
@@ -1782,7 +1797,7 @@ GetLockConflicts(const LOCKTAG *locktag, LOCKMODE lockmode)
* Do the preparatory work for a PREPARE: make 2PC state file records
* for all locks currently held.
*
* Non-transactional locks are ignored.
* Non-transactional locks are ignored, as are VXID locks.
*
* There are some special cases that we error out on: we can't be holding
* any session locks (should be OK since only VACUUM uses those) and we
@@ -1812,6 +1827,13 @@ AtPrepare_Locks(void)
if (!LockMethods[LOCALLOCK_LOCKMETHOD(*locallock)]->transactional)
continue;
/*
* Ignore VXID locks. We don't want those to be held by prepared
* transactions, since they aren't meaningful after a restart.
*/
if (locallock->tag.lock.locktag_type == LOCKTAG_VIRTUALTRANSACTION)
continue;
/* Ignore it if we don't actually hold the lock */
if (locallock->nLocks <= 0)
continue;
@@ -1899,6 +1921,10 @@ PostPrepare_Locks(TransactionId xid)
if (!LockMethods[LOCALLOCK_LOCKMETHOD(*locallock)]->transactional)
continue;
/* Ignore VXID locks */
if (locallock->tag.lock.locktag_type == LOCKTAG_VIRTUALTRANSACTION)
continue;
/* We already checked there are no session locks */
/* Mark the proclock to show we need to release this lockmode */
@@ -1944,6 +1970,10 @@ PostPrepare_Locks(TransactionId xid)
if (!LockMethods[LOCK_LOCKMETHOD(*lock)]->transactional)
goto next_item;
/* Ignore VXID locks */
if (lock->tag.locktag_type == LOCKTAG_VIRTUALTRANSACTION)
goto next_item;
PROCLOCK_PRINT("PostPrepare_Locks", proclock);
LOCK_PRINT("PostPrepare_Locks", lock, 0);
Assert(lock->nRequested >= 0);