On Wed, 2010-01-13 at 19:23 +0000, Simon Riggs wrote:
> On Wed, 2010-01-13 at 19:58 +0100, Andres Freund wrote:
> 
> > > I am still testing patch, so should be confident to commit tomorrow
> > > barring issues.
> > I have only looked at briefly because right now I dont have the time (going 
> > to 
> > eat at a friends place...) but I think I spotted an issue:
> > The IsAbortedTransactionBlockState() check in RecoveryConflictInterrupt is 
> > not 
> > correct right now because that returns true for TBLOCK_SUBABORT as well.
> > Wouldnt that mess with the case where were in a failed subxact and then 
> > rollback only that subxact?
> 
> Well spotted, yes. 

Latest version of same patch, but uses conflict reasons passed-thru
directly from recovery to backend.

Please review, no commit before tomorrow.

-- 
 Simon Riggs           www.2ndQuadrant.com
*** a/src/backend/access/transam/xact.c
--- b/src/backend/access/transam/xact.c
***************
*** 313,320 **** IsTransactionState(void)
  /*
   *	IsAbortedTransactionBlockState
   *
!  *	This returns true if we are currently running a query
!  *	within an aborted transaction block.
   */
  bool
  IsAbortedTransactionBlockState(void)
--- 313,319 ----
  /*
   *	IsAbortedTransactionBlockState
   *
!  *	This returns true if we are within an aborted transaction block.
   */
  bool
  IsAbortedTransactionBlockState(void)
*** a/src/backend/storage/ipc/procarray.c
--- b/src/backend/storage/ipc/procarray.c
***************
*** 324,329 **** ProcArrayEndTransaction(PGPROC *proc, TransactionId latestXid)
--- 324,330 ----
  		/* must be cleared with xid/xmin: */
  		proc->vacuumFlags &= ~PROC_VACUUM_STATE_MASK;
  		proc->inCommit = false; /* be sure this is cleared in abort */
+ 		proc->recoveryConflictPending = false;
  
  		/* Clear the subtransaction-XID cache too while holding the lock */
  		proc->subxids.nxids = 0;
***************
*** 350,355 **** ProcArrayEndTransaction(PGPROC *proc, TransactionId latestXid)
--- 351,357 ----
  		/* must be cleared with xid/xmin: */
  		proc->vacuumFlags &= ~PROC_VACUUM_STATE_MASK;
  		proc->inCommit = false; /* be sure this is cleared in abort */
+ 		proc->recoveryConflictPending = false;
  
  		Assert(proc->subxids.nxids == 0);
  		Assert(proc->subxids.overflowed == false);
***************
*** 377,383 **** ProcArrayClearTransaction(PGPROC *proc)
  	proc->xid = InvalidTransactionId;
  	proc->lxid = InvalidLocalTransactionId;
  	proc->xmin = InvalidTransactionId;
! 	proc->recoveryConflictMode = 0;
  
  	/* redundant, but just in case */
  	proc->vacuumFlags &= ~PROC_VACUUM_STATE_MASK;
--- 379,385 ----
  	proc->xid = InvalidTransactionId;
  	proc->lxid = InvalidLocalTransactionId;
  	proc->xmin = InvalidTransactionId;
! 	proc->recoveryConflictPending = false;
  
  	/* redundant, but just in case */
  	proc->vacuumFlags &= ~PROC_VACUUM_STATE_MASK;
