Skip to content

Commit 95f08d3

Browse files
committed
Avoid holding AutovacuumScheduleLock while rechecking table statistics.
In databases with many tables, re-fetching the statistics takes some time, so that this behavior seriously decreases the available concurrency for multiple autovac workers. There's discussion afoot about more complete fixes, but a simple and back-patchable amelioration is to claim the table and release the lock before rechecking stats. If we find out there's no longer a reason to process the table, re-taking the lock to un-claim the table is cheap enough. (This patch is quite old, but got lost amongst a discussion of more aggressive fixes. It's not clear when or if such a fix will be accepted, but in any case it'd be unlikely to get back-patched. Let's do this now so we have some improvement for the back branches.) In passing, make the normal un-claim step take AutovacuumScheduleLock not AutovacuumLock, since that is what is documented to protect the wi_tableoid field. This wasn't an actual bug in view of the fact that readers of that field hold both locks, but it creates some concurrency penalty against operations that need only AutovacuumLock. Back-patch to all supported versions. Jeff Janes Discussion: https://postgr.es/m/26118.1520865816@sss.pgh.pa.us
1 parent bd7eb6f commit 95f08d3

File tree

1 file changed

+37
-16
lines changed

1 file changed

+37
-16
lines changed

src/backend/postmaster/autovacuum.c

Lines changed: 37 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -215,9 +215,9 @@ typedef struct autovac_table
215215
* wi_launchtime Time at which this worker was launched
216216
* wi_cost_* Vacuum cost-based delay parameters current in this worker
217217
*
218-
* All fields are protected by AutovacuumLock, except for wi_tableoid which is
219-
* protected by AutovacuumScheduleLock (which is read-only for everyone except
220-
* that worker itself).
218+
* All fields are protected by AutovacuumLock, except for wi_tableoid and
219+
* wi_sharedrel which are protected by AutovacuumScheduleLock (note these
220+
* two fields are read-only for everyone except that worker itself).
221221
*-------------
222222
*/
223223
typedef struct WorkerInfoData
@@ -2261,7 +2261,9 @@ do_autovacuum(void)
22612261
foreach(cell, table_oids)
22622262
{
22632263
Oid relid = lfirst_oid(cell);
2264+
HeapTuple classTup;
22642265
autovac_table *tab;
2266+
bool isshared;
22652267
bool skipit;
22662268
int stdVacuumCostDelay;
22672269
int stdVacuumCostLimit;
@@ -2270,9 +2272,23 @@ do_autovacuum(void)
22702272
CHECK_FOR_INTERRUPTS();
22712273

22722274
/*
2273-
* hold schedule lock from here until we're sure that this table still
2274-
* needs vacuuming. We also need the AutovacuumLock to walk the
2275-
* worker array, but we'll let go of that one quickly.
2275+
* Find out whether the table is shared or not. (It's slightly
2276+
* annoying to fetch the syscache entry just for this, but in typical
2277+
* cases it adds little cost because table_recheck_autovac would
2278+
* refetch the entry anyway. We could buy that back by copying the
2279+
* tuple here and passing it to table_recheck_autovac, but that
2280+
* increases the odds of that function working with stale data.)
2281+
*/
2282+
classTup = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
2283+
if (!HeapTupleIsValid(classTup))
2284+
continue; /* somebody deleted the rel, forget it */
2285+
isshared = ((Form_pg_class) GETSTRUCT(classTup))->relisshared;
2286+
ReleaseSysCache(classTup);
2287+
2288+
/*
2289+
* Hold schedule lock from here until we've claimed the table. We
2290+
* also need the AutovacuumLock to walk the worker array, but that one
2291+
* can just be a shared lock.
22762292
*/
22772293
LWLockAcquire(AutovacuumScheduleLock, LW_EXCLUSIVE);
22782294
LWLockAcquire(AutovacuumLock, LW_SHARED);
@@ -2308,6 +2324,16 @@ do_autovacuum(void)
23082324
continue;
23092325
}
23102326

2327+
/*
2328+
* Store the table's OID in shared memory before releasing the
2329+
* schedule lock, so that other workers don't try to vacuum it
2330+
* concurrently. (We claim it here so as not to hold
2331+
* AutovacuumScheduleLock while rechecking the stats.)
2332+
*/
2333+
MyWorkerInfo->wi_tableoid = relid;
2334+
MyWorkerInfo->wi_sharedrel = isshared;
2335+
LWLockRelease(AutovacuumScheduleLock);
2336+
23112337
/*
23122338
* Check whether pgstat data still says we need to vacuum this table.
23132339
* It could have changed if something else processed the table while
@@ -2324,18 +2350,13 @@ do_autovacuum(void)
23242350
if (tab == NULL)
23252351
{
23262352
/* someone else vacuumed the table, or it went away */
2353+
LWLockAcquire(AutovacuumScheduleLock, LW_EXCLUSIVE);
2354+
MyWorkerInfo->wi_tableoid = InvalidOid;
2355+
MyWorkerInfo->wi_sharedrel = false;
23272356
LWLockRelease(AutovacuumScheduleLock);
23282357
continue;
23292358
}
23302359

2331-
/*
2332-
* Ok, good to go. Store the table in shared memory before releasing
2333-
* the lock so that other workers don't vacuum it concurrently.
2334-
*/
2335-
MyWorkerInfo->wi_tableoid = relid;
2336-
MyWorkerInfo->wi_sharedrel = tab->at_sharedrel;
2337-
LWLockRelease(AutovacuumScheduleLock);
2338-
23392360
/*
23402361
* Remember the prevailing values of the vacuum cost GUCs. We have to
23412362
* restore these at the bottom of the loop, else we'll compute wrong
@@ -2445,10 +2466,10 @@ do_autovacuum(void)
24452466
* settings, so we don't want to give up our share of I/O for a very
24462467
* short interval and thereby thrash the global balance.
24472468
*/
2448-
LWLockAcquire(AutovacuumLock, LW_EXCLUSIVE);
2469+
LWLockAcquire(AutovacuumScheduleLock, LW_EXCLUSIVE);
24492470
MyWorkerInfo->wi_tableoid = InvalidOid;
24502471
MyWorkerInfo->wi_sharedrel = false;
2451-
LWLockRelease(AutovacuumLock);
2472+
LWLockRelease(AutovacuumScheduleLock);
24522473

24532474
/* restore vacuum cost GUCs for the next iteration */
24542475
VacuumCostDelay = stdVacuumCostDelay;

0 commit comments

Comments
 (0)