Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commiteeb6f37

Browse files
committed
Add a small cache of locks owned by a resource owner in ResourceOwner.
This speeds up reassigning locks to the parent owner, when the transactionholds a lot of locks, but only a few of them belong to the current resourceowner. This is particularly helps pg_dump when dumping a large number ofobjects.The cache can hold up to 15 locks in each resource owner. After that, thecache is marked as overflowed, and we fall back to the old method ofscanning the whole local lock table. The tradeoff here is that the cache hasto 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, anddoesn't have much impact on lock release.Jeff Janes, reviewed by Amit Kapila and Heikki Linnakangas.
1 parentdfd9c11 commiteeb6f37

File tree

4 files changed

+205
-53
lines changed

4 files changed

+205
-53
lines changed

‎src/backend/storage/lmgr/lock.c

Lines changed: 105 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -345,6 +345,7 @@ static void BeginStrongLockAcquire(LOCALLOCK *locallock, uint32 fasthashcode);
345345
staticvoidFinishStrongLockAcquire(void);
346346
staticvoidWaitOnLock(LOCALLOCK*locallock,ResourceOwnerowner);
347347
staticvoidReleaseLockIfHeld(LOCALLOCK*locallock,boolsessionLock);
348+
staticvoidLockReassignOwner(LOCALLOCK*locallock,ResourceOwnerparent);
348349
staticboolUnGrantLock(LOCK*lock,LOCKMODElockmode,
349350
PROCLOCK*proclock,LockMethodlockMethodTable);
350351
staticvoidCleanUpLock(LOCK*lock,PROCLOCK*proclock,
@@ -1098,8 +1099,16 @@ SetupLockInTable(LockMethod lockMethodTable, PGPROC *proc,
10981099
staticvoid
10991100
RemoveLocalLock(LOCALLOCK*locallock)
11001101
{
1102+
inti;
1103+
1104+
for (i=locallock->numLockOwners-1;i >=0;i--)
1105+
{
1106+
if (locallock->lockOwners[i].owner!=NULL)
1107+
ResourceOwnerForgetLock(locallock->lockOwners[i].owner,locallock);
1108+
}
11011109
pfree(locallock->lockOwners);
11021110
locallock->lockOwners=NULL;
1111+
11031112
if (locallock->holdsStrongLockCount)
11041113
{
11051114
uint32fasthashcode;
@@ -1112,6 +1121,7 @@ RemoveLocalLock(LOCALLOCK *locallock)
11121121
locallock->holdsStrongLockCount= FALSE;
11131122
SpinLockRelease(&FastPathStrongRelationLocks->mutex);
11141123
}
1124+
11151125
if (!hash_search(LockMethodLocalHash,
11161126
(void*)&(locallock->tag),
11171127
HASH_REMOVE,NULL))
@@ -1355,6 +1365,8 @@ GrantLockLocal(LOCALLOCK *locallock, ResourceOwner owner)
13551365
lockOwners[i].owner=owner;
13561366
lockOwners[i].nLocks=1;
13571367
locallock->numLockOwners++;
1368+
if (owner!=NULL)
1369+
ResourceOwnerRememberLock(owner,locallock);
13581370
}
13591371

13601372
/*
@@ -1670,6 +1682,8 @@ LockRelease(const LOCKTAG *locktag, LOCKMODE lockmode, bool sessionLock)
16701682
Assert(lockOwners[i].nLocks>0);
16711683
if (--lockOwners[i].nLocks==0)
16721684
{
1685+
if (owner!=NULL)
1686+
ResourceOwnerForgetLock(owner,locallock);
16731687
/* compact out unused slot */
16741688
locallock->numLockOwners--;
16751689
if (i<locallock->numLockOwners)
@@ -1862,14 +1876,13 @@ LockReleaseAll(LOCKMETHODID lockmethodid, bool allLocks)
18621876
{
18631877
LOCALLOCKOWNER*lockOwners=locallock->lockOwners;
18641878

1865-
/* Ifit's above array position 0, move it down to 0 */
1866-
for (i=locallock->numLockOwners-1;i>0;i--)
1879+
/* Ifsession lock is above array position 0, move it down to 0 */
1880+
for (i=0;i<locallock->numLockOwners;i++)
18671881
{
18681882
if (lockOwners[i].owner==NULL)
1869-
{
18701883
lockOwners[0]=lockOwners[i];
1871-
break;
1872-
}
1884+
else
1885+
ResourceOwnerForgetLock(lockOwners[i].owner,locallock);
18731886
}
18741887

18751888
if (locallock->numLockOwners>0&&
@@ -1882,6 +1895,8 @@ LockReleaseAll(LOCKMETHODID lockmethodid, bool allLocks)
18821895
/* We aren't deleting this locallock, so done */
18831896
continue;
18841897
}
1898+
else
1899+
locallock->numLockOwners=0;
18851900
}
18861901

18871902
/*
@@ -2067,18 +2082,31 @@ LockReleaseSession(LOCKMETHODID lockmethodid)
20672082
/*
20682083
* LockReleaseCurrentOwner
20692084
*Release all locks belonging to CurrentResourceOwner
2085+
*
2086+
* If the caller knows what those locks are, it can pass them as an array.
2087+
* That speeds up the call significantly, when a lot of locks are held.
2088+
* Otherwise, pass NULL for locallocks, and we'll traverse through our hash
2089+
* table to find them.
20702090
*/
20712091
void
2072-
LockReleaseCurrentOwner(void)
2092+
LockReleaseCurrentOwner(LOCALLOCK**locallocks,intnlocks)
20732093
{
2074-
HASH_SEQ_STATUSstatus;
2075-
LOCALLOCK*locallock;
2094+
if (locallocks==NULL)
2095+
{
2096+
HASH_SEQ_STATUSstatus;
2097+
LOCALLOCK*locallock;
20762098

2077-
hash_seq_init(&status,LockMethodLocalHash);
2099+
hash_seq_init(&status,LockMethodLocalHash);
20782100

2079-
while ((locallock= (LOCALLOCK*)hash_seq_search(&status))!=NULL)
2101+
while ((locallock= (LOCALLOCK*)hash_seq_search(&status))!=NULL)
2102+
ReleaseLockIfHeld(locallock, false);
2103+
}
2104+
else
20802105
{
2081-
ReleaseLockIfHeld(locallock, false);
2106+
inti;
2107+
2108+
for (i=nlocks-1;i >=0;i--)
2109+
ReleaseLockIfHeld(locallocks[i], false);
20822110
}
20832111
}
20842112

@@ -2124,6 +2152,8 @@ ReleaseLockIfHeld(LOCALLOCK *locallock, bool sessionLock)
21242152
locallock->nLocks-=lockOwners[i].nLocks;
21252153
/* compact out unused slot */
21262154
locallock->numLockOwners--;
2155+
if (owner!=NULL)
2156+
ResourceOwnerForgetLock(owner,locallock);
21272157
if (i<locallock->numLockOwners)
21282158
lockOwners[i]=lockOwners[locallock->numLockOwners];
21292159
}
@@ -2146,57 +2176,83 @@ ReleaseLockIfHeld(LOCALLOCK *locallock, bool sessionLock)
21462176
/*
21472177
* LockReassignCurrentOwner
21482178
*Reassign all locks belonging to CurrentResourceOwner to belong
2149-
*to its parent resource owner
2179+
*to its parent resource owner.
2180+
*
2181+
* If the caller knows what those locks are, it can pass them as an array.
2182+
* That speeds up the call significantly, when a lot of locks are held
2183+
* (e.g pg_dump with a large schema). Otherwise, pass NULL for locallocks,
2184+
* and we'll traverse through our hash table to find them.
21502185
*/
21512186
void
2152-
LockReassignCurrentOwner(void)
2187+
LockReassignCurrentOwner(LOCALLOCK**locallocks,intnlocks)
21532188
{
21542189
ResourceOwnerparent=ResourceOwnerGetParent(CurrentResourceOwner);
2155-
HASH_SEQ_STATUSstatus;
2156-
LOCALLOCK*locallock;
2157-
LOCALLOCKOWNER*lockOwners;
21582190

21592191
Assert(parent!=NULL);
21602192

2161-
hash_seq_init(&status,LockMethodLocalHash);
2193+
if (locallocks==NULL)
2194+
{
2195+
HASH_SEQ_STATUSstatus;
2196+
LOCALLOCK*locallock;
21622197

2163-
while ((locallock= (LOCALLOCK*)hash_seq_search(&status))!=NULL)
2198+
hash_seq_init(&status,LockMethodLocalHash);
2199+
2200+
while ((locallock= (LOCALLOCK*)hash_seq_search(&status))!=NULL)
2201+
LockReassignOwner(locallock,parent);
2202+
}
2203+
else
21642204
{
2165-
inti;
2166-
intic=-1;
2167-
intip=-1;
2205+
inti;
21682206

2169-
/*
2170-
* Scan to see if there are any locks belonging to current owner or
2171-
* its parent
2172-
*/
2173-
lockOwners=locallock->lockOwners;
2174-
for (i=locallock->numLockOwners-1;i >=0;i--)
2175-
{
2176-
if (lockOwners[i].owner==CurrentResourceOwner)
2177-
ic=i;
2178-
elseif (lockOwners[i].owner==parent)
2179-
ip=i;
2180-
}
2207+
for (i=nlocks-1;i >=0;i--)
2208+
LockReassignOwner(locallocks[i],parent);
2209+
}
2210+
}
21812211

2182-
if (ic<0)
2183-
continue;/* no current locks */
2212+
/*
2213+
* Subroutine of LockReassignCurrentOwner. Reassigns a given lock belonging to
2214+
* CurrentResourceOwner to its parent.
2215+
*/
2216+
staticvoid
2217+
LockReassignOwner(LOCALLOCK*locallock,ResourceOwnerparent)
2218+
{
2219+
LOCALLOCKOWNER*lockOwners;
2220+
inti;
2221+
intic=-1;
2222+
intip=-1;
21842223

2185-
if (ip<0)
2186-
{
2187-
/* Parent has no slot, so just give it child's slot */
2188-
lockOwners[ic].owner=parent;
2189-
}
2190-
else
2191-
{
2192-
/* Merge child's count with parent's */
2193-
lockOwners[ip].nLocks+=lockOwners[ic].nLocks;
2194-
/* compact out unused slot */
2195-
locallock->numLockOwners--;
2196-
if (ic<locallock->numLockOwners)
2197-
lockOwners[ic]=lockOwners[locallock->numLockOwners];
2198-
}
2224+
/*
2225+
* Scan to see if there are any locks belonging to current owner or
2226+
* its parent
2227+
*/
2228+
lockOwners=locallock->lockOwners;
2229+
for (i=locallock->numLockOwners-1;i >=0;i--)
2230+
{
2231+
if (lockOwners[i].owner==CurrentResourceOwner)
2232+
ic=i;
2233+
elseif (lockOwners[i].owner==parent)
2234+
ip=i;
2235+
}
2236+
2237+
if (ic<0)
2238+
return;/* no current locks */
2239+
2240+
if (ip<0)
2241+
{
2242+
/* Parent has no slot, so just give it the child's slot */
2243+
lockOwners[ic].owner=parent;
2244+
ResourceOwnerRememberLock(parent,locallock);
2245+
}
2246+
else
2247+
{
2248+
/* Merge child's count with parent's */
2249+
lockOwners[ip].nLocks+=lockOwners[ic].nLocks;
2250+
/* compact out unused slot */
2251+
locallock->numLockOwners--;
2252+
if (ic<locallock->numLockOwners)
2253+
lockOwners[ic]=lockOwners[locallock->numLockOwners];
21992254
}
2255+
ResourceOwnerForgetLock(CurrentResourceOwner,locallock);
22002256
}
22012257

22022258
/*

‎src/backend/utils/resowner/resowner.c

Lines changed: 93 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,23 @@
2727
#include"utils/rel.h"
2828
#include"utils/snapmgr.h"
2929

30+
/*
31+
* To speed up bulk releasing or reassigning locks from a resource owner to
32+
* its parent, each resource owner has a small cache of locks it owns. The
33+
* lock manager has the same information in its local lock hash table, and
34+
* we fall back on that if cache overflows, but traversing the hash table
35+
* is slower when there are a lot of locks belonging to other resource owners.
36+
*
37+
* MAX_RESOWNER_LOCKS is the size of the per-resource owner cache. It's
38+
* chosen based on some testing with pg_dump with a large schema. When the
39+
* tests were done (on 9.2), resource owners in a pg_dump run contained up
40+
* to 9 locks, regardless of the schema size, except for the top resource
41+
* owner which contained much more (overflowing the cache). 15 seems like a
42+
* nice round number that's somewhat higher than what pg_dump needs. Note that
43+
* making this number larger is not free - the bigger the cache, the slower
44+
* it is to release locks (in retail), when a resource owner holds many locks.
45+
*/
46+
#defineMAX_RESOWNER_LOCKS 15
3047

3148
/*
3249
* ResourceOwner objects look like this
@@ -43,6 +60,10 @@ typedef struct ResourceOwnerData
4360
Buffer*buffers;/* dynamically allocated array */
4461
intmaxbuffers;/* currently allocated array size */
4562

63+
/* We can remember up to MAX_RESOWNER_LOCKS references to local locks. */
64+
intnlocks;/* number of owned locks */
65+
LOCALLOCK*locks[MAX_RESOWNER_LOCKS];/* list of owned locks */
66+
4667
/* We have built-in support for remembering catcache references */
4768
intncatrefs;/* number of owned catcache pins */
4869
HeapTuple*catrefs;/* dynamically allocated array */
@@ -272,11 +293,30 @@ ResourceOwnerReleaseInternal(ResourceOwner owner,
272293
* subtransaction, we do NOT release its locks yet, but transfer
273294
* them to the parent.
274295
*/
296+
LOCALLOCK**locks;
297+
intnlocks;
298+
275299
Assert(owner->parent!=NULL);
300+
301+
/*
302+
* Pass the list of locks owned by this resource owner to the lock
303+
* manager, unless it has overflowed.
304+
*/
305+
if (owner->nlocks>MAX_RESOWNER_LOCKS)
306+
{
307+
locks=NULL;
308+
nlocks=0;
309+
}
310+
else
311+
{
312+
locks=owner->locks;
313+
nlocks=owner->nlocks;
314+
}
315+
276316
if (isCommit)
277-
LockReassignCurrentOwner();
317+
LockReassignCurrentOwner(locks,nlocks);
278318
else
279-
LockReleaseCurrentOwner();
319+
LockReleaseCurrentOwner(locks,nlocks);
280320
}
281321
}
282322
elseif (phase==RESOURCE_RELEASE_AFTER_LOCKS)
@@ -357,6 +397,7 @@ ResourceOwnerDelete(ResourceOwner owner)
357397

