aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorAlvaro Herrera <alvherre@alvh.no-ip.org>2007-10-26 20:45:10 +0000
committerAlvaro Herrera <alvherre@alvh.no-ip.org>2007-10-26 20:45:10 +0000
commitacac68b2bcae818bc8803b8cb8cbb17eee8d5e2b (patch)
tree437f3d5c6499a7c844450e305e9d5248af44c117 /src
parentffda32e9fd1878cd3896e71b4f48e97c22479335 (diff)
downloadpostgresql-acac68b2bcae818bc8803b8cb8cbb17eee8d5e2b.tar.gz
postgresql-acac68b2bcae818bc8803b8cb8cbb17eee8d5e2b.zip
Allow an autovacuum worker to be interrupted automatically when it is found
to be locking another process (except when it's working to prevent Xid wraparound problems).
Diffstat (limited to 'src')
-rw-r--r--src/backend/postmaster/autovacuum.c10
-rw-r--r--src/backend/storage/lmgr/README9
-rw-r--r--src/backend/storage/lmgr/deadlock.c45
-rw-r--r--src/backend/storage/lmgr/proc.c50
-rw-r--r--src/include/storage/lock.h7
5 files changed, 114 insertions, 7 deletions
diff --git a/src/backend/postmaster/autovacuum.c b/src/backend/postmaster/autovacuum.c
index 82c31d918cd..73cb19757c9 100644
--- a/src/backend/postmaster/autovacuum.c
+++ b/src/backend/postmaster/autovacuum.c
@@ -55,7 +55,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/postmaster/autovacuum.c,v 1.65 2007/10/25 19:13:37 alvherre Exp $
+ * $PostgreSQL: pgsql/src/backend/postmaster/autovacuum.c,v 1.66 2007/10/26 20:45:10 alvherre Exp $
*
*-------------------------------------------------------------------------
*/
@@ -2120,6 +2120,14 @@ next_worker:
tab->at_doanalyze,
tab->at_freeze_min_age,
bstrategy);
+
+ /*
+ * Clear a possible query-cancel signal, to avoid a late reaction
+ * to an automatically-sent signal because of vacuuming the current
+ * table (we're done with it, so it would make no sense to cancel
+ * at this point.)
+ */
+ QueryCancelPending = false;
}
PG_CATCH();
{
diff --git a/src/backend/storage/lmgr/README b/src/backend/storage/lmgr/README
index 8884676d284..5abef171169 100644
--- a/src/backend/storage/lmgr/README
+++ b/src/backend/storage/lmgr/README
@@ -1,4 +1,4 @@
-$PostgreSQL: pgsql/src/backend/storage/lmgr/README,v 1.21 2006/09/18 22:40:36 tgl Exp $
+$PostgreSQL: pgsql/src/backend/storage/lmgr/README,v 1.22 2007/10/26 20:45:10 alvherre Exp $
LOCKING OVERVIEW
@@ -487,6 +487,13 @@ seems a safer approach than trying to allocate workspace on the fly; we
don't want to risk having the deadlock detector run out of memory, else
we really have no guarantees at all that deadlock will be detected.
+6. We abuse the deadlock detector to implement autovacuum cancellation.
+When we run the detector and we find that there's an autovacuum worker
+involved in the waits-for graph, we store a pointer to its PGPROC, and
+return a special return code (unless a hard deadlock has been detected).
+The caller can then send a cancellation signal. This implements the
+principle that autovacuum has a low locking priority (eg it must not block
+DDL on the table).
USER LOCKS
diff --git a/src/backend/storage/lmgr/deadlock.c b/src/backend/storage/lmgr/deadlock.c
index 36e1b84a083..e599fa3fb8d 100644
--- a/src/backend/storage/lmgr/deadlock.c
+++ b/src/backend/storage/lmgr/deadlock.c
@@ -12,7 +12,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/storage/lmgr/deadlock.c,v 1.48 2007/06/19 20:13:21 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/storage/lmgr/deadlock.c,v 1.49 2007/10/26 20:45:10 alvherre Exp $
*
* Interface:
*
@@ -109,6 +109,9 @@ static int maxPossibleConstraints;
static DEADLOCK_INFO *deadlockDetails;
static int nDeadlockDetails;
+/* PGPROC pointer of any blocking autovacuum worker found */
+static PGPROC *blocking_autovacuum_proc = NULL;
+
/*
* InitDeadLockChecking -- initialize deadlock checker during backend startup
@@ -206,6 +209,9 @@ DeadLockCheck(PGPROC *proc)
nPossibleConstraints = 0;
nWaitOrders = 0;
+ /* Initialize to not blocked by an autovacuum worker */
+ blocking_autovacuum_proc = NULL;
+
/* Search for deadlocks and possible fixes */
if (DeadLockCheckRecurse(proc))
{
@@ -255,11 +261,29 @@ DeadLockCheck(PGPROC *proc)
/* Return code tells caller if we had to escape a deadlock or not */
if (nWaitOrders > 0)
return DS_SOFT_DEADLOCK;
+ else if (blocking_autovacuum_proc != NULL)
+ return DS_BLOCKED_BY_AUTOVACUUM;
else
return DS_NO_DEADLOCK;
}
/*
+ * Return the PGPROC of the autovacuum that's blocking a process.
+ *
+ * We reset the saved pointer as soon as we pass it back.
+ */
+PGPROC *
+GetBlockingAutoVacuumPgproc(void)
+{
+ PGPROC *ptr;
+
+ ptr = blocking_autovacuum_proc;
+ blocking_autovacuum_proc = NULL;
+
+ return ptr;
+}
+
+/*
* DeadLockCheckRecurse -- recursively search for valid orderings
*
* curConstraints[] holds the current set of constraints being considered
@@ -497,6 +521,25 @@ FindLockCycleRecurse(PGPROC *checkProc,
if ((proclock->holdMask & LOCKBIT_ON(lm)) &&
(conflictMask & LOCKBIT_ON(lm)))
{
+ /*
+ * Look for a blocking autovacuum. There can be more than
+ * one in the deadlock cycle, in which case we just pick a
+ * random one. We stash the autovacuum worker's PGPROC so
+ * that the caller can send a cancel signal to it, if
+ * appropriate.
+ *
+ * Note we read vacuumFlags without any locking. This is
+ * OK only for checking the PROC_IS_AUTOVACUUM flag,
+ * because that flag is set at process start and never
+ * reset; there is logic elsewhere to avoid cancelling an
+ * autovacuum that is working for preventing Xid wraparound
+ * problems (which needs to read a different vacuumFlag
+ * bit), but we don't do that here to avoid grabbing
+ * ProcArrayLock.
+ */
+ if (proc->vacuumFlags & PROC_IS_AUTOVACUUM)
+ blocking_autovacuum_proc = proc;
+
/* This proc hard-blocks checkProc */
if (FindLockCycleRecurse(proc, depth + 1,
softEdges, nSoftEdges))
diff --git a/src/backend/storage/lmgr/proc.c b/src/backend/storage/lmgr/proc.c
index fdf089f836c..4b2280b5506 100644
--- a/src/backend/storage/lmgr/proc.c
+++ b/src/backend/storage/lmgr/proc.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/storage/lmgr/proc.c,v 1.195 2007/10/24 20:55:36 alvherre Exp $
+ * $PostgreSQL: pgsql/src/backend/storage/lmgr/proc.c,v 1.196 2007/10/26 20:45:10 alvherre Exp $
*
*-------------------------------------------------------------------------
*/
@@ -734,6 +734,7 @@ ProcSleep(LOCALLOCK *locallock, LockMethod lockMethodTable)
PROC_QUEUE *waitQueue = &(lock->waitProcs);
LOCKMASK myHeldLocks = MyProc->heldLocks;
bool early_deadlock = false;
+ bool allow_autovacuum_cancel = true;
int myWaitStatus;
PGPROC *proc;
int i;
@@ -894,6 +895,48 @@ ProcSleep(LOCALLOCK *locallock, LockMethod lockMethodTable)
myWaitStatus = MyProc->waitStatus;
/*
+ * If we are not deadlocked, but are waiting on an autovacuum-induced
+ * task, send a signal to interrupt it.
+ */
+ if (deadlock_state == DS_BLOCKED_BY_AUTOVACUUM && allow_autovacuum_cancel)
+ {
+ PGPROC *autovac = GetBlockingAutoVacuumPgproc();
+
+ LWLockAcquire(ProcArrayLock, LW_EXCLUSIVE);
+
+ /*
+ * Only do it if the worker is not working to protect against Xid
+ * wraparound.
+ */
+ if ((autovac != NULL) &&
+ (autovac->vacuumFlags & PROC_IS_AUTOVACUUM) &&
+ !(autovac->vacuumFlags & PROC_VACUUM_FOR_WRAPAROUND))
+ {
+ int pid = autovac->pid;
+
+ elog(DEBUG2, "sending cancel to blocking autovacuum pid = %d",
+ pid);
+
+ /* don't hold the lock across the kill() syscall */
+ LWLockRelease(ProcArrayLock);
+
+ /* send the autovacuum worker Back to Old Kent Road */
+ if (kill(pid, SIGINT) < 0)
+ {
+ /* Just a warning to allow multiple callers */
+ ereport(WARNING,
+ (errmsg("could not send signal to process %d: %m",
+ pid)));
+ }
+ }
+ else
+ LWLockRelease(ProcArrayLock);
+
+ /* prevent signal from being resent more than once */
+ allow_autovacuum_cancel = false;
+ }
+
+ /*
* If awoken after the deadlock check interrupt has run, and
* log_lock_waits is on, then report about the wait.
*/
@@ -1189,13 +1232,16 @@ CheckDeadLock(void)
* RemoveFromWaitQueue took care of waking up any such processes.
*/
}
- else if (log_lock_waits)
+ else if (log_lock_waits || deadlock_state == DS_BLOCKED_BY_AUTOVACUUM)
{
/*
* Unlock my semaphore so that the interrupted ProcSleep() call can
* print the log message (we daren't do it here because we are inside
* a signal handler). It will then sleep again until someone
* releases the lock.
+ *
+ * If blocked by autovacuum, this wakeup will enable ProcSleep to send
+ * the cancelling signal to the autovacuum worker.
*/
PGSemaphoreUnlock(&MyProc->sem);
}
diff --git a/src/include/storage/lock.h b/src/include/storage/lock.h
index 30c8a3fa2bc..005c99ee7dc 100644
--- a/src/include/storage/lock.h
+++ b/src/include/storage/lock.h
@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/include/storage/lock.h,v 1.107 2007/09/05 18:10:48 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/storage/lock.h,v 1.108 2007/10/26 20:45:10 alvherre Exp $
*
*-------------------------------------------------------------------------
*/
@@ -442,7 +442,9 @@ typedef enum
DS_NOT_YET_CHECKED, /* no deadlock check has run yet */
DS_NO_DEADLOCK, /* no deadlock detected */
DS_SOFT_DEADLOCK, /* deadlock avoided by queue rearrangement */
- DS_HARD_DEADLOCK /* deadlock, no way out but ERROR */
+ DS_HARD_DEADLOCK, /* deadlock, no way out but ERROR */
+ DS_BLOCKED_BY_AUTOVACUUM /* no deadlock; queue blocked by autovacuum
+ worker */
} DeadLockState;
@@ -495,6 +497,7 @@ extern void lock_twophase_postabort(TransactionId xid, uint16 info,
void *recdata, uint32 len);
extern DeadLockState DeadLockCheck(PGPROC *proc);
+extern PGPROC *GetBlockingAutoVacuumPgproc(void);
extern void DeadLockReport(void);
extern void RememberSimpleDeadLock(PGPROC *proc1,
LOCKMODE lockmode,