mirror of
https://github.com/postgres/postgres.git
synced 2025-11-06 07:49:08 +03:00
Add a small cache of locks owned by a resource owner in ResourceOwner.
This speeds up reassigning locks to the parent owner, when the transaction holds a lot of locks, but only a few of them belong to the current resource owner. This is particularly helps pg_dump when dumping a large number of objects. The cache can hold up to 15 locks in each resource owner. After that, the cache is marked as overflowed, and we fall back to the old method of scanning the whole local lock table. The tradeoff here is that the cache has to be scanned whenever a lock is released, so if the cache is too large, lock release becomes more expensive. 15 seems enough to cover pg_dump, and doesn't have much impact on lock release. Jeff Janes, reviewed by Amit Kapila and Heikki Linnakangas.
This commit is contained in:
@@ -345,6 +345,7 @@ static void BeginStrongLockAcquire(LOCALLOCK *locallock, uint32 fasthashcode);
|
||||
static void FinishStrongLockAcquire(void);
|
||||
static void WaitOnLock(LOCALLOCK *locallock, ResourceOwner owner);
|
||||
static void ReleaseLockIfHeld(LOCALLOCK *locallock, bool sessionLock);
|
||||
static void LockReassignOwner(LOCALLOCK *locallock, ResourceOwner parent);
|
||||
static bool UnGrantLock(LOCK *lock, LOCKMODE lockmode,
|
||||
PROCLOCK *proclock, LockMethod lockMethodTable);
|
||||
static void CleanUpLock(LOCK *lock, PROCLOCK *proclock,
|
||||
@@ -1098,8 +1099,16 @@ SetupLockInTable(LockMethod lockMethodTable, PGPROC *proc,
|
||||
static void
|
||||
RemoveLocalLock(LOCALLOCK *locallock)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = locallock->numLockOwners - 1; i >= 0; i--)
|
||||
{
|
||||
if (locallock->lockOwners[i].owner != NULL)
|
||||
ResourceOwnerForgetLock(locallock->lockOwners[i].owner, locallock);
|
||||
}
|
||||
pfree(locallock->lockOwners);
|
||||
locallock->lockOwners = NULL;
|
||||
|
||||
if (locallock->holdsStrongLockCount)
|
||||
{
|
||||
uint32 fasthashcode;
|
||||
@@ -1112,6 +1121,7 @@ RemoveLocalLock(LOCALLOCK *locallock)
|
||||
locallock->holdsStrongLockCount = FALSE;
|
||||
SpinLockRelease(&FastPathStrongRelationLocks->mutex);
|
||||
}
|
||||
|
||||
if (!hash_search(LockMethodLocalHash,
|
||||
(void *) &(locallock->tag),
|
||||
HASH_REMOVE, NULL))
|
||||
@@ -1355,6 +1365,8 @@ GrantLockLocal(LOCALLOCK *locallock, ResourceOwner owner)
|
||||
lockOwners[i].owner = owner;
|
||||
lockOwners[i].nLocks = 1;
|
||||
locallock->numLockOwners++;
|
||||
if (owner != NULL)
|
||||
ResourceOwnerRememberLock(owner, locallock);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1670,6 +1682,8 @@ LockRelease(const LOCKTAG *locktag, LOCKMODE lockmode, bool sessionLock)
|
||||
Assert(lockOwners[i].nLocks > 0);
|
||||
if (--lockOwners[i].nLocks == 0)
|
||||
{
|
||||
if (owner != NULL)
|
||||
ResourceOwnerForgetLock(owner, locallock);
|
||||
/* compact out unused slot */
|
||||
locallock->numLockOwners--;
|
||||
if (i < locallock->numLockOwners)
|
||||
@@ -1862,14 +1876,13 @@ LockReleaseAll(LOCKMETHODID lockmethodid, bool allLocks)
|
||||
{
|
||||
LOCALLOCKOWNER *lockOwners = locallock->lockOwners;
|
||||
|
||||
/* If it's above array position 0, move it down to 0 */
|
||||
for (i = locallock->numLockOwners - 1; i > 0; i--)
|
||||
/* If session lock is above array position 0, move it down to 0 */
|
||||
for (i = 0; i < locallock->numLockOwners ; i++)
|
||||
{
|
||||
if (lockOwners[i].owner == NULL)
|
||||
{
|
||||
lockOwners[0] = lockOwners[i];
|
||||
break;
|
||||
}
|
||||
else
|
||||
ResourceOwnerForgetLock(lockOwners[i].owner, locallock);
|
||||
}
|
||||
|
||||
if (locallock->numLockOwners > 0 &&
|
||||
@@ -1882,6 +1895,8 @@ LockReleaseAll(LOCKMETHODID lockmethodid, bool allLocks)
|
||||
/* We aren't deleting this locallock, so done */
|
||||
continue;
|
||||
}
|
||||
else
|
||||
locallock->numLockOwners = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -2067,18 +2082,31 @@ LockReleaseSession(LOCKMETHODID lockmethodid)
|
||||
/*
|
||||
* LockReleaseCurrentOwner
|
||||
* Release all locks belonging to CurrentResourceOwner
|
||||
*
|
||||
* If the caller knows what those locks are, it can pass them as an array.
|
||||
* That speeds up the call significantly, when a lot of locks are held.
|
||||
* Otherwise, pass NULL for locallocks, and we'll traverse through our hash
|
||||
* table to find them.
|
||||
*/
|
||||
void
|
||||
LockReleaseCurrentOwner(void)
|
||||
LockReleaseCurrentOwner(LOCALLOCK **locallocks, int nlocks)
|
||||
{
|
||||
HASH_SEQ_STATUS status;
|
||||
LOCALLOCK *locallock;
|
||||
|
||||
hash_seq_init(&status, LockMethodLocalHash);
|
||||
|
||||
while ((locallock = (LOCALLOCK *) hash_seq_search(&status)) != NULL)
|
||||
if (locallocks == NULL)
|
||||
{
|
||||
ReleaseLockIfHeld(locallock, false);
|
||||
HASH_SEQ_STATUS status;
|
||||
LOCALLOCK *locallock;
|
||||
|
||||
hash_seq_init(&status, LockMethodLocalHash);
|
||||
|
||||
while ((locallock = (LOCALLOCK *) hash_seq_search(&status)) != NULL)
|
||||
ReleaseLockIfHeld(locallock, false);
|
||||
}
|
||||
else
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = nlocks - 1; i >= 0; i--)
|
||||
ReleaseLockIfHeld(locallocks[i], false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2124,6 +2152,8 @@ ReleaseLockIfHeld(LOCALLOCK *locallock, bool sessionLock)
|
||||
locallock->nLocks -= lockOwners[i].nLocks;
|
||||
/* compact out unused slot */
|
||||
locallock->numLockOwners--;
|
||||
if (owner != NULL)
|
||||
ResourceOwnerForgetLock(owner, locallock);
|
||||
if (i < locallock->numLockOwners)
|
||||
lockOwners[i] = lockOwners[locallock->numLockOwners];
|
||||
}
|
||||
@@ -2146,57 +2176,83 @@ ReleaseLockIfHeld(LOCALLOCK *locallock, bool sessionLock)
|
||||
/*
|
||||
* LockReassignCurrentOwner
|
||||
* Reassign all locks belonging to CurrentResourceOwner to belong
|
||||
* to its parent resource owner
|
||||
* to its parent resource owner.
|
||||
*
|
||||
* If the caller knows what those locks are, it can pass them as an array.
|
||||
* That speeds up the call significantly, when a lot of locks are held
|
||||
* (e.g pg_dump with a large schema). Otherwise, pass NULL for locallocks,
|
||||
* and we'll traverse through our hash table to find them.
|
||||
*/
|
||||
void
|
||||
LockReassignCurrentOwner(void)
|
||||
LockReassignCurrentOwner(LOCALLOCK **locallocks, int nlocks)
|
||||
{
|
||||
ResourceOwner parent = ResourceOwnerGetParent(CurrentResourceOwner);
|
||||
HASH_SEQ_STATUS status;
|
||||
LOCALLOCK *locallock;
|
||||
LOCALLOCKOWNER *lockOwners;
|
||||
|
||||
Assert(parent != NULL);
|
||||
|
||||
hash_seq_init(&status, LockMethodLocalHash);
|
||||
|
||||
while ((locallock = (LOCALLOCK *) hash_seq_search(&status)) != NULL)
|
||||
if (locallocks == NULL)
|
||||
{
|
||||
int i;
|
||||
int ic = -1;
|
||||
int ip = -1;
|
||||
HASH_SEQ_STATUS status;
|
||||
LOCALLOCK *locallock;
|
||||
|
||||
/*
|
||||
* Scan to see if there are any locks belonging to current owner or
|
||||
* its parent
|
||||
*/
|
||||
lockOwners = locallock->lockOwners;
|
||||
for (i = locallock->numLockOwners - 1; i >= 0; i--)
|
||||
{
|
||||
if (lockOwners[i].owner == CurrentResourceOwner)
|
||||
ic = i;
|
||||
else if (lockOwners[i].owner == parent)
|
||||
ip = i;
|
||||
}
|
||||
hash_seq_init(&status, LockMethodLocalHash);
|
||||
|
||||
if (ic < 0)
|
||||
continue; /* no current locks */
|
||||
|
||||
if (ip < 0)
|
||||
{
|
||||
/* Parent has no slot, so just give it child's slot */
|
||||
lockOwners[ic].owner = parent;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Merge child's count with parent's */
|
||||
lockOwners[ip].nLocks += lockOwners[ic].nLocks;
|
||||
/* compact out unused slot */
|
||||
locallock->numLockOwners--;
|
||||
if (ic < locallock->numLockOwners)
|
||||
lockOwners[ic] = lockOwners[locallock->numLockOwners];
|
||||
}
|
||||
while ((locallock = (LOCALLOCK *) hash_seq_search(&status)) != NULL)
|
||||
LockReassignOwner(locallock, parent);
|
||||
}
|
||||
else
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = nlocks - 1; i >= 0; i--)
|
||||
LockReassignOwner(locallocks[i], parent);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Subroutine of LockReassignCurrentOwner. Reassigns a given lock belonging to
|
||||
* CurrentResourceOwner to its parent.
|
||||
*/
|
||||
static void
|
||||
LockReassignOwner(LOCALLOCK *locallock, ResourceOwner parent)
|
||||
{
|
||||
LOCALLOCKOWNER *lockOwners;
|
||||
int i;
|
||||
int ic = -1;
|
||||
int ip = -1;
|
||||
|
||||
/*
|
||||
* Scan to see if there are any locks belonging to current owner or
|
||||
* its parent
|
||||
*/
|
||||
lockOwners = locallock->lockOwners;
|
||||
for (i = locallock->numLockOwners - 1; i >= 0; i--)
|
||||
{
|
||||
if (lockOwners[i].owner == CurrentResourceOwner)
|
||||
ic = i;
|
||||
else if (lockOwners[i].owner == parent)
|
||||
ip = i;
|
||||
}
|
||||
|
||||
if (ic < 0)
|
||||
return; /* no current locks */
|
||||
|
||||
if (ip < 0)
|
||||
{
|
||||
/* Parent has no slot, so just give it the child's slot */
|
||||
lockOwners[ic].owner = parent;
|
||||
ResourceOwnerRememberLock(parent, locallock);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Merge child's count with parent's */
|
||||
lockOwners[ip].nLocks += lockOwners[ic].nLocks;
|
||||
/* compact out unused slot */
|
||||
locallock->numLockOwners--;
|
||||
if (ic < locallock->numLockOwners)
|
||||
lockOwners[ic] = lockOwners[locallock->numLockOwners];
|
||||
}
|
||||
ResourceOwnerForgetLock(CurrentResourceOwner, locallock);
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
Reference in New Issue
Block a user