***************
*** 1665,1671 **** GetConflictingVirtualXIDs(TransactionId limitXmin, Oid dbOid,
  		if (proc->pid == 0)
  			continue;
  
! 		if (skipExistingConflicts && proc->recoveryConflictMode > 0)
  			continue;
  
  		if (!OidIsValid(dbOid) ||
--- 1667,1673 ----
  		if (proc->pid == 0)
  			continue;
  
! 		if (skipExistingConflicts && proc->recoveryConflictPending)
  			continue;
  
  		if (!OidIsValid(dbOid) ||
***************
*** 1704,1710 **** GetConflictingVirtualXIDs(TransactionId limitXmin, Oid dbOid,
   * Returns pid of the process signaled, or 0 if not found.
   */
  pid_t
! CancelVirtualTransaction(VirtualTransactionId vxid, int cancel_mode)
  {
  	ProcArrayStruct *arrayP = procArray;
  	int			index;
--- 1706,1712 ----
   * Returns pid of the process signaled, or 0 if not found.
   */
  pid_t
! CancelVirtualTransaction(VirtualTransactionId vxid, ProcSignalReason sigmode)
  {
  	ProcArrayStruct *arrayP = procArray;
  	int			index;
***************
*** 1722,1749 **** CancelVirtualTransaction(VirtualTransactionId vxid, int cancel_mode)
  		if (procvxid.backendId == vxid.backendId &&
  			procvxid.localTransactionId == vxid.localTransactionId)
  		{
! 			/*
! 			 * Issue orders for the proc to read next time it receives SIGINT
! 			 */
! 			if (proc->recoveryConflictMode < cancel_mode)
! 				proc->recoveryConflictMode = cancel_mode;
! 
  			pid = proc->pid;
  			break;
  		}
  	}
  
  	LWLockRelease(ProcArrayLock);
  
- 	if (pid != 0)
- 	{
- 		/*
- 		 * Kill the pid if it's still here. If not, that's what we wanted
- 		 * so ignore any errors.
- 		 */
- 		kill(pid, SIGINT);
- 	}
- 
  	return pid;
  }
  
--- 1724,1745 ----
  		if (procvxid.backendId == vxid.backendId &&
  			procvxid.localTransactionId == vxid.localTransactionId)
  		{
! 			proc->recoveryConflictPending = true;
  			pid = proc->pid;
+ 			if (pid != 0)
+ 			{
+ 				/*
+ 				 * Kill the pid if it's still here. If not, that's what we wanted
+ 				 * so ignore any errors.
+ 				 */
+ 				(void) SendProcSignal(pid, sigmode, vxid.backendId);
+ 			}
  			break;
  		}
  	}
  
  	LWLockRelease(ProcArrayLock);
  
  	return pid;
  }
  
***************
*** 1834,1839 **** CancelDBBackends(Oid databaseid)
--- 1830,1836 ----
  {
  	ProcArrayStruct *arrayP = procArray;
  	int			index;
+ 	pid_t		pid = 0;
  
  	/* tell all backends to die */
  	LWLockAcquire(ProcArrayLock, LW_EXCLUSIVE);
***************
*** 1844,1851 **** CancelDBBackends(Oid databaseid)
  
  		if (proc->databaseId == databaseid)
  		{
! 			proc->recoveryConflictMode = CONFLICT_MODE_FATAL;
! 			kill(proc->pid, SIGINT);
  		}
  	}
  
--- 1841,1861 ----
  
  		if (proc->databaseId == databaseid)
  		{
! 			VirtualTransactionId procvxid;
! 
! 			GET_VXID_FROM_PGPROC(procvxid, *proc);
! 
! 			proc->recoveryConflictPending = true;
! 			pid = proc->pid;
! 			if (pid != 0)
! 			{
! 				/*
! 				 * Kill the pid if it's still here. If not, that's what we wanted
! 				 * so ignore any errors.
! 				 */
! 				(void) SendProcSignal(pid, PROCSIG_RECOVERY_CONFLICT_DATABASE,
! 										procvxid.backendId);
! 			}
  		}
  	}
  
*** a/src/backend/storage/ipc/procsignal.c
--- b/src/backend/storage/ipc/procsignal.c
***************
*** 24,29 ****
--- 24,31 ----
  #include "storage/procsignal.h"
  #include "storage/shmem.h"
  #include "storage/sinval.h"
+ #include "storage/standby.h"
+ #include "tcop/tcopprot.h"
  
  
  /*
***************
*** 258,262 **** procsignal_sigusr1_handler(SIGNAL_ARGS)
--- 260,276 ----
  	if (CheckProcSignal(PROCSIG_NOTIFY_INTERRUPT))
  		HandleNotifyInterrupt();
  
+ 	if (CheckProcSignal(PROCSIG_RECOVERY_CONFLICT_DATABASE))
+ 		RecoveryConflictInterrupt(PROCSIG_RECOVERY_CONFLICT_DATABASE);
+ 
+ 	if (CheckProcSignal(PROCSIG_RECOVERY_CONFLICT_TABLESPACE))
+ 		RecoveryConflictInterrupt(PROCSIG_RECOVERY_CONFLICT_TABLESPACE);
+ 
+ 	if (CheckProcSignal(PROCSIG_RECOVERY_CONFLICT_LOCK))
+ 		RecoveryConflictInterrupt(PROCSIG_RECOVERY_CONFLICT_LOCK);
+ 
+ 	if (CheckProcSignal(PROCSIG_RECOVERY_CONFLICT_SNAPSHOT))
+ 		RecoveryConflictInterrupt(PROCSIG_RECOVERY_CONFLICT_SNAPSHOT);
+ 
  	errno = save_errno;
  }
*** a/src/backend/storage/ipc/standby.c
--- b/src/backend/storage/ipc/standby.c
***************
*** 3,15 ****
   * standby.c
   *	  Misc functions used in Hot Standby mode.
   *
-  *	InitRecoveryTransactionEnvironment()
-  *  ShutdownRecoveryTransactionEnvironment()
-  *
-  *  ResolveRecoveryConflictWithVirtualXIDs()
-  *
   *  All functions for handling RM_STANDBY_ID, which relate to
   *  AccessExclusiveLocks and starting snapshots for Hot Standby mode.
   *
   * Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group
   * Portions Copyright (c) 1994, Regents of the University of California
--- 3,11 ----
   * standby.c
   *	  Misc functions used in Hot Standby mode.
   *
   *  All functions for handling RM_STANDBY_ID, which relate to
   *  AccessExclusiveLocks and starting snapshots for Hot Standby mode.
+  *  Plus conflict recovery processing.
   *
   * Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group
   * Portions Copyright (c) 1994, Regents of the University of California
***************
*** 38,44 **** int		vacuum_defer_cleanup_age;
  static List *RecoveryLockList;
  
  static void ResolveRecoveryConflictWithVirtualXIDs(VirtualTransactionId *waitlist,
! 									   char *reason, int cancel_mode);
  static void ResolveRecoveryConflictWithLock(Oid dbOid, Oid relOid);
  static void LogCurrentRunningXacts(RunningTransactions CurrRunningXacts);
  static void LogAccessExclusiveLocks(int nlocks, xl_standby_lock *locks);
--- 34,40 ----
  static List *RecoveryLockList;
  
  static void ResolveRecoveryConflictWithVirtualXIDs(VirtualTransactionId *waitlist,
! 									   ProcSignalReason reason);
  static void ResolveRecoveryConflictWithLock(Oid dbOid, Oid relOid);
  static void LogCurrentRunningXacts(RunningTransactions CurrRunningXacts);
  static void LogAccessExclusiveLocks(int nlocks, xl_standby_lock *locks);
***************
*** 162,178 **** WaitExceedsMaxStandbyDelay(void)
   * recovery processing. Judgement has already been passed on it within
   * a specific rmgr. Here we just issue the orders to the procs. The procs
   * then throw the required error as instructed.
-  *
-  * We may ask for a specific cancel_mode, typically ERROR or FATAL.
   */
  static void
  ResolveRecoveryConflictWithVirtualXIDs(VirtualTransactionId *waitlist,
! 									   char *reason, int cancel_mode)
  {
  	char		waitactivitymsg[100];
  
- 	Assert(cancel_mode > 0);
- 
  	while (VirtualTransactionIdIsValid(*waitlist))
  	{
  		long wait_s;
--- 158,170 ----
   * recovery processing. Judgement has already been passed on it within
   * a specific rmgr. Here we just issue the orders to the procs. The procs
   * then throw the required error as instructed.
   */
  static void
  ResolveRecoveryConflictWithVirtualXIDs(VirtualTransactionId *waitlist,
! 									   ProcSignalReason reason)
  {
  	char		waitactivitymsg[100];
  
  	while (VirtualTransactionIdIsValid(*waitlist))
  	{
  		long wait_s;
***************
*** 206,217 **** ResolveRecoveryConflictWithVirtualXIDs(VirtualTransactionId *waitlist,
  					len = 100;
  				memcpy(waitactivitymsg, oldactivitymsg, len);
  
- 				ereport(trace_recovery(DEBUG5),
- 						(errmsg("virtual transaction %u/%u is blocking %s",
- 								waitlist->backendId,
- 								waitlist->localTransactionId,
- 								reason)));
- 
  				pgstat_report_waiting(true);
  
  				logged = true;
--- 198,203 ----
***************
*** 226,265 **** ResolveRecoveryConflictWithVirtualXIDs(VirtualTransactionId *waitlist,
  				 * Now find out who to throw out of the balloon.
  				 */
  				Assert(VirtualTransactionIdIsValid(*waitlist));
! 				pid = CancelVirtualTransaction(*waitlist, cancel_mode);
  
  				if (pid != 0)
- 				{
- 					/*
- 					 * Startup process debug messages
- 					 */
- 					switch (cancel_mode)
- 					{
- 						case CONFLICT_MODE_FATAL:
- 							elog(trace_recovery(DEBUG1),
- 								 "recovery disconnects session with pid %ld because of conflict with %s",
- 								 (long) pid,
- 								 reason);
- 							break;
- 						case CONFLICT_MODE_ERROR:
- 							elog(trace_recovery(DEBUG1),
- 								 "recovery cancels virtual transaction %u/%u pid %ld because of conflict with %s",
- 								 waitlist->backendId,
- 								 waitlist->localTransactionId,
- 								 (long) pid,
- 								 reason);
- 							break;
- 						default:
- 							/* No conflict pending, so fall through */
- 							break;
- 					}
- 
- 					/*
- 					 * Wait awhile for it to die so that we avoid flooding an
- 					 * unresponsive backend when system is heavily loaded.
- 					 */
  					pg_usleep(5000);
- 				}
  			}
  		}
  
--- 212,225 ----
  				 * Now find out who to throw out of the balloon.
  				 */
  				Assert(VirtualTransactionIdIsValid(*waitlist));
! 				pid = CancelVirtualTransaction(*waitlist, reason);
  
+ 				/*
+ 				 * Wait awhile for it to die so that we avoid flooding an
+ 				 * unresponsive backend when system is heavily loaded.
+ 				 */
  				if (pid != 0)
  					pg_usleep(5000);
  			}
  		}
  
***************
*** 285,292 **** ResolveRecoveryConflictWithSnapshot(TransactionId latestRemovedXid)
  										 true);
  
  	ResolveRecoveryConflictWithVirtualXIDs(backends,
! 										   "snapshot conflict",
! 										   CONFLICT_MODE_ERROR);
  }
  
  void
--- 245,251 ----
  										 true);
  
  	ResolveRecoveryConflictWithVirtualXIDs(backends,
! 										   PROCSIG_RECOVERY_CONFLICT_SNAPSHOT);
  }
  
  void
