Add tap test for pg_signal_autovacuum role
authorMichael Paquier <[email protected]>
Tue, 16 Jul 2024 01:05:46 +0000 (10:05 +0900)
committerMichael Paquier <[email protected]>
Tue, 16 Jul 2024 01:05:46 +0000 (10:05 +0900)
This commit provides testig coverage for ccd38024bc3c, checking that a
role granted pg_signal_autovacuum_worker is able to stop a vacuum
worker.

An injection point with a wait is placed at the beginning of autovacuum
worker startup to make sure that a worker is still alive when sending
and processing the signal sent.

Author: Anthony Leung, Michael Paquier, Kirill Reshke
Reviewed-by: Andrey Borodin, Nathan Bossart
Discussion: https://postgr.es/m/CALdSSPiQPuuQpOkF7x0g2QkA5eE-3xXt7hiJFvShV1bHKDvf8w@mail.gmail.com

src/backend/postmaster/autovacuum.c
src/test/modules/test_misc/meson.build
src/test/modules/test_misc/t/006_signal_autovacuum.pl [new file with mode: 0644]

index 928754b51c4e572a0f0eed95af9c4baaa54c747d..4e4a0ccbefbedda9bd7b763de54311c69f553f74 100644 (file)
 #include "utils/fmgroids.h"
 #include "utils/fmgrprotos.h"
 #include "utils/guc_hooks.h"
+#include "utils/injection_point.h"
 #include "utils/lsyscache.h"
 #include "utils/memutils.h"
 #include "utils/ps_status.h"
@@ -1902,6 +1903,12 @@ do_autovacuum(void)
    /* Start a transaction so our commands have one to play into. */
    StartTransactionCommand();
 
+   /*
+    * This injection point is put in a transaction block to work with a wait
+    * that uses a condition variable.
+    */
+   INJECTION_POINT("autovacuum-worker-start");
+
    /*
     * Compute the multixact age for which freezing is urgent.  This is
     * normally autovacuum_multixact_freeze_max_age, but may be less if we are
index df2913e8938e6f3753ea82dd201dba6862b2dd5f..283ffa751aa292bf558df70f6c9c258c9b4a54c9 100644 (file)
@@ -13,7 +13,8 @@ tests += {
       't/002_tablespace.pl',
       't/003_check_guc.pl',
       't/004_io_direct.pl',
-      't/005_timeouts.pl'
+      't/005_timeouts.pl',
+      't/006_signal_autovacuum.pl',
     ],
   },
 }
diff --git a/src/test/modules/test_misc/t/006_signal_autovacuum.pl b/src/test/modules/test_misc/t/006_signal_autovacuum.pl
new file mode 100644 (file)
index 0000000..f37f8ff
--- /dev/null
@@ -0,0 +1,95 @@
+# Copyright (c) 2024, PostgreSQL Global Development Group
+
+# Test signaling autovacuum worker with pg_signal_autovacuum_worker.
+#
+# Only roles with privileges of pg_signal_autovacuum_worker are allowed to
+# signal autovacuum workers.  This test uses an injection point located
+# at the beginning of the autovacuum worker startup.
+
+use strict;
+use warnings;
+use PostgreSQL::Test::Cluster;
+use Test::More;
+
+if ($ENV{enable_injection_points} ne 'yes')
+{
+   plan skip_all => 'Injection points not supported by this build';
+}
+
+# Initialize postgres
+my $psql_err = '';
+my $psql_out = '';
+my $node = PostgreSQL::Test::Cluster->new('node');
+$node->init;
+
+# This ensures a quick worker spawn.
+$node->append_conf('postgresql.conf', 'autovacuum_naptime = 1');
+$node->start;
+$node->safe_psql('postgres', 'CREATE EXTENSION injection_points;');
+
+$node->safe_psql(
+   'postgres', qq(
+    CREATE ROLE regress_regular_role;
+    CREATE ROLE regress_worker_role;
+    GRANT pg_signal_autovacuum_worker TO regress_worker_role;
+));
+
+# From this point, autovacuum worker will wait at startup.
+$node->safe_psql('postgres',
+   "SELECT injection_points_attach('autovacuum-worker-start', 'wait');");
+
+# Accelerate worker creation in case we reach this point before the naptime
+# ends.
+$node->reload();
+
+# Wait until an autovacuum worker starts.
+$node->wait_for_event('autovacuum worker', 'autovacuum-worker-start');
+
+# And grab one of them.
+my $av_pid = $node->safe_psql(
+   'postgres', qq(
+    SELECT pid FROM pg_stat_activity WHERE backend_type = 'autovacuum worker' AND wait_event = 'autovacuum-worker-start' LIMIT 1;
+));
+
+# Regular role cannot terminate autovacuum worker.
+my $terminate_with_no_pg_signal_av = $node->psql(
+   'postgres', qq(
+    SET ROLE regress_regular_role;
+    SELECT pg_terminate_backend('$av_pid');
+),
+   stdout => \$psql_out,
+   stderr => \$psql_err);
+
+like(
+   $psql_err,
+   qr/ERROR:  permission denied to terminate process\nDETAIL:  Only roles with privileges of the "pg_signal_autovacuum_worker" role may terminate autovacuum workers./,
+   "autovacuum worker not signaled with regular role");
+
+my $offset = -s $node->logfile;
+
+# Role with pg_signal_autovacuum can terminate autovacuum worker.
+my $terminate_with_pg_signal_av = $node->psql(
+   'postgres', qq(
+    SET ROLE regress_worker_role;
+    SELECT pg_terminate_backend('$av_pid');
+),
+   stdout => \$psql_out,
+   stderr => \$psql_err);
+
+# Wait for the autovacuum worker to exit before scanning the logs.
+$node->poll_query_until('postgres',
+       "SELECT count(*) = 0 FROM pg_stat_activity "
+     . "WHERE pid = '$av_pid' AND backend_type = 'autovacuum worker';");
+
+# Check that the primary server logs a FATAL indicating that autovacuum
+# is terminated.
+ok( $node->log_contains(
+       qr/FATAL:  terminating autovacuum process due to administrator command/,
+       $offset),
+   "autovacuum worker signaled with pg_signal_autovacuum_worker granted");
+
+# Release injection point.
+$node->safe_psql('postgres',
+   "SELECT injection_points_detach('autovacuum-worker-start');");
+
+done_testing();