Skip to content

Commit f9ed327

Browse files
committed
Clean up some awkward, inaccurate, and inefficient processing around
MaxStandbyDelay. Use the GUC units mechanism for the value, and choose more appropriate timestamp functions for performing tests with it. Make the ps_activity manipulation in ResolveRecoveryConflictWithVirtualXIDs have behavior similar to ps_activity code elsewhere, notably not updating the display when update_process_title is off and not truncating the display contents at an arbitrarily-chosen length. Improve the docs to be explicit about what MaxStandbyDelay actually measures, viz the difference between primary and standby servers' clocks, and the possible hazards if their clocks aren't in sync.
1 parent 1541632 commit f9ed327

File tree

6 files changed

+78
-94
lines changed

6 files changed

+78
-94
lines changed

doc/src/sgml/config.sgml

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
<!-- $PostgreSQL: pgsql/doc/src/sgml/config.sgml,v 1.272 2010/04/29 21:36:18 tgl Exp $ -->
1+
<!-- $PostgreSQL: pgsql/doc/src/sgml/config.sgml,v 1.273 2010/05/02 02:10:32 tgl Exp $ -->
22

33
<chapter Id="runtime-config">
44
<title>Server Configuration</title>
@@ -1947,13 +1947,12 @@ SET ENABLE_SEQSCAN TO OFF;
19471947
<para>
19481948
When Hot Standby is active, this parameter specifies a wait policy
19491949
for applying WAL entries that conflict with active queries.
1950-
If a conflict should occur the server will delay up to this number
1951-
of seconds before it cancels conflicting queries, as
1950+
If a conflict should occur the server will delay up to this long
1951+
before it cancels conflicting queries, as
19521952
described in <xref linkend="hot-standby-conflict">.
1953-
Typically, this parameter is used only during replication.
1954-
The value is specified in seconds, and -1 causes the standby to wait
1955-
forever for a conflicting query to complete.
19561953
The default is 30 seconds.
1954+
A value of -1 causes the standby to wait forever for a conflicting
1955+
query to complete.
19571956
This parameter can only be set in the <filename>postgresql.conf</>
19581957
file or on the server command line.
19591958
</para>
@@ -1964,7 +1963,7 @@ SET ENABLE_SEQSCAN TO OFF;
19641963
</para>
19651964
<para>
19661965
While it is tempting to believe that <varname>max_standby_delay</>
1967-
is the maximum number of seconds a query can run before
1966+
is the maximum length of time a query can run before
19681967
cancellation is possible, this is not true. When a long-running
19691968
query ends, there is a finite time required to apply backlogged
19701969
WAL logs. If a second long-running query appears before the

doc/src/sgml/high-availability.sgml

Lines changed: 24 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
<!-- $PostgreSQL: pgsql/doc/src/sgml/high-availability.sgml,v 1.65 2010/04/29 21:36:18 tgl Exp $ -->
1+
<!-- $PostgreSQL: pgsql/doc/src/sgml/high-availability.sgml,v 1.66 2010/05/02 02:10:32 tgl Exp $ -->
22

33
<chapter id="high-availability">
44
<title>High Availability, Load Balancing, and Replication</title>
@@ -1480,7 +1480,8 @@ if (!triggered)
14801480
There are a number of choices for resolving query conflicts. The default
14811481
is to wait and hope the query finishes. The server will wait
14821482
automatically until the lag between primary and standby is at most
1483-
<varname>max_standby_delay</> seconds. Once that grace period expires,
1483+
<xref linkend="guc-max-standby-delay"> (30 seconds by default).
1484+
Once that grace period expires,
14841485
one of the following actions is taken:
14851486

14861487
<itemizedlist>
@@ -1514,15 +1515,28 @@ if (!triggered)
15141515
</para>
15151516

15161517
<para>
1517-
<varname>max_standby_delay</> is set in <filename>postgresql.conf</>.
1518-
The parameter applies to the server as a whole, so if the delay is consumed
1519-
by a single query then there may be little or no waiting for queries that
1520-
follow, though they will have benefited equally from the initial
1521-
waiting period. The server may take time to catch up again before the grace
1522-
period is available again, though if there is a heavy and constant stream
1523-
of conflicts it may seldom catch up fully.
1518+
Keep in mind that <varname>max_standby_delay</> is compared to the
1519+
difference between the standby server's clock and the transaction
1520+
commit timestamps read from the WAL log. Thus, the grace period
1521+
allowed to any one query on the standby is never more than
1522+
<varname>max_standby_delay</>, and could be considerably less if the
1523+
standby has already fallen behind as a result of waiting for previous
1524+
queries to complete, or as a result of being unable to keep up with a
1525+
heavy update load.
15241526
</para>
15251527

1528+
<caution>
1529+
<para>
1530+
Be sure that the primary and standby servers' clocks are kept in sync;
1531+
otherwise the values compared to <varname>max_standby_delay</> will be
1532+
erroneous, possibly leading to undesirable query cancellations.
1533+
If the clocks are intentionally not in sync, or if there is a large
1534+
propagation delay from primary to standby, it is advisable to set
1535+
<varname>max_standby_delay</> to -1. In any case the value should be
1536+
larger than the largest expected clock skew between primary and standby.
1537+
</para>
1538+
</caution>
1539+
15261540
<para>
15271541
Users should be clear that tables that are regularly and heavily updated on the
15281542
primary server will quickly cause cancellation of longer running queries on
@@ -1656,7 +1670,7 @@ LOG: database system is ready to accept read only connections
16561670
<varname>max_standby_delay</> or even set it to zero, though that is a
16571671
very aggressive setting. If the standby server is tasked as an additional
16581672
server for decision support queries then it might be acceptable to set this
1659-
to a value of many hours (in seconds). It is also possible to set
1673+
to a value of many hours. It is also possible to set
16601674
<varname>max_standby_delay</> to -1 which means wait forever for queries
16611675
to complete; this will be useful when performing
16621676
an archive recovery from a backup.

src/backend/access/transam/xlog.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
* Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group
88
* Portions Copyright (c) 1994, Regents of the University of California
99
*
10-
* $PostgreSQL: pgsql/src/backend/access/transam/xlog.c,v 1.407 2010/04/29 21:49:03 tgl Exp $
10+
* $PostgreSQL: pgsql/src/backend/access/transam/xlog.c,v 1.408 2010/05/02 02:10:33 tgl Exp $
1111
*
1212
*-------------------------------------------------------------------------
1313
*/
@@ -72,7 +72,7 @@ int XLogArchiveTimeout = 0;
7272
bool XLogArchiveMode = false;
7373
char *XLogArchiveCommand = NULL;
7474
bool EnableHotStandby = false;
75-
int MaxStandbyDelay = 30;
75+
int MaxStandbyDelay = 30 * 1000;
7676
bool fullPageWrites = true;
7777
bool log_checkpoints = false;
7878
int sync_method = DEFAULT_SYNC_METHOD;

src/backend/storage/ipc/standby.c

