1
0
mirror of https://github.com/postgres/postgres.git synced 2025-07-28 23:42:10 +03:00

Add functions to wait for backend termination

This adds a function, pg_wait_for_backend_termination(), and a new
timeout argument to pg_terminate_backend(), which will wait for the
backend to actually terminate (with or without signaling it to do so
depending on which function is called). The default behaviour of
pg_terminate_backend() remains being timeout=0 which does not waiting.
For pg_wait_for_backend_termination() the default wait is 5 seconds.

Author: Bharath Rupireddy
Reviewed-By: Fujii Masao, David Johnston, Muhammad Usama,
             Hou Zhijie, Magnus Hagander
Discussion: https://postgr.es/m/CALj2ACUBpunmyhYZw-kXCYs5NM+h6oG_7Df_Tn4mLmmUQifkqA@mail.gmail.com
This commit is contained in:
Magnus Hagander
2021-04-08 11:32:14 +02:00
parent fb310f1781
commit aaf0432572
7 changed files with 174 additions and 7 deletions

View File

@ -1347,6 +1347,16 @@ CREATE OR REPLACE FUNCTION
RETURNS boolean STRICT VOLATILE LANGUAGE INTERNAL AS 'pg_promote'
PARALLEL SAFE;
CREATE OR REPLACE FUNCTION
pg_terminate_backend(pid integer, timeout int8 DEFAULT 0)
RETURNS boolean STRICT VOLATILE LANGUAGE INTERNAL AS 'pg_terminate_backend'
PARALLEL SAFE;
CREATE OR REPLACE FUNCTION
pg_wait_for_backend_termination(pid integer, timeout int8 DEFAULT 5000)
RETURNS boolean STRICT VOLATILE LANGUAGE INTERNAL AS 'pg_wait_for_backend_termination'
PARALLEL SAFE;
-- legacy definition for compatibility with 9.3
CREATE OR REPLACE FUNCTION
json_populate_record(base anyelement, from_json json, use_json_as_text boolean DEFAULT false)

View File

