Skip to content

Commit a169503

Browse files
committed
Make checkpoint requests more robust.
Commit 6f6a6d8 introduced a delay of up to 2 seconds if we're trying to request a checkpoint but the checkpointer hasn't started yet (or, much less likely, our kill() call fails). However buildfarm experience shows that that's not quite enough for slow or heavily-loaded machines. There's no good reason to assume that the checkpointer won't start eventually, so we may as well make the timeout much longer, say 60 sec. However, if the caller didn't say CHECKPOINT_WAIT, it seems like a bad idea to be waiting at all, much less for as long as 60 sec. We can remove the need for that, and make this whole thing more robust, by adjusting the code so that the existence of a pending checkpoint request is clear from the contents of shared memory, and making sure that the checkpointer process will notice it at startup even if it did not get a signal. In this way there's no need for a non-CHECKPOINT_WAIT call to wait at all; if it can't send the signal, it can nonetheless assume that the checkpointer will eventually service the request. A potential downside of this change is that "kill -INT" on the checkpointer process is no longer enough to trigger a checkpoint, should anyone be relying on something so hacky. But there's no obvious reason to do it like that rather than issuing a plain old CHECKPOINT command, so we'll assume that nobody is. There doesn't seem to be a way to preserve this undocumented quasi-feature without introducing race conditions. Since a principal reason for messing with this is to prevent intermittent buildfarm failures, back-patch to all supported branches. Discussion: https://postgr.es/m/27830.1552752475@sss.pgh.pa.us
1 parent 98f8ffa commit a169503

File tree

2 files changed

+35
-25
lines changed

2 files changed

+35
-25
lines changed

src/backend/postmaster/checkpointer.c

Lines changed: 33 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -149,7 +149,6 @@ double CheckPointCompletionTarget = 0.5;
149149
* Flags set by interrupt handlers for later service in the main loop.
150150
*/
151151
static volatile sig_atomic_t got_SIGHUP = false;
152-
static volatile sig_atomic_t checkpoint_requested = false;
153152
static volatile sig_atomic_t shutdown_requested = false;
154153

155154
/*
@@ -396,12 +395,6 @@ CheckpointerMain(void)
396395
*/
397396
UpdateSharedMemoryConfig();
398397
}
399-
if (checkpoint_requested)
400-
{
401-
checkpoint_requested = false;
402-
do_checkpoint = true;
403-
BgWriterStats.m_requested_checkpoints++;
404-
}
405398
if (shutdown_requested)
406399
{
407400
/*
@@ -415,6 +408,17 @@ CheckpointerMain(void)
415408
proc_exit(0); /* done */
416409
}
417410

411+
/*
412+
* Detect a pending checkpoint request by checking whether the flags
413+
* word in shared memory is nonzero. We shouldn't need to acquire the
414+
* ckpt_lck for this.
415+
*/
416+
if (((volatile CheckpointerShmemStruct *) CheckpointerShmem)->ckpt_flags)
417+
{
418+
do_checkpoint = true;
419+
BgWriterStats.m_requested_checkpoints++;
420+
}
421+
418422
/*
419423
* Force a checkpoint if too much time has elapsed since the last one.
420424
* Note that we count a timed checkpoint in stats only when this
@@ -646,17 +650,14 @@ CheckArchiveTimeout(void)
646650
static bool
647651
ImmediateCheckpointRequested(void)
648652
{
649-
if (checkpoint_requested)
650-
{
651-
volatile CheckpointerShmemStruct *cps = CheckpointerShmem;
653+
volatile CheckpointerShmemStruct *cps = CheckpointerShmem;
652654

653-
/*
654-
* We don't need to acquire the ckpt_lck in this case because we're
655-
* only looking at a single flag bit.
656-
*/
657-
if (cps->ckpt_flags & CHECKPOINT_IMMEDIATE)
658-
return true;
659-
}
655+
/*
656+
* We don't need to acquire the ckpt_lck in this case because we're only
657+
* looking at a single flag bit.
658+
*/
659+
if (cps->ckpt_flags & CHECKPOINT_IMMEDIATE)
660+
return true;
660661
return false;
661662
}
662663

@@ -849,7 +850,10 @@ ReqCheckpointHandler(SIGNAL_ARGS)
849850
{
850851
int save_errno = errno;
851852

852-
checkpoint_requested = true;
853+
/*
854+
* The signalling process should have set ckpt_flags nonzero, so all we
855+
* need do is ensure that our main loop gets kicked out of any wait.
856+
*/
853857
if (MyProc)
854858
SetLatch(&MyProc->procLatch);
855859

@@ -992,31 +996,35 @@ RequestCheckpoint(int flags)
992996

993997
old_failed = cps->ckpt_failed;
994998
old_started = cps->ckpt_started;
995-
cps->ckpt_flags |= flags;
999+
cps->ckpt_flags |= (flags | CHECKPOINT_REQUESTED);
9961000

9971001
SpinLockRelease(&cps->ckpt_lck);
9981002

9991003
/*
10001004
* Send signal to request checkpoint. It's possible that the checkpointer
10011005
* hasn't started yet, or is in process of restarting, so we will retry a
1002-
* few times if needed. Also, if not told to wait for the checkpoint to
1003-
* occur, we consider failure to send the signal to be nonfatal and merely
1004-
* LOG it.
1006+
* few times if needed. (Actually, more than a few times, since on slow
1007+
* or overloaded buildfarm machines, it's been observed that the
1008+
* checkpointer can take several seconds to start.) However, if not told
1009+
* to wait for the checkpoint to occur, we consider failure to send the
1010+
* signal to be nonfatal and merely LOG it. The checkpointer should see
1011+
* the request when it does start, with or without getting a signal.
10051012
*/
1013+
#define MAX_SIGNAL_TRIES 600 /* max wait 60.0 sec */
10061014
for (ntries = 0;; ntries++)
10071015
{
10081016
if (CheckpointerShmem->checkpointer_pid == 0)
10091017
{
1010-
if (ntries >= 20) /* max wait 2.0 sec */
1018+
if (ntries >= MAX_SIGNAL_TRIES || !(flags & CHECKPOINT_WAIT))
10111019
{
10121020
elog((flags & CHECKPOINT_WAIT) ? ERROR : LOG,
1013-
"could not request checkpoint because checkpointer not running");
1021+
"could not signal for checkpoint: checkpointer is not running");
10141022
break;
10151023
}
10161024
}
10171025
else if (kill(CheckpointerShmem->checkpointer_pid, SIGINT) != 0)
10181026
{
1019-
if (ntries >= 20) /* max wait 2.0 sec */
1027+
if (ntries >= MAX_SIGNAL_TRIES || !(flags & CHECKPOINT_WAIT))
10201028
{
10211029
elog((flags & CHECKPOINT_WAIT) ? ERROR : LOG,
10221030
"could not signal for checkpoint: %m");

src/include/access/xlog.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -253,6 +253,8 @@ extern bool XLOG_DEBUG;
253253
#define CHECKPOINT_CAUSE_TIME 0x0040 /* Elapsed time */
254254
#define CHECKPOINT_FLUSH_ALL 0x0080 /* Flush all pages, including those
255255
* belonging to unlogged tables */
256+
/* We set this to ensure that ckpt_flags is not 0 if a request has been made */
257+
#define CHECKPOINT_REQUESTED 0x0100 /* Checkpoint request has been made */
256258

257259
/* Checkpoint statistics */
258260
typedef struct CheckpointStatsData

0 commit comments

Comments
 (0)