358398
/* And it better not own any resources, either */
359399
Assert(owner->nbuffers==0);
400+
Assert(owner->nlocks==0||owner->nlocks==MAX_RESOWNER_LOCKS+1);
360401
Assert(owner->ncatrefs==0);
361402
Assert(owner->ncatlistrefs==0);
362403
Assert(owner->nrelrefs==0);
@@ -588,6 +629,56 @@ ResourceOwnerForgetBuffer(ResourceOwner owner, Buffer buffer)
588629
}
589630
}
590631

632+
/*
633+
* Remember that a Local Lock is owned by a ResourceOwner
634+
*
635+
* This is different from the other Remember functions in that the list of
636+
* locks is only a lossy cache. It can hold up to MAX_RESOWNER_LOCKS entries,
637+
* and when it overflows, we stop tracking locks. The point of only remembering
638+
* only up to MAX_RESOWNER_LOCKS entries is that if a lot of locks are held,
639+
* ResourceOwnerForgetLock doesn't need to scan through a large array to find
640+
* the entry.
641+
*/
642+
void
643+
ResourceOwnerRememberLock(ResourceOwnerowner,LOCALLOCK*locallock)
644+
{
645+
if (owner->nlocks>MAX_RESOWNER_LOCKS)
646+
return;/* we have already overflowed */
647+
648+
if (owner->nlocks<MAX_RESOWNER_LOCKS)
649+
owner->locks[owner->nlocks]=locallock;
650+
else
651+
{
652+
/* overflowed */
653+
}
654+
owner->nlocks++;
655+
}
656+
657+
/*
658+
* Forget that a Local Lock is owned by a ResourceOwner
659+
*/
660+
void
661+
ResourceOwnerForgetLock(ResourceOwnerowner,LOCALLOCK*locallock)
662+
{
663+
inti;
664+
665+
if (owner->nlocks>MAX_RESOWNER_LOCKS)
666+
return;/* we have overflowed */
667+
668+
Assert(owner->nlocks>0);
669+
for (i=owner->nlocks-1;i >=0;i--)
670+
{
671+
if (locallock==owner->locks[i])
672+
{
673+
owner->locks[i]=owner->locks[owner->nlocks-1];
674+
owner->nlocks--;
675+
return;
676+
}
677+
}
678+
elog(ERROR,"lock reference %p is not owned by resource owner %s",
679+
locallock,owner->name);
680+
}
681+
591682
/*
592683
* Make sure there is room for at least one more entry in a ResourceOwner's
593684
* catcache reference array.

‎src/include/storage/lock.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -492,8 +492,8 @@ extern bool LockRelease(const LOCKTAG *locktag,
492492
LOCKMODElockmode,boolsessionLock);
493493
externvoidLockReleaseAll(LOCKMETHODIDlockmethodid,boolallLocks);
494494
externvoidLockReleaseSession(LOCKMETHODIDlockmethodid);
495-
externvoidLockReleaseCurrentOwner(void);
496-
externvoidLockReassignCurrentOwner(void);
495+
externvoidLockReleaseCurrentOwner(LOCALLOCK**locallocks,intnlocks);
496+
externvoidLockReassignCurrentOwner(LOCALLOCK**locallocks,intnlocks);
497497
externVirtualTransactionId*GetLockConflicts(constLOCKTAG*locktag,
498498
LOCKMODElockmode);
499499
externvoidAtPrepare_Locks(void);

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp