On Mon, 2010-02-01 at 17:50 +0200, Heikki Linnakangas wrote:

> Umm, so why not run the deadlock check immediately in
> max_standby_delay=-1 case as well? Why is that case handled differently
> from max_standby_delay>0 case?

Done, tested, working.

Will commit tomorrow if no further questions or comments.

-- 
 Simon Riggs           www.2ndQuadrant.com
*** a/src/backend/storage/ipc/procsignal.c
--- b/src/backend/storage/ipc/procsignal.c
***************
*** 272,277 **** procsignal_sigusr1_handler(SIGNAL_ARGS)
--- 272,280 ----
  	if (CheckProcSignal(PROCSIG_RECOVERY_CONFLICT_SNAPSHOT))
  		RecoveryConflictInterrupt(PROCSIG_RECOVERY_CONFLICT_SNAPSHOT);
  
+ 	if (CheckProcSignal(PROCSIG_RECOVERY_CONFLICT_STARTUP_DEADLOCK))
+ 		RecoveryConflictInterrupt(PROCSIG_RECOVERY_CONFLICT_STARTUP_DEADLOCK);
+ 
  	if (CheckProcSignal(PROCSIG_RECOVERY_CONFLICT_BUFFERPIN))
  		RecoveryConflictInterrupt(PROCSIG_RECOVERY_CONFLICT_BUFFERPIN);
  
*** a/src/backend/storage/ipc/standby.c
--- b/src/backend/storage/ipc/standby.c
***************
*** 127,132 **** WaitExceedsMaxStandbyDelay(void)
--- 127,135 ----
  	long	delay_secs;
  	int		delay_usecs;
  
+ 	if (MaxStandbyDelay == -1)
+ 		return false;
+ 
  	/* Are we past max_standby_delay? */
  	TimestampDifference(GetLatestXLogTime(), GetCurrentTimestamp(),
  						&delay_secs, &delay_usecs);
***************
*** 351,365 **** ResolveRecoveryConflictWithLock(Oid dbOid, Oid relOid)
   * they hold one of the buffer pins that is blocking Startup process. If so,
   * backends will take an appropriate error action, ERROR or FATAL.
   *
!  * A secondary purpose of this is to avoid deadlocks that might occur between
!  * the Startup process and lock waiters. Deadlocks occur because if queries
   * wait on a lock, that must be behind an AccessExclusiveLock, which can only
!  * be clared if the Startup process replays a transaction completion record.
!  * If Startup process is waiting then that is a deadlock. If we allowed a
!  * setting of max_standby_delay that meant "wait forever" we would then need
!  * special code to protect against deadlock. Such deadlocks are rare, so the
!  * code would be almost certainly buggy, so we avoid both long waits and
!  * deadlocks using the same mechanism.
   */
  void
  ResolveRecoveryConflictWithBufferPin(void)
--- 354,368 ----
   * they hold one of the buffer pins that is blocking Startup process. If so,
   * backends will take an appropriate error action, ERROR or FATAL.
   *
!  * We also check for deadlocks before we wait, though applications that cause
!  * these will be extremely rare.  Deadlocks occur because if queries
   * wait on a lock, that must be behind an AccessExclusiveLock, which can only
!  * be cleared if the Startup process replays a transaction completion record.
!  * If Startup process is also waiting then that is a deadlock. The deadlock
!  * can occur if the query is waiting and then the Startup sleeps, or if
!  * Startup is sleeping and the the query waits on a lock. We protect against
!  * only the former sequence here, the latter sequence is checked prior to
!  * the query sleeping, in CheckRecoveryConflictDeadlock().
   */
  void
  ResolveRecoveryConflictWithBufferPin(void)
***************
*** 368,378 **** ResolveRecoveryConflictWithBufferPin(void)
  
  	Assert(InHotStandby);
  
- 	/*
- 	 * Signal immediately or set alarm for later.
- 	 */
  	if (MaxStandbyDelay == 0)
! 		SendRecoveryConflictWithBufferPin();
  	else
  	{
  		TimestampTz now;
--- 371,393 ----
  
  	Assert(InHotStandby);
  
  	if (MaxStandbyDelay == 0)
! 	{
! 		/*
! 		 * We don't want to wait, so just tell everybody holding the pin to 
! 		 * get out of town.
! 		 */
! 		SendRecoveryConflictWithBufferPin(PROCSIG_RECOVERY_CONFLICT_BUFFERPIN);
! 	}
! 	else if (MaxStandbyDelay == -1)
! 	{
! 		/*
! 		 * Send out a request to check for buffer pin deadlocks before we wait.
! 		 * This is fairly cheap, so no need to wait for deadlock timeout before
! 		 * trying to send it out.
! 		 */
! 		SendRecoveryConflictWithBufferPin(PROCSIG_RECOVERY_CONFLICT_STARTUP_DEADLOCK);
! 	}
  	else
  	{
  		TimestampTz now;
***************
*** 386,392 **** ResolveRecoveryConflictWithBufferPin(void)
  							&standby_delay_secs, &standby_delay_usecs);
  
  		if (standby_delay_secs >= MaxStandbyDelay)
! 			SendRecoveryConflictWithBufferPin();
  		else
  		{
  			TimestampTz fin_time;			/* Expected wake-up time by timer */
--- 401,412 ----
  							&standby_delay_secs, &standby_delay_usecs);
  
  		if (standby_delay_secs >= MaxStandbyDelay)
! 		{
! 			/*
! 			 * We're already behind, so clear a path as quickly as possible.
! 			 */
! 			SendRecoveryConflictWithBufferPin(PROCSIG_RECOVERY_CONFLICT_BUFFERPIN);
! 		}
  		else
  		{
  			TimestampTz fin_time;			/* Expected wake-up time by timer */
***************
*** 394,399 **** ResolveRecoveryConflictWithBufferPin(void)
--- 414,426 ----
  			int		timer_delay_usecs = 0;
  
  			/*
+ 			 * Send out a request to check for buffer pin deadlocks before we wait.
+ 			 * This is fairly cheap, so no need to wait for deadlock timeout before
+ 			 * trying to send it out.
+ 			 */
+ 			SendRecoveryConflictWithBufferPin(PROCSIG_RECOVERY_CONFLICT_STARTUP_DEADLOCK);
+ 
+ 			/*
  			 * How much longer we should wait?
  			 */
  			timer_delay_secs = MaxStandbyDelay - standby_delay_secs;
***************
*** 435,449 **** ResolveRecoveryConflictWithBufferPin(void)
  }
  
  void
! SendRecoveryConflictWithBufferPin(void)
  {
  	/*
  	 * We send signal to all backends to ask them if they are holding
  	 * the buffer pin which is delaying the Startup process. We must
  	 * not set the conflict flag yet, since most backends will be innocent.
  	 * Let the SIGUSR1 handling in each backend decide their own fate.
  	 */
! 	CancelDBBackends(InvalidOid, PROCSIG_RECOVERY_CONFLICT_BUFFERPIN, false);
  }
  
  /*
--- 462,479 ----
  }
  
  void
! SendRecoveryConflictWithBufferPin(ProcSignalReason reason)
  {
+ 	Assert(reason == PROCSIG_RECOVERY_CONFLICT_BUFFERPIN ||
+ 		   reason == PROCSIG_RECOVERY_CONFLICT_STARTUP_DEADLOCK);
+ 
  	/*
  	 * We send signal to all backends to ask them if they are holding
  	 * the buffer pin which is delaying the Startup process. We must
  	 * not set the conflict flag yet, since most backends will be innocent.
  	 * Let the SIGUSR1 handling in each backend decide their own fate.
  	 */
! 	CancelDBBackends(InvalidOid, reason, false);
  }
  
  /*
*** a/src/backend/storage/lmgr/proc.c
--- b/src/backend/storage/lmgr/proc.c
***************
*** 45,50 ****
--- 45,51 ----
  #include "storage/pmsignal.h"
  #include "storage/proc.h"
  #include "storage/procarray.h"
+ #include "storage/procsignal.h"
  #include "storage/spin.h"
  
  
***************
*** 556,561 **** HaveNFreeProcs(int n)
--- 557,571 ----
  	return (n <= 0);
  }
  
+ bool
+ IsWaitingForLock(void)
+ {
+ 	if (lockAwaited == NULL)
+ 		return false;
+ 
+ 	return true;
+ }
+ 
  /*
   * Cancel any pending wait for lock, when aborting a transaction.
   *
***************
*** 1671,1677 **** CheckStandbyTimeout(void)
  	now = GetCurrentTimestamp();
  
  	if (now >= statement_fin_time)
! 		SendRecoveryConflictWithBufferPin();
  	else
  	{
  		/* Not time yet, so (re)schedule the interrupt */
--- 1681,1687 ----
  	now = GetCurrentTimestamp();
  
  	if (now >= statement_fin_time)
