I came up with the following patch. What this does is cancel any ANALYZE started by autovacuum, at the top of ALTER TABLE.
There is a new function relation_openrv_cav(). This is the same as relation_openrv, except that it will also cancel analyzes. I'm still wondering if I should merge the two and have a third boolean argument to specify whether to do the cancel. Comments? -- Alvaro Herrera http://www.amazon.com/gp/registry/CTMLCN8V17R4 "Ciencias políticas es la ciencia de entender por qué los políticos actúan como lo hacen" (netfunny.com)
Index: src/backend/access/heap/heapam.c =================================================================== RCS file: /home/alvherre/Code/cvs/pgsql/src/backend/access/heap/heapam.c,v retrieving revision 1.242 diff -c -p -r1.242 heapam.c *** src/backend/access/heap/heapam.c 21 Sep 2007 21:25:42 -0000 1.242 --- src/backend/access/heap/heapam.c 3 Oct 2007 22:12:34 -0000 *************** relation_openrv(const RangeVar *relation *** 971,976 **** --- 971,996 ---- return relation_open(relOid, lockmode); } + /* + * relation_openrv_cav + * + * As above, but cancel any autovacuum-induced ANALYZE. + */ + Relation + relation_openrv_cav(const RangeVar *relation, LOCKMODE lockmode) + { + Oid relOid; + + if (lockmode != NoLock) + AcceptInvalidationMessages(); + + relOid = RangeVarGetRelid(relation, false); + + autovac_cancel_analyze(relOid); + + return relation_open(relOid, lockmode); + } + /* ---------------- * relation_close - close any relation * Index: src/backend/commands/tablecmds.c =================================================================== RCS file: /home/alvherre/Code/cvs/pgsql/src/backend/commands/tablecmds.c,v retrieving revision 1.233 diff -c -p -r1.233 tablecmds.c *** src/backend/commands/tablecmds.c 29 Sep 2007 17:18:58 -0000 1.233 --- src/backend/commands/tablecmds.c 3 Oct 2007 22:24:17 -0000 *************** renamerel(Oid myrelid, const char *newre *** 1748,1754 **** void AlterTable(AlterTableStmt *stmt) { ! Relation rel = relation_openrv(stmt->relation, AccessExclusiveLock); int expected_refcnt; /* --- 1748,1754 ---- void AlterTable(AlterTableStmt *stmt) { ! Relation rel = relation_openrv_cav(stmt->relation, AccessExclusiveLock); int expected_refcnt; /* Index: src/backend/postmaster/autovacuum.c =================================================================== RCS file: /home/alvherre/Code/cvs/pgsql/src/backend/postmaster/autovacuum.c,v retrieving revision 1.61 diff -c -p -r1.61 autovacuum.c *** src/backend/postmaster/autovacuum.c 24 Sep 2007 04:12:01 -0000 1.61 --- src/backend/postmaster/autovacuum.c 3 Oct 2007 22:50:14 -0000 *************** typedef struct autovac_table *** 183,188 **** --- 183,189 ---- * wi_dboid OID of the database this worker is supposed to work on * wi_tableoid OID of the table currently being vacuumed * wi_workerpid PID of the running worker, 0 if not yet started + * wi_activity Type of task this worker is currently executing * wi_launchtime Time at which this worker was launched * wi_cost_* Vacuum cost-based delay parameters current in this worker * *************** typedef struct autovac_table *** 191,202 **** --- 192,211 ---- * that worker itself). *------------- */ + typedef enum + { + AvActivityNone, + AvActivityVacuum, + AvActivityAnalyze + } AvActivity; + typedef struct WorkerInfoData { SHM_QUEUE wi_links; Oid wi_dboid; Oid wi_tableoid; int wi_workerpid; + AvActivity wi_activity; TimestampTz wi_launchtime; int wi_cost_delay; int wi_cost_limit; *************** AutoVacLauncherMain(int argc, char *argv *** 695,700 **** --- 704,710 ---- worker->wi_dboid = InvalidOid; worker->wi_tableoid = InvalidOid; worker->wi_workerpid = 0; + worker->wi_activity = AvActivityNone; worker->wi_launchtime = 0; worker->wi_links.next = AutoVacuumShmem->av_freeWorkers; AutoVacuumShmem->av_freeWorkers = MAKE_OFFSET(worker); *************** do_start_worker(void) *** 1199,1204 **** --- 1209,1215 ---- worker->wi_dboid = avdb->adw_datid; worker->wi_workerpid = 0; + worker->wi_activity = AvActivityNone; worker->wi_launchtime = GetCurrentTimestamp(); AutoVacuumShmem->av_startingWorker = sworker; *************** FreeWorkerInfo(int code, Datum arg) *** 1638,1643 **** --- 1649,1655 ---- MyWorkerInfo->wi_dboid = InvalidOid; MyWorkerInfo->wi_tableoid = InvalidOid; MyWorkerInfo->wi_workerpid = 0; + MyWorkerInfo->wi_activity = AvActivityNone; MyWorkerInfo->wi_launchtime = 0; MyWorkerInfo->wi_cost_delay = 0; MyWorkerInfo->wi_cost_limit = 0; *************** next_worker: *** 2062,2076 **** VacuumCostDelay = tab->at_vacuum_cost_delay; VacuumCostLimit = tab->at_vacuum_cost_limit; ! /* ! * Advertise my cost delay parameters for the balancing algorithm, and ! * do a balance ! */ LWLockAcquire(AutovacuumLock, LW_EXCLUSIVE); MyWorkerInfo->wi_cost_delay = tab->at_vacuum_cost_delay; MyWorkerInfo->wi_cost_limit = tab->at_vacuum_cost_limit; MyWorkerInfo->wi_cost_limit_base = tab->at_vacuum_cost_limit; autovac_balance_cost(); LWLockRelease(AutovacuumLock); /* clean up memory before each iteration */ --- 2074,2095 ---- VacuumCostDelay = tab->at_vacuum_cost_delay; VacuumCostLimit = tab->at_vacuum_cost_limit; ! /* Last fixups before actually starting to work */ LWLockAcquire(AutovacuumLock, LW_EXCLUSIVE); + + /* advertise my cost delay parameters for the balancing algorithm */ MyWorkerInfo->wi_cost_delay = tab->at_vacuum_cost_delay; MyWorkerInfo->wi_cost_limit = tab->at_vacuum_cost_limit; MyWorkerInfo->wi_cost_limit_base = tab->at_vacuum_cost_limit; + + /* do a balance */ autovac_balance_cost(); + + /* advertise my current activity */ + MyWorkerInfo->wi_activity = + tab->at_dovacuum ? AvActivityVacuum : AvActivityAnalyze; + + /* done */ LWLockRelease(AutovacuumLock); /* clean up memory before each iteration */ *************** next_worker: *** 2078,2086 **** /* * We will abort vacuuming the current table if we are interrupted, and ! * continue with the next one in schedule; but if anything else ! * happens, we will do our usual error handling which is to cause the ! * worker process to exit. */ PG_TRY(); { --- 2097,2103 ---- /* * We will abort vacuuming the current table if we are interrupted, and ! * continue with the next one in schedule. */ PG_TRY(); { *************** next_worker: *** 2100,2132 **** errdata = CopyErrorData(); /* ! * If we errored out due to a cancel request, abort and restart the ! * transaction and go to the next table. Otherwise rethrow the ! * error so that the outermost handler deals with it. */ ! if (errdata->sqlerrcode == ERRCODE_QUERY_CANCELED) ! { ! HOLD_INTERRUPTS(); ! elog(LOG, "cancelling autovacuum of table \"%s.%s.%s\"", ! get_database_name(MyDatabaseId), ! get_namespace_name(get_rel_namespace(tab->at_relid)), ! get_rel_name(tab->at_relid)); ! ! AbortOutOfAnyTransaction(); ! FlushErrorState(); ! MemoryContextResetAndDeleteChildren(PortalContext); ! ! /* restart our transaction for the following operations */ ! StartTransactionCommand(); ! RESUME_INTERRUPTS(); ! } ! else ! PG_RE_THROW(); } PG_END_TRY(); /* be tidy */ pfree(tab); } /* --- 2117,2150 ---- errdata = CopyErrorData(); /* ! * Abort the transaction, restart a new one, and proceed with the ! * next table in our list. */ ! HOLD_INTERRUPTS(); ! ereport(LOG, ! (errmsg("cancelling autovacuum of table \"%s.%s.%s\"", ! get_database_name(MyDatabaseId), ! get_namespace_name(get_rel_namespace(tab->at_relid)), ! get_rel_name(tab->at_relid)))); ! ! AbortOutOfAnyTransaction(); ! FlushErrorState(); ! MemoryContextResetAndDeleteChildren(PortalContext); ! ! /* restart our transaction for the following operations */ ! StartTransactionCommand(); ! RESUME_INTERRUPTS(); } PG_END_TRY(); /* be tidy */ pfree(tab); + + /* remove my info from shared memory */ + LWLockAcquire(AutovacuumLock, LW_EXCLUSIVE); + MyWorkerInfo->wi_tableoid = InvalidOid; + MyWorkerInfo->wi_activity = AvActivityNone; + LWLockRelease(AutovacuumLock); } /* *************** autovac_refresh_stats(void) *** 2764,2766 **** --- 2782,2828 ---- pgstat_clear_snapshot(); } + + /* + * autovac_cancel_analyze + * + * Cancel any running autovacuum-induced ANALYZE in the given relation. + */ + void + autovac_cancel_analyze(Oid relid) + { + WorkerInfo worker; + + restart: + LWLockAcquire(AutovacuumLock, LW_SHARED); + worker = (WorkerInfo) SHMQueueNext(&AutoVacuumShmem->av_runningWorkers, + &AutoVacuumShmem->av_runningWorkers, + offsetof(WorkerInfoData, wi_links)); + while (worker) + { + if (worker->wi_dboid != MyDatabaseId) + goto next_worker; + if (worker->wi_tableoid != relid) + goto next_worker; + + if (worker->wi_activity == AvActivityAnalyze) + { + int pid; + + pid = worker->wi_workerpid; + + LWLockRelease(AutovacuumLock); + + elog(NOTICE, "cancelling auto-analyze"); + (void) kill(pid, SIGINT); + goto restart; + } + + next_worker: + worker = (WorkerInfo) SHMQueueNext(&AutoVacuumShmem->av_runningWorkers, + &worker->wi_links, + offsetof(WorkerInfoData, wi_links)); + } + + LWLockRelease(AutovacuumLock); + } Index: src/include/access/heapam.h =================================================================== RCS file: /home/alvherre/Code/cvs/pgsql/src/include/access/heapam.h,v retrieving revision 1.127 diff -c -p -r1.127 heapam.h *** src/include/access/heapam.h 20 Sep 2007 17:56:32 -0000 1.127 --- src/include/access/heapam.h 3 Oct 2007 22:13:17 -0000 *************** extern Relation relation_open(Oid relati *** 132,137 **** --- 132,138 ---- extern Relation try_relation_open(Oid relationId, LOCKMODE lockmode); extern Relation relation_open_nowait(Oid relationId, LOCKMODE lockmode); extern Relation relation_openrv(const RangeVar *relation, LOCKMODE lockmode); + extern Relation relation_openrv_cav(const RangeVar *relation, LOCKMODE lockmode); extern void relation_close(Relation relation, LOCKMODE lockmode); extern Relation heap_open(Oid relationId, LOCKMODE lockmode); Index: src/include/postmaster/autovacuum.h =================================================================== RCS file: /home/alvherre/Code/cvs/pgsql/src/include/postmaster/autovacuum.h,v retrieving revision 1.12 diff -c -p -r1.12 autovacuum.h *** src/include/postmaster/autovacuum.h 24 Sep 2007 03:12:23 -0000 1.12 --- src/include/postmaster/autovacuum.h 3 Oct 2007 22:13:02 -0000 *************** extern int StartAutoVacLauncher(void); *** 44,49 **** --- 44,51 ---- extern int StartAutoVacWorker(void); /* called from postmaster when a worker could not be forked */ extern void AutoVacWorkerFailed(void); + /* called from backends before they want to lock a table */ + extern void autovac_cancel_analyze(Oid relid); /* autovacuum cost-delay balancer */ extern void AutoVacuumUpdateDelay(void);
---------------------------(end of broadcast)--------------------------- TIP 9: In versions below 8.0, the planner will ignore your desire to choose an index scan if your joining column's datatypes do not match