shrink_bloated_locallocktable.patch
application/octet-stream
Filename: shrink_bloated_locallocktable.patch
Type: application/octet-stream
Part: 0
Patch
Format: unified
| File | + | − |
|---|---|---|
| src/backend/storage/lmgr/lock.c | 114 | 5 |
diff --git a/src/backend/storage/lmgr/lock.c b/src/backend/storage/lmgr/lock.c
index c895876..91e3924 100644
--- a/src/backend/storage/lmgr/lock.c
+++ b/src/backend/storage/lmgr/lock.c
@@ -254,6 +254,20 @@ static HTAB *LockMethodLockHash;
static HTAB *LockMethodProcLockHash;
static HTAB *LockMethodLocalHash;
+/* Initial size of local lock hash */
+#define LOCKMETHODLOCALHASH_INIT_SIZE 16
+
+/*
+ * Attempt to shrink the LockMethodLocalHash after this many calls to
+ * LockRelaseAll()
+ */
+#define LOCKMETHODLOCALHASH_SIZE_CHECK_FREQ 10
+
+/*
+ * Counters to track bloat in the LockMethodLocalHash table
+ */
+static unsigned int lock_release_count = 0;
+static uint64 locks_released = 0;
/* private state for error cleanup */
static LOCALLOCK *StrongLockInProgress;
@@ -339,6 +353,7 @@ PROCLOCK_PRINT(const char *where, const PROCLOCK *proclockP)
#endif /* not LOCK_DEBUG */
+static void CreateLocalLockHash(long size, bool copyOldLocks);
static uint32 proclock_hash(const void *key, Size keysize);
static void RemoveLocalLock(LOCALLOCK *locallock);
static PROCLOCK *SetupLockInTable(LockMethod lockMethodTable, PGPROC *proc,
@@ -441,17 +456,66 @@ InitLocks(void)
* ought to be empty in the postmaster, but for safety let's zap it.)
*/
if (LockMethodLocalHash)
+ {
hash_destroy(LockMethodLocalHash);
+ LockMethodLocalHash = NULL;
+ }
+
+ CreateLocalLockHash(LOCKMETHODLOCALHASH_INIT_SIZE, false);
+}
+
+/*
+ * CreateLocalLockHash
+ * Build or rebuild the LockMethodLocalHash hash table. If copyOldLocks
+ * is true we populate the new table with the locks from the old version
+ * and then destroy it.
+ */
+static void
+CreateLocalLockHash(long size, bool copyOldLocks)
+{
+ static HTAB *htab;
+ HASHCTL info;
info.keysize = sizeof(LOCALLOCKTAG);
info.entrysize = sizeof(LOCALLOCK);
- LockMethodLocalHash = hash_create("LOCALLOCK hash",
- 16,
- &info,
- HASH_ELEM | HASH_BLOBS);
-}
+ htab = hash_create("LOCALLOCK hash", size, &info, HASH_ELEM | HASH_BLOBS);
+
+ if (copyOldLocks)
+ {
+ HASH_SEQ_STATUS status;
+ LOCALLOCK *locallock;
+
+ hash_seq_init(&status, LockMethodLocalHash);
+
+ /* scan over the old table and add all the locks into the new table */
+ while ((locallock = (LOCALLOCK *) hash_seq_search(&status)) != NULL)
+ {
+ LOCALLOCK *new_lock;
+ bool found;
+
+ new_lock = hash_search(htab,
+ (void *) &locallock->tag,
+ HASH_ENTER, &found);
+ Assert(!found);
+ memcpy(new_lock, locallock, sizeof(LOCALLOCK));
+ }
+
+ hash_destroy(LockMethodLocalHash);
+ }
+ else
+ {
+ /*
+ * Ensure that if not copying old locks that the table contains no
+ * locks.
+ */
+ Assert(LockMethodLocalHash == NULL ||
+ hash_get_num_entries(LockMethodLocalHash) == 0);
+ }
+
+ LockMethodLocalHash = htab;
+}
/*
* Fetch the lock method table associated with a given lock
@@ -2097,6 +2161,7 @@ LockReleaseAll(LOCKMETHODID lockmethodid, bool allLocks)
PROCLOCK *proclock;
int partition;
bool have_fast_path_lwlock = false;
+ long total_locks;
if (lockmethodid <= 0 || lockmethodid >= lengthof(LockMethods))
elog(ERROR, "unrecognized lock method: %d", lockmethodid);
@@ -2118,6 +2183,8 @@ LockReleaseAll(LOCKMETHODID lockmethodid, bool allLocks)
numLockModes = lockMethodTable->numLockModes;
+ total_locks = hash_get_num_entries(LockMethodLocalHash);
+
/*
* First we run through the locallock table and get rid of unwanted
* entries, then we scan the process's proclocks and get rid of those. We
@@ -2349,6 +2416,48 @@ LockReleaseAll(LOCKMETHODID lockmethodid, bool allLocks)
LWLockRelease(partitionLock);
} /* loop over partitions */
+ /* track average locks */
+ locks_released += total_locks;
+ lock_release_count++;
+
+ /* determine if it's worth shrinking the LockMethodLocalHash table */
+ if (lock_release_count >= LOCKMETHODLOCALHASH_SIZE_CHECK_FREQ)
+ {
+ long avglocks = (long) locks_released / lock_release_count;
+
+ /*
+ * The hash_seq_search can become inefficient when the hash table has
+ * grown significantly larger than the default size due to the backend
+ * having run queries which obtained a large numbers of locks at once.
+ * Here we'll check for that and shrink the table if we deem it a
+ * worthwhile thing to do.
+ *
+ * We need only bother checking this if the hash_seq_search is
+ * possibly becoming inefficient. We check this by looking if the
+ * curBucket is larger than the initial size of the table. We then
+ * only bother shrinking the table if the average locks for the
+ * previous few transactions is lower than half this value.
+ */
+ if (status.curBucket > LOCKMETHODLOCALHASH_INIT_SIZE &&
+ avglocks < status.curBucket / 2)
+ {
+ long newsize = LOCKMETHODLOCALHASH_INIT_SIZE;
+
+ while (newsize < avglocks)
+ newsize *= 2;
+
+ /*
+ * If we're releasing all locks then the table will be empty, so
+ * no need to copy out the old locks into the new table.
+ */
+ CreateLocalLockHash(newsize, !allLocks);
+ }
+
+ /* Reset the counters */
+ locks_released = 0;
+ lock_release_count = 0;
+ }
+
#ifdef LOCK_DEBUG
if (*(lockMethodTable->trace_flag))
elog(LOG, "LockReleaseAll done");