! 		SendRecoveryConflictWithBufferPin(PROCSIG_RECOVERY_CONFLICT_BUFFERPIN);
  	else
  	{
  		/* Not time yet, so (re)schedule the interrupt */
*** a/src/backend/tcop/postgres.c
--- b/src/backend/tcop/postgres.c
***************
*** 2278,2283 **** errdetail_recovery_conflict(void)
--- 2278,2286 ----
  		case PROCSIG_RECOVERY_CONFLICT_SNAPSHOT:
  				errdetail("User query might have needed to see row versions that must be removed.");
  				break;
+ 		case PROCSIG_RECOVERY_CONFLICT_STARTUP_DEADLOCK:
+ 				errdetail("User transaction caused buffer deadlock with recovery.");
+ 				break;
  		case PROCSIG_RECOVERY_CONFLICT_DATABASE:
  				errdetail("User was connected to a database that must be dropped.");
  				break;
***************
*** 2754,2759 **** RecoveryConflictInterrupt(ProcSignalReason reason)
--- 2757,2771 ----
  		RecoveryConflictReason = reason;
  		switch (reason)
  		{
+ 			case PROCSIG_RECOVERY_CONFLICT_STARTUP_DEADLOCK:
+ 					/*
+ 					 * If we aren't waiting for a lock we can never deadlock.
+ 					 */
+ 					if (!IsWaitingForLock())
+ 						return;
+ 
+ 					/* Intentional drop through to check wait for pin */
+ 
  			case PROCSIG_RECOVERY_CONFLICT_BUFFERPIN:
  					/*
  					 * If we aren't blocking the Startup process there is
***************
*** 2819,2824 **** RecoveryConflictInterrupt(ProcSignalReason reason)
--- 2831,2838 ----
  					elog(FATAL, "Unknown conflict mode");
  		}
  
+ 		Assert(RecoveryConflictPending && (QueryCancelPending || ProcDiePending));
+ 
  		/*
  		 * If it's safe to interrupt, and we're waiting for input or a lock,
  		 * service the interrupt immediately
*** a/src/backend/utils/misc/guc.c
--- b/src/backend/utils/misc/guc.c
***************
*** 1381,1387 **** static struct config_int ConfigureNamesInt[] =
  			NULL
  		},
  		&MaxStandbyDelay,
! 		30, 0, INT_MAX, NULL, NULL
  	},
  
  	{
--- 1381,1387 ----
  			NULL
  		},
  		&MaxStandbyDelay,
! 		30, -1, INT_MAX, NULL, NULL
  	},
  
  	{
*** a/src/include/storage/proc.h
--- b/src/include/storage/proc.h
***************
*** 189,194 **** extern void ProcQueueInit(PROC_QUEUE *queue);
--- 189,195 ----
  extern int	ProcSleep(LOCALLOCK *locallock, LockMethod lockMethodTable);
  extern PGPROC *ProcWakeup(PGPROC *proc, int waitStatus);
  extern void ProcLockWakeup(LockMethod lockMethodTable, LOCK *lock);
+ extern bool IsWaitingForLock(void);
  extern void LockWaitCancel(void);
  
  extern void ProcWaitForSignal(void);
*** a/src/include/storage/procsignal.h
--- b/src/include/storage/procsignal.h
***************
*** 38,43 **** typedef enum
--- 38,44 ----
  	PROCSIG_RECOVERY_CONFLICT_LOCK,
  	PROCSIG_RECOVERY_CONFLICT_SNAPSHOT,
  	PROCSIG_RECOVERY_CONFLICT_BUFFERPIN,
+ 	PROCSIG_RECOVERY_CONFLICT_STARTUP_DEADLOCK,
  
  	NUM_PROCSIGNALS				/* Must be last! */
  } ProcSignalReason;
*** a/src/include/storage/standby.h
--- b/src/include/storage/standby.h
***************
*** 16,21 ****
--- 16,22 ----
  
  #include "access/xlog.h"
  #include "storage/lock.h"
+ #include "storage/procsignal.h"
  #include "storage/relfilenode.h"
  
  extern int	vacuum_defer_cleanup_age;
***************
*** 30,36 **** extern void ResolveRecoveryConflictWithTablespace(Oid tsid);
  extern void ResolveRecoveryConflictWithDatabase(Oid dbid);
  
  extern void ResolveRecoveryConflictWithBufferPin(void);
! extern void SendRecoveryConflictWithBufferPin(void);
  extern void CheckRecoveryConflictDeadlock(LWLockId partitionLock);
  
  /*
--- 31,37 ----
  extern void ResolveRecoveryConflictWithDatabase(Oid dbid);
  
  extern void ResolveRecoveryConflictWithBufferPin(void);
! extern void SendRecoveryConflictWithBufferPin(ProcSignalReason reason);
  extern void CheckRecoveryConflictDeadlock(LWLockId partitionLock);
  
  /*
-- 
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

Reply via email to