Skip to content

Commit aaf0432

Browse files
committed
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
1 parent fb310f1 commit aaf0432

File tree

7 files changed

+174
-7
lines changed

7 files changed

+174
-7
lines changed

doc/src/sgml/func.sgml

+29-1
Original file line numberDiff line numberDiff line change
@@ -24977,7 +24977,7 @@ SELECT collation for ('foo' COLLATE "de_DE");
2497724977
<indexterm>
2497824978
<primary>pg_terminate_backend</primary>
2497924979
</indexterm>
24980-
<function>pg_terminate_backend</function> ( <parameter>pid</parameter> <type>integer</type> )
24980+
<function>pg_terminate_backend</function> ( <parameter>pid</parameter> <type>integer</type>, <parameter>timeout</parameter> <type>bigint</type> <literal>DEFAULT</literal> <literal>0</literal> )
2498124981
<returnvalue>boolean</returnvalue>
2498224982
</para>
2498324983
<para>
@@ -24986,6 +24986,34 @@ SELECT collation for ('foo' COLLATE "de_DE");
2498624986
is a member of the role whose backend is being terminated or the
2498724987
calling role has been granted <literal>pg_signal_backend</literal>,
2498824988
however only superusers can terminate superuser backends.
24989+
</para>
24990+
<para>
24991+
If <parameter>timeout</parameter> is not specified or zero, this
24992+
function returns <literal>true</literal> whether the process actually
24993+
terminates or not, indicating only that the sending of the signal was
24994+
successful. If the <parameter>timeout</parameter> is specified (in
24995+
milliseconds) and greater than zero, the function waits until the
24996+
process is actually terminated or until the given time has passed. If
24997+
the process is terminated, the function
24998+
returns <literal>true</literal>. On timeout a warning is emitted and
24999+
<literal>false</literal> is returned.
25000+
</para></entry>
25001+
</row>
25002+
25003+
<row>
25004+
<entry role="func_table_entry"><para role="func_signature">
25005+
<indexterm>
25006+
<primary>pg_wait_for_backend_termination</primary>
25007+
</indexterm>
25008+
<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> )
25009+
<returnvalue>boolean</returnvalue>
25010+
</para>
25011+
<para>
25012+
Waits for the backend process with the specified Process ID to
25013+
terminate. If the process terminates before
25014+
the <parameter>timeout</parameter> (in milliseconds)
25015+
expires, <literal>true</literal> is returned. On timeout, a warning
25016+
is emitted and <literal>false</literal> is returned.
2498925017
</para></entry>
2499025018
</row>
2499125019
</tbody>

doc/src/sgml/monitoring.sgml

+4
Original file line numberDiff line numberDiff line change
@@ -1585,6 +1585,10 @@ postgres 27093 0.0 0.0 30096 2752 ? Ss 11:34 0:00 postgres: ser
15851585
<entry>Waiting for subplan nodes of an <literal>Append</literal> plan
15861586
node to be ready.</entry>
15871587
</row>
1588+
<row>
1589+
<entry><literal>BackendTermination</literal></entry>
1590+
<entry>Waiting for the termination of another backend.</entry>
1591+
</row>
15881592
<row>
15891593
<entry><literal>BackupWaitWalArchive</literal></entry>
15901594
<entry>Waiting for WAL files required for a backup to be successfully

src/backend/catalog/system_views.sql

+10
Original file line numberDiff line numberDiff line change
@@ -1347,6 +1347,16 @@ CREATE OR REPLACE FUNCTION
13471347
RETURNS boolean STRICT VOLATILE LANGUAGE INTERNAL AS 'pg_promote'
13481348
PARALLEL SAFE;
13491349

1350+
CREATE OR REPLACE FUNCTION
1351+
pg_terminate_backend(pid integer, timeout int8 DEFAULT 0)
1352+
RETURNS boolean STRICT VOLATILE LANGUAGE INTERNAL AS 'pg_terminate_backend'
1353+
PARALLEL SAFE;
1354+
1355+
CREATE OR REPLACE FUNCTION
1356+
pg_wait_for_backend_termination(pid integer, timeout int8 DEFAULT 5000)
1357+
RETURNS boolean STRICT VOLATILE LANGUAGE INTERNAL AS 'pg_wait_for_backend_termination'
1358+
PARALLEL SAFE;
1359+
13501360
-- legacy definition for compatibility with 9.3
13511361
CREATE OR REPLACE FUNCTION
13521362
json_populate_record(base anyelement, from_json json, use_json_as_text boolean DEFAULT false)