***************
*** 317,324 **** ResolveRecoveryConflictWithTablespace(Oid tsid)
  												InvalidOid,
  												false);
  	ResolveRecoveryConflictWithVirtualXIDs(temp_file_users,
! 										   "drop tablespace",
! 										   CONFLICT_MODE_ERROR);
  }
  
  void
--- 276,282 ----
  												InvalidOid,
  												false);
  	ResolveRecoveryConflictWithVirtualXIDs(temp_file_users,
! 										   PROCSIG_RECOVERY_CONFLICT_TABLESPACE);
  }
  
  void
***************
*** 379,386 **** ResolveRecoveryConflictWithLock(Oid dbOid, Oid relOid)
  		}
  
  		ResolveRecoveryConflictWithVirtualXIDs(backends,
! 											   "exclusive lock",
! 											   CONFLICT_MODE_ERROR);
  
  		if (LockAcquireExtended(&locktag, AccessExclusiveLock, true, true, false)
  											!= LOCKACQUIRE_NOT_AVAIL)
--- 337,343 ----
  		}
  
  		ResolveRecoveryConflictWithVirtualXIDs(backends,
! 											   PROCSIG_RECOVERY_CONFLICT_LOCK);
  
  		if (LockAcquireExtended(&locktag, AccessExclusiveLock, true, true, false)
  											!= LOCKACQUIRE_NOT_AVAIL)
*** a/src/backend/storage/lmgr/proc.c
--- b/src/backend/storage/lmgr/proc.c
***************
*** 318,324 **** InitProcess(void)
  	MyProc->waitProcLock = NULL;
  	for (i = 0; i < NUM_LOCK_PARTITIONS; i++)
  		SHMQueueInit(&(MyProc->myProcLocks[i]));
! 	MyProc->recoveryConflictMode = 0;
  
  	/*
  	 * We might be reusing a semaphore that belonged to a failed process. So
--- 318,324 ----
  	MyProc->waitProcLock = NULL;
  	for (i = 0; i < NUM_LOCK_PARTITIONS; i++)
  		SHMQueueInit(&(MyProc->myProcLocks[i]));
! 	MyProc->recoveryConflictPending = false;
  
  	/*
  	 * We might be reusing a semaphore that belonged to a failed process. So
*** a/src/backend/tcop/postgres.c
--- b/src/backend/tcop/postgres.c
***************
*** 62,68 ****
  #include "storage/proc.h"
  #include "storage/procsignal.h"
  #include "storage/sinval.h"
- #include "storage/standby.h"
  #include "tcop/fastpath.h"
  #include "tcop/pquery.h"
  #include "tcop/tcopprot.h"
--- 62,67 ----
***************
*** 172,177 **** static int	UseNewLine = 1;		/* Use newlines query delimiters (the default) */
--- 171,178 ----
  static int	UseNewLine = 0;		/* Use EOF as query delimiters */
  #endif   /* TCOP_DONTUSENEWLINE */
  
+ /* whether we were cancelled during recovery by conflict processing or not */
+ static bool RecoveryConflictPending = false;
  
  /* ----------------------------------------------------------------
   *		decls for routines only used in this file
***************
*** 185,190 **** static List *pg_rewrite_query(Query *query);
--- 186,192 ----
  static bool check_log_statement(List *stmt_list);
  static int	errdetail_execute(List *raw_parsetree_list);
  static int	errdetail_params(ParamListInfo params);
+ static int  errdetail_abort(void);
  static void start_xact_command(void);
  static void finish_xact_command(void);
  static bool IsTransactionExitStmt(Node *parsetree);
***************
*** 943,949 **** exec_simple_query(const char *query_string)
  			ereport(ERROR,
  					(errcode(ERRCODE_IN_FAILED_SQL_TRANSACTION),
  					 errmsg("current transaction is aborted, "
! 						"commands ignored until end of transaction block")));
  
  		/* Make sure we are in a transaction command */
  		start_xact_command();