@ -18,6 +18,7 @@
#include "catalog/pg_authid.h"
#include "miscadmin.h"
#include "pgstat.h"
#include "postmaster/syslogger.h"
#include "storage/pmsignal.h"
#include "storage/proc.h"
@ -126,15 +127,90 @@ pg_cancel_backend(PG_FUNCTION_ARGS)
}
/*
* Signal to terminate a backend process. This is allowed if you are a member
* of the role whose process is being terminated.
* Wait until there is no backend process with the given PID and return true.
* On timeout, a warning is emitted and false is returned.
*/
static bool
pg_wait_until_termination(int pid, int64 timeout)
{
/*
* Wait in steps of waittime milliseconds until this function exits or
* timeout.
*/
int64 waittime = 100;
/*
* Initially remaining time is the entire timeout specified by the user.
*/
int64 remainingtime = timeout;
/*
* Check existence of the backend. If the backend still exists, then wait
* for waittime milliseconds, again check for the existence. Repeat this
* until timeout or an error occurs or a pending interrupt such as query
* cancel gets processed.
*/
do
{
if (remainingtime < waittime)
waittime = remainingtime;
if (kill(pid, 0) == -1)
{
if (errno == ESRCH)
return true;
else
ereport(ERROR,
(errcode(ERRCODE_INTERNAL_ERROR),
errmsg("could not check the existence of the backend with PID %d: %m",
pid)));
}
/* Process interrupts, if any, before waiting */
CHECK_FOR_INTERRUPTS();
(void) WaitLatch(MyLatch,
WL_LATCH_SET | WL_TIMEOUT | WL_EXIT_ON_PM_DEATH,
waittime,
WAIT_EVENT_BACKEND_TERMINATION);
ResetLatch(MyLatch);
remainingtime -= waittime;
} while (remainingtime > 0);
ereport(WARNING,
(errmsg("backend with PID %d did not terminate within %lld milliseconds",
pid, (long long int) timeout)));
return false;
}
/*
* Signal to terminate a backend process. This is allowed if you are a member
* of the role whose process is being terminated. If timeout input argument is
* 0 (which is default), then this function just signals the backend and
* doesn't wait. Otherwise it waits until given the timeout milliseconds or no
* process has the given PID and returns true. On timeout, a warning is emitted
* and false is returned.
*
* Note that only superusers can signal superuser-owned processes.
*/
Datum
pg_terminate_backend(PG_FUNCTION_ARGS)
{
int r = pg_signal_backend(PG_GETARG_INT32(0), SIGTERM);
int pid;
int r;
int timeout;
pid = PG_GETARG_INT32(0);
timeout = PG_GETARG_INT64(1);
if (timeout < 0)
ereport(ERROR,
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
errmsg("\"timeout\" must not be negative")));
r = pg_signal_backend(pid, SIGTERM);
if (r == SIGNAL_BACKEND_NOSUPERUSER)
ereport(ERROR,
@ -146,7 +222,47 @@ pg_terminate_backend(PG_FUNCTION_ARGS)
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
errmsg("must be a member of the role whose process is being terminated or member of pg_signal_backend")));
PG_RETURN_BOOL(r == SIGNAL_BACKEND_SUCCESS);
/* Wait only on success and if actually requested */
if (r == SIGNAL_BACKEND_SUCCESS && timeout > 0)
PG_RETURN_BOOL(pg_wait_until_termination(pid, timeout));
else
PG_RETURN_BOOL(r == SIGNAL_BACKEND_SUCCESS);
}
/*
* Wait for a backend process with the given PID to exit or until the given
* timeout milliseconds occurs. Returns true if the backend has exited. On
* timeout a warning is emitted and false is returned.
*
* We allow any user to call this function, consistent with any user being
* able to view the pid of the process in pg_stat_activity etc.
*/
Datum
pg_wait_for_backend_termination(PG_FUNCTION_ARGS)
{
int pid;
int64 timeout;
PGPROC *proc = NULL;
pid = PG_GETARG_INT32(0);
timeout = PG_GETARG_INT64(1);
if (timeout <= 0)
ereport(ERROR,
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
errmsg("\"timeout\" must not be negative or zero")));
proc = BackendPidGetProc(pid);
if (proc == NULL)
{
ereport(WARNING,
(errmsg("PID %d is not a PostgreSQL server process", pid)));
PG_RETURN_BOOL(false);
}
PG_RETURN_BOOL(pg_wait_until_termination(pid, timeout));
}
/*

View File

@ -313,6 +313,9 @@ pgstat_get_wait_ipc(WaitEventIPC w)
case WAIT_EVENT_APPEND_READY:
event_name = "AppendReady";
break;
case WAIT_EVENT_BACKEND_TERMINATION:
event_name = "BackendTermination";
break;
case WAIT_EVENT_BACKUP_WAIT_WAL_ARCHIVE:
event_name = "BackupWaitWalArchive";
break;

View File

@ -6190,9 +6190,14 @@
{ oid => '2171', descr => 'cancel a server process\' current query',
proname => 'pg_cancel_backend', provolatile => 'v', prorettype => 'bool',
proargtypes => 'int4', prosrc => 'pg_cancel_backend' },
{ oid => '2096', descr => 'terminate a server process',
{ oid => '2096', descr => 'terminate a backend process and if timeout is specified, wait for its exit or until timeout occurs',
proname => 'pg_terminate_backend', provolatile => 'v', prorettype => 'bool',
proargtypes => 'int4', prosrc => 'pg_terminate_backend' },
proargtypes => 'int4 int8', proargnames => '{pid,timeout}',
prosrc => 'pg_terminate_backend' },
{ oid => '2137', descr => 'wait for a backend process exit or timeout occurs',
proname => 'pg_wait_for_backend_termination', provolatile => 'v', prorettype => 'bool',
proargtypes => 'int4 int8', proargnames => '{pid,timeout}',
prosrc => 'pg_wait_for_backend_termination' },
{ oid => '2172', descr => 'prepare for taking an online backup',
proname => 'pg_start_backup', provolatile => 'v', proparallel => 'r',
prorettype => 'pg_lsn', proargtypes => 'text bool bool',

View File

@ -80,6 +80,7 @@ typedef enum
typedef enum
{
WAIT_EVENT_APPEND_READY = PG_WAIT_IPC,
WAIT_EVENT_BACKEND_TERMINATION,
WAIT_EVENT_BACKUP_WAIT_WAL_ARCHIVE,
WAIT_EVENT_BGWORKER_SHUTDOWN,
WAIT_EVENT_BGWORKER_STARTUP,