Commit b95a720a authored by Simon Riggs's avatar Simon Riggs

Re-enable max_standby_delay = -1 using deadlock detection on startup

process. If startup waits on a buffer pin we send a request to all
backends to cancel themselves if they are holding the buffer pin
required and they are also waiting on a lock. If not, startup waits
until max_standby_delay before cancelling any backend waiting for
the requested buffer pin.
parent fafa374f
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/storage/ipc/procsignal.c,v 1.4 2010/01/23 16:37:12 sriggs Exp $ * $PostgreSQL: pgsql/src/backend/storage/ipc/procsignal.c,v 1.5 2010/02/13 01:32:19 sriggs Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -272,6 +272,9 @@ procsignal_sigusr1_handler(SIGNAL_ARGS) ...@@ -272,6 +272,9 @@ procsignal_sigusr1_handler(SIGNAL_ARGS)
if (CheckProcSignal(PROCSIG_RECOVERY_CONFLICT_SNAPSHOT)) if (CheckProcSignal(PROCSIG_RECOVERY_CONFLICT_SNAPSHOT))
RecoveryConflictInterrupt(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)) if (CheckProcSignal(PROCSIG_RECOVERY_CONFLICT_BUFFERPIN))
RecoveryConflictInterrupt(PROCSIG_RECOVERY_CONFLICT_BUFFERPIN); RecoveryConflictInterrupt(PROCSIG_RECOVERY_CONFLICT_BUFFERPIN);
......
...@@ -11,7 +11,7 @@ ...@@ -11,7 +11,7 @@
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/storage/ipc/standby.c,v 1.11 2010/02/11 19:35:22 sriggs Exp $ * $PostgreSQL: pgsql/src/backend/storage/ipc/standby.c,v 1.12 2010/02/13 01:32:19 sriggs Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -127,6 +127,9 @@ WaitExceedsMaxStandbyDelay(void) ...@@ -127,6 +127,9 @@ WaitExceedsMaxStandbyDelay(void)
long delay_secs; long delay_secs;
int delay_usecs; int delay_usecs;
if (MaxStandbyDelay == -1)
return false;
/* Are we past max_standby_delay? */ /* Are we past max_standby_delay? */
TimestampDifference(GetLatestXLogTime(), GetCurrentTimestamp(), TimestampDifference(GetLatestXLogTime(), GetCurrentTimestamp(),
&delay_secs, &delay_usecs); &delay_secs, &delay_usecs);
...@@ -351,15 +354,15 @@ ResolveRecoveryConflictWithLock(Oid dbOid, Oid relOid) ...@@ -351,15 +354,15 @@ ResolveRecoveryConflictWithLock(Oid dbOid, Oid relOid)
* they hold one of the buffer pins that is blocking Startup process. If so, * they hold one of the buffer pins that is blocking Startup process. If so,
* backends will take an appropriate error action, ERROR or FATAL. * backends will take an appropriate error action, ERROR or FATAL.
* *
* A secondary purpose of this is to avoid deadlocks that might occur between * We also check for deadlocks before we wait, though applications that cause
* the Startup process and lock waiters. Deadlocks occur because if queries * these will be extremely rare. Deadlocks occur because if queries
* wait on a lock, that must be behind an AccessExclusiveLock, which can only * wait on a lock, that must be behind an AccessExclusiveLock, which can only
* be clared if the Startup process replays a transaction completion record. * be cleared if the Startup process replays a transaction completion record.
* If Startup process is waiting then that is a deadlock. If we allowed a * If Startup process is also waiting then that is a deadlock. The deadlock
* setting of max_standby_delay that meant "wait forever" we would then need * can occur if the query is waiting and then the Startup sleeps, or if
* special code to protect against deadlock. Such deadlocks are rare, so the * Startup is sleeping and the the query waits on a lock. We protect against
* code would be almost certainly buggy, so we avoid both long waits and * only the former sequence here, the latter sequence is checked prior to
* deadlocks using the same mechanism. * the query sleeping, in CheckRecoveryConflictDeadlock().
*/ */
void void
ResolveRecoveryConflictWithBufferPin(void) ResolveRecoveryConflictWithBufferPin(void)
...@@ -368,11 +371,23 @@ ResolveRecoveryConflictWithBufferPin(void) ...@@ -368,11 +371,23 @@ ResolveRecoveryConflictWithBufferPin(void)
Assert(InHotStandby); Assert(InHotStandby);
/*
* Signal immediately or set alarm for later.
*/
if (MaxStandbyDelay == 0) if (MaxStandbyDelay == 0)
SendRecoveryConflictWithBufferPin(); {
/*
* 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 else
{ {
TimestampTz now; TimestampTz now;
...@@ -386,13 +401,25 @@ ResolveRecoveryConflictWithBufferPin(void) ...@@ -386,13 +401,25 @@ ResolveRecoveryConflictWithBufferPin(void)
&standby_delay_secs, &standby_delay_usecs); &standby_delay_secs, &standby_delay_usecs);
if (standby_delay_secs >= MaxStandbyDelay) if (standby_delay_secs >= MaxStandbyDelay)
SendRecoveryConflictWithBufferPin(); {
/*
* We're already behind, so clear a path as quickly as possible.
*/
SendRecoveryConflictWithBufferPin(PROCSIG_RECOVERY_CONFLICT_BUFFERPIN);
}
else else
{ {
TimestampTz fin_time; /* Expected wake-up time by timer */ TimestampTz fin_time; /* Expected wake-up time by timer */
long timer_delay_secs; /* Amount of time we set timer for */ long timer_delay_secs; /* Amount of time we set timer for */
int timer_delay_usecs = 0; 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? * How much longer we should wait?
*/ */
...@@ -435,15 +462,18 @@ ResolveRecoveryConflictWithBufferPin(void) ...@@ -435,15 +462,18 @@ ResolveRecoveryConflictWithBufferPin(void)
} }
void void
SendRecoveryConflictWithBufferPin(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 * We send signal to all backends to ask them if they are holding
* the buffer pin which is delaying the Startup process. We must * the buffer pin which is delaying the Startup process. We must
* not set the conflict flag yet, since most backends will be innocent. * not set the conflict flag yet, since most backends will be innocent.
* Let the SIGUSR1 handling in each backend decide their own fate. * Let the SIGUSR1 handling in each backend decide their own fate.
*/ */
CancelDBBackends(InvalidOid, PROCSIG_RECOVERY_CONFLICT_BUFFERPIN, false); CancelDBBackends(InvalidOid, reason, false);
} }
/* /*
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/storage/lmgr/proc.c,v 1.215 2010/02/08 04:33:54 tgl Exp $ * $PostgreSQL: pgsql/src/backend/storage/lmgr/proc.c,v 1.216 2010/02/13 01:32:19 sriggs Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -45,6 +45,7 @@ ...@@ -45,6 +45,7 @@
#include "storage/pmsignal.h" #include "storage/pmsignal.h"
#include "storage/proc.h" #include "storage/proc.h"
#include "storage/procarray.h" #include "storage/procarray.h"
#include "storage/procsignal.h"
#include "storage/spin.h" #include "storage/spin.h"
...@@ -556,6 +557,15 @@ HaveNFreeProcs(int n) ...@@ -556,6 +557,15 @@ HaveNFreeProcs(int n)
return (n <= 0); return (n <= 0);
} }
bool
IsWaitingForLock(void)
{
if (lockAwaited == NULL)
return false;
return true;
}
/* /*
* Cancel any pending wait for lock, when aborting a transaction. * Cancel any pending wait for lock, when aborting a transaction.
* *
...@@ -1670,7 +1680,7 @@ CheckStandbyTimeout(void) ...@@ -1670,7 +1680,7 @@ CheckStandbyTimeout(void)
now = GetCurrentTimestamp(); now = GetCurrentTimestamp();
if (now >= statement_fin_time) if (now >= statement_fin_time)
SendRecoveryConflictWithBufferPin(); SendRecoveryConflictWithBufferPin(PROCSIG_RECOVERY_CONFLICT_BUFFERPIN);
else else
{ {
/* Not time yet, so (re)schedule the interrupt */ /* Not time yet, so (re)schedule the interrupt */
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/tcop/postgres.c,v 1.587 2010/01/23 17:04:05 sriggs Exp $ * $PostgreSQL: pgsql/src/backend/tcop/postgres.c,v 1.588 2010/02/13 01:32:19 sriggs Exp $
* *
* NOTES * NOTES
* this is the "main" module of the postgres backend and * this is the "main" module of the postgres backend and
...@@ -2278,6 +2278,9 @@ errdetail_recovery_conflict(void) ...@@ -2278,6 +2278,9 @@ errdetail_recovery_conflict(void)
case PROCSIG_RECOVERY_CONFLICT_SNAPSHOT: case PROCSIG_RECOVERY_CONFLICT_SNAPSHOT:
errdetail("User query might have needed to see row versions that must be removed."); errdetail("User query might have needed to see row versions that must be removed.");
break; break;
case PROCSIG_RECOVERY_CONFLICT_STARTUP_DEADLOCK:
errdetail("User transaction caused buffer deadlock with recovery.");
break;
case PROCSIG_RECOVERY_CONFLICT_DATABASE: case PROCSIG_RECOVERY_CONFLICT_DATABASE:
errdetail("User was connected to a database that must be dropped."); errdetail("User was connected to a database that must be dropped.");
break; break;
...@@ -2754,6 +2757,15 @@ RecoveryConflictInterrupt(ProcSignalReason reason) ...@@ -2754,6 +2757,15 @@ RecoveryConflictInterrupt(ProcSignalReason reason)
RecoveryConflictReason = reason; RecoveryConflictReason = reason;
switch (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: case PROCSIG_RECOVERY_CONFLICT_BUFFERPIN:
/* /*
* If we aren't blocking the Startup process there is * If we aren't blocking the Startup process there is
...@@ -2819,6 +2831,8 @@ RecoveryConflictInterrupt(ProcSignalReason reason) ...@@ -2819,6 +2831,8 @@ RecoveryConflictInterrupt(ProcSignalReason reason)
elog(FATAL, "Unknown conflict mode"); elog(FATAL, "Unknown conflict mode");
} }
Assert(RecoveryConflictPending && (QueryCancelPending || ProcDiePending));
/* /*
* If it's safe to interrupt, and we're waiting for input or a lock, * If it's safe to interrupt, and we're waiting for input or a lock,
* service the interrupt immediately * service the interrupt immediately
......
...@@ -10,7 +10,7 @@ ...@@ -10,7 +10,7 @@
* Written by Peter Eisentraut <peter_e@gmx.net>. * Written by Peter Eisentraut <peter_e@gmx.net>.
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/utils/misc/guc.c,v 1.538 2010/02/01 13:40:28 sriggs Exp $ * $PostgreSQL: pgsql/src/backend/utils/misc/guc.c,v 1.539 2010/02/13 01:32:20 sriggs Exp $
* *
*-------------------------------------------------------------------- *--------------------------------------------------------------------
*/ */
...@@ -1381,7 +1381,7 @@ static struct config_int ConfigureNamesInt[] = ...@@ -1381,7 +1381,7 @@ static struct config_int ConfigureNamesInt[] =
NULL NULL
}, },
&MaxStandbyDelay, &MaxStandbyDelay,
30, 0, INT_MAX, NULL, NULL 30, -1, INT_MAX, NULL, NULL
}, },
{ {
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $PostgreSQL: pgsql/src/include/storage/proc.h,v 1.119 2010/01/23 16:37:12 sriggs Exp $ * $PostgreSQL: pgsql/src/include/storage/proc.h,v 1.120 2010/02/13 01:32:20 sriggs Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -189,6 +189,7 @@ extern void ProcQueueInit(PROC_QUEUE *queue); ...@@ -189,6 +189,7 @@ extern void ProcQueueInit(PROC_QUEUE *queue);
extern int ProcSleep(LOCALLOCK *locallock, LockMethod lockMethodTable); extern int ProcSleep(LOCALLOCK *locallock, LockMethod lockMethodTable);
extern PGPROC *ProcWakeup(PGPROC *proc, int waitStatus); extern PGPROC *ProcWakeup(PGPROC *proc, int waitStatus);
extern void ProcLockWakeup(LockMethod lockMethodTable, LOCK *lock); extern void ProcLockWakeup(LockMethod lockMethodTable, LOCK *lock);
extern bool IsWaitingForLock(void);
extern void LockWaitCancel(void); extern void LockWaitCancel(void);
extern void ProcWaitForSignal(void); extern void ProcWaitForSignal(void);
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $PostgreSQL: pgsql/src/include/storage/procsignal.h,v 1.4 2010/01/23 16:37:12 sriggs Exp $ * $PostgreSQL: pgsql/src/include/storage/procsignal.h,v 1.5 2010/02/13 01:32:20 sriggs Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -38,6 +38,7 @@ typedef enum ...@@ -38,6 +38,7 @@ typedef enum
PROCSIG_RECOVERY_CONFLICT_LOCK, PROCSIG_RECOVERY_CONFLICT_LOCK,
PROCSIG_RECOVERY_CONFLICT_SNAPSHOT, PROCSIG_RECOVERY_CONFLICT_SNAPSHOT,
PROCSIG_RECOVERY_CONFLICT_BUFFERPIN, PROCSIG_RECOVERY_CONFLICT_BUFFERPIN,
PROCSIG_RECOVERY_CONFLICT_STARTUP_DEADLOCK,
NUM_PROCSIGNALS /* Must be last! */ NUM_PROCSIGNALS /* Must be last! */
} ProcSignalReason; } ProcSignalReason;
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $PostgreSQL: pgsql/src/include/storage/standby.h,v 1.7 2010/01/31 19:01:11 sriggs Exp $ * $PostgreSQL: pgsql/src/include/storage/standby.h,v 1.8 2010/02/13 01:32:20 sriggs Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
#include "access/xlog.h" #include "access/xlog.h"
#include "storage/lock.h" #include "storage/lock.h"
#include "storage/procsignal.h"
#include "storage/relfilenode.h" #include "storage/relfilenode.h"
extern int vacuum_defer_cleanup_age; extern int vacuum_defer_cleanup_age;
...@@ -30,7 +31,7 @@ extern void ResolveRecoveryConflictWithTablespace(Oid tsid); ...@@ -30,7 +31,7 @@ extern void ResolveRecoveryConflictWithTablespace(Oid tsid);
extern void ResolveRecoveryConflictWithDatabase(Oid dbid); extern void ResolveRecoveryConflictWithDatabase(Oid dbid);
extern void ResolveRecoveryConflictWithBufferPin(void); extern void ResolveRecoveryConflictWithBufferPin(void);
extern void SendRecoveryConflictWithBufferPin(void); extern void SendRecoveryConflictWithBufferPin(ProcSignalReason reason);
extern void CheckRecoveryConflictDeadlock(LWLockId partitionLock); extern void CheckRecoveryConflictDeadlock(LWLockId partitionLock);
/* /*
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment