aboutsummaryrefslogtreecommitdiff
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
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).
-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,