Simon Riggs escribió: > Seems like we don't need to mess with the deadlock checker itself. > > We can rely on the process at the head of the lock wait queue to sort > this out for us. So all we need do is look at the isAutovacuum flag on > the process that is holding the lock we're waiting on. If it isn't an > autoANALYZE we can carry on with the main deadlock check. We just need a > new kind of deadlock state to handle this, then let ProcSleep send > SIGINT to the autoANALYZE and then go back to sleep, waiting to be > reawoken when the auotANALYZE aborts.
Ok, I think this makes sense. I can offer the following patch -- it makes it possible to determine whether an autovacuum process is doing analyze or not, by comparing the PGPROC of the running WorkerInfo list (the list has at most max_autovacuum_workers entries, so this is better than trolling ProcGlobal). -- Alvaro Herrera http://www.CommandPrompt.com/ PostgreSQL Replication, Consulting, Custom Development, 24x7 support
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 4 Oct 2007 21:32:11 -0000 *************** typedef struct autovac_table *** 182,188 **** * wi_links entry into free list or running list * 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_launchtime Time at which this worker was launched * wi_cost_* Vacuum cost-based delay parameters current in this worker * --- 182,189 ---- * wi_links entry into free list or running list * wi_dboid OID of the database this worker is supposed to work on * wi_tableoid OID of the table currently being vacuumed ! * wi_proc pointer to PGPROC of the running worker, NULL if not 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 **** * that worker itself). *------------- */ typedef struct WorkerInfoData { SHM_QUEUE wi_links; Oid wi_dboid; Oid wi_tableoid; ! int wi_workerpid; TimestampTz wi_launchtime; int wi_cost_delay; int wi_cost_limit; --- 192,211 ---- * that worker itself). *------------- */ + typedef enum + { + AvActivityNone, + AvActivityVacuum, + AvActivityAnalyze + } AvActivity; + typedef struct WorkerInfoData { SHM_QUEUE wi_links; Oid wi_dboid; Oid wi_tableoid; ! PGPROC *wi_proc; ! AvActivity wi_activity; TimestampTz wi_launchtime; int wi_cost_delay; int wi_cost_limit; *************** AutoVacLauncherMain(int argc, char *argv *** 694,700 **** worker = (WorkerInfo) MAKE_PTR(AutoVacuumShmem->av_startingWorker); worker->wi_dboid = InvalidOid; worker->wi_tableoid = InvalidOid; ! worker->wi_workerpid = 0; worker->wi_launchtime = 0; worker->wi_links.next = AutoVacuumShmem->av_freeWorkers; AutoVacuumShmem->av_freeWorkers = MAKE_OFFSET(worker); --- 703,710 ---- worker = (WorkerInfo) MAKE_PTR(AutoVacuumShmem->av_startingWorker); worker->wi_dboid = InvalidOid; worker->wi_tableoid = InvalidOid; ! worker->wi_proc = NULL; ! 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) *** 1198,1204 **** AutoVacuumShmem->av_freeWorkers = worker->wi_links.next; worker->wi_dboid = avdb->adw_datid; ! worker->wi_workerpid = 0; worker->wi_launchtime = GetCurrentTimestamp(); AutoVacuumShmem->av_startingWorker = sworker; --- 1208,1215 ---- AutoVacuumShmem->av_freeWorkers = worker->wi_links.next; worker->wi_dboid = avdb->adw_datid; ! worker->wi_proc = NULL; ! worker->wi_activity = AvActivityNone; worker->wi_launchtime = GetCurrentTimestamp(); AutoVacuumShmem->av_startingWorker = sworker; *************** AutoVacWorkerMain(int argc, char *argv[] *** 1542,1548 **** { MyWorkerInfo = (WorkerInfo) MAKE_PTR(AutoVacuumShmem->av_startingWorker); dbid = MyWorkerInfo->wi_dboid; ! MyWorkerInfo->wi_workerpid = MyProcPid; /* insert into the running list */ SHMQueueInsertBefore(&AutoVacuumShmem->av_runningWorkers, --- 1553,1559 ---- { MyWorkerInfo = (WorkerInfo) MAKE_PTR(AutoVacuumShmem->av_startingWorker); dbid = MyWorkerInfo->wi_dboid; ! MyWorkerInfo->wi_proc = MyProc; /* insert into the running list */ SHMQueueInsertBefore(&AutoVacuumShmem->av_runningWorkers, *************** FreeWorkerInfo(int code, Datum arg) *** 1637,1643 **** MyWorkerInfo->wi_links.next = AutoVacuumShmem->av_freeWorkers; MyWorkerInfo->wi_dboid = InvalidOid; MyWorkerInfo->wi_tableoid = InvalidOid; ! MyWorkerInfo->wi_workerpid = 0; MyWorkerInfo->wi_launchtime = 0; MyWorkerInfo->wi_cost_delay = 0; MyWorkerInfo->wi_cost_limit = 0; --- 1648,1655 ---- MyWorkerInfo->wi_links.next = AutoVacuumShmem->av_freeWorkers; MyWorkerInfo->wi_dboid = InvalidOid; MyWorkerInfo->wi_tableoid = InvalidOid; ! MyWorkerInfo->wi_proc = NULL; ! MyWorkerInfo->wi_activity = AvActivityNone; MyWorkerInfo->wi_launchtime = 0; MyWorkerInfo->wi_cost_delay = 0; MyWorkerInfo->wi_cost_limit = 0; *************** autovac_balance_cost(void) *** 1701,1707 **** offsetof(WorkerInfoData, wi_links)); while (worker) { ! if (worker->wi_workerpid != 0 && worker->wi_cost_limit_base > 0 && worker->wi_cost_delay > 0) cost_total += (double) worker->wi_cost_limit_base / worker->wi_cost_delay; --- 1713,1719 ---- offsetof(WorkerInfoData, wi_links)); while (worker) { ! if (worker->wi_proc != NULL && worker->wi_cost_limit_base > 0 && worker->wi_cost_delay > 0) cost_total += (double) worker->wi_cost_limit_base / worker->wi_cost_delay; *************** autovac_balance_cost(void) *** 1724,1730 **** offsetof(WorkerInfoData, wi_links)); while (worker) { ! if (worker->wi_workerpid != 0 && worker->wi_cost_limit_base > 0 && worker->wi_cost_delay > 0) { int limit = (int) --- 1736,1742 ---- offsetof(WorkerInfoData, wi_links)); while (worker) { ! if (worker->wi_proc != NULL && worker->wi_cost_limit_base > 0 && worker->wi_cost_delay > 0) { int limit = (int) *************** autovac_balance_cost(void) *** 1737,1743 **** worker->wi_cost_limit = Max(Min(limit, worker->wi_cost_limit_base), 1); elog(DEBUG2, "autovac_balance_cost(pid=%u db=%u, rel=%u, cost_limit=%d, cost_delay=%d)", ! worker->wi_workerpid, worker->wi_dboid, worker->wi_tableoid, worker->wi_cost_limit, worker->wi_cost_delay); } --- 1749,1755 ---- worker->wi_cost_limit = Max(Min(limit, worker->wi_cost_limit_base), 1); elog(DEBUG2, "autovac_balance_cost(pid=%u db=%u, rel=%u, cost_limit=%d, cost_delay=%d)", ! worker->wi_proc->pid, worker->wi_dboid, worker->wi_tableoid, worker->wi_cost_limit, worker->wi_cost_delay); } *************** 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,2819 ---- pgstat_clear_snapshot(); } + + /* + * avworker_is_analyze + * + * Determine whether an autovacuum worker (specified by PGPROC) is running + * ANALYZE. + */ + bool + avworker_is_analyze(PGPROC *proc) + { + WorkerInfo worker; + bool answer = false; + + Assert(proc->isAutovacuum); + + LWLockAcquire(AutovacuumLock, LW_SHARED); + worker = (WorkerInfo) SHMQueueNext(&AutoVacuumShmem->av_runningWorkers, + &AutoVacuumShmem->av_runningWorkers, + offsetof(WorkerInfoData, wi_links)); + while (worker) + { + if (worker->wi_proc == proc) + { + answer = worker->wi_activity == AvActivityAnalyze; + break; + } + + worker = (WorkerInfo) SHMQueueNext(&AutoVacuumShmem->av_runningWorkers, + &worker->wi_links, + offsetof(WorkerInfoData, wi_links)); + } + LWLockRelease(AutovacuumLock); + + return answer; + } 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 4 Oct 2007 21:31:03 -0000 *************** extern int Log_autovacuum_min_duration; *** 37,42 **** --- 37,43 ---- extern bool AutoVacuumingActive(void); extern bool IsAutoVacuumLauncherProcess(void); extern bool IsAutoVacuumWorkerProcess(void); + extern bool avworker_is_analyze(PGPROC *proc); /* Functions to start autovacuum process, called from postmaster */ extern void autovac_init(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