Lines changed: 41 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
* Portions Copyright (c) 1994, Regents of the University of California
1212
*
1313
* IDENTIFICATION
14-
* $PostgreSQL: pgsql/src/backend/storage/ipc/standby.c,v 1.20 2010/04/28 16:10:42 heikki Exp $
14+
* $PostgreSQL: pgsql/src/backend/storage/ipc/standby.c,v 1.21 2010/05/02 02:10:33 tgl Exp $
1515
*
1616
*-------------------------------------------------------------------------
1717
*/
@@ -124,31 +124,24 @@ static int standbyWait_us = STANDBY_INITIAL_WAIT_US;
124124
static bool
125125
WaitExceedsMaxStandbyDelay(void)
126126
{
127-
long delay_secs;
128-
int delay_usecs;
129-
130-
if (MaxStandbyDelay == -1)
131-
return false;
132-
133127
/* Are we past max_standby_delay? */
134-
TimestampDifference(GetLatestXLogTime(), GetCurrentTimestamp(),
135-
&delay_secs, &delay_usecs);
136-
if (delay_secs > MaxStandbyDelay)
128+
if (MaxStandbyDelay >= 0 &&
129+
TimestampDifferenceExceeds(GetLatestXLogTime(), GetCurrentTimestamp(),
130+
MaxStandbyDelay))
137131
return true;
138132

139133
/*
140-
* Sleep, then do bookkeeping.
134+
* Sleep a bit (this is essential to avoid busy-waiting).
141135
*/
142136
pg_usleep(standbyWait_us);
143137

144138
/*
145-
* Progressively increase the sleep times.
139+
* Progressively increase the sleep times, but not to more than 1s,
140+
* since pg_usleep isn't interruptable on some platforms.
146141
*/
147142
standbyWait_us *= 2;
148143
if (standbyWait_us > 1000000)
149144
standbyWait_us = 1000000;
150-
if (standbyWait_us > MaxStandbyDelay * 1000000 / 4)
151-
standbyWait_us = MaxStandbyDelay * 1000000 / 4;
152145

153146
return false;
154147
}
@@ -163,50 +156,41 @@ static void
163156
ResolveRecoveryConflictWithVirtualXIDs(VirtualTransactionId *waitlist,
164157
ProcSignalReason reason)
165158
{
166-
char waitactivitymsg[100];
167-
char oldactivitymsg[101];
168-
169159
while (VirtualTransactionIdIsValid(*waitlist))
170160
{
171-
long wait_s;
172-
int wait_us; /* wait in microseconds (us) */
173161
TimestampTz waitStart;
174-
bool logged;
162+
char *new_status;
163+
164+
pgstat_report_waiting(true);
175165

176166
waitStart = GetCurrentTimestamp();
167+
new_status = NULL; /* we haven't changed the ps display */
168+
169+
/* reset standbyWait_us for each xact we wait for */
177170
standbyWait_us = STANDBY_INITIAL_WAIT_US;
178-
logged = false;
179171

180172
/* wait until the virtual xid is gone */
181173
while (!ConditionalVirtualXactLockTableWait(*waitlist))
182174
{
183175
/*
184-
* Report if we have been waiting for a while now...
176+
* Report via ps if we have been waiting for more than 500 msec
177+
* (should that be configurable?)
185178
*/
186-
TimestampTz now = GetCurrentTimestamp();
187-
188-
TimestampDifference(waitStart, now, &wait_s, &wait_us);
189-
if (!logged && (wait_s > 0 || wait_us > 500000))
179+
if (update_process_title && new_status == NULL &&
180+
TimestampDifferenceExceeds(waitStart, GetCurrentTimestamp(),
181+
500))
190182
{
191-
const char *oldactivitymsgp;
183+
const char *old_status;
192184
int len;
193185

194-
oldactivitymsgp = get_ps_display(&len);
195-
196-
if (len > 100)
197-
len = 100;
198-
199-
memcpy(oldactivitymsg, oldactivitymsgp, len);
200-
oldactivitymsg[len] = 0;
201-
202-
snprintf(waitactivitymsg, sizeof(waitactivitymsg),
203-
"waiting for max_standby_delay (%u s)",
186+
old_status = get_ps_display(&len);
187+
new_status = (char *) palloc(len + 50);
188+
memcpy(new_status, old_status, len);
189+
snprintf(new_status + len, 50,
190+
" waiting for max_standby_delay (%d ms)",
204191
MaxStandbyDelay);
205-
set_ps_display(waitactivitymsg, false);
206-
207-
pgstat_report_waiting(true);
208-
209-
logged = true;
192+
set_ps_display(new_status, false);
193+
new_status[len] = '\0'; /* truncate off " waiting" */
210194
}
211195

212196
/* Is it time to kill it? */
@@ -225,16 +209,17 @@ ResolveRecoveryConflictWithVirtualXIDs(VirtualTransactionId *waitlist,
225209
* unresponsive backend when system is heavily loaded.
226210
*/
227211
if (pid != 0)
228-
pg_usleep(5000);
212+
pg_usleep(5000L);
229213
}
230214
}
231215

232-
/* Reset ps display */
233-
if (logged)
216+
/* Reset ps display if we changed it */
217+
if (new_status)
234218
{
235-
set_ps_display(oldactivitymsg, false);
236-
pgstat_report_waiting(false);
219+
set_ps_display(new_status, false);
220+
pfree(new_status);
237221
}
222+
pgstat_report_waiting(false);
238223

239224
/* The virtual transaction is gone now, wait for the next one */
240225
waitlist++;
@@ -401,7 +386,7 @@ ResolveRecoveryConflictWithBufferPin(void)
401386
*/
402387
SendRecoveryConflictWithBufferPin(PROCSIG_RECOVERY_CONFLICT_BUFFERPIN);
403388
}
404-
else if (MaxStandbyDelay == -1)
389+
else if (MaxStandbyDelay < 0)
405390
{
406391
/*
407392
* Send out a request to check for buffer pin deadlocks before we
@@ -412,17 +397,11 @@ ResolveRecoveryConflictWithBufferPin(void)
412397
}
413398
else
414399
{
415-
TimestampTz now;
416-
long standby_delay_secs; /* How far Startup process is lagging */
417-
int standby_delay_usecs;
418-
419-
now = GetCurrentTimestamp();
400+
TimestampTz then = GetLatestXLogTime();
401+
TimestampTz now = GetCurrentTimestamp();
420402

421403
/* Are we past max_standby_delay? */
422-
TimestampDifference(GetLatestXLogTime(), now,
423-
&standby_delay_secs, &standby_delay_usecs);
424-
425-
if (standby_delay_secs >= MaxStandbyDelay)
404+
if (TimestampDifferenceExceeds(then, now, MaxStandbyDelay))
426405
{
427406
/*
428407
* We're already behind, so clear a path as quickly as possible.
@@ -434,7 +413,7 @@ ResolveRecoveryConflictWithBufferPin(void)
434413
TimestampTz fin_time; /* Expected wake-up time by timer */
435414
long timer_delay_secs; /* Amount of time we set timer
436415
* for */
437-
int timer_delay_usecs = 0;
416+
int timer_delay_usecs;
438417

439418
/*
440419
* Send out a request to check for buffer pin deadlocks before we
@@ -446,12 +425,10 @@ ResolveRecoveryConflictWithBufferPin(void)
446425
/*
447426
* How much longer we should wait?
448427
*/
449-
timer_delay_secs = MaxStandbyDelay - standby_delay_secs;
450-
if (standby_delay_usecs > 0)
451-
{
452-
timer_delay_secs -= 1;
453-
timer_delay_usecs = 1000000 - standby_delay_usecs;
454-
}
428+
fin_time = TimestampTzPlusMilliseconds(then, MaxStandbyDelay);
429+
430+
TimestampDifference(now, fin_time,
431+
&timer_delay_secs, &timer_delay_usecs);
455432

456433
/*
457434
* It's possible that the difference is less than a microsecond;
@@ -460,13 +437,6 @@ ResolveRecoveryConflictWithBufferPin(void)
460437
if (timer_delay_secs == 0 && timer_delay_usecs == 0)
461438
timer_delay_usecs = 1;
462439

463-
/*
464-
* When is the finish time? We recheck this if we are woken early.
465-
*/
466-
fin_time = TimestampTzPlusMilliseconds(now,
467-
(timer_delay_secs * 1000) +
468-
(timer_delay_usecs / 1000));
469-
470440
if (enable_standby_sig_alarm(timer_delay_secs, timer_delay_usecs, fin_time))
471441
sig_alarm_enabled = true;
472442
else

src/backend/utils/misc/guc.c

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
* Written by Peter Eisentraut <peter_e@gmx.net>.
1111
*
1212
* IDENTIFICATION
13-
* $PostgreSQL: pgsql/src/backend/utils/misc/guc.c,v 1.553 2010/04/29 21:36:19 tgl Exp $
13+
* $PostgreSQL: pgsql/src/backend/utils/misc/guc.c,v 1.554 2010/05/02 02:10:33 tgl Exp $
1414
*
1515
*--------------------------------------------------------------------
1616
*/
@@ -1386,10 +1386,11 @@ static struct config_int ConfigureNamesInt[] =
13861386
{
13871387
{"max_standby_delay", PGC_SIGHUP, WAL_SETTINGS,
13881388
gettext_noop("Sets the maximum delay to avoid conflict processing on hot standby servers."),
1389-
NULL
1389+
NULL,
1390+
GUC_UNIT_MS
13901391
},
13911392
&MaxStandbyDelay,
1392-
30, -1, INT_MAX, NULL, NULL
1393+
30 * 1000, -1, INT_MAX / 1000, NULL, NULL
13931394
},
13941395

13951396
{

src/backend/utils/misc/postgresql.conf.sample

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -186,7 +186,7 @@
186186
# - Hot Standby -
187187

188188
#hot_standby = off # allows queries during recovery
189-
#max_standby_delay = 30s # max acceptable lag (s) to allow queries to
189+
#max_standby_delay = 30s # max acceptable lag to allow queries to
190190
# complete without conflict; -1 means forever
191191
#vacuum_defer_cleanup_age = 0 # num transactions by which cleanup is deferred
192192

0 commit comments

Comments
 (0)