* Portions Copyright (c) 1994, Regents of the University of California
*
* 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 $
*
*-------------------------------------------------------------------------
*/
#include "access/xlog.h"
#include "miscadmin.h"
#include "pgstat.h"
+#include "storage/bufmgr.h"
#include "storage/lmgr.h"
#include "storage/proc.h"
#include "storage/procarray.h"
TimestampDifference(GetLatestXLogTime(), now,
&standby_delay_secs, &standby_delay_usecs);
- if (standby_delay_secs >= (long) MaxStandbyDelay)
+ if (standby_delay_secs >= MaxStandbyDelay)
SendRecoveryConflictWithBufferPin();
else
{
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
*
*
* 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
* A lock table is a shared memory hash table. When
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.
*/
* Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group
* 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 $
*
*-------------------------------------------------------------------------
*/
extern void ResolveRecoveryConflictWithBufferPin(void);
extern void SendRecoveryConflictWithBufferPin(void);
+extern void CheckRecoveryConflictDeadlock(LWLockId partitionLock);
/*
* Standby Rmgr (RM_STANDBY_ID)