--- 945,952 ----
  			ereport(ERROR,
  					(errcode(ERRCODE_IN_FAILED_SQL_TRANSACTION),
  					 errmsg("current transaction is aborted, "
! 							"commands ignored until end of transaction block"),
! 					 errdetail_abort()));
  
  		/* Make sure we are in a transaction command */
  		start_xact_command();
***************
*** 1252,1258 **** exec_parse_message(const char *query_string,	/* string to execute */
  			ereport(ERROR,
  					(errcode(ERRCODE_IN_FAILED_SQL_TRANSACTION),
  					 errmsg("current transaction is aborted, "
! 						"commands ignored until end of transaction block")));
  
  		/*
  		 * Set up a snapshot if parse analysis/planning will need one.
--- 1255,1262 ----
  			ereport(ERROR,
  					(errcode(ERRCODE_IN_FAILED_SQL_TRANSACTION),
  					 errmsg("current transaction is aborted, "
! 							"commands ignored until end of transaction block"),
! 					 errdetail_abort()));
  
  		/*
  		 * Set up a snapshot if parse analysis/planning will need one.
***************
*** 1532,1538 **** exec_bind_message(StringInfo input_message)
  		ereport(ERROR,
  				(errcode(ERRCODE_IN_FAILED_SQL_TRANSACTION),
  				 errmsg("current transaction is aborted, "
! 						"commands ignored until end of transaction block")));
  
  	/*
  	 * Create the portal.  Allow silent replacement of an existing portal only
--- 1536,1543 ----
  		ereport(ERROR,
  				(errcode(ERRCODE_IN_FAILED_SQL_TRANSACTION),
  				 errmsg("current transaction is aborted, "
! 						"commands ignored until end of transaction block"),
! 				 errdetail_abort()));
  
  	/*
  	 * Create the portal.  Allow silent replacement of an existing portal only
***************
*** 1973,1979 **** exec_execute_message(const char *portal_name, long max_rows)
  		ereport(ERROR,
  				(errcode(ERRCODE_IN_FAILED_SQL_TRANSACTION),
  				 errmsg("current transaction is aborted, "
! 						"commands ignored until end of transaction block")));
  
  	/* Check for cancel signal before we start execution */
  	CHECK_FOR_INTERRUPTS();
--- 1978,1985 ----
  		ereport(ERROR,
  				(errcode(ERRCODE_IN_FAILED_SQL_TRANSACTION),
  				 errmsg("current transaction is aborted, "
! 						"commands ignored until end of transaction block"),
! 				 errdetail_abort()));
  
  	/* Check for cancel signal before we start execution */
  	CHECK_FOR_INTERRUPTS();
***************
*** 2234,2239 **** errdetail_params(ParamListInfo params)
--- 2240,2259 ----
  }
  
  /*
+  * errdetail_abort
+  *
+  * Add an errdetail() line showing abort reason, if any.
+  */
+ static int
+ errdetail_abort(void)
+ {
+ 	if (MyProc->recoveryConflictPending)
+ 		errdetail("abort reason: recovery conflict");
+ 
+ 	return 0;
+ }
+ 
+ /*
   * exec_describe_statement_message
   *
   * Process a "Describe" message for a prepared statement
***************
*** 2290,2296 **** exec_describe_statement_message(const char *stmt_name)
  		ereport(ERROR,
  				(errcode(ERRCODE_IN_FAILED_SQL_TRANSACTION),
  				 errmsg("current transaction is aborted, "
! 						"commands ignored until end of transaction block")));
  
  	if (whereToSendOutput != DestRemote)
  		return;					/* can't actually do anything... */
--- 2310,2317 ----
  		ereport(ERROR,
  				(errcode(ERRCODE_IN_FAILED_SQL_TRANSACTION),
  				 errmsg("current transaction is aborted, "
! 						"commands ignored until end of transaction block"),
! 				 errdetail_abort()));
  
  	if (whereToSendOutput != DestRemote)
  		return;					/* can't actually do anything... */
***************
*** 2370,2376 **** exec_describe_portal_message(const char *portal_name)
  		ereport(ERROR,
  				(errcode(ERRCODE_IN_FAILED_SQL_TRANSACTION),
  				 errmsg("current transaction is aborted, "
! 						"commands ignored until end of transaction block")));
  
  	if (whereToSendOutput != DestRemote)
  		return;					/* can't actually do anything... */
