Commit c85c9414 authored by Simon Riggs's avatar Simon Riggs

Detect early deadlock in Hot Standby when Startup is already waiting. First

stage of required deadlock detection to allow re-enabling max_standby_delay
setting of -1, which is now essential in the absence of improved relation-
specific conflict resoluton. Requested by Greg Stark et al.
parent 034fffbf
...@@ -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.8 2010/01/29 17:10:05 sriggs Exp $ * $PostgreSQL: pgsql/src/backend/storage/ipc/standby.c,v 1.9 2010/01/31 19:01:11 sriggs Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -22,6 +22,7 @@ ...@@ -22,6 +22,7 @@
#include "access/xlog.h" #include "access/xlog.h"
#include "miscadmin.h" #include "miscadmin.h"
#include "pgstat.h" #include "pgstat.h"
#include "storage/bufmgr.h"
#include "storage/lmgr.h" #include "storage/lmgr.h"
#include "storage/proc.h" #include "storage/proc.h"
#include "storage/procarray.h" #include "storage/procarray.h"
...@@ -384,7 +385,7 @@ ResolveRecoveryConflictWithBufferPin(void) ...@@ -384,7 +385,7 @@ ResolveRecoveryConflictWithBufferPin(void)
TimestampDifference(GetLatestXLogTime(), now, TimestampDifference(GetLatestXLogTime(), now,
&standby_delay_secs, &standby_delay_usecs); &standby_delay_secs, &standby_delay_usecs);
if (standby_delay_secs >= (long) MaxStandbyDelay) if (standby_delay_secs >= MaxStandbyDelay)
SendRecoveryConflictWithBufferPin(); SendRecoveryConflictWithBufferPin();
else else
{ {
...@@ -445,6 +446,39 @@ SendRecoveryConflictWithBufferPin(void) ...@@ -445,6 +446,39 @@ SendRecoveryConflictWithBufferPin(void)
CancelDBBackends(InvalidOid, PROCSIG_RECOVERY_CONFLICT_BUFFERPIN, false); CancelDBBackends(InvalidOid, PROCSIG_RECOVERY_CONFLICT_BUFFERPIN, false);
} }
/*
* In Hot Standby perform early deadlock detection. We abort the lock
* wait if are about to sleep while holding the buffer pin that Startup
* process is waiting for. The deadlock occurs because we can only be
* waiting behind an AccessExclusiveLock, which can only clear when a
* transaction completion record is replayed, which can only occur when
* Startup process is not waiting. So if Startup process is waiting we
* never will clear that lock, so if we wait we cause deadlock. If we
* are the Startup process then no need to check for deadlocks.
*/
void
CheckRecoveryConflictDeadlock(LWLockId partitionLock)
{
Assert(!InRecovery);
if (!HoldingBufferPinThatDelaysRecovery())
return;
LWLockRelease(partitionLock);
/*
* Error message should match ProcessInterrupts() but we avoid calling
* that because we aren't handling an interrupt at this point. Note
* that we only cancel the current transaction here, so if we are in a
* subtransaction and the pin is held by a parent, then the Startup
* process will continue to wait even though we have avoided deadlock.
*/
ereport(ERROR,
(errcode(ERRCODE_QUERY_CANCELED),
errmsg("canceling statement due to conflict with recovery"),
errdetail("User transaction caused buffer deadlock with recovery.")));
}
/* /*
* ----------------------------------------------------- * -----------------------------------------------------
* Locking in Recovery Mode * Locking in Recovery Mode
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/storage/lmgr/lock.c,v 1.193 2010/01/29 19:45:12 sriggs Exp $ * $PostgreSQL: pgsql/src/backend/storage/lmgr/lock.c,v 1.194 2010/01/31 19:01:11 sriggs Exp $
* *
* NOTES * NOTES
* A lock table is a shared memory hash table. When * A lock table is a shared memory hash table. When
...@@ -814,6 +814,13 @@ LockAcquireExtended(const LOCKTAG *locktag, ...@@ -814,6 +814,13 @@ LockAcquireExtended(const LOCKTAG *locktag,
return LOCKACQUIRE_NOT_AVAIL; return LOCKACQUIRE_NOT_AVAIL;
} }
/*
* In Hot Standby perform early deadlock detection in normal backends.
* If deadlock found we release partition lock but do not return.
*/
if (RecoveryInProgress() && !InRecovery)
CheckRecoveryConflictDeadlock(partitionLock);
/* /*
* Set bitmask of locks this process already holds on this object. * Set bitmask of locks this process already holds on this object.
*/ */
......
...@@ -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.6 2010/01/29 17:10:05 sriggs Exp $ * $PostgreSQL: pgsql/src/include/storage/standby.h,v 1.7 2010/01/31 19:01:11 sriggs Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -31,6 +31,7 @@ extern void ResolveRecoveryConflictWithDatabase(Oid dbid); ...@@ -31,6 +31,7 @@ extern void ResolveRecoveryConflictWithDatabase(Oid dbid);
extern void ResolveRecoveryConflictWithBufferPin(void); extern void ResolveRecoveryConflictWithBufferPin(void);
extern void SendRecoveryConflictWithBufferPin(void); extern void SendRecoveryConflictWithBufferPin(void);
extern void CheckRecoveryConflictDeadlock(LWLockId partitionLock);
/* /*
* Standby Rmgr (RM_STANDBY_ID) * Standby Rmgr (RM_STANDBY_ID)
......
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