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

Commit5f6ec27

Browse files
anarazelmichaelpq
authored andcommitted
lwlock: Fix quadratic behavior with very long wait lists
Until now LWLockDequeueSelf() sequentially searched the list of waiters to seeif the current proc is still is on the list of waiters, or has already beenremoved. In extreme workloads, where the wait lists are very long, this leadsto a quadratic behavior. #backends iterating over a list #backendslong. Additionally, the likelihood of needing to call LWLockDequeueSelf() inthe first place also increases with the increased length of the wait queue, asit becomes more likely that a lock is released while waiting for the wait listlock, which is held for longer during lock release.Due to the exponential back-off in perform_spin_delay() this is surprisinglyhard to detect. We should make that easier, e.g. by adding a wait event aroundthe pg_usleep() - but that's a separate patch.The fix is simple - track whether a proc is currently waiting in the wait listor already removed but waiting to be woken up in PGPROC->lwWaiting.In some workloads with a lot of clients contending for a small number oflwlocks (e.g. WALWriteLock), the fix can substantially increase throughput.This has been originally fixed for 16~ witha4adc31 without abackpatch, and we have heard complaints from users impacted by thisquadratic behavior in older versions as well.Author: Andres Freund <andres@anarazel.de>Reviewed-by: Bharath Rupireddy <bharath.rupireddyforpostgres@gmail.com>Discussion:https://postgr.es/m/20221027165914.2hofzp4cvutj6gin@awork3.anarazel.deDiscussion:https://postgr.es/m/CALj2ACXktNbG=K8Xi7PSqbofTZozavhaxjatVc14iYaLu4Maag@mail.gmail.comBackpatch-through: 12
1 parent28215a3 commit5f6ec27

File tree

5 files changed

+42
-27
lines changed

5 files changed

+42
-27
lines changed

‎src/backend/access/transam/twophase.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -482,7 +482,7 @@ MarkAsPreparingGuts(GlobalTransaction gxact, TransactionId xid, const char *gid,
482482
proc->roleId=owner;
483483
proc->tempNamespaceId=InvalidOid;
484484
proc->isBackgroundWorker= false;
485-
proc->lwWaiting=false;
485+
proc->lwWaiting=LW_WS_NOT_WAITING;
486486
proc->lwWaitMode=0;
487487
proc->waitLock=NULL;
488488
proc->waitProcLock=NULL;

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

Lines changed: 30 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -993,6 +993,15 @@ LWLockWakeup(LWLock *lock)
993993
wokeup_somebody= true;
994994
}
995995

996+
/*
997+
* Signal that the process isn't on the wait list anymore. This allows
998+
* LWLockDequeueSelf() to remove itself of the waitlist with a
999+
* proclist_delete(), rather than having to check if it has been
1000+
* removed from the list.
1001+
*/
1002+
Assert(waiter->lwWaiting==LW_WS_WAITING);
1003+
waiter->lwWaiting=LW_WS_PENDING_WAKEUP;
1004+
9961005
/*
9971006
* Once we've woken up an exclusive lock, there's no point in waking
9981007
* up anybody else.
@@ -1050,7 +1059,7 @@ LWLockWakeup(LWLock *lock)
10501059
* another lock.
10511060
*/
10521061
pg_write_barrier();
1053-
waiter->lwWaiting=false;
1062+
waiter->lwWaiting=LW_WS_NOT_WAITING;
10541063
PGSemaphoreUnlock(waiter->sem);
10551064
}
10561065
}
@@ -1071,15 +1080,15 @@ LWLockQueueSelf(LWLock *lock, LWLockMode mode)
10711080
if (MyProc==NULL)
10721081
elog(PANIC,"cannot wait without a PGPROC structure");
10731082

1074-
if (MyProc->lwWaiting)
1083+
if (MyProc->lwWaiting!=LW_WS_NOT_WAITING)
10751084
elog(PANIC,"queueing for lock while waiting on another one");
10761085

10771086
LWLockWaitListLock(lock);
10781087

10791088
/* setting the flag is protected by the spinlock */
10801089
pg_atomic_fetch_or_u32(&lock->state,LW_FLAG_HAS_WAITERS);
10811090

1082-
MyProc->lwWaiting=true;
1091+
MyProc->lwWaiting=LW_WS_WAITING;
10831092
MyProc->lwWaitMode=mode;
10841093