--- 2391,2398 ----
  		ereport(ERROR,
  				(errcode(ERRCODE_IN_FAILED_SQL_TRANSACTION),
  				 errmsg("current transaction is aborted, "
! 						"commands ignored until end of transaction block"),
! 				 errdetail_abort()));
  
  	if (whereToSendOutput != DestRemote)
  		return;					/* can't actually do anything... */
***************
*** 2677,2682 **** SigHupHandler(SIGNAL_ARGS)
--- 2699,2795 ----
  	got_SIGHUP = true;
  }
  
+ /*
+  * RecoveryConflictInterrupt: out-of-line portion of recovery conflict
+  * handling ollowing receipt of SIGUSR1. Designed to be similar to die()
+  * and StatementCancelHandler(). Called only by a normal user backend
+  * that begins a transaction during recovery.
+  */
+ void
+ RecoveryConflictInterrupt(ProcSignalReason reason)
+ {
+ 	int                     save_errno = errno;
+ 
+ 	/*
+ 	* Don't joggle the elbow of proc_exit
+ 	*/
+ 	if (!proc_exit_inprogress)
+ 	{
+ 		switch (reason)
+ 		{
+ 			case PROCSIG_RECOVERY_CONFLICT_LOCK:
+ 			case PROCSIG_RECOVERY_CONFLICT_TABLESPACE:
+ 			case PROCSIG_RECOVERY_CONFLICT_SNAPSHOT:
+ 					/*
+ 					 * If we aren't in a transaction any longer then ignore.
+ 					 */
+ 					if (!IsTransactionOrTransactionBlock())
+ 						return;
+ 
+ 					/*
+ 					 * If we can abort just the current subtransaction then we
+ 					 * are OK to throw an ERROR to resolve the conflict. Otherwise
+ 					 * drop through to the FATAL case.
+ 					 *
+ 					 * XXX other times that we can throw just an ERROR *may* be
+ 					 *   PROCSIG_RECOVERY_CONFLICT_LOCK
+ 					 *		if no locks are held in parent transactions
+ 					 *
+ 					 *   PROCSIG_RECOVERY_CONFLICT_SNAPSHOT
+ 					 *		if no snapshots are held by parent transactions
+ 					 *		and the transaction is not serializable
+ 					 *
+ 					 *   PROCSIG_RECOVERY_CONFLICT_TABLESPACE
+ 					 *		if no temp files or cursors open in parent transactions
+ 					 */
+ 					if (!IsSubTransaction())
+ 					{
+ 						/*
+ 						 * If we already aborted then we no longer need to cancel.
+ 						 * We do this here since we do not wish to ignore aborted
+ 						 * subtransactions, which must cause FATAL, currently.
+ 						 */
+ 						if (IsAbortedTransactionBlockState())
+ 							return;
+ 
+ 						RecoveryConflictPending = true;
+ 						QueryCancelPending = true;
+ 						InterruptPending = true;
+ 						break;
+ 					}
+ 
+ 					/* Intentional drop through to session cancel */
+ 
+ 			case PROCSIG_RECOVERY_CONFLICT_DATABASE:
+ 					RecoveryConflictPending = true;
+ 					ProcDiePending = true;
+ 					InterruptPending = true;
+ 					break;
+ 
+ 			default:
+ 					elog(FATAL, "Unknown conflict mode");
+ 		}
+ 
+ 		/*
+ 		 * If it's safe to interrupt, and we're waiting for input or a lock,
+ 		 * service the interrupt immediately
+ 		 */
+ 		if (ImmediateInterruptOK && InterruptHoldoffCount == 0 &&
+ 			CritSectionCount == 0)
+ 		{
+ 			/* bump holdoff count to make ProcessInterrupts() a no-op */
+ 			/* until we are done getting ready for it */
+ 			InterruptHoldoffCount++;
+ 			LockWaitCancel();	/* prevent CheckDeadLock from running */
+ 			DisableNotifyInterrupt();
+ 			DisableCatchupInterrupt();
+ 			InterruptHoldoffCount--;
+ 			ProcessInterrupts();
+ 		}
+ 	}
+ 
+ 	errno = save_errno;
+ }
  
  /*
   * ProcessInterrupts: out-of-line portion of CHECK_FOR_INTERRUPTS() macro
***************
*** 2706,2711 **** ProcessInterrupts(void)
--- 2819,2828 ----
  			ereport(FATAL,
  					(errcode(ERRCODE_ADMIN_SHUTDOWN),
  					 errmsg("terminating autovacuum process due to administrator command")));
+ 		else if (RecoveryConflictPending)
+ 			ereport(FATAL,
+ 					(errcode(ERRCODE_ADMIN_SHUTDOWN),
+ 					 errmsg("terminating connection due to conflict with recovery")));
  		else
  			ereport(FATAL,
  					(errcode(ERRCODE_ADMIN_SHUTDOWN),
***************
*** 2744,2800 **** ProcessInterrupts(void)
  					(errcode(ERRCODE_QUERY_CANCELED),
  					 errmsg("canceling autovacuum task")));
  		}
  		{
! 			int cancelMode = MyProc->recoveryConflictMode;
  
! 			/*
! 			 * XXXHS: We don't yet have a clean way to cancel an
! 			 * idle-in-transaction session, so make it FATAL instead.
! 			 * This isn't as bad as it looks because we don't issue a
! 			 * CONFLICT_MODE_ERROR for a session with proc->xmin == 0
! 			 * on cleanup conflicts. There's a possibility that we
! 			 * marked somebody as a conflict and then they go idle.
! 			 */
! 			if (DoingCommandRead && IsTransactionBlock() &&
! 				cancelMode == CONFLICT_MODE_ERROR)
  			{
! 				cancelMode = CONFLICT_MODE_FATAL;
  			}