src/backend/storage/ipc/signalfuncs.c

+120-4
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818

1919
#include "catalog/pg_authid.h"
2020
#include "miscadmin.h"
21+
#include "pgstat.h"
2122
#include "postmaster/syslogger.h"
2223
#include "storage/pmsignal.h"
2324
#include "storage/proc.h"
@@ -126,15 +127,90 @@ pg_cancel_backend(PG_FUNCTION_ARGS)
126127
}
127128

128129
/*
129-
* Signal to terminate a backend process. This is allowed if you are a member
130-
* of the role whose process is being terminated.
130+
* Wait until there is no backend process with the given PID and return true.
131+
* On timeout, a warning is emitted and false is returned.
132+
*/
133+
static bool
134+
pg_wait_until_termination(int pid, int64 timeout)
135+
{
136+
/*
137+
* Wait in steps of waittime milliseconds until this function exits or
138+
* timeout.
139+
*/
140+
int64 waittime = 100;
141+
/*
142+
* Initially remaining time is the entire timeout specified by the user.
143+
*/
144+
int64 remainingtime = timeout;
145+
146+
/*
147+
* Check existence of the backend. If the backend still exists, then wait
148+
* for waittime milliseconds, again check for the existence. Repeat this
149+
* until timeout or an error occurs or a pending interrupt such as query
150+
* cancel gets processed.
151+
*/
152+
do
153+
{
154+
if (remainingtime < waittime)
155+
waittime = remainingtime;
156+
157+
if (kill(pid, 0) == -1)
158+
{
159+
if (errno == ESRCH)
160+
return true;
161+
else
162+
ereport(ERROR,
163+
(errcode(ERRCODE_INTERNAL_ERROR),
164+
errmsg("could not check the existence of the backend with PID %d: %m",
165+
pid)));
166+
}
167+
168+
/* Process interrupts, if any, before waiting */
169+
CHECK_FOR_INTERRUPTS();
170+
171+
(void) WaitLatch(MyLatch,
172+
WL_LATCH_SET | WL_TIMEOUT | WL_EXIT_ON_PM_DEATH,
173+
waittime,
174+
WAIT_EVENT_BACKEND_TERMINATION);
175+
176+
ResetLatch(MyLatch);
177+
178+
remainingtime -= waittime;
179+
} while (remainingtime > 0);
180+
181+
ereport(WARNING,
182+
(errmsg("backend with PID %d did not terminate within %lld milliseconds",
183+
pid, (long long int) timeout)));
184+
185+
return false;
186+
}
187+
188+
/*
189+
* Signal to terminate a backend process. This is allowed if you are a member
190+
* of the role whose process is being terminated. If timeout input argument is
191+
* 0 (which is default), then this function just signals the backend and
192+
* doesn't wait. Otherwise it waits until given the timeout milliseconds or no
193+
* process has the given PID and returns true. On timeout, a warning is emitted
194+
* and false is returned.
131195
*
132196
* Note that only superusers can signal superuser-owned processes.
133197
*/
134198
Datum
135199
pg_terminate_backend(PG_FUNCTION_ARGS)
136200
{
137-
int r = pg_signal_backend(PG_GETARG_INT32(0), SIGTERM);
201+
int pid;
202+
int r;
203+
int timeout;
204+
205+
pid = PG_GETARG_INT32(0);
206+
timeout = PG_GETARG_INT64(1);
207+
208+
if (timeout < 0)
209+
ereport(ERROR,
210+
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
211+
errmsg("\"timeout\" must not be negative")));
212+
213+
r = pg_signal_backend(pid, SIGTERM);
138214

139215
if (r == SIGNAL_BACKEND_NOSUPERUSER)
140216
ereport(ERROR,
@@ -146,7 +222,47 @@ pg_terminate_backend(PG_FUNCTION_ARGS)
146222
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
147223
errmsg("must be a member of the role whose process is being terminated or member of pg_signal_backend")));
148224

