Simon Riggs wrote:

> Just noticed you've made these changes. I was working on them, but
> hadn't fully tested the patch because of all the different touch points.
> Sorry for the delay.
> 
> Would you like me to refresh my earlier patch against the newly
> committed state (or did you commit that aspect already as well?).

Patch attached, please comment.  It only avoids cancelling when the
vacuum is for wraparound.

What we're missing here is doc updates (mainly to lmgr/README, I think)

-- 
Alvaro Herrera                                http://www.CommandPrompt.com/
The PostgreSQL Company - Command Prompt, Inc.
Index: src/backend/storage/lmgr/deadlock.c
===================================================================
RCS file: /home/alvherre/Code/cvs/pgsql/src/backend/storage/lmgr/deadlock.c,v
retrieving revision 1.48
diff -c -p -r1.48 deadlock.c
*** src/backend/storage/lmgr/deadlock.c	19 Jun 2007 20:13:21 -0000	1.48
--- src/backend/storage/lmgr/deadlock.c	25 Oct 2007 20:06:36 -0000
*************** static int	maxPossibleConstraints;
*** 109,114 ****
--- 109,117 ----
  static DEADLOCK_INFO *deadlockDetails;
  static int	nDeadlockDetails;
  
+ /* PGPROC pointer of any blocking av worker found */
+ static PGPROC *blocking_autovacuum_proc = NULL; 
+ 
  
  /*
   * InitDeadLockChecking -- initialize deadlock checker during backend startup
*************** DeadLockCheck(PGPROC *proc)
*** 206,211 ****
--- 209,217 ----
  	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))
  	{
*************** DeadLockCheck(PGPROC *proc)
*** 255,265 ****
--- 261,289 ----
  	/* 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
*************** FindLockCycleRecurse(PGPROC *checkProc,
*** 497,502 ****
--- 521,534 ----
  				if ((proclock->holdMask & LOCKBIT_ON(lm)) &&
  					(conflictMask & LOCKBIT_ON(lm)))
  				{
+ 					/*
+ 					 * Look for a blocking autovacuum. There will only ever
+ 					 * be one, since the autovacuum workers are careful
+ 					 * not to operate concurrently on the same table. 
+ 					 */
+ 					if (proc->vacuumFlags & PROC_IS_AUTOVACUUM)
+ 						blocking_autovacuum_proc = proc;
+ 
  					/* This proc hard-blocks checkProc */
  					if (FindLockCycleRecurse(proc, depth + 1,
  											 softEdges, nSoftEdges))
Index: src/backend/storage/lmgr/proc.c
===================================================================
RCS file: /home/alvherre/Code/cvs/pgsql/src/backend/storage/lmgr/proc.c,v
retrieving revision 1.195
diff -c -p -r1.195 proc.c
*** src/backend/storage/lmgr/proc.c	24 Oct 2007 20:55:36 -0000	1.195
--- src/backend/storage/lmgr/proc.c	25 Oct 2007 20:03:58 -0000
*************** ProcSleep(LOCALLOCK *locallock, LockMeth
*** 734,739 ****
--- 734,740 ----
  	PROC_QUEUE *waitQueue = &(lock->waitProcs);
  	LOCKMASK	myHeldLocks = MyProc->heldLocks;
  	bool		early_deadlock = false;
+ 	bool 		allow_autovacuum_cancel = true;
  	int			myWaitStatus;
  	PGPROC	   *proc;
  	int			i;
*************** ProcSleep(LOCALLOCK *locallock, LockMeth
*** 894,899 ****
--- 895,949 ----
  		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_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 we have setsid(), signal the backend's whole process group 
+ 				 */
+ #ifdef HAVE_SETSID
+ 				if (kill(-pid, SIGINT))
+ #else
+ 					if (kill(pid, SIGINT))
+ #endif
+ 					{
+ 						/* 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.
  		 */
*************** CheckDeadLock(void)
*** 1189,1201 ****
  		 * RemoveFromWaitQueue took care of waking up any such processes.
  		 */
  	}
! 	else if (log_lock_waits)
  	{
  		/*
  		 * 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.
  		 */
  		PGSemaphoreUnlock(&MyProc->sem);
  	}
--- 1239,1254 ----
  		 * RemoveFromWaitQueue took care of waking up any such processes.
  		 */
  	}
! 	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);
  	}
Index: src/include/storage/lock.h
===================================================================
RCS file: /home/alvherre/Code/cvs/pgsql/src/include/storage/lock.h,v
retrieving revision 1.107
diff -c -p -r1.107 lock.h
*** src/include/storage/lock.h	5 Sep 2007 18:10:48 -0000	1.107
--- src/include/storage/lock.h	25 Oct 2007 17:11:02 -0000
*************** typedef enum
*** 442,448 ****
  	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 */
  } DeadLockState;
  
  
--- 442,449 ----
  	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_BLOCKED_BY_AUTOVACUUM	/* queue blocked by autovacuum worker */
  } DeadLockState;
  
  
*************** extern void lock_twophase_postabort(Tran
*** 495,500 ****
--- 496,502 ----
  						void *recdata, uint32 len);
  
  extern DeadLockState DeadLockCheck(PGPROC *proc);
+ extern PGPROC *GetBlockingAutoVacuumPgproc(void);
  extern void DeadLockReport(void);
  extern void RememberSimpleDeadLock(PGPROC *proc1,
  					   LOCKMODE lockmode,
---------------------------(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

Reply via email to