mirror of
https://github.com/postgres/postgres.git
synced 2025-05-05 09:19:17 +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:
parent
fb310f1781
commit
aaf0432572
@ -24977,7 +24977,7 @@ SELECT collation for ('foo' COLLATE "de_DE");
|
||||
<indexterm>
|
||||
<primary>pg_terminate_backend</primary>
|
||||
</indexterm>
|
||||
<function>pg_terminate_backend</function> ( <parameter>pid</parameter> <type>integer</type> )
|
||||
<function>pg_terminate_backend</function> ( <parameter>pid</parameter> <type>integer</type>, <parameter>timeout</parameter> <type>bigint</type> <literal>DEFAULT</literal> <literal>0</literal> )
|
||||
<returnvalue>boolean</returnvalue>
|
||||
</para>
|
||||
<para>
|
||||
@ -24986,6 +24986,34 @@ SELECT collation for ('foo' COLLATE "de_DE");
|
||||
is a member of the role whose backend is being terminated or the
|
||||
calling role has been granted <literal>pg_signal_backend</literal>,
|
||||
however only superusers can terminate superuser backends.
|
||||
</para>
|
||||
<para>
|
||||
If <parameter>timeout</parameter> is not specified or zero, this
|
||||
function returns <literal>true</literal> whether the process actually
|
||||
terminates or not, indicating only that the sending of the signal was
|
||||
successful. If the <parameter>timeout</parameter> is specified (in
|
||||
milliseconds) and greater than zero, the function waits until the
|
||||
process is actually terminated or until the given time has passed. If
|
||||
the process is terminated, the function
|
||||
returns <literal>true</literal>. On timeout a warning is emitted and
|
||||
<literal>false</literal> is returned.
|
||||
</para></entry>
|
||||
</row>
|
||||
|
||||
<row>
|
||||
<entry role="func_table_entry"><para role="func_signature">
|
||||
<indexterm>
|
||||
<primary>pg_wait_for_backend_termination</primary>
|
||||
</indexterm>
|
||||
<function>pg_wait_for_backend_termination</function> ( <parameter>pid</parameter> <type>integer</type>, <parameter>timeout</parameter> <type>bigint</type> <literal>DEFAULT</literal> <literal>5000</literal> )
|
||||
<returnvalue>boolean</returnvalue>
|
||||
</para>
|
||||
<para>
|
||||
Waits for the backend process with the specified Process ID to
|
||||
terminate. If the process terminates before
|
||||
the <parameter>timeout</parameter> (in milliseconds)
|
||||
expires, <literal>true</literal> is returned. On timeout, a warning
|
||||
is emitted and <literal>false</literal> is returned.
|
||||
</para></entry>
|
||||
</row>
|
||||
</tbody>
|
||||
|
@ -1585,6 +1585,10 @@ postgres 27093 0.0 0.0 30096 2752 ? Ss 11:34 0:00 postgres: ser
|
||||
<entry>Waiting for subplan nodes of an <literal>Append</literal> plan
|
||||
node to be ready.</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry><literal>BackendTermination</literal></entry>
|
||||
<entry>Waiting for the termination of another backend.</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry><literal>BackupWaitWalArchive</literal></entry>
|
||||
<entry>Waiting for WAL files required for a backup to be successfully
|
||||
|
@ -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)
|
||||
|
@ -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"
|
||||
@ -125,16 +126,91 @@ pg_cancel_backend(PG_FUNCTION_ARGS)
|
||||
PG_RETURN_BOOL(r == SIGNAL_BACKEND_SUCCESS);
|
||||
}
|
||||
|
||||
/*
|
||||
* 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.
|
||||
* 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,9 +222,49 @@ 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")));
|
||||
|
||||
/* 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));
|
||||
}
|
||||
|
||||
/*
|
||||
* Signal to reload the database configuration
|
||||
*
|
||||
|
@ -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;
|
||||
|
@ -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',
|
||||
|
@ -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,
|
||||
|
Loading…
x
Reference in New Issue
Block a user