149-
PG_RETURN_BOOL(r == SIGNAL_BACKEND_SUCCESS);
225+
/* Wait only on success and if actually requested */
226+
if (r == SIGNAL_BACKEND_SUCCESS && timeout > 0)
227+
PG_RETURN_BOOL(pg_wait_until_termination(pid, timeout));
228+
else
229+
PG_RETURN_BOOL(r == SIGNAL_BACKEND_SUCCESS);
230+
}
231+
232+
/*
233+
* Wait for a backend process with the given PID to exit or until the given
234+
* timeout milliseconds occurs. Returns true if the backend has exited. On
235+
* timeout a warning is emitted and false is returned.
236+
*
237+
* We allow any user to call this function, consistent with any user being
238+
* able to view the pid of the process in pg_stat_activity etc.
239+
*/
240+
Datum
241+
pg_wait_for_backend_termination(PG_FUNCTION_ARGS)
242+
{
243+
int pid;
244+
int64 timeout;
245+
PGPROC *proc = NULL;
246+
247+
pid = PG_GETARG_INT32(0);
248+
timeout = PG_GETARG_INT64(1);
249+
250+
if (timeout <= 0)
251+
ereport(ERROR,
252+
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
253+
errmsg("\"timeout\" must not be negative or zero")));
254+
255+
proc = BackendPidGetProc(pid);
256+
257+
if (proc == NULL)
258+
{
259+
ereport(WARNING,
260+
(errmsg("PID %d is not a PostgreSQL server process", pid)));
261+
262+
PG_RETURN_BOOL(false);
263+
}
264+
265+
PG_RETURN_BOOL(pg_wait_until_termination(pid, timeout));
150266
}
151267

152268
/*

src/backend/utils/activity/wait_event.c

+3
Original file line numberDiff line numberDiff line change
@@ -313,6 +313,9 @@ pgstat_get_wait_ipc(WaitEventIPC w)
313313
case WAIT_EVENT_APPEND_READY:
314314
event_name = "AppendReady";
315315
break;
316+
case WAIT_EVENT_BACKEND_TERMINATION:
317+
event_name = "BackendTermination";
318+
break;
316319
case WAIT_EVENT_BACKUP_WAIT_WAL_ARCHIVE:
317320
event_name = "BackupWaitWalArchive";
318321
break;

src/include/catalog/pg_proc.dat

+7-2
Original file line numberDiff line numberDiff line change
@@ -6190,9 +6190,14 @@
61906190
{ oid => '2171', descr => 'cancel a server process\' current query',
61916191
proname => 'pg_cancel_backend', provolatile => 'v', prorettype => 'bool',
61926192
proargtypes => 'int4', prosrc => 'pg_cancel_backend' },
6193-
{ oid => '2096', descr => 'terminate a server process',
6193+
{ oid => '2096', descr => 'terminate a backend process and if timeout is specified, wait for its exit or until timeout occurs',
61946194
proname => 'pg_terminate_backend', provolatile => 'v', prorettype => 'bool',
6195-
proargtypes => 'int4', prosrc => 'pg_terminate_backend' },
6195+
proargtypes => 'int4 int8', proargnames => '{pid,timeout}',
6196+
prosrc => 'pg_terminate_backend' },
6197+
{ oid => '2137', descr => 'wait for a backend process exit or timeout occurs',
6198+
proname => 'pg_wait_for_backend_termination', provolatile => 'v', prorettype => 'bool',
6199+
proargtypes => 'int4 int8', proargnames => '{pid,timeout}',
6200+
prosrc => 'pg_wait_for_backend_termination' },
61966201
{ oid => '2172', descr => 'prepare for taking an online backup',
61976202
proname => 'pg_start_backup', provolatile => 'v', proparallel => 'r',
61986203
prorettype => 'pg_lsn', proargtypes => 'text bool bool',

src/include/utils/wait_event.h

+1
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ typedef enum
8080
typedef enum
8181
{
8282
WAIT_EVENT_APPEND_READY = PG_WAIT_IPC,
83+
WAIT_EVENT_BACKEND_TERMINATION,
8384
WAIT_EVENT_BACKUP_WAIT_WAL_ARCHIVE,
8485
WAIT_EVENT_BGWORKER_SHUTDOWN,
8586
WAIT_EVENT_BGWORKER_STARTUP,

0 commit comments

Comments
 (0)