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

Commitdc9d424

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 parenta6463d2 commitdc9d424

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
@@ -484,7 +484,7 @@ MarkAsPreparingGuts(GlobalTransaction gxact, TransactionId xid, const char *gid,
484484
proc->roleId=owner;
485485
proc->tempNamespaceId=InvalidOid;
486486
proc->isBackgroundWorker= false;
487-
proc->lwWaiting=false;
487+
proc->lwWaiting=LW_WS_NOT_WAITING;
488488
proc->lwWaitMode=0;
489489
proc->waitLock=NULL;
490490
proc->waitProcLock=NULL;

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

Lines changed: 30 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -998,6 +998,15 @@ LWLockWakeup(LWLock *lock)
998998
wokeup_somebody= true;
999999
}
10001000

1001+
/*
1002+
* Signal that the process isn't on the wait list anymore. This allows
1003+
* LWLockDequeueSelf() to remove itself of the waitlist with a
1004+
* proclist_delete(), rather than having to check if it has been
1005+
* removed from the list.
1006+
*/
1007+
Assert(waiter->lwWaiting==LW_WS_WAITING);
1008+
waiter->lwWaiting=LW_WS_PENDING_WAKEUP;
1009+
10011010
/*
10021011
* Once we've woken up an exclusive lock, there's no point in waking
10031012
* up anybody else.
@@ -1055,7 +1064,7 @@ LWLockWakeup(LWLock *lock)
10551064
* another lock.
10561065
*/
10571066
pg_write_barrier();
1058-
waiter->lwWaiting=false;
1067+
waiter->lwWaiting=LW_WS_NOT_WAITING;
10591068
PGSemaphoreUnlock(waiter->sem);
10601069
}
10611070
}
@@ -1076,15 +1085,15 @@ LWLockQueueSelf(LWLock *lock, LWLockMode mode)
10761085
if (MyProc==NULL)
10771086
elog(PANIC,"cannot wait without a PGPROC structure");
10781087

1079-
if (MyProc->lwWaiting)
1088+
if (MyProc->lwWaiting!=LW_WS_NOT_WAITING)
10801089
elog(PANIC,"queueing for lock while waiting on another one");
10811090

10821091
LWLockWaitListLock(lock);
10831092

10841093
/* setting the flag is protected by the spinlock */
10851094
pg_atomic_fetch_or_u32(&lock->state,LW_FLAG_HAS_WAITERS);
10861095

1087-
MyProc->lwWaiting=true;
1096+
MyProc->lwWaiting=LW_WS_WAITING;
10881097
MyProc->lwWaitMode=mode;
10891098

