autovac.patch
application/octet-stream
Filename: autovac.patch
Type: application/octet-stream
Part: 0
Patch
Format: unified
| File | + | − |
|---|---|---|
| src/backend/postmaster/autovacuum.c | 65 | 0 |
diff --git a/src/backend/postmaster/autovacuum.c b/src/backend/postmaster/autovacuum.c
index a8d4dfdd7c..3c2027e77c 100644
--- a/src/backend/postmaster/autovacuum.c
+++ b/src/backend/postmaster/autovacuum.c
@@ -1947,6 +1947,8 @@ do_autovacuum(void)
bool did_vacuum = false;
bool found_concurrent_worker = false;
int i;
+ int skipped, modulo, to_skip;
+ skipped = modulo = to_skip = 0;
/*
* StartTransactionCommand and CommitTransactionCommand will automatically
@@ -2325,6 +2327,66 @@ do_autovacuum(void)
*/
}
+ /*
+ * If we've had to skip a large number of tables consecutively, that's
+ * an indication that either our initial list is now very out-of-date
+ * with reality, or we are competing with other workers to process a
+ * lot of really small tables. Both cases are problematic because on a
+ * database with enough tables to trigger this the hash search to find
+ * updated stats for the table we're looking at (in
+ * table_recheck_autovac) will be non-trivially expensive. When this
+ * happens, we need to take some defensive measures to avoid hopelessly
+ * spinning our wheels.
+ */
+ if (skipped > 1000)
+ {
+ if (modulo == 0)
+ {
+ /*
+ * The first time we get here, we just assume it's because we're
+ * competing with other workers over the same set of tables, not
+ * because our list is very out of date. If it turns out our list
+ * is way out of date we'll quickly max out skipped again and exit
+ * the loop.
+ *
+ * Figure out how many other workers are handling this database and
+ * start skipping enough records to "stay ahead of" them. This
+ * doesn't need to be perfect; the goal is simply to try and get
+ * real work done. If it turns out there's no competing workers
+ * we'll break out soon enough anyway.
+ */
+ LWLockAcquire(AutovacuumLock, LW_SHARED);
+ dlist_foreach(iter, &AutoVacuumShmem->av_runningWorkers)
+ {
+ WorkerInfo worker = dlist_container(WorkerInfoData, wi_links, iter.cur);
+
+ /* we intentionally count ourselves to ensure modulo > 0 */
+
+ /* ignore workers in other databases */
+ if (worker->wi_dboid != MyDatabaseId)
+ continue;
+
+ modulo++;
+ }
+ LWLockRelease(AutovacuumLock);
+ to_skip = modulo;
+ }
+ /*
+ * Handle the case of our list being hopelessly out of date. In
+ * this scenario we built a very large initial list, then spent
+ * enough time processing a table (while other workers carried on)
+ * that our list is hopelessly out of date, so just exit and let
+ * the launcher fire up a new worker.
+ */
+ else
+ break;
+ }
+
+ if (--to_skip > 0)
+ continue;
+ else
+ to_skip = modulo;
+
/*
* Find out whether the table is shared or not. (It's slightly
* annoying to fetch the syscache entry just for this, but in typical
@@ -2408,8 +2470,11 @@ do_autovacuum(void)
MyWorkerInfo->wi_tableoid = InvalidOid;
MyWorkerInfo->wi_sharedrel = false;
LWLockRelease(AutovacuumScheduleLock);
+ skipped++;
continue;
}
+ else
+ skipped = 0;
/*
* Remember the prevailing values of the vacuum cost GUCs. We have to