! 
! 			switch (cancelMode)
  			{
! 				case CONFLICT_MODE_FATAL:
! 					ImmediateInterruptOK = false;	/* not idle anymore */
! 					DisableNotifyInterrupt();
! 					DisableCatchupInterrupt();
! 					Assert(RecoveryInProgress());
! 					ereport(FATAL,
! 							(errcode(ERRCODE_QUERY_CANCELED),
! 							 errmsg("canceling session due to conflict with recovery")));
! 
! 				case CONFLICT_MODE_ERROR:
! 					/*
! 					 * We are aborting because we need to release
! 					 * locks. So we need to abort out of all
! 					 * subtransactions to make sure we release
! 					 * all locks at whatever their level.
! 					 *
! 					 * XXX Should we try to examine the
! 					 * transaction tree and cancel just enough
! 					 * subxacts to remove locks? Doubt it.
! 					 */
! 					ImmediateInterruptOK = false;	/* not idle anymore */
! 					DisableNotifyInterrupt();
! 					DisableCatchupInterrupt();
! 					Assert(RecoveryInProgress());
! 					AbortOutOfAnyTransaction();
! 					ereport(ERROR,
! 							(errcode(ERRCODE_QUERY_CANCELED),
! 							 errmsg("canceling statement due to conflict with recovery")));
! 
! 				default:
! 					/* No conflict pending, so fall through */
! 					break;
  			}
  		}
  
--- 2861,2886 ----
  					(errcode(ERRCODE_QUERY_CANCELED),
  					 errmsg("canceling autovacuum task")));
  		}
+ 		if (RecoveryConflictPending)
  		{
! 			ImmediateInterruptOK = false;	/* not idle anymore */
! 			DisableNotifyInterrupt();
! 			DisableCatchupInterrupt();
  
! 			if (DoingCommandRead)
  			{
! 				ProcDiePending = false;
! 				QueryCancelPending = false;
! 				ereport(FATAL,
! 						(errcode(ERRCODE_ADMIN_SHUTDOWN),
! 						 errmsg("terminating connection due to conflict with recovery")));
  			}
! 			else
  			{
! 				QueryCancelPending = false;
! 				ereport(ERROR,
! 						(errcode(ERRCODE_QUERY_CANCELED),
! 						 errmsg("canceling statement due to conflict with recovery")));
  			}
  		}
  