10851094
/* LW_WAIT_UNTIL_FREE waiters are always at the front of the queue */
@@ -1107,8 +1116,7 @@ LWLockQueueSelf(LWLock *lock, LWLockMode mode)
11071116
staticvoid
11081117
LWLockDequeueSelf(LWLock*lock)
11091118
{
1110-
boolfound= false;
1111-
proclist_mutable_iteriter;
1119+
boolon_waitlist;
11121120

11131121
#ifdefLWLOCK_STATS
11141122
lwlock_stats*lwstats;
@@ -1121,18 +1129,13 @@ LWLockDequeueSelf(LWLock *lock)
11211129
LWLockWaitListLock(lock);
11221130

11231131
/*
1124-
* Can't just remove ourselves from the list, but we need to iterate over
1125-
* all entries as somebody else could have dequeued us.
1132+
* Remove ourselves from the waitlist, unless we've already been
1133+
* removed. The removal happens with the wait list lock held, so there's
1134+
* no race in this check.
11261135
*/
1127-
proclist_foreach_modify(iter,&lock->waiters,lwWaitLink)
1128-
{
1129-
if (iter.cur==MyProc->pgprocno)
1130-
{
1131-
found= true;
1132-
proclist_delete(&lock->waiters,iter.cur,lwWaitLink);
1133-
break;
1134-
}
1135-
}
1136+
on_waitlist=MyProc->lwWaiting==LW_WS_WAITING;
1137+
if (on_waitlist)
1138+
proclist_delete(&lock->waiters,MyProc->pgprocno,lwWaitLink);
11361139

11371140
if (proclist_is_empty(&lock->waiters)&&
11381141
(pg_atomic_read_u32(&lock->state)&LW_FLAG_HAS_WAITERS)!=0)
@@ -1144,8 +1147,8 @@ LWLockDequeueSelf(LWLock *lock)
11441147
LWLockWaitListUnlock(lock);
11451148

11461149
/* clear waiting state again, nice for debugging */
1147-
if (found)
1148-
MyProc->lwWaiting=false;
1150+
if (on_waitlist)
1151+
MyProc->lwWaiting=LW_WS_NOT_WAITING;
11491152
else
11501153
{
11511154
intextraWaits=0;
@@ -1169,7 +1172,7 @@ LWLockDequeueSelf(LWLock *lock)
11691172
for (;;)
11701173
{
11711174
PGSemaphoreLock(MyProc->sem);
1172-
if (!MyProc->lwWaiting)
1175+
if (MyProc->lwWaiting==LW_WS_NOT_WAITING)
11731176
break;
11741177
extraWaits++;
11751178
}
@@ -1320,7 +1323,7 @@ LWLockAcquire(LWLock *lock, LWLockMode mode)
13201323
for (;;)
13211324
{
13221325
PGSemaphoreLock(proc->sem);
1323-
if (!proc->lwWaiting)
1326+
if (proc->lwWaiting==LW_WS_NOT_WAITING)
13241327
break;
13251328
extraWaits++;
13261329
}
@@ -1485,7 +1488,7 @@ LWLockAcquireOrWait(LWLock *lock, LWLockMode mode)
14851488
for (;;)
14861489
{
14871490
PGSemaphoreLock(proc->sem);
1488-
if (!proc->lwWaiting)
1491+
if (proc->lwWaiting==LW_WS_NOT_WAITING)
14891492
break;
14901493
extraWaits++;
14911494
}
@@ -1701,7 +1704,7 @@ LWLockWaitForVar(LWLock *lock, uint64 *valptr, uint64 oldval, uint64 *newval)
17011704
for (;;)
17021705
{
17031706
PGSemaphoreLock(proc->sem);
1704-
if (!proc->lwWaiting)
1707+
if (proc->lwWaiting==LW_WS_NOT_WAITING)
17051708
break;
17061709
extraWaits++;
17071710
}
@@ -1779,6 +1782,10 @@ LWLockUpdateVar(LWLock *lock, uint64 *valptr, uint64 val)
17791782

17801783
proclist_delete(&lock->waiters,iter.cur,lwWaitLink);
17811784
proclist_push_tail(&wakeup,iter.cur,lwWaitLink);
1785+
1786+
/* see LWLockWakeup() */
1787+
Assert(waiter->lwWaiting==LW_WS_WAITING);
1788+
waiter->lwWaiting=LW_WS_PENDING_WAKEUP;
17821789
}
17831790

17841791
/* We are done updating shared state of the lock itself. */
@@ -1794,7 +1801,7 @@ LWLockUpdateVar(LWLock *lock, uint64 *valptr, uint64 val)
17941801
proclist_delete(&wakeup,iter.cur,lwWaitLink);
17951802
/* check comment in LWLockWakeup() about this barrier */
17961803
pg_write_barrier();
1797-
waiter->lwWaiting=false;
1804+
waiter->lwWaiting=LW_WS_NOT_WAITING;
17981805
PGSemaphoreUnlock(waiter->sem);
17991806
}
18001807
}

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -400,7 +400,7 @@ InitProcess(void)
400400
/* NB -- autovac launcher intentionally does not set IS_AUTOVACUUM */
401401
if (IsAutoVacuumWorkerProcess())
402402
MyProc->statusFlags |=PROC_IS_AUTOVACUUM;
403-
MyProc->lwWaiting=false;
403+
MyProc->lwWaiting=LW_WS_NOT_WAITING;
404404
MyProc->lwWaitMode=0;
405405
MyProc->waitLock=NULL;
406406
MyProc->waitProcLock=NULL;
@@ -583,7 +583,7 @@ InitAuxiliaryProcess(void)
583583
MyProc->delayChkpt= false;
584584
MyProc->delayChkptEnd= false;
585585
MyProc->statusFlags=0;
586-
MyProc->lwWaiting=false;
586+
MyProc->lwWaiting=LW_WS_NOT_WAITING;
587587
MyProc->lwWaitMode=0;
588588
MyProc->waitLock=NULL;
589589
MyProc->waitProcLock=NULL;

‎src/include/storage/lwlock.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,14 @@
2323

2424
structPGPROC;
2525

26+
/* what state of the wait process is a backend in */
27+
typedefenumLWLockWaitState
28+
{
29+
LW_WS_NOT_WAITING,/* not currently waiting / woken up */
30+
LW_WS_WAITING,/* currently waiting */
31+
LW_WS_PENDING_WAKEUP/* removed from waitlist, but not yet signalled */
32+
}LWLockWaitState;
33+
2634
/*
2735
* Code outside of lwlock.c should not manipulate the contents of this
2836
* structure directly, but we have to declare it here to allow LWLocks to be

‎src/include/storage/proc.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -180,7 +180,7 @@ struct PGPROC
180180
boolrecoveryConflictPending;
181181

182182
/* Info about LWLock the process is currently waiting for, if any. */
183-
boollwWaiting;/*true if waiting for an LW lock */
183+
uint8lwWaiting;/*see LWLockWaitState */
184184
uint8lwWaitMode;/* lwlock mode being waited for */
185185
proclist_nodelwWaitLink;/* position in LW lock wait list */
186186

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp