* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/storage/ipc/standby.c,v 1.23 2010/05/14 07:11:49 sriggs Exp $
+ * $PostgreSQL: pgsql/src/backend/storage/ipc/standby.c,v 1.24 2010/05/26 19:52:52 sriggs Exp $
*
*-------------------------------------------------------------------------
*/
}
else if (MaxStandbyDelay < 0)
{
+ TimestampTz now = GetCurrentTimestamp();
+
/*
- * 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.
+ * Set timeout for deadlock check (only)
*/
- SendRecoveryConflictWithBufferPin(PROCSIG_RECOVERY_CONFLICT_STARTUP_DEADLOCK);
+ if (enable_standby_sig_alarm(now, now, true))
+ sig_alarm_enabled = true;
+ else
+ elog(FATAL, "could not set timer for process wakeup");
}
else
{
}
else
{
- TimestampTz fin_time; /* Expected wake-up time by timer */
- long timer_delay_secs; /* Amount of time we set timer
- * for */
- int timer_delay_usecs;
-
- /*
- * 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);
+ TimestampTz max_standby_time;
/*
- * How much longer we should wait?
+ * At what point in the future do we hit MaxStandbyDelay?
*/
- fin_time = TimestampTzPlusMilliseconds(then, MaxStandbyDelay);
-
- TimestampDifference(now, fin_time,
- &timer_delay_secs, &timer_delay_usecs);
+ max_standby_time = TimestampTzPlusMilliseconds(then, MaxStandbyDelay);
+ Assert(max_standby_time > now);
/*
- * It's possible that the difference is less than a microsecond;
- * ensure we don't cancel, rather than set, the interrupt.
+ * Wake up at MaxStandby delay, and check for deadlocks as well
+ * if we will be waiting longer than deadlock_timeout
*/
- if (timer_delay_secs == 0 && timer_delay_usecs == 0)
- timer_delay_usecs = 1;
-
- if (enable_standby_sig_alarm(timer_delay_secs, timer_delay_usecs, fin_time))
+ if (enable_standby_sig_alarm(now, max_standby_time, false))
sig_alarm_enabled = true;
else
elog(FATAL, "could not set timer for process wakeup");
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/storage/lmgr/proc.c,v 1.218 2010/04/28 16:54:16 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/storage/lmgr/proc.c,v 1.219 2010/05/26 19:52:52 sriggs Exp $
*
*-------------------------------------------------------------------------
*/
/* statement_fin_time is valid only if statement_timeout_active is true */
static TimestampTz statement_fin_time;
+static TimestampTz statement_fin_time2; /* valid only in recovery */
static void RemoveProcFromArray(int code, Datum arg);
* To avoid various edge cases, we must be careful to do nothing
* when there is nothing to be done. We also need to be able to
* reschedule the timer interrupt if called before end of statement.
+ *
+ * We set either deadlock_timeout_active or statement_timeout_active
+ * or both. Interrupts are enabled if standby_timeout_active.
*/
bool
-enable_standby_sig_alarm(long delay_s, int delay_us, TimestampTz fin_time)
+enable_standby_sig_alarm(TimestampTz now, TimestampTz fin_time, bool deadlock_only)
{
- struct itimerval timeval;
-
- Assert(delay_s >= 0 && delay_us >= 0);
+ TimestampTz deadlock_time = TimestampTzPlusMilliseconds(now, DeadlockTimeout);
- statement_fin_time = fin_time;
+ if (deadlock_only)
+ {
+ /*
+ * Wake up at DeadlockTimeout only, then wait forever
+ */
+ statement_fin_time = deadlock_time;
+ deadlock_timeout_active = true;
+ statement_timeout_active = false;
+ }
+ else if (fin_time > deadlock_time)
+ {
+ /*
+ * Wake up at DeadlockTimeout, then again at MaxStandbyDelay
+ */
+ statement_fin_time = deadlock_time;
+ statement_fin_time2 = fin_time;
+ deadlock_timeout_active = true;
+ statement_timeout_active = true;
+ }
+ else
+ {
+ /*
+ * Wake only at MaxStandbyDelay because its fairly soon
+ */
+ statement_fin_time = fin_time;
+ deadlock_timeout_active = false;
+ statement_timeout_active = true;
+ }
- standby_timeout_active = true;
+ if (deadlock_timeout_active || statement_timeout_active)
+ {
+ long secs;
+ int usecs;
+ struct itimerval timeval;
+ TimestampDifference(now, statement_fin_time,
+ &secs, &usecs);
+ if (secs == 0 && usecs == 0)
+ usecs = 1;
+ MemSet(&timeval, 0, sizeof(struct itimerval));
+ timeval.it_value.tv_sec = secs;
+ timeval.it_value.tv_usec = usecs;
+ if (setitimer(ITIMER_REAL, &timeval, NULL))
+ return false;
+ standby_timeout_active = true;
+ }
- MemSet(&timeval, 0, sizeof(struct itimerval));
- timeval.it_value.tv_sec = delay_s;
- timeval.it_value.tv_usec = delay_us;
- if (setitimer(ITIMER_REAL, &timeval, NULL))
- return false;
return true;
}
CheckStandbyTimeout(void)
{
TimestampTz now;
+ bool reschedule = false;
standby_timeout_active = false;
now = GetCurrentTimestamp();
+ /*
+ * Reschedule the timer if its not time to wake yet, or if we
+ * have both timers set and the first one has just been reached.
+ */
if (now >= statement_fin_time)
- SendRecoveryConflictWithBufferPin(PROCSIG_RECOVERY_CONFLICT_BUFFERPIN);
+ {
+ if (deadlock_timeout_active)
+ {
+ /*
+ * We're still waiting when we reach DeadlockTimeout, so send out a request
+ * to have other backends check themselves for deadlock. Then continue
+ * waiting until MaxStandbyDelay.
+ */
+ SendRecoveryConflictWithBufferPin(PROCSIG_RECOVERY_CONFLICT_STARTUP_DEADLOCK);
+ deadlock_timeout_active = false;
+
+ /*
+ * Begin second waiting period to MaxStandbyDelay if required.
+ */
+ if (statement_timeout_active)
+ {
+ reschedule = true;
+ statement_fin_time = statement_fin_time2;
+ }
+ }
+ else
+ {
+ /*
+ * We've now reached MaxStandbyDelay, so ask all conflicts to leave, cos
+ * its time for us to press ahead with applying changes in recovery.
+ */
+ SendRecoveryConflictWithBufferPin(PROCSIG_RECOVERY_CONFLICT_BUFFERPIN);
+ }
+ }
else
+ reschedule = true;
+
+ if (reschedule)
{
- /* Not time yet, so (re)schedule the interrupt */
long secs;
int usecs;
struct itimerval timeval;
-
TimestampDifference(now, statement_fin_time,
&secs, &usecs);
-
- /*
- * It's possible that the difference is less than a microsecond;
- * ensure we don't cancel, rather than set, the interrupt.
- */
if (secs == 0 && usecs == 0)
usecs = 1;
-
- standby_timeout_active = true;
-
MemSet(&timeval, 0, sizeof(struct itimerval));
timeval.it_value.tv_sec = secs;
timeval.it_value.tv_usec = usecs;
if (setitimer(ITIMER_REAL, &timeval, NULL))
return false;
+ standby_timeout_active = true;
}
return true;