10901099
/* LW_WAIT_UNTIL_FREE waiters are always at the front of the queue */
@@ -1112,8 +1121,7 @@ LWLockQueueSelf(LWLock *lock, LWLockMode mode)
11121121
staticvoid
11131122
LWLockDequeueSelf(LWLock*lock)
11141123
{
1115-
boolfound= false;
1116-
proclist_mutable_iteriter;
1124+
boolon_waitlist;
11171125

11181126
#ifdefLWLOCK_STATS
11191127
lwlock_stats*lwstats;
@@ -1126,18 +1134,13 @@ LWLockDequeueSelf(LWLock *lock)
11261134
LWLockWaitListLock(lock);
11271135

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

11421145
if (proclist_is_empty(&lock->waiters)&&
11431146
(pg_atomic_read_u32(&lock->state)&LW_FLAG_HAS_WAITERS)!=0)
@@ -1149,8 +1152,8 @@ LWLockDequeueSelf(LWLock *lock)
11491152
LWLockWaitListUnlock(lock);
11501153

11511154
/* clear waiting state again, nice for debugging */
1152-
if (found)
1153-
MyProc->lwWaiting=false;
1155+
if (on_waitlist)
1156+
MyProc->lwWaiting=LW_WS_NOT_WAITING;
11541157
else
11551158
{
11561159
intextraWaits=0;
@@ -1174,7 +1177,7 @@ LWLockDequeueSelf(LWLock *lock)
11741177
for (;;)
11751178
{
11761179
PGSemaphoreLock(MyProc->sem);
1177-
if (!MyProc->lwWaiting)
1180+
if (MyProc->lwWaiting==LW_WS_NOT_WAITING)
11781181
break;
11791182
extraWaits++;
11801183
}
@@ -1325,7 +1328,7 @@ LWLockAcquire(LWLock *lock, LWLockMode mode)
13251328
for (;;)
13261329
{
13271330
PGSemaphoreLock(proc->sem);
1328-
if (!proc->lwWaiting)
1331+
if (proc->lwWaiting==LW_WS_NOT_WAITING)
13291332
break;
13301333
extraWaits++;
13311334
}
@@ -1490,7 +1493,7 @@ LWLockAcquireOrWait(LWLock *lock, LWLockMode mode)
14901493
for (;;)
14911494
{
14921495
PGSemaphoreLock(proc->sem);
1493-
if (!proc->lwWaiting)
1496+
if (proc->lwWaiting==LW_WS_NOT_WAITING)
14941497
break;
14951498
extraWaits++;
14961499
}
@@ -1706,7 +1709,7 @@ LWLockWaitForVar(LWLock *lock, uint64 *valptr, uint64 oldval, uint64 *newval)
17061709
for (;;)
17071710
{
17081711
PGSemaphoreLock(proc->sem);
1709-
if (!proc->lwWaiting)
1712+
if (proc->lwWaiting==LW_WS_NOT_WAITING)
17101713
break;
17111714
extraWaits++;
17121715
}
@@ -1787,6 +1790,10 @@ LWLockUpdateVar(LWLock *lock, uint64 *valptr, uint64 val)
17871790

17881791
proclist_delete(&lock->waiters,iter.cur,lwWaitLink);
17891792
proclist_push_tail(&wakeup,iter.cur,lwWaitLink);
1793+
1794+
/* see LWLockWakeup() */
1795+
Assert(waiter->lwWaiting==LW_WS_WAITING);
1796+
waiter->lwWaiting=LW_WS_PENDING_WAKEUP;
17901797
}
17911798

17921799
/* We are done updating shared state of the lock itself. */
@@ -1802,7 +1809,7 @@ LWLockUpdateVar(LWLock *lock, uint64 *valptr, uint64 val)
18021809
proclist_delete(&wakeup,iter.cur,lwWaitLink);
18031810
/* check comment in LWLockWakeup() about this barrier */
18041811
pg_write_barrier();
1805-
waiter->lwWaiting=false;
1812+
waiter->lwWaiting=LW_WS_NOT_WAITING;
18061813
PGSemaphoreUnlock(waiter->sem);
18071814
}
18081815
}

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -402,7 +402,7 @@ InitProcess(void)
402402
/* NB -- autovac launcher intentionally does not set IS_AUTOVACUUM */
403403
if (IsAutoVacuumWorkerProcess())
404404
MyPgXact->vacuumFlags |=PROC_IS_AUTOVACUUM;
405-
MyProc->lwWaiting=false;
405+
MyProc->lwWaiting=LW_WS_NOT_WAITING;
406406
MyProc->lwWaitMode=0;
407407
MyProc->waitLock=NULL;
408408
MyProc->waitProcLock=NULL;
@@ -582,7 +582,7 @@ InitAuxiliaryProcess(void)
582582
MyProc->delayChkpt= false;
583583
MyProc->delayChkptEnd= false;
584584
MyPgXact->vacuumFlags=0;
585-
MyProc->lwWaiting=false;
585+
MyProc->lwWaiting=LW_WS_NOT_WAITING;
586586
MyProc->lwWaitMode=0;
587587
MyProc->waitLock=NULL;
588588
MyProc->waitProcLock=NULL;

‎src/include/storage/lwlock.h

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

2525
structPGPROC;
2626

27+
/* what state of the wait process is a backend in */
28+
typedefenumLWLockWaitState
29+
{
30+
LW_WS_NOT_WAITING,/* not currently waiting / woken up */
31+
LW_WS_WAITING,/* currently waiting */
32+
LW_WS_PENDING_WAKEUP/* removed from waitlist, but not yet signalled */
33+
}LWLockWaitState;
34+
2735
/*
2836
* Code outside of lwlock.c should not manipulate the contents of this
2937
* 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
@@ -140,7 +140,7 @@ struct PGPROC
140140
boolrecoveryConflictPending;
141141

142142
/* Info about LWLock the process is currently waiting for, if any. */
143-
boollwWaiting;/*true if waiting for an LW lock */
143+
uint8lwWaiting;/*see LWLockWaitState */
144144
uint8lwWaitMode;/* lwlock mode being waited for */
145145
proclist_nodelwWaitLink;/* position in LW lock wait list */
146146

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp