1
0
mirror of https://github.com/postgres/postgres.git synced 2025-12-19 17:02:53 +03:00

Add a small cache of locks owned by a resource owner in ResourceOwner.

Back-patch 9.3-era commit eeb6f37d89, to
improve the older branches' ability to cope with pg_dump dumping a large
number of tables.

I back-patched into 9.2 and 9.1, but not 9.0 as it would have required a
significant amount of refactoring, thus negating the argument that this
is by-now-well-tested code.

Jeff Janes, reviewed by Amit Kapila and Heikki Linnakangas.
This commit is contained in:
Tom Lane
2015-08-27 12:22:10 -04:00
parent 8cffc4f5bd
commit 0e933fdf94
4 changed files with 209 additions and 57 deletions

View File

@@ -27,6 +27,23 @@
#include "utils/rel.h"
#include "utils/snapmgr.h"
/*
* To speed up bulk releasing or reassigning locks from a resource owner to
* its parent, each resource owner has a small cache of locks it owns. The
* lock manager has the same information in its local lock hash table, and
* we fall back on that if cache overflows, but traversing the hash table
* is slower when there are a lot of locks belonging to other resource owners.
*
* MAX_RESOWNER_LOCKS is the size of the per-resource owner cache. It's
* chosen based on some testing with pg_dump with a large schema. When the
* tests were done (on 9.2), resource owners in a pg_dump run contained up
* to 9 locks, regardless of the schema size, except for the top resource
* owner which contained much more (overflowing the cache). 15 seems like a
* nice round number that's somewhat higher than what pg_dump needs. Note that
* making this number larger is not free - the bigger the cache, the slower
* it is to release locks (in retail), when a resource owner holds many locks.
*/
#define MAX_RESOWNER_LOCKS 15
/*
* ResourceOwner objects look like this
@@ -43,6 +60,10 @@ typedef struct ResourceOwnerData
Buffer *buffers; /* dynamically allocated array */
int maxbuffers; /* currently allocated array size */
/* We can remember up to MAX_RESOWNER_LOCKS references to local locks. */
int nlocks; /* number of owned locks */
LOCALLOCK *locks[MAX_RESOWNER_LOCKS]; /* list of owned locks */
/* We have built-in support for remembering catcache references */
int ncatrefs; /* number of owned catcache pins */
HeapTuple *catrefs; /* dynamically allocated array */
@@ -272,11 +293,30 @@ ResourceOwnerReleaseInternal(ResourceOwner owner,
* subtransaction, we do NOT release its locks yet, but transfer
* them to the parent.
*/
LOCALLOCK **locks;
int nlocks;
Assert(owner->parent != NULL);
if (isCommit)
LockReassignCurrentOwner();
/*
* Pass the list of locks owned by this resource owner to the lock
* manager, unless it has overflowed.
*/
if (owner->nlocks > MAX_RESOWNER_LOCKS)
{
locks = NULL;
nlocks = 0;
}
else
LockReleaseCurrentOwner();
{
locks = owner->locks;
nlocks = owner->nlocks;
}
if (isCommit)
LockReassignCurrentOwner(locks, nlocks);
else
LockReleaseCurrentOwner(locks, nlocks);
}
}
else if (phase == RESOURCE_RELEASE_AFTER_LOCKS)
@@ -357,6 +397,7 @@ ResourceOwnerDelete(ResourceOwner owner)
/* And it better not own any resources, either */
Assert(owner->nbuffers == 0);
Assert(owner->nlocks == 0 || owner->nlocks == MAX_RESOWNER_LOCKS + 1);
Assert(owner->ncatrefs == 0);
Assert(owner->ncatlistrefs == 0);
Assert(owner->nrelrefs == 0);
@@ -588,6 +629,56 @@ ResourceOwnerForgetBuffer(ResourceOwner owner, Buffer buffer)
}
}
/*
* Remember that a Local Lock is owned by a ResourceOwner
*
* This is different from the other Remember functions in that the list of
* locks is only a lossy cache. It can hold up to MAX_RESOWNER_LOCKS entries,
* and when it overflows, we stop tracking locks. The point of only remembering
* only up to MAX_RESOWNER_LOCKS entries is that if a lot of locks are held,
* ResourceOwnerForgetLock doesn't need to scan through a large array to find
* the entry.
*/
void
ResourceOwnerRememberLock(ResourceOwner owner, LOCALLOCK *locallock)
{
if (owner->nlocks > MAX_RESOWNER_LOCKS)
return; /* we have already overflowed */
if (owner->nlocks < MAX_RESOWNER_LOCKS)
owner->locks[owner->nlocks] = locallock;
else
{
/* overflowed */
}
owner->nlocks++;
}
/*
* Forget that a Local Lock is owned by a ResourceOwner
*/
void
ResourceOwnerForgetLock(ResourceOwner owner, LOCALLOCK *locallock)
{
int i;
if (owner->nlocks > MAX_RESOWNER_LOCKS)
return; /* we have overflowed */
Assert(owner->nlocks > 0);
for (i = owner->nlocks - 1; i >= 0; i--)
{
if (locallock == owner->locks[i])
{
owner->locks[i] = owner->locks[owner->nlocks - 1];
owner->nlocks--;
return;
}
}
elog(ERROR, "lock reference %p is not owned by resource owner %s",
locallock, owner->name);
}
/*
* Make sure there is room for at least one more entry in a ResourceOwner's
* catcache reference array.