1
0
mirror of https://github.com/postgres/postgres.git synced 2025-08-25 20:23:07 +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

@@ -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));
}
/*