***************
*** 3627,3633 **** PostgresMain(int argc, char *argv[], const char *username)
  		 */
  		if (send_ready_for_query)
  		{
! 			if (IsTransactionOrTransactionBlock())
  			{
  				set_ps_display("idle in transaction", false);
  				pgstat_report_activity("<IDLE> in transaction");
--- 3713,3724 ----
  		 */
  		if (send_ready_for_query)
  		{
! 			if (IsAbortedTransactionBlockState())
! 			{
! 				set_ps_display("idle in transaction (aborted)", false);
! 				pgstat_report_activity("<IDLE> in transaction (aborted)");
! 			}
! 			else if (IsTransactionOrTransactionBlock())
  			{
  				set_ps_display("idle in transaction", false);
  				pgstat_report_activity("<IDLE> in transaction");
*** a/src/include/storage/proc.h
--- b/src/include/storage/proc.h
***************
*** 96,106 **** struct PGPROC
  	uint8		vacuumFlags;	/* vacuum-related flags, see above */
  
  	/*
! 	 * While in hot standby mode, setting recoveryConflictMode instructs
! 	 * the backend to commit suicide. Possible values are the same as those
! 	 * passed to ResolveRecoveryConflictWithVirtualXIDs().
  	 */
! 	int			recoveryConflictMode;
  
  	/* Info about LWLock the process is currently waiting for, if any. */
  	bool		lwWaiting;		/* true if waiting for an LW lock */
--- 96,106 ----
  	uint8		vacuumFlags;	/* vacuum-related flags, see above */
  
  	/*
! 	 * While in hot standby mode, shows that a conflict signal has been sent
! 	 * for the current transaction. Set/cleared while holding ProcArrayLock,
! 	 * though not required. Accessed without lock, if needed.
  	 */
! 	bool		recoveryConflictPending;
  
  	/* Info about LWLock the process is currently waiting for, if any. */
  	bool		lwWaiting;		/* true if waiting for an LW lock */
*** a/src/include/storage/procarray.h
--- b/src/include/storage/procarray.h
***************
*** 15,20 ****
--- 15,21 ----
  #define PROCARRAY_H
  
  #include "storage/lock.h"
+ #include "storage/procsignal.h"
  #include "storage/standby.h"
  #include "utils/snapshot.h"
  
***************
*** 58,65 **** extern VirtualTransactionId *GetCurrentVirtualXIDs(TransactionId limitXmin,
  					  int *nvxids);
  extern VirtualTransactionId *GetConflictingVirtualXIDs(TransactionId limitXmin,
  					Oid dbOid, bool skipExistingConflicts);
! extern pid_t CancelVirtualTransaction(VirtualTransactionId vxid,
! 						 int cancel_mode);
  
  extern int	CountActiveBackends(void);
  extern int	CountDBBackends(Oid databaseid);
--- 59,65 ----
  					  int *nvxids);
  extern VirtualTransactionId *GetConflictingVirtualXIDs(TransactionId limitXmin,
  					Oid dbOid, bool skipExistingConflicts);
! extern pid_t CancelVirtualTransaction(VirtualTransactionId vxid, ProcSignalReason sigmode);
  
  extern int	CountActiveBackends(void);
  extern int	CountDBBackends(Oid databaseid);
*** a/src/include/storage/procsignal.h
--- b/src/include/storage/procsignal.h
***************
*** 32,37 **** typedef enum
--- 32,43 ----
  	PROCSIG_CATCHUP_INTERRUPT,	/* sinval catchup interrupt */
  	PROCSIG_NOTIFY_INTERRUPT,	/* listen/notify interrupt */
  
+ 	/* Recovery conflict reasons */
+ 	PROCSIG_RECOVERY_CONFLICT_DATABASE,
+ 	PROCSIG_RECOVERY_CONFLICT_TABLESPACE,
+ 	PROCSIG_RECOVERY_CONFLICT_LOCK,
+ 	PROCSIG_RECOVERY_CONFLICT_SNAPSHOT,
+ 
  	NUM_PROCSIGNALS				/* Must be last! */
  } ProcSignalReason;
  
*** a/src/include/storage/standby.h
--- b/src/include/storage/standby.h
***************
*** 19,29 ****
  
  extern int	vacuum_defer_cleanup_age;
  
- /* cancel modes for ResolveRecoveryConflictWithVirtualXIDs */
- #define CONFLICT_MODE_NOT_SET		0
- #define CONFLICT_MODE_ERROR			1	/* Conflict can be resolved by canceling query */
- #define CONFLICT_MODE_FATAL			2	/* Conflict can only be resolved by disconnecting session */
- 
  extern void ResolveRecoveryConflictWithSnapshot(TransactionId latestRemovedXid);
  extern void ResolveRecoveryConflictWithTablespace(Oid tsid);
  extern void ResolveRecoveryConflictWithDatabase(Oid dbid);
--- 19,24 ----
*** a/src/include/tcop/tcopprot.h
--- b/src/include/tcop/tcopprot.h
***************
*** 21,26 ****
--- 21,27 ----
  
  #include "executor/execdesc.h"
  #include "nodes/parsenodes.h"
+ #include "storage/procsignal.h"
  #include "utils/guc.h"
  
  
***************
*** 64,69 **** extern void die(SIGNAL_ARGS);
--- 65,71 ----
  extern void quickdie(SIGNAL_ARGS);
  extern void StatementCancelHandler(SIGNAL_ARGS);
  extern void FloatExceptionHandler(SIGNAL_ARGS);
+ extern void RecoveryConflictInterrupt(ProcSignalReason reason); /* called from SIGUSR1 handler */
  extern void prepare_for_client_read(void);
  extern void client_read_ended(void);
  extern const char *process_postgres_switches(int argc, char *argv[],
-- 
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