Received: from malur.postgresql.org ([217.196.149.56]) by arkaria.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.94.2) (envelope-from ) id 1rchJB-001XVg-0w for pgsql-hackers@arkaria.postgresql.org; Wed, 21 Feb 2024 07:46:26 +0000 Received: from localhost ([127.0.0.1] helo=malur.postgresql.org) by malur.postgresql.org with esmtp (Exim 4.94.2) (envelope-from ) id 1rchJ9-003Qn3-La for pgsql-hackers@arkaria.postgresql.org; Wed, 21 Feb 2024 07:46:24 +0000 Received: from magus.postgresql.org ([2a02:c0:301:0:ffff::29]) by malur.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.94.2) (envelope-from ) id 1rchJ8-003Qmv-QF for pgsql-hackers@lists.postgresql.org; Wed, 21 Feb 2024 07:46:23 +0000 Received: from wfhigh4-smtp.messagingengine.com ([64.147.123.155]) by magus.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.94.2) (envelope-from ) id 1rchJ1-0008VB-B8 for pgsql-hackers@lists.postgresql.org; Wed, 21 Feb 2024 07:46:22 +0000 Received: from compute4.internal (compute4.nyi.internal [10.202.2.44]) by mailfhigh.west.internal (Postfix) with ESMTP id 22D9C1800086; Wed, 21 Feb 2024 02:46:12 -0500 (EST) Received: from mailfrontend2 ([10.202.2.163]) by compute4.internal (MEProxy); Wed, 21 Feb 2024 02:46:12 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=paquier.xyz; h= cc:cc:content-type:content-type:date:date:from:from:in-reply-to :in-reply-to:message-id:mime-version:references:reply-to:subject :subject:to:to; s=fm1; t=1708501571; x=1708587971; bh=pmujz6q3PG 78uqpIF5BCIclqRGITP4eYwUwVm7ZxBOU=; b=GkEoxhVCUWmdcFffP63uWtSxvA cxv4L8MUIeniSUHpPzQNbkHxxTv8sYWf2opipcdxX2ArZAanZH2/VgqFASXrCrVZ U0vi9VkHDNoghZNkRgl7kUSXcIXMhwDl/y1kwR0F3uHbJVLf6nsmV/5Taie66fXn FyyJC0xbThSE6SMTQ5fWnhvXr+ESA1DOCMrC8eBf7SRlS3k8FNDOdW2NJmg7CDK1 SpNUILYUVCoXq/EzFOIFF6EOQi/n8dEk6tk59gwjSulSrK7zFb/4vzUQDiFgVtf2 om8eQPueeF5+Nxo8w3yUZF5AFFq2ws9I7CXvpp1s+V5nJQp+cXWU1s7Atp8g== DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d= messagingengine.com; h=cc:cc:content-type:content-type:date:date :feedback-id:feedback-id:from:from:in-reply-to:in-reply-to :message-id:mime-version:references:reply-to:subject:subject:to :to:x-me-proxy:x-me-proxy:x-me-sender:x-me-sender:x-sasl-enc; s= fm1; t=1708501571; x=1708587971; bh=pmujz6q3PG78uqpIF5BCIclqRGIT P4eYwUwVm7ZxBOU=; b=d2HTCiDi/HrIoNxajjQuHWBxwT9dVimI+vAurg5Kgx35 Wl1pid3vY3cgrKgah5SBgKm6QCGyRQv0gVwOVWYnmTQVktuqntDiYGUi8ZVXqN8u 2/84Ew9tKozm5xI5SuV/JQ6cdF+t1j79gtAUFaNrv0lADtEpd4+ZVK0OuMhOVK1p 0CczUmevnzBeNSAkMAx/dRdjxGREFf5PxCh2H9Rb/u2dUFR9AlyY0ZezJfE3s2MF 5oT56Ipy3Xw3/BF3mCDHK8f7CDVO7fEjGOiPMjw27S8nUweTtiLVPj/fh1UtMboi Lb0y8OfNlN2IOtEnefz6NyesqTxXN+OcLIFZHIwdzw== X-ME-Sender: X-ME-Received: X-ME-Proxy-Cause: gggruggvucftvghtrhhoucdtuddrgedvledrfedugdduudduucetufdoteggodetrfdotf fvucfrrhhofhhilhgvmecuhfgrshhtofgrihhlpdfqfgfvpdfurfetoffkrfgpnffqhgen uceurghilhhouhhtmecufedttdenucesvcftvggtihhpihgvnhhtshculddquddttddmne gfrhhlucfvnfffucdljedtmdenucfjughrpeffhffvvefukfhfgggtuggjsehgtderredt tddvnecuhfhrohhmpefoihgthhgrvghlucfrrghquhhivghruceomhhitghhrggvlhesph grqhhuihgvrhdrgiihiieqnecuggftrfgrthhtvghrnhepteelieefudffhffhtdetleeg geegfffhkeeuveetiefgudduvedutefggeeivdejnecuvehluhhsthgvrhfuihiivgeptd enucfrrghrrghmpehmrghilhhfrhhomhepmhhitghhrggvlhesphgrqhhuihgvrhdrgiih ii X-ME-Proxy: Feedback-ID: i0fe9450f:Fastmail Received: by mail.messagingengine.com (Postfix) with ESMTPA; Wed, 21 Feb 2024 02:46:09 -0500 (EST) Date: Wed, 21 Feb 2024 16:46:00 +0900 From: Michael Paquier To: Bertrand Drouvot Cc: Postgres hackers , Ashutosh Bapat Subject: Re: Injection points: some tools to wait and wake Message-ID: References: MIME-Version: 1.0 Content-Type: multipart/signed; micalg=pgp-sha512; protocol="application/pgp-signature"; boundary="iCbp6g+LomrTrWWM" Content-Disposition: inline In-Reply-To: List-Id: List-Help: List-Subscribe: List-Post: List-Owner: List-Archive: Archived-At: Precedence: bulk --iCbp6g+LomrTrWWM Content-Type: multipart/mixed; boundary="vqoHqOFijmK2IcWe" Content-Disposition: inline --vqoHqOFijmK2IcWe Content-Type: text/plain; charset=us-ascii Content-Disposition: inline Content-Transfer-Encoding: quoted-printable On Wed, Feb 21, 2024 at 07:08:03AM +0900, Michael Paquier wrote: > Well, both you and Andrey are asking for it now, so let's do it. The > implementation is simple: > - Store in InjectionPointSharedState an array of wait_counts and an > array of names. There is only one condition variable. > - When a point wants to wait, it takes the spinlock and looks within > the array of names until it finds a free slot, adds its name into the > array to reserve a wait counter at the same position, releases the > spinlock. Then it loops on the condition variable for an update of > the counter it has reserved. It is possible to make something more > efficient, but at a small size it would not really matter. > - The wakeup takes a point name in argument, acquires the spinlock, > and checks if it can find the point into the array, pinpoints the > location of the counter to update and updates it. Then it broadcasts > the change. > - The wait loop checks its counter, leaves its loop, cancels the > sleep, takes the spinlock to unregister from the array, and leaves. >=20 > I would just hardcode the number of points that can wait, say 5 of > them tracked in shmem? Does that look like what you are looking at? I was looking at that, and it proves to work OK, so you can do stuff like waits and wakeups for multiple processes in a controlled manner. The attached patch authorizes up to 32 waiters. I have switched things so as the information reported in pg_stat_activity is the name of the injection point itself. Comments and ideas are welcome. -- Michael --vqoHqOFijmK2IcWe Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v2-0001-injection_points-Add-routines-to-wait-and-wake-pr.patch" Content-Transfer-Encoding: quoted-printable =46rom 75302cba302b83ce2a6d6eaf30b163f473b87276 Mon Sep 17 00:00:00 2001 =46rom: Michael Paquier Date: Wed, 21 Feb 2024 16:36:25 +0900 Subject: [PATCH v2 1/2] injection_points: Add routines to wait and wake processes This commit is made of two parts: - A new callback that can be attached to a process to make it wait on a condition variable. The condition checked is registered in shared memory by the module injection_points. - A new SQL function to update the shared state and broadcast the update using a condition variable. The shared state used by the module is registered using the DSM registry, and is optional. --- .../injection_points--1.0.sql | 10 ++ .../injection_points/injection_points.c | 151 ++++++++++++++++++ src/tools/pgindent/typedefs.list | 1 + 3 files changed, 162 insertions(+) diff --git a/src/test/modules/injection_points/injection_points--1.0.sql b/= src/test/modules/injection_points/injection_points--1.0.sql index 5944c41716..eed0310cf6 100644 --- a/src/test/modules/injection_points/injection_points--1.0.sql +++ b/src/test/modules/injection_points/injection_points--1.0.sql @@ -24,6 +24,16 @@ RETURNS void AS 'MODULE_PATHNAME', 'injection_points_run' LANGUAGE C STRICT PARALLEL UNSAFE; =20 +-- +-- injection_points_wakeup() +-- +-- Wakes a condition variable waited on in an injection point. +-- +CREATE FUNCTION injection_points_wakeup(IN point_name TEXT) +RETURNS void +AS 'MODULE_PATHNAME', 'injection_points_wakeup' +LANGUAGE C STRICT PARALLEL UNSAFE; + -- -- injection_points_detach() -- diff --git a/src/test/modules/injection_points/injection_points.c b/src/tes= t/modules/injection_points/injection_points.c index e843e6594f..052b20f9c8 100644 --- a/src/test/modules/injection_points/injection_points.c +++ b/src/test/modules/injection_points/injection_points.c @@ -18,18 +18,72 @@ #include "postgres.h" =20 #include "fmgr.h" +#include "storage/condition_variable.h" #include "storage/lwlock.h" #include "storage/shmem.h" +#include "storage/dsm_registry.h" #include "utils/builtins.h" #include "utils/injection_point.h" #include "utils/wait_event.h" =20 PG_MODULE_MAGIC; =20 +/* Maximum number of wait usable in injection points at once */ +#define INJ_MAX_WAIT 32 +#define INJ_NAME_MAXLEN 64 + +/* Shared state information for injection points. */ +typedef struct InjectionPointSharedState +{ + /* protects accesses to wait_counts */ + slock_t lock; + + /* Counters advancing when injection_points_wakeup() is called */ + int wait_counts[INJ_MAX_WAIT]; + + /* Names of injection points attached to wait counters */ + char name[INJ_MAX_WAIT][INJ_NAME_MAXLEN]; + + /* + * Condition variable used for waits and wakeups, checking upon the set of + * wait_counts when waiting. + */ + ConditionVariable wait_point; +} InjectionPointSharedState; + +/* Pointer to shared-memory state. */ +static InjectionPointSharedState *inj_state =3D NULL; + extern PGDLLEXPORT void injection_error(const char *name); extern PGDLLEXPORT void injection_notice(const char *name); +extern PGDLLEXPORT void injection_wait(const char *name); =20 =20 +static void +injection_point_init_state(void *ptr) +{ + InjectionPointSharedState *state =3D (InjectionPointSharedState *) ptr; + + SpinLockInit(&state->lock); + memset(state->wait_counts, 0, sizeof(state->wait_counts)); + memset(state->name, 0, sizeof(state->name)); + ConditionVariableInit(&state->wait_point); +} + +static void +injection_init_shmem(void) +{ + bool found; + + if (inj_state !=3D NULL) + return; + + inj_state =3D GetNamedDSMSegment("injection_points", + sizeof(InjectionPointSharedState), + injection_point_init_state, + &found); +} + /* Set of callbacks available to be attached to an injection point. */ void injection_error(const char *name) @@ -43,6 +97,65 @@ injection_notice(const char *name) elog(NOTICE, "notice triggered for injection point %s", name); } =20 +/* Wait on a condition variable, awaken by injection_points_wakeup() */ +void +injection_wait(const char *name) +{ + int old_wait_counts =3D -1; + int index =3D -1; + uint32 injection_wait_event =3D 0; + + if (inj_state =3D=3D NULL) + injection_init_shmem(); + + /* + * This custom wait event name is not released, but we don't care much for + * testing as this will be short-lived. + */ + injection_wait_event =3D WaitEventExtensionNew(name); + + /* + * Find a free slot to wait for, and register this injection point's name. + */ + SpinLockAcquire(&inj_state->lock); + for (int i =3D 0; i < INJ_MAX_WAIT; i++) + { + if (inj_state->name[i][0] =3D=3D '\0') + { + index =3D i; + strlcpy(inj_state->name[i], name, INJ_NAME_MAXLEN); + old_wait_counts =3D inj_state->wait_counts[i]; + break; + } + } + SpinLockRelease(&inj_state->lock); + + if (index < 0) + elog(ERROR, "could not find free slot for wait of injection point %s ", + name); + + /* And sleep.. */ + ConditionVariablePrepareToSleep(&inj_state->wait_point); + for (;;) + { + int new_wait_counts; + + SpinLockAcquire(&inj_state->lock); + new_wait_counts =3D inj_state->wait_counts[index]; + SpinLockRelease(&inj_state->lock); + + if (old_wait_counts !=3D new_wait_counts) + break; + ConditionVariableSleep(&inj_state->wait_point, injection_wait_event); + } + ConditionVariableCancelSleep(); + + /* Remove us from the waiting list */ + SpinLockAcquire(&inj_state->lock); + inj_state->name[index][0] =3D '\0'; + SpinLockRelease(&inj_state->lock); +} + /* * SQL function for creating an injection point. */ @@ -58,6 +171,8 @@ injection_points_attach(PG_FUNCTION_ARGS) function =3D "injection_error"; else if (strcmp(action, "notice") =3D=3D 0) function =3D "injection_notice"; + else if (strcmp(action, "wait") =3D=3D 0) + function =3D "injection_wait"; else elog(ERROR, "incorrect action \"%s\" for injection point creation", acti= on); =20 @@ -80,6 +195,42 @@ injection_points_run(PG_FUNCTION_ARGS) PG_RETURN_VOID(); } =20 +/* + * SQL function for waking a condition variable. + */ +PG_FUNCTION_INFO_V1(injection_points_wakeup); +Datum +injection_points_wakeup(PG_FUNCTION_ARGS) +{ + char *name =3D text_to_cstring(PG_GETARG_TEXT_PP(0)); + int index =3D -1; + + if (inj_state =3D=3D NULL) + injection_init_shmem(); + + /* First bump the wait counter for the injection point to wake */ + SpinLockAcquire(&inj_state->lock); + for (int i =3D 0; i < INJ_MAX_WAIT; i++) + { + if (strcmp(name, inj_state->name[i]) =3D=3D 0) + { + index =3D i; + break; + } + } + if (index < 0) + { + SpinLockRelease(&inj_state->lock); + elog(ERROR, "could not find injection point %s to wake", name); + } + inj_state->wait_counts[index]++; + SpinLockRelease(&inj_state->lock); + + /* And broadcast the change for the waiters */ + ConditionVariableBroadcast(&inj_state->wait_point); + PG_RETURN_VOID(); +} + /* * SQL function for dropping an injection point. */ diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs= =2Elist index d808aad8b0..d7eca00502 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1208,6 +1208,7 @@ InitializeDSMForeignScan_function InitializeWorkerForeignScan_function InjectionPointCacheEntry InjectionPointEntry +InjectionPointSharedState InlineCodeBlock InsertStmt Instrumentation --=20 2.43.0 --vqoHqOFijmK2IcWe Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v2-0002-Add-regression-test-for-restart-points-during-pro.patch" Content-Transfer-Encoding: quoted-printable =46rom 79cc3e309c15bec19fec8c3cb2ac2e48bd42d7a2 Mon Sep 17 00:00:00 2001 =46rom: Michael Paquier Date: Wed, 21 Feb 2024 16:37:17 +0900 Subject: [PATCH v2 2/2] Add regression test for restart points during promotion This test fails when 7863ee4def65 is reverted, checking that a node is able to properly restart following a crash when a restart point was finishing across a promotion. This is an old bug that had no coverage, and injection points make that cheap to achieve. --- src/backend/access/transam/xlog.c | 7 + src/test/recovery/Makefile | 7 +- src/test/recovery/meson.build | 4 + .../t/041_invalid_checkpoint_after_promote.pl | 176 ++++++++++++++++++ 4 files changed, 193 insertions(+), 1 deletion(-) create mode 100644 src/test/recovery/t/041_invalid_checkpoint_after_promot= e.pl diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam= /xlog.c index 50c347a679..50b045ff08 100644 --- a/src/backend/access/transam/xlog.c +++ b/src/backend/access/transam/xlog.c @@ -100,6 +100,7 @@ #include "storage/sync.h" #include "utils/guc_hooks.h" #include "utils/guc_tables.h" +#include "utils/injection_point.h" #include "utils/memutils.h" #include "utils/ps_status.h" #include "utils/relmapper.h" @@ -7536,6 +7537,12 @@ CreateRestartPoint(int flags) =20 CheckPointGuts(lastCheckPoint.redo, flags); =20 + /* + * This location needs to be after CheckPointGuts() to ensure that some + * work has already happened during this checkpoint. + */ + INJECTION_POINT("CreateRestartPoint"); + /* * Remember the prior checkpoint's redo ptr for * UpdateCheckPointDistanceEstimate() diff --git a/src/test/recovery/Makefile b/src/test/recovery/Makefile index 17ee353735..f57baba5e8 100644 --- a/src/test/recovery/Makefile +++ b/src/test/recovery/Makefile @@ -9,12 +9,17 @@ # #------------------------------------------------------------------------- =20 -EXTRA_INSTALL=3Dcontrib/pg_prewarm contrib/pg_stat_statements contrib/test= _decoding +EXTRA_INSTALL=3Dcontrib/pg_prewarm \ + contrib/pg_stat_statements \ + contrib/test_decoding \ + src/test/modules/injection_points =20 subdir =3D src/test/recovery top_builddir =3D ../../.. include $(top_builddir)/src/Makefile.global =20 +export enable_injection_points enable_injection_points + # required for 017_shm.pl and 027_stream_regress.pl REGRESS_SHLIB=3D$(abs_top_builddir)/src/test/regress/regress$(DLSUFFIX) export REGRESS_SHLIB diff --git a/src/test/recovery/meson.build b/src/test/recovery/meson.build index bf087ac2a9..e4e0e2b4cc 100644 --- a/src/test/recovery/meson.build +++ b/src/test/recovery/meson.build @@ -6,6 +6,9 @@ tests +=3D { 'bd': meson.current_build_dir(), 'tap': { 'test_kwargs': {'priority': 40}, # recovery tests are slow, start early + 'env': { + 'enable_injection_points': get_option('injection_points') ? 'yes' := 'no', + }, 'tests': [ 't/001_stream_rep.pl', 't/002_archiving.pl', @@ -46,6 +49,7 @@ tests +=3D { 't/038_save_logical_slots_shutdown.pl', 't/039_end_of_wal.pl', 't/040_standby_failover_slots_sync.pl', + 't/041_invalid_checkpoint_after_promote.pl', ], }, } diff --git a/src/test/recovery/t/041_invalid_checkpoint_after_promote.pl b/= src/test/recovery/t/041_invalid_checkpoint_after_promote.pl new file mode 100644 index 0000000000..e91f360d12 --- /dev/null +++ b/src/test/recovery/t/041_invalid_checkpoint_after_promote.pl @@ -0,0 +1,176 @@ + +# Copyright (c) 2024, PostgreSQL Global Development Group + +use strict; +use warnings; +use PostgreSQL::Test::Cluster; +use PostgreSQL::Test::Utils; +use Time::HiRes qw(usleep); +use Test::More; + +################################################## +# Test race condition when a restart point is running during a promotion, +# checking that WAL segments are correctly removed in the restart point +# while the promotion finishes. +# +# This test relies on an injection point that causes the checkpointer to +# wait in the middle of a restart point on a standby. The checkpointer +# is awaken to finish its restart point only once the promotion of the +# standby is completed, and the node should be able to restart properly. +################################################## + +if ($ENV{enable_injection_points} ne 'yes') +{ + plan skip_all =3D> 'Injection points not supported by this build'; +} + +# Initialize primary node +my $node_primary =3D PostgreSQL::Test::Cluster->new('master'); +$node_primary->init(allows_streaming =3D> 1); +$node_primary->append_conf( + 'postgresql.conf', q[ +checkpoint_timeout =3D 30s +log_checkpoints =3D on +restart_after_crash =3D on +]); +$node_primary->start; + +my $backup_name =3D 'my_backup'; +$node_primary->backup($backup_name); + +# Setup a standby +my $node_standby =3D PostgreSQL::Test::Cluster->new('standby1'); +$node_standby->init_from_backup($node_primary, $backup_name, + has_streaming =3D> 1); +$node_standby->start; + +# Dummy table for the upcoming tests. +$node_primary->safe_psql('postgres', 'checkpoint'); +$node_primary->safe_psql('postgres', 'CREATE TABLE prim_tab (a int);'); + +# Register an injection point on the standby so as the follow-up +# restart point will wait on it. +$node_primary->safe_psql('postgres', 'CREATE EXTENSION injection_points;'); +# Wait until the extension has been created on the standby +$node_primary->wait_for_replay_catchup($node_standby); + +# Note that from this point the checkpointer will wait in the middle of +# a restart point on the standby. +$node_standby->safe_psql('postgres', + "SELECT injection_points_attach('CreateRestartPoint', 'wait');"); + +# Execute a restart point on the standby, that we will now be waiting on. +# This needs to be in the background. +my $logstart =3D -s $node_standby->logfile; +my $psql_session =3D + $node_standby->background_psql('postgres', on_error_stop =3D> 0); +$psql_session->query_until( + qr/starting_checkpoint/, q( + \echo starting_checkpoint + CHECKPOINT; +)); + +# Switch one WAL segment to make the previous restart point remove the +# segment once the restart point completes. +$node_primary->safe_psql('postgres', 'INSERT INTO prim_tab VALUES (1);'); +$node_primary->safe_psql('postgres', 'SELECT pg_switch_wal();'); +$node_primary->wait_for_replay_catchup($node_standby); + +# Wait until the checkpointer is in the middle of the restart point +# processing, relying on the custom wait event generated in the +# wait callback used in the injection point previously attached. +ok( $node_standby->poll_query_until( + 'postgres', + qq[SELECT count(*) FROM pg_stat_activity + WHERE backend_type =3D 'checkpointer' AND wait_event =3D 'Creat= eRestartPoint' ;], + '1'), + 'checkpointer is waiting in restart point' +) or die "Timed out while waiting for checkpointer to run restart point"; + +# Check the logs that the restart point has started on standby. This is +# optional, but let's be sure. +my $log =3D slurp_file($node_standby->logfile, $logstart); +my $checkpoint_start =3D 0; +if ($log =3D~ m/restartpoint starting: immediate wait/) +{ + $checkpoint_start =3D 1; +} +is($checkpoint_start, 1, 'restartpoint has started'); + +# Trigger promotion during the restart point. +$node_primary->stop; +$node_standby->promote; + +# Update the start position before waking up the checkpointer! +$logstart =3D -s $node_standby->logfile; + +# Now wake up the checkpointer. +$node_standby->safe_psql('postgres', + "SELECT injection_points_wakeup('CreateRestartPoint');"); + +# Wait until the previous restart point completes on the newly-promoted +# standby, checking the logs for that. +my $checkpoint_complete =3D 0; +foreach my $i (0 .. 10 * $PostgreSQL::Test::Utils::timeout_default) +{ + my $log =3D slurp_file($node_standby->logfile, $logstart); + if ($log =3D~ m/restartpoint complete/) + { + $checkpoint_complete =3D 1; + last; + } + usleep(100_000); +} +is($checkpoint_complete, 1, 'restart point has completed'); + +# Kill with SIGKILL, forcing all the backends to restart. +my $psql_timeout =3D IPC::Run::timer(3600); +my ($killme_stdin, $killme_stdout, $killme_stderr) =3D ('', '', ''); +my $killme =3D IPC::Run::start( + [ + 'psql', '-XAtq', '-v', 'ON_ERROR_STOP=3D1', '-f', '-', '-d', + $node_standby->connstr('postgres') + ], + '<', + \$killme_stdin, + '>', + \$killme_stdout, + '2>', + \$killme_stderr, + $psql_timeout); +$killme_stdin .=3D q[ +SELECT pg_backend_pid(); +]; +$killme->pump until $killme_stdout =3D~ /[[:digit:]]+[\r\n]$/; +my $pid =3D $killme_stdout; +chomp($pid); +$killme_stdout =3D ''; +$killme_stderr =3D ''; + +my $ret =3D PostgreSQL::Test::Utils::system_log('pg_ctl', 'kill', 'KILL', = $pid); +is($ret, 0, 'killed process with KILL'); + +# Wait until the server restarts, finish consuming output. +$killme_stdin .=3D q[ +SELECT 1; +]; +ok( pump_until( + $killme, + $psql_timeout, + \$killme_stderr, + qr/server closed the connection unexpectedly|connection to server was lo= st|could not send data to server/m + ), + "psql query died successfully after SIGKILL"); +$killme->finish; + +# Wait till server finishes restarting +$node_standby->poll_query_until('postgres', undef, ''); + +# After recovery, the server should be able to start. +my $stdout; +my $stderr; +($ret, $stdout, $stderr) =3D $node_standby->psql('postgres', 'select 1'); +is($ret, 0, "psql connect success"); +is($stdout, 1, "psql select 1"); + +done_testing(); --=20 2.43.0 --vqoHqOFijmK2IcWe-- --iCbp6g+LomrTrWWM Content-Type: application/pgp-signature; name="signature.asc" -----BEGIN PGP SIGNATURE----- iQIzBAABCgAdFiEEG72nH6vTowiyblFKnvQgOdbyQH0FAmXVqjgACgkQnvQgOdby QH0hhRAAp3pzDumSXnav9/Bv3sGK/Il/MK/tmbRqPxknzfoxFlTF7cJvCJa6FBuN GaMnnySJfBJH9lZHDqO+NLHso1OzlzWdNqZe5vu8fQX2sDICEIg4nGqEaK+pDZ0G Mvt6DhG9iOMlfjpxk7QYtOkhpzsseo7ybAUMx10cUuC29iWU6LijaWXo/80aqgqz lrmroZvTMoEtDEec41VS1W4cufcWlzAR6Nxwl1bXy1pIVmkA/+XEcid6mK1mL8vR 4SEXShSaymgcOCM/AUhEQszmjoZsN2gLBWO9uuABsfEN/QYAzakPR00cWY1mzwIk uA2ISLkFqXDhE7ZYJVbnYS2zX7gJ4bBCtxiV7tBGC4LvvWK7NfZB8pWYmYB6mb1p DqzCrzNwWSe0NrQ2P48wGOcLtZDfrie3Lx2/m6SjtqVzQWuqABwtzt/KR/nuOkI6 w+TBBSy7z9oJraXR79e7c9HvuZDOVw6ZsJpENTWqhJBEeQPlrxcrLqUMT4gXR4N5 c6/BsqXAVLoD9tR/OXJT1f32Do8yVc4pd3laXYaJysmsHm9GIWKGOo5OSUyGbqGz UisMs8HFCGxEY8hjknCZT9w0DoFvY8SCN8w1AuSBTg5am+q4lI4MTA5Dbtne4wwd pCCFTTLAfmLdTn32aNHa8BlN39S4ZwISTDOk3eWmR2p1a8EfApY= =4PMR -----END PGP SIGNATURE----- --iCbp6g+LomrTrWWM--