</listitem>
</varlistentry>
+ <varlistentry id="guc-send-abort-for-crash" xreflabel="send_abort_for_crash">
+ <term><varname>send_abort_for_crash</varname> (<type>boolean</type>)
+ <indexterm>
+ <primary><varname>send_abort_for_crash</varname> configuration parameter</primary>
+ </indexterm>
+ </term>
+ <listitem>
+ <para>
+ By default, after a backend crash the postmaster will stop remaining
+ child processes by sending them <systemitem>SIGQUIT</systemitem>
+ signals, which permits them to exit more-or-less gracefully. When
+ this option is set to <literal>on</literal>,
+ <systemitem>SIGABRT</systemitem> is sent instead. That normally
+ results in production of a core dump file for each such child
+ process.
+ This can be handy for investigating the states of other processes
+ after a crash. It can also consume lots of disk space in the event
+ of repeated crashes, so do not enable this on systems you are not
+ monitoring carefully.
+ Beware that no support exists for cleaning up the core file(s)
+ automatically.
+ This parameter can only be set in
+ the <filename>postgresql.conf</filename> file or on the server
+ command line.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry id="guc-send-abort-for-kill" xreflabel="send_abort_for_kill">
+ <term><varname>send_abort_for_kill</varname> (<type>boolean</type>)
+ <indexterm>
+ <primary><varname>send_abort_for_kill</varname> configuration parameter</primary>
+ </indexterm>
+ </term>
+ <listitem>
+ <para>
+ By default, after attempting to stop a child process with
+ <systemitem>SIGQUIT</systemitem>, the postmaster will wait five
+ seconds and then send <systemitem>SIGKILL</systemitem> to force
+ immediate termination. When this option is set
+ to <literal>on</literal>, <systemitem>SIGABRT</systemitem> is sent
+ instead of <systemitem>SIGKILL</systemitem>. That normally results
+ in production of a core dump file for each such child process.
+ This can be handy for investigating the states
+ of <quote>stuck</quote> child processes. It can also consume lots
+ of disk space in the event of repeated crashes, so do not enable
+ this on systems you are not monitoring carefully.
+ Beware that no support exists for cleaning up the core file(s)
+ automatically.
+ This parameter can only be set in
+ the <filename>postgresql.conf</filename> file or on the server
+ command line.
+ </para>
+ </listitem>
+ </varlistentry>
+
</variablelist>
</sect1>
<sect1 id="runtime-config-short">
#define MAXLISTEN 64
static pgsocket ListenSocket[MAXLISTEN];
-/*
- * These globals control the behavior of the postmaster in case some
- * backend dumps core. Normally, it kills all peers of the dead backend
- * and reinitializes shared memory. By specifying -s or -n, we can have
- * the postmaster stop (rather than kill) peers and not reinitialize
- * shared data structures. (Reinit is currently dead code, though.)
- */
-static bool Reinit = true;
-static int SendStop = false;
-
/* still more option variables */
bool EnableSSL = false;
char *bonjour_name;
bool restart_after_crash = true;
bool remove_temp_files_after_crash = true;
+bool send_abort_for_crash = false;
+bool send_abort_for_kill = false;
/* PIDs of special child processes; 0 when not running */
static pid_t StartupPID = 0,
static CAC_state canAcceptConnections(int backend_type);
static bool RandomCancelKey(int32 *cancel_key);
static void signal_child(pid_t pid, int signal);
+static void sigquit_child(pid_t pid);
static bool SignalSomeChildren(int signal, int target);
static void TerminateChildren(int signal);
* tcop/postgres.c (the option sets should not conflict) and with the
* common help() function in main/main.c.
*/
- while ((opt = getopt(argc, argv, "B:bc:C:D:d:EeFf:h:ijk:lN:nOPp:r:S:sTt:W:-:")) != -1)
+ while ((opt = getopt(argc, argv, "B:bc:C:D:d:EeFf:h:ijk:lN:OPp:r:S:sTt:W:-:")) != -1)
{
switch (opt)
{
SetConfigOption("max_connections", optarg, PGC_POSTMASTER, PGC_S_ARGV);
break;
- case 'n':
- /* Don't reinit shared mem after abnormal exit */
- Reinit = false;
- break;
-
case 'O':
SetConfigOption("allow_system_table_mods", "true", PGC_POSTMASTER, PGC_S_ARGV);
break;
case 'T':
/*
- * In the event that some backend dumps core, send SIGSTOP,
- * rather than SIGQUIT, to all its peers. This lets the wily
- * post_hacker collect core dumps from everyone.
+ * This option used to be defined as sending SIGSTOP after a
+ * backend crash, but sending SIGABRT seems more useful.
*/
- SendStop = true;
+ SetConfigOption("send_abort_for_crash", "true", PGC_POSTMASTER, PGC_S_ARGV);
break;
case 't':
/*
* If we already sent SIGQUIT to children and they are slow to shut
- * down, it's time to send them SIGKILL. This doesn't happen
- * normally, but under certain conditions backends can get stuck while
- * shutting down. This is a last measure to get them unwedged.
+ * down, it's time to send them SIGKILL (or SIGABRT if requested).
+ * This doesn't happen normally, but under certain conditions backends
+ * can get stuck while shutting down. This is a last measure to get
+ * them unwedged.
*
* Note we also do this during recovery from a process crash.
*/
- if ((Shutdown >= ImmediateShutdown || (FatalError && !SendStop)) &&
+ if ((Shutdown >= ImmediateShutdown || FatalError) &&
AbortStartTime != 0 &&
(now - AbortStartTime) >= SIGKILL_CHILDREN_AFTER_SECS)
{
/* We were gentle with them before. Not anymore */
ereport(LOG,
- (errmsg("issuing SIGKILL to recalcitrant children")));
- TerminateChildren(SIGKILL);
+ /* translator: %s is SIGKILL or SIGABRT */
+ (errmsg("issuing %s to recalcitrant children",
+ send_abort_for_kill ? "SIGABRT" : "SIGKILL")));
+ TerminateChildren(send_abort_for_kill ? SIGABRT : SIGKILL);
/* reset flag so we don't SIGKILL again */
AbortStartTime = 0;
}
#endif
/* tell children to shut down ASAP */
+ /* (note we don't apply send_abort_for_crash here) */
SetQuitSignalReason(PMQUIT_FOR_STOP);
TerminateChildren(SIGQUIT);
pmState = PM_WAIT_BACKENDS;
/*
* This worker is still alive. Unless we did so already, tell it
* to commit hara-kiri.
- *
- * SIGQUIT is the special signal that says exit without proc_exit
- * and let the user know what's going on. But if SendStop is set
- * (-T on command line), then we send SIGSTOP instead, so that we
- * can get core dumps from all backends by hand.
*/
if (take_action)
- {
- ereport(DEBUG2,
- (errmsg_internal("sending %s to process %d",
- (SendStop ? "SIGSTOP" : "SIGQUIT"),
- (int) rw->rw_pid)));
- signal_child(rw->rw_pid, (SendStop ? SIGSTOP : SIGQUIT));
- }
+ sigquit_child(rw->rw_pid);
}
}
* This backend is still alive. Unless we did so already, tell it
* to commit hara-kiri.
*
- * SIGQUIT is the special signal that says exit without proc_exit
- * and let the user know what's going on. But if SendStop is set
- * (-T on command line), then we send SIGSTOP instead, so that we
- * can get core dumps from all backends by hand.
- *
- * We could exclude dead_end children here, but at least in the
- * SIGSTOP case it seems better to include them.
+ * We could exclude dead_end children here, but at least when
+ * sending SIGABRT it seems better to include them.
*
* Background workers were already processed above; ignore them
* here.
continue;
if (take_action)
- {
- ereport(DEBUG2,
- (errmsg_internal("sending %s to process %d",
- (SendStop ? "SIGSTOP" : "SIGQUIT"),
- (int) bp->pid)));
- signal_child(bp->pid, (SendStop ? SIGSTOP : SIGQUIT));
- }
+ sigquit_child(bp->pid);
}
}
}
else if (StartupPID != 0 && take_action)
{
- ereport(DEBUG2,
- (errmsg_internal("sending %s to process %d",
- (SendStop ? "SIGSTOP" : "SIGQUIT"),
- (int) StartupPID)));
- signal_child(StartupPID, (SendStop ? SIGSTOP : SIGQUIT));
+ sigquit_child(StartupPID);
StartupStatus = STARTUP_SIGNALED;
}
if (pid == BgWriterPID)
BgWriterPID = 0;
else if (BgWriterPID != 0 && take_action)
- {
- ereport(DEBUG2,
- (errmsg_internal("sending %s to process %d",
- (SendStop ? "SIGSTOP" : "SIGQUIT"),
- (int) BgWriterPID)));
- signal_child(BgWriterPID, (SendStop ? SIGSTOP : SIGQUIT));
- }
+ sigquit_child(BgWriterPID);
/* Take care of the checkpointer too */
if (pid == CheckpointerPID)
CheckpointerPID = 0;
else if (CheckpointerPID != 0 && take_action)
- {
- ereport(DEBUG2,
- (errmsg_internal("sending %s to process %d",
- (SendStop ? "SIGSTOP" : "SIGQUIT"),
- (int) CheckpointerPID)));
- signal_child(CheckpointerPID, (SendStop ? SIGSTOP : SIGQUIT));
- }
+ sigquit_child(CheckpointerPID);
/* Take care of the walwriter too */
if (pid == WalWriterPID)
WalWriterPID = 0;
else if (WalWriterPID != 0 && take_action)
- {
- ereport(DEBUG2,
- (errmsg_internal("sending %s to process %d",
- (SendStop ? "SIGSTOP" : "SIGQUIT"),
- (int) WalWriterPID)));
- signal_child(WalWriterPID, (SendStop ? SIGSTOP : SIGQUIT));
- }
+ sigquit_child(WalWriterPID);
/* Take care of the walreceiver too */
if (pid == WalReceiverPID)
WalReceiverPID = 0;
else if (WalReceiverPID != 0 && take_action)
- {
- ereport(DEBUG2,
- (errmsg_internal("sending %s to process %d",
- (SendStop ? "SIGSTOP" : "SIGQUIT"),
- (int) WalReceiverPID)));
- signal_child(WalReceiverPID, (SendStop ? SIGSTOP : SIGQUIT));
- }
+ sigquit_child(WalReceiverPID);
/* Take care of the autovacuum launcher too */
if (pid == AutoVacPID)
AutoVacPID = 0;
else if (AutoVacPID != 0 && take_action)
- {
- ereport(DEBUG2,
- (errmsg_internal("sending %s to process %d",
- (SendStop ? "SIGSTOP" : "SIGQUIT"),
- (int) AutoVacPID)));
- signal_child(AutoVacPID, (SendStop ? SIGSTOP : SIGQUIT));
- }
+ sigquit_child(AutoVacPID);
/* Take care of the archiver too */
if (pid == PgArchPID)
PgArchPID = 0;
else if (PgArchPID != 0 && take_action)
- {
- ereport(DEBUG2,
- (errmsg_internal("sending %s to process %d",
- (SendStop ? "SIGSTOP" : "SIGQUIT"),
- (int) PgArchPID)));
- signal_child(PgArchPID, (SendStop ? SIGSTOP : SIGQUIT));
- }
+ sigquit_child(PgArchPID);
/* We do NOT restart the syslogger */
* Any required cleanup will happen at next restart. We
* set FatalError so that an "abnormal shutdown" message
* gets logged when we exit.
+ *
+ * We don't consult send_abort_for_crash here, as it's
+ * unlikely that dumping cores would illuminate the reason
+ * for checkpointer fork failure.
*/
FatalError = true;
pmState = PM_WAIT_DEAD_END;
case SIGINT:
case SIGTERM:
case SIGQUIT:
- case SIGSTOP:
case SIGKILL:
+ case SIGABRT:
if (kill(-pid, signal) < 0)
elog(DEBUG3, "kill(%ld,%d) failed: %m", (long) (-pid), signal);
break;
#endif
}
+/*
+ * Convenience function for killing a child process after a crash of some
+ * other child process. We log the action at a higher level than we would
+ * otherwise do, and we apply send_abort_for_crash to decide which signal
+ * to send. Normally it's SIGQUIT -- and most other comments in this file
+ * are written on the assumption that it is -- but developers might prefer
+ * to use SIGABRT to collect per-child core dumps.
+ */
+static void
+sigquit_child(pid_t pid)
+{
+ ereport(DEBUG2,
+ (errmsg_internal("sending %s to process %d",
+ (send_abort_for_crash ? "SIGABRT" : "SIGQUIT"),
+ (int) pid)));
+ signal_child(pid, (send_abort_for_crash ? SIGABRT : SIGQUIT));
+}
+
/*
* Send a signal to the targeted children (but NOT special children;
* dead_end children are never signaled, either).
if (StartupPID != 0)
{
signal_child(StartupPID, signal);
- if (signal == SIGQUIT || signal == SIGKILL)
+ if (signal == SIGQUIT || signal == SIGKILL || signal == SIGABRT)
StartupStatus = STARTUP_SIGNALED;
}
if (BgWriterPID != 0)