summaryrefslogtreecommitdiff
path: root/src/backend/storage/ipc/standby.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/storage/ipc/standby.c')
-rw-r--r--src/backend/storage/ipc/standby.c80
1 files changed, 55 insertions, 25 deletions
diff --git a/src/backend/storage/ipc/standby.c b/src/backend/storage/ipc/standby.c
index 995b68aae5..43f7411273 100644
--- a/src/backend/storage/ipc/standby.c
+++ b/src/backend/storage/ipc/standby.c
@@ -28,6 +28,7 @@
#include "storage/sinvaladt.h"
#include "storage/standby.h"
#include "utils/ps_status.h"
+#include "utils/timeout.h"
#include "utils/timestamp.h"
/* User-settable GUC parameters */
@@ -40,6 +41,7 @@ static List *RecoveryLockList;
static void ResolveRecoveryConflictWithVirtualXIDs(VirtualTransactionId *waitlist,
ProcSignalReason reason);
static void ResolveRecoveryConflictWithLock(Oid dbOid, Oid relOid);
+static void SendRecoveryConflictWithBufferPin(ProcSignalReason reason);
static void LogCurrentRunningXacts(RunningTransactions CurrRunningXacts);
static void LogAccessExclusiveLocks(int nlocks, xl_standby_lock *locks);
@@ -370,13 +372,15 @@ ResolveRecoveryConflictWithLock(Oid dbOid, Oid relOid)
* ResolveRecoveryConflictWithBufferPin is called from LockBufferForCleanup()
* to resolve conflicts with other backends holding buffer pins.
*
- * We either resolve conflicts immediately or set a SIGALRM to wake us at
- * the limit of our patience. The sleep in LockBufferForCleanup() is
- * performed here, for code clarity.
+ * The ProcWaitForSignal() sleep normally done in LockBufferForCleanup()
+ * (when not InHotStandby) is performed here, for code clarity.
+ *
+ * We either resolve conflicts immediately or set a timeout to wake us at
+ * the limit of our patience.
*
* Resolve conflicts by sending a PROCSIG signal to all backends to check if
* they hold one of the buffer pins that is blocking Startup process. If so,
- * backends will take an appropriate error action, ERROR or FATAL.
+ * those backends will take an appropriate error action, ERROR or FATAL.
*
* We also must check for deadlocks. Deadlocks occur because if queries
* wait on a lock, that must be behind an AccessExclusiveLock, which can only
@@ -389,32 +393,26 @@ ResolveRecoveryConflictWithLock(Oid dbOid, Oid relOid)
*
* Deadlocks are extremely rare, and relatively expensive to check for,
* so we don't do a deadlock check right away ... only if we have had to wait
- * at least deadlock_timeout. Most of the logic about that is in proc.c.
+ * at least deadlock_timeout.
*/
void
ResolveRecoveryConflictWithBufferPin(void)
{
- bool sig_alarm_enabled = false;
TimestampTz ltime;
- TimestampTz now;
Assert(InHotStandby);
ltime = GetStandbyLimitTime();
- now = GetCurrentTimestamp();
- if (!ltime)
+ if (ltime == 0)
{
/*
* We're willing to wait forever for conflicts, so set timeout for
- * deadlock check (only)
+ * deadlock check only
*/
- if (enable_standby_sig_alarm(now, now, true))
- sig_alarm_enabled = true;
- else
- elog(FATAL, "could not set timer for process wakeup");
+ enable_timeout_after(STANDBY_DEADLOCK_TIMEOUT, DeadlockTimeout);
}
- else if (now >= ltime)
+ else if (GetCurrentTimestamp() >= ltime)
{
/*
* We're already behind, so clear a path as quickly as possible.
@@ -427,23 +425,23 @@ ResolveRecoveryConflictWithBufferPin(void)
* Wake up at ltime, and check for deadlocks as well if we will be
* waiting longer than deadlock_timeout
*/
- if (enable_standby_sig_alarm(now, ltime, false))
- sig_alarm_enabled = true;
- else
- elog(FATAL, "could not set timer for process wakeup");
+ enable_timeout_after(STANDBY_DEADLOCK_TIMEOUT, DeadlockTimeout);
+ enable_timeout_at(STANDBY_TIMEOUT, ltime);
}
/* Wait to be signaled by UnpinBuffer() */
ProcWaitForSignal();
- if (sig_alarm_enabled)
- {
- if (!disable_standby_sig_alarm())
- elog(FATAL, "could not disable timer for process wakeup");
- }
+ /*
+ * Clear any timeout requests established above. We assume here that
+ * the Startup process doesn't have any other timeouts than what this
+ * function uses. If that stops being true, we could cancel the
+ * timeouts individually, but that'd be slower.
+ */
+ disable_all_timeouts(false);
}
-void
+static void
SendRecoveryConflictWithBufferPin(ProcSignalReason reason)
{
Assert(reason == PROCSIG_RECOVERY_CONFLICT_BUFFERPIN ||
@@ -492,6 +490,38 @@ CheckRecoveryConflictDeadlock(void)
errdetail("User transaction caused buffer deadlock with recovery.")));
}
+
+/* --------------------------------
+ * timeout handler routines
+ * --------------------------------
+ */
+
+/*
+ * StandbyDeadLockHandler() will be called if STANDBY_DEADLOCK_TIMEOUT
+ * occurs before STANDBY_TIMEOUT. Send out a request for hot-standby
+ * backends to check themselves for deadlocks.
+ */
+void
+StandbyDeadLockHandler(void)
+{
+ SendRecoveryConflictWithBufferPin(PROCSIG_RECOVERY_CONFLICT_STARTUP_DEADLOCK);
+}
+
+/*
+ * StandbyTimeoutHandler() will be called if STANDBY_TIMEOUT is exceeded.
+ * Send out a request to release conflicting buffer pins unconditionally,
+ * so we can press ahead with applying changes in recovery.
+ */
+void
+StandbyTimeoutHandler(void)
+{
+ /* forget any pending STANDBY_DEADLOCK_TIMEOUT request */
+ disable_timeout(STANDBY_DEADLOCK_TIMEOUT, false);
+
+ SendRecoveryConflictWithBufferPin(PROCSIG_RECOVERY_CONFLICT_BUFFERPIN);
+}
+
+
/*
* -----------------------------------------------------
* Locking in Recovery Mode