提交 f9dbac94 编写于 作者: S Simon Riggs

HS Defer buffer pin deadlock check until deadlock_timeout has expired.

During Hot Standby we need to check for buffer pin deadlocks when the
Startup process begins to wait, in case it never wakes up again. We
previously made the deadlock check immediately on the basis it was
cheap, though clearer thinking and prima facie evidence shows that
was too simple. Refactor existing code to make it easy to add in
deferral of deadlock check until deadlock_timeout allowing a good
reduction in deadlock checks since far few buffer pins are held for
that duration. It's worth doing anyway, though major goal is to
prevent further reports of context switching with high numbers of
users on occasional tests.
上级 5234a952
......@@ -11,7 +11,7 @@
* 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 $
*
*-------------------------------------------------------------------------
*/
......@@ -388,12 +388,15 @@ ResolveRecoveryConflictWithBufferPin(void)
}
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
{
......@@ -410,34 +413,19 @@ ResolveRecoveryConflictWithBufferPin(void)
}
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");
......
......@@ -8,7 +8,7 @@
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
......@@ -85,6 +85,7 @@ static TimestampTz timeout_start_time;
/* 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);
......@@ -1619,23 +1620,61 @@ handle_sig_alarm(SIGNAL_ARGS)
* 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;
}
......@@ -1675,37 +1714,64 @@ static bool
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;
......
......@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $PostgreSQL: pgsql/src/include/storage/proc.h,v 1.121 2010/02/26 02:01:27 momjian Exp $
* $PostgreSQL: pgsql/src/include/storage/proc.h,v 1.122 2010/05/26 19:52:52 sriggs Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -199,7 +199,8 @@ extern bool enable_sig_alarm(int delayms, bool is_statement_timeout);
extern bool disable_sig_alarm(bool is_statement_timeout);
extern void handle_sig_alarm(SIGNAL_ARGS);
extern bool enable_standby_sig_alarm(long delay_s, int delay_us, TimestampTz fin_time);
extern bool enable_standby_sig_alarm(TimestampTz now,
TimestampTz fin_time, bool deadlock_only);
extern bool disable_standby_sig_alarm(void);
extern void handle_standby_sig_alarm(SIGNAL_ARGS);
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册