From a585ba41faf640d34b319472343caadb38f6b1e8 Mon Sep 17 00:00:00 2001 From: Takayuki Tsunakawa Date: Tue, 19 Mar 2019 16:46:51 +0900 Subject: [PATCH 2/2] speed up LOCALLOCK scan --- src/backend/storage/lmgr/lock.c | 63 ++++++++++++++++++++++++----------------- src/include/storage/lock.h | 2 ++ 2 files changed, 39 insertions(+), 26 deletions(-) diff --git a/src/backend/storage/lmgr/lock.c b/src/backend/storage/lmgr/lock.c index 78fdbd6..29a199b 100644 --- a/src/backend/storage/lmgr/lock.c +++ b/src/backend/storage/lmgr/lock.c @@ -255,6 +255,17 @@ static HTAB *LockMethodProcLockHash; static HTAB *LockMethodLocalHash; +/* + * List of LOCALLOCK structures that each backend acquired + * + * If a transaction acquires many locks, LockMethodLocalHash bloats, making + * the hash table scans in subsequent transactions (e.g., in LockReleaseAll) + * even though they only acquire a few locks. To speed up iteration over + * acquired locks in a backend, we use a list of LOCALLOCKs instead. + */ +static dlist_head LocalLocks = DLIST_STATIC_INIT(LocalLocks); + + /* private state for error cleanup */ static LOCALLOCK *StrongLockInProgress; static LOCALLOCK *awaitedLock; @@ -794,6 +805,7 @@ LockAcquireExtended(const LOCKTAG *locktag, */ if (!found) { + dlist_push_head(&LocalLocks, &locallock->procLink); locallock->lock = NULL; locallock->proclock = NULL; locallock->hashcode = LockTagHashCode(&(localtag.lock)); @@ -1320,6 +1332,7 @@ RemoveLocalLock(LOCALLOCK *locallock) SpinLockRelease(&FastPathStrongRelationLocks->mutex); } + dlist_delete(&locallock->procLink); if (!hash_search(LockMethodLocalHash, (void *) &(locallock->tag), HASH_REMOVE, NULL)) @@ -2088,7 +2101,7 @@ LockRelease(const LOCKTAG *locktag, LOCKMODE lockmode, bool sessionLock) void LockReleaseAll(LOCKMETHODID lockmethodid, bool allLocks) { - HASH_SEQ_STATUS status; + dlist_mutable_iter iter; LockMethod lockMethodTable; int i, numLockModes; @@ -2126,10 +2139,10 @@ LockReleaseAll(LOCKMETHODID lockmethodid, bool allLocks) * pointers. Fast-path locks are cleaned up during the locallock table * scan, though. */ - hash_seq_init(&status, LockMethodLocalHash); - - while ((locallock = (LOCALLOCK *) hash_seq_search(&status)) != NULL) + dlist_foreach_modify(iter, &LocalLocks) { + locallock = dlist_container(LOCALLOCK, procLink, iter.cur); + /* * If the LOCALLOCK entry is unused, we must've run out of shared * memory while trying to set up this lock. Just forget the local @@ -2362,16 +2375,16 @@ LockReleaseAll(LOCKMETHODID lockmethodid, bool allLocks) void LockReleaseSession(LOCKMETHODID lockmethodid) { - HASH_SEQ_STATUS status; + dlist_mutable_iter iter; LOCALLOCK *locallock; if (lockmethodid <= 0 || lockmethodid >= lengthof(LockMethods)) elog(ERROR, "unrecognized lock method: %d", lockmethodid); - hash_seq_init(&status, LockMethodLocalHash); - - while ((locallock = (LOCALLOCK *) hash_seq_search(&status)) != NULL) + dlist_foreach_modify(iter, &LocalLocks) { + locallock = dlist_container(LOCALLOCK, procLink, iter.cur); + /* Ignore items that are not of the specified lock method */ if (LOCALLOCK_LOCKMETHOD(*locallock) != lockmethodid) continue; @@ -2394,13 +2407,14 @@ LockReleaseCurrentOwner(LOCALLOCK **locallocks, int nlocks) { if (locallocks == NULL) { - HASH_SEQ_STATUS status; + dlist_mutable_iter iter; LOCALLOCK *locallock; - hash_seq_init(&status, LockMethodLocalHash); - - while ((locallock = (LOCALLOCK *) hash_seq_search(&status)) != NULL) + dlist_foreach_modify(iter, &LocalLocks) + { + locallock = dlist_container(LOCALLOCK, procLink, iter.cur); ReleaseLockIfHeld(locallock, false); + } } else { @@ -2493,13 +2507,14 @@ LockReassignCurrentOwner(LOCALLOCK **locallocks, int nlocks) if (locallocks == NULL) { - HASH_SEQ_STATUS status; + dlist_mutable_iter iter; LOCALLOCK *locallock; - hash_seq_init(&status, LockMethodLocalHash); - - while ((locallock = (LOCALLOCK *) hash_seq_search(&status)) != NULL) + dlist_foreach_modify(iter, &LocalLocks) + { + locallock = dlist_container(LOCALLOCK, procLink, iter.cur); LockReassignOwner(locallock, parent); + } } else { @@ -3133,8 +3148,7 @@ LockRefindAndRelease(LockMethod lockMethodTable, PGPROC *proc, void AtPrepare_Locks(void) { - HASH_SEQ_STATUS status; - LOCALLOCK *locallock; + dlist_mutable_iter iter; /* * For the most part, we don't need to touch shared memory for this --- @@ -3142,10 +3156,9 @@ AtPrepare_Locks(void) * Fast-path locks are an exception, however: we move any such locks to * the main table before allowing PREPARE TRANSACTION to succeed. */ - hash_seq_init(&status, LockMethodLocalHash); - - while ((locallock = (LOCALLOCK *) hash_seq_search(&status)) != NULL) + dlist_foreach_modify(iter, &LocalLocks) { + LOCALLOCK *locallock = dlist_container(LOCALLOCK, procLink, iter.cur); TwoPhaseLockRecord record; LOCALLOCKOWNER *lockOwners = locallock->lockOwners; bool haveSessionLock; @@ -3244,8 +3257,7 @@ void PostPrepare_Locks(TransactionId xid) { PGPROC *newproc = TwoPhaseGetDummyProc(xid, false); - HASH_SEQ_STATUS status; - LOCALLOCK *locallock; + dlist_mutable_iter iter; LOCK *lock; PROCLOCK *proclock; PROCLOCKTAG proclocktag; @@ -3267,10 +3279,9 @@ PostPrepare_Locks(TransactionId xid) * pointing to the same proclock, and we daren't end up with any dangling * pointers. */ - hash_seq_init(&status, LockMethodLocalHash); - - while ((locallock = (LOCALLOCK *) hash_seq_search(&status)) != NULL) + dlist_foreach_modify(iter, &LocalLocks) { + LOCALLOCK *locallock = dlist_container(LOCALLOCK, procLink, iter.cur); LOCALLOCKOWNER *lockOwners = locallock->lockOwners; bool haveSessionLock; bool haveXactLock; diff --git a/src/include/storage/lock.h b/src/include/storage/lock.h index badf7fd..6bb907d 100644 --- a/src/include/storage/lock.h +++ b/src/include/storage/lock.h @@ -18,6 +18,7 @@ #error "lock.h may not be included from frontend code" #endif +#include "lib/ilist.h" #include "storage/lockdefs.h" #include "storage/backendid.h" #include "storage/lwlock.h" @@ -407,6 +408,7 @@ typedef struct LOCALLOCK uint32 hashcode; /* copy of LOCKTAG's hash value */ LOCK *lock; /* associated LOCK object, if any */ PROCLOCK *proclock; /* associated PROCLOCK object, if any */ + dlist_node procLink; /* list link in a backend's list of LOCALLOCKs */ int64 nLocks; /* total number of times lock is held */ int numLockOwners; /* # of relevant ResourceOwners */ int maxLockOwners; /* allocated size of array */ -- 2.10.1