From 68a164ff4c44ddac64f8a2c3a1fe7fb3f0f16da3 Mon Sep 17 00:00:00 2001
From: Daniil Davidov <d.davydov@postgrespro.ru>
Date: Wed, 8 Apr 2026 02:55:48 +0700
Subject: [PATCH] Improvements for parallel autovacuum testing

---
 src/backend/access/heap/vacuumlazy.c          |   2 +-
 src/test/modules/test_autovacuum/Makefile     |   8 +
 src/test/modules/test_autovacuum/meson.build  |  21 +++
 .../t/001_parallel_autovacuum.pl              |  19 ++-
 .../test_autovacuum/test_autovacuum--1.0.sql  |  19 +++
 .../modules/test_autovacuum/test_autovacuum.c | 158 ++++++++++++++++++
 .../test_autovacuum/test_autovacuum.control   |   3 +
 7 files changed, 222 insertions(+), 8 deletions(-)
 create mode 100644 src/test/modules/test_autovacuum/test_autovacuum--1.0.sql
 create mode 100644 src/test/modules/test_autovacuum/test_autovacuum.c
 create mode 100644 src/test/modules/test_autovacuum/test_autovacuum.control

diff --git a/src/backend/access/heap/vacuumlazy.c b/src/backend/access/heap/vacuumlazy.c
index 39395aed0d5..36558f19c7c 100644
--- a/src/backend/access/heap/vacuumlazy.c
+++ b/src/backend/access/heap/vacuumlazy.c
@@ -871,7 +871,7 @@ heap_vacuum_rel(Relation rel, const VacuumParams *params,
 	 * workers.
 	 */
 	if (AmAutoVacuumWorkerProcess() && ParallelVacuumIsActive(vacrel))
-		INJECTION_POINT("autovacuum-start-parallel-vacuum", NULL);
+		INJECTION_POINT("autovacuum-start-parallel-vacuum", &rel->rd_id);
 #endif
 
 	/*
diff --git a/src/test/modules/test_autovacuum/Makefile b/src/test/modules/test_autovacuum/Makefile
index 15e83010c1c..1cbe7125bae 100644
--- a/src/test/modules/test_autovacuum/Makefile
+++ b/src/test/modules/test_autovacuum/Makefile
@@ -2,6 +2,14 @@
 
 PGFILEDESC = "test_autovacuum - test code for autovacuum"
 
+MODULE_big = test_autovacuum
+OBJS = \
+	$(WIN32RES) \
+	test_autovacuum.o
+
+EXTENSION = test_autovacuum
+DATA = test_autovacuum--1.0.sql
+
 TAP_TESTS = 1
 
 EXTRA_INSTALL = src/test/modules/injection_points
diff --git a/src/test/modules/test_autovacuum/meson.build b/src/test/modules/test_autovacuum/meson.build
index 86e392bc0de..f9f31084b54 100644
--- a/src/test/modules/test_autovacuum/meson.build
+++ b/src/test/modules/test_autovacuum/meson.build
@@ -1,5 +1,26 @@
 # Copyright (c) 2024-2026, PostgreSQL Global Development Group
 
+test_autovacuum_sources = files(
+  'test_autovacuum.c',
+)
+
+if host_system == 'windows'
+  test_autovacuum_sources += rc_lib_gen.process(win32ver_rc, extra_args: [
+    '--NAME', 'test_autovacuum',
+    '--FILEDESC', 'test_autovacuum - test code for autovacuum',])
+endif
+
+test_autovacuum = shared_module('test_autovacuum',
+  test_autovacuum_sources,
+  kwargs: pg_test_mod_args,
+)
+test_install_libs += test_autovacuum
+
+test_install_data += files(
+  'test_autovacuum.control',
+  'test_autovacuum--1.0.sql',
+)
+
 tests += {
   'name': 'test_autovacuum',
   'sd': meson.current_source_dir(),
diff --git a/src/test/modules/test_autovacuum/t/001_parallel_autovacuum.pl b/src/test/modules/test_autovacuum/t/001_parallel_autovacuum.pl
index c5a2e78246a..e5ce1a12cef 100644
--- a/src/test/modules/test_autovacuum/t/001_parallel_autovacuum.pl
+++ b/src/test/modules/test_autovacuum/t/001_parallel_autovacuum.pl
@@ -63,6 +63,7 @@ log_min_messages = debug2
 autovacuum_naptime = '1s'
 min_parallel_index_scan_size = 0
 log_autovacuum_min_duration = -1
+shared_preload_libraries = 'test_autovacuum'
 });
 $node->start;
 
@@ -78,6 +79,7 @@ if (!$node->check_extension('injection_points'))
 $node->safe_psql(
 	'postgres', qq{
 	CREATE EXTENSION injection_points;
+	CREATE EXTENSION test_autovacuum;
 });
 
 my $indexes_num = 3;
@@ -141,16 +143,19 @@ ok( $node->log_contains(
 $av_count = prepare_for_next_test($node, 2);
 $log_offset = -s $node->logfile;
 
+my $tableoid = $node->safe_psql(
+	'postgres',
+	"SELECT 'test_autovac'::regclass::oid");
+
 $node->safe_psql(
 	'postgres', qq{
-	SELECT injection_points_attach('autovacuum-start-parallel-vacuum', 'wait');
-
+	SELECT inj_start_parallel_vacuum_attach($tableoid);
 	ALTER TABLE test_autovac SET (autovacuum_parallel_workers = 1, autovacuum_enabled = true);
 });
 
-# Wait until parallel autovacuum is inited
-$node->wait_for_event('autovacuum worker',
-	'autovacuum-start-parallel-vacuum');
+# !!Hardcoded badness!!
+# Of cource, we need another mechanis to wait until parallel vacuum is started
+sleep(5);
 
 # Update the shared cost-based delay parameters.
 $node->safe_psql(
@@ -168,7 +173,7 @@ $node->safe_psql(
 # before vacuuming indexes due to the injection point.
 $node->safe_psql(
 	'postgres', qq{
-	SELECT injection_points_wakeup('autovacuum-start-parallel-vacuum');
+	SELECT inj_start_parallel_vacuum_wakeup();
 });
 
 # Check whether parallel worker successfully updated all parameters during
@@ -182,7 +187,7 @@ wait_for_autovacuum_complete($node, $av_count);
 # Cleanup
 $node->safe_psql(
 	'postgres', qq{
-	SELECT injection_points_detach('autovacuum-start-parallel-vacuum');
+	SELECT inj_start_parallel_vacuum_detach();
 });
 
 $node->stop;
diff --git a/src/test/modules/test_autovacuum/test_autovacuum--1.0.sql b/src/test/modules/test_autovacuum/test_autovacuum--1.0.sql
new file mode 100644
index 00000000000..2d22e4ddc8c
--- /dev/null
+++ b/src/test/modules/test_autovacuum/test_autovacuum--1.0.sql
@@ -0,0 +1,19 @@
+/* src/test/modules/test_autovacuum/test_autovacuum--1.0.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION test_autovacuum" to load this file. \quit
+
+/*
+ * Injection point related functions
+ */
+CREATE FUNCTION inj_start_parallel_vacuum_attach(table_oid oid)
+RETURNS VOID STRICT
+AS 'MODULE_PATHNAME' LANGUAGE C;
+
+CREATE FUNCTION inj_start_parallel_vacuum_wakeup()
+RETURNS VOID STRICT
+AS 'MODULE_PATHNAME' LANGUAGE C;
+
+CREATE FUNCTION inj_start_parallel_vacuum_detach()
+RETURNS VOID STRICT
+AS 'MODULE_PATHNAME' LANGUAGE C;
diff --git a/src/test/modules/test_autovacuum/test_autovacuum.c b/src/test/modules/test_autovacuum/test_autovacuum.c
new file mode 100644
index 00000000000..6a1d218d7e8
--- /dev/null
+++ b/src/test/modules/test_autovacuum/test_autovacuum.c
@@ -0,0 +1,158 @@
+/*-------------------------------------------------------------------------
+ *
+ * test_autovacuum.c
+ *		Helpers to write tests for autovacuum
+ *
+ * Copyright (c) 2026, PostgreSQL Global Development Group
+ *
+ * IDENTIFICATION
+ *	  src/test/modules/test_autovacuum/test_autovacuum.c
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include "fmgr.h"
+#include "miscadmin.h"
+#include "postmaster/autovacuum.h"
+#include "storage/shmem.h"
+#include "storage/ipc.h"
+#include "storage/lwlock.h"
+#include "storage/condition_variable.h"
+#include "utils/builtins.h"
+#include "utils/injection_point.h"
+#include "utils/wait_event_types.h"
+
+PG_MODULE_MAGIC;
+
+typedef struct InjPointState
+{
+	ConditionVariable	cv;
+	Oid					target_oid;
+	bool				enabled_start_parallel_vacuum;
+}			InjPointState;
+
+static InjPointState * inj_point_state;
+
+/* Shared memory init callbacks */
+static shmem_request_hook_type prev_shmem_request_hook = NULL;
+static shmem_startup_hook_type prev_shmem_startup_hook = NULL;
+
+static void
+test_autovacuum_shmem_request(void)
+{
+	if (prev_shmem_request_hook)
+		prev_shmem_request_hook();
+
+	RequestAddinShmemSpace(sizeof(InjPointState));
+}
+
+static void
+test_autovacuum_shmem_startup(void)
+{
+	bool		found;
+
+	if (prev_shmem_startup_hook)
+		prev_shmem_startup_hook();
+
+	/* Create or attach to the shared memory state */
+	LWLockAcquire(AddinShmemInitLock, LW_EXCLUSIVE);
+
+	inj_point_state = ShmemInitStruct("injection_points for autovacuum testing",
+									  sizeof(InjPointState),
+									  &found);
+
+	if (!found)
+	{
+		/* First time through, initialize */
+		inj_point_state->enabled_start_parallel_vacuum = false;
+		inj_point_state->target_oid = InvalidOid;
+
+		ConditionVariableInit(&inj_point_state->cv);
+
+		InjectionPointAttach("autovacuum-start-parallel-vacuum",
+							 "test_autovacuum",
+							 "inj_start_parallel_vacuum",
+							 NULL,
+							 0);
+	}
+
+	LWLockRelease(AddinShmemInitLock);
+}
+
+void
+_PG_init(void)
+{
+	if (!process_shared_preload_libraries_in_progress)
+		return;
+
+	prev_shmem_request_hook = shmem_request_hook;
+	shmem_request_hook = test_autovacuum_shmem_request;
+	prev_shmem_startup_hook = shmem_startup_hook;
+	shmem_startup_hook = test_autovacuum_shmem_startup;
+}
+
+extern PGDLLEXPORT void inj_start_parallel_vacuum(const char *name,
+												  const void *private_data,
+												  void *arg);
+
+/*
+ * Set number of currently available parallel a/v workers. This value may
+ * change after reserving or releasing such workers.
+ *
+ * Function called from parallel autovacuum leader.
+ */
+void
+inj_start_parallel_vacuum(const char *name, const void *private_data, void *arg)
+{
+	if (!inj_point_state->enabled_start_parallel_vacuum)
+		return;
+
+	Assert(arg != NULL);
+
+	if (inj_point_state->target_oid != *(Oid *) arg)
+		return;
+
+	ConditionVariablePrepareToSleep(&inj_point_state->cv);
+	ConditionVariableSleep(&inj_point_state->cv,
+						   WAIT_EVENT_ARCHIVE_COMMAND); /* another wait event needed */
+}
+
+PG_FUNCTION_INFO_V1(inj_start_parallel_vacuum_attach);
+Datum
+inj_start_parallel_vacuum_attach(PG_FUNCTION_ARGS)
+{
+#ifdef USE_INJECTION_POINTS
+	inj_point_state->enabled_start_parallel_vacuum = true;
+	inj_point_state->target_oid = PG_ARGISNULL(0) ? InvalidOid : PG_GETARG_OID(0);
+#else
+	elog(ERROR, "injection points not supported");
+#endif
+	PG_RETURN_VOID();
+}
+
+PG_FUNCTION_INFO_V1(inj_start_parallel_vacuum_wakeup);
+Datum
+inj_start_parallel_vacuum_wakeup(PG_FUNCTION_ARGS)
+{
+#ifdef USE_INJECTION_POINTS
+	ConditionVariableBroadcast(&inj_point_state->cv);
+#else
+	elog(ERROR, "injection points not supported");
+#endif
+	PG_RETURN_VOID();
+}
+
+PG_FUNCTION_INFO_V1(inj_start_parallel_vacuum_detach);
+Datum
+inj_start_parallel_vacuum_detach(PG_FUNCTION_ARGS)
+{
+#ifdef USE_INJECTION_POINTS
+	inj_point_state->enabled_start_parallel_vacuum = false;
+	inj_point_state->target_oid = InvalidOid;
+#else
+	elog(ERROR, "injection points not supported");
+#endif
+	PG_RETURN_VOID();
+}
diff --git a/src/test/modules/test_autovacuum/test_autovacuum.control b/src/test/modules/test_autovacuum/test_autovacuum.control
new file mode 100644
index 00000000000..62bc6e53e29
--- /dev/null
+++ b/src/test/modules/test_autovacuum/test_autovacuum.control
@@ -0,0 +1,3 @@
+comment = 'Test code for autovacuum'
+default_version = '1.0'
+module_pathname = '$libdir/test_autovacuum'
\ No newline at end of file
-- 
2.43.0

