Subject: test_autovacuum/001_parallel_autovacuum is broken

From: Sami Imseih <[email-masked]>
Date: Mon, 6 Apr 2026 21:23:57 -0500

Hi,

I noticed that the test introduced in parallel autovacuum in 1ff3180ca01 was
very slow, but eventually succeeded. I tracked it down to the point in
the test that is waiting for "parallel autovacuum worker updated cost params".

This portion of the test that is waiting for the cost params to propagate to the
workers is getting stuck on wait_for_autovacuum_complete(). At the time
it's stuck the injection point from the previous test
autovacuum-start-parallel-vacuum
is still active on template1 tables.

  datname  |                           query
|            wait_event
-----------+-----------------------------------------------------------+----------------------------------
 postgres  | select datname, query, wait_event from pg_stat_activity ; |
 template1 | autovacuum: VACUUM ANALYZE pg_catalog.pg_attribute
| autovacuum-start-parallel-vacuum
           |
| AutovacuumMain
           |
| LogicalLauncherMain
           |
| IoWorkerMain
           |
| IoWorkerMain
           |
| IoWorkerMain
           |
| CheckpointerMain
           |
| BgwriterMain
           |
| WalWriterMain
(10 rows)

The poll_query_until eventually just times out, but this does not
cause the test to fail.

# test succeeded
----------------------------------- stderr -----------------------------------
# poll_query_until timed out executing this query:
#
#         SELECT autovacuum_count > 1 FROM pg_stat_user_tables WHERE
relname = 'test_autovac'
#
# expecting this output:
# t
# last actual query output:
# f
# with stderr:
================

This issue only occurs when I run all tests and not when I run
test_autovacuum in isolation. It makes sense the issue only occurs
for all tests only since autovacuum running runs for template1 and other
tables unrelated to the test.

I run all the tests ( equivelant of check-wold) with:
```
meson test -q --print-errorlogs
```

I think we can remove the second wait_for_autovacuum_complete()
call in the test, as all we really need is to wait_for_log to guarantee
the cost parameters were updated. No need to wait for the autovacuum
to complete.

--- a/src/test/modules/test_autovacuum/t/001_parallel_autovacuum.pl
+++ b/src/test/modules/test_autovacuum/t/001_parallel_autovacuum.pl
@@ -177,8 +177,6 @@ $node->wait_for_log(
        qr/parallel autovacuum worker updated cost params:
cost_limit=500, cost_delay=5, cost_page_miss=10, cost_page_dirty=10,
cost_page_hit=10/,
        $log_offset);

-wait_for_autovacuum_complete($node, $av_count);
-
 # Cleanup
 $node->safe_psql(
        'postgres', qq{

Regards,

--
Sami Imseih
Amazon Web Services (AWS)

---

From: Masahiko Sawada <[email-masked]>
Date: Tue, 7 Apr 2026 00:28:56 -0700

> the test that is waiting for "parallel autovacuum worker updated cost params".

Thank you for the report.

> is still active on template1 tables.
(snip)
> the cost parameters were updated. No need to wait for the autovacuum

It sound like a good idea. In the test 2, we make garbage tuples on
test_autovac table but it doesn't necessarily mean that autovacuum
would work only on that table. Also given that the purpose of this
test is to check the propagation works fine, we can verify it whatever
tables eligible for parallel vacuum.

I've attached the patch, and am going to push it barring any objections.

Regards,

-- 
Masahiko Sawada
Amazon Web Services: https://aws.amazon.com
From 2ebc8df9f7ce757820247d6e97b4109f95ce85f6 Mon Sep 17 00:00:00 2001
From: Masahiko Sawada <[email-masked]>
Date: Mon, 6 Apr 2026 21:38:15 -0700
Subject: [PATCH] Remove an unstable wait from parallel autovacuum regression
 test.
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

The test 001_parallel_autovacuum.pl verified that vacuum delay
parameters are propagated to parallel vacuum workers by using
injection points. It previously waited for autovacuum to complete on
the test_autovac table. However, since injection points are
cluster-wide, an autovacuum worker could be triggered on tables in
other databases (e.g., template1) and get stuck at the same injection
point. This could lead to a timeout when the test waits for the
expected table's autovacuum to finish.

This commit removes the wait for autovacuum completion from this
specific test case. Since the primary goal is to verify the
propagation of parameter updates—which is already confirmed via log
messages—waiting for the entire vacuum process to finish is
unnecessary and prone to instability in concurrent test environments.

Author:	Sami Imseih <[email-masked]>
Discussion: https://postgr.es/m/[email-masked]
---
 src/test/modules/test_autovacuum/t/001_parallel_autovacuum.pl | 2 --
 1 file changed, 2 deletions(-)

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..fc4dd22f5eb 100644
--- a/src/test/modules/test_autovacuum/t/001_parallel_autovacuum.pl
+++ b/src/test/modules/test_autovacuum/t/001_parallel_autovacuum.pl
@@ -177,8 +177,6 @@ $node->wait_for_log(
 	qr/parallel autovacuum worker updated cost params: cost_limit=500, cost_delay=5, cost_page_miss=10, cost_page_dirty=10, cost_page_hit=10/,
 	$log_offset);
 
2.53.0

---

From: Daniil Davydov <[email-masked]>
Date: Tue, 7 Apr 2026 21:06:09 +0700

Hi,

> the test that is waiting for "parallel autovacuum worker updated cost params".

Good catch!

> tables eligible for parallel vacuum.

Yeah, we only need to ensure that there will be free parallel workers in
bgworkers pool. Since only autovacuum can launch parallel workers in this
test, I think everything is OK.

The proposed patch fixes the problem, but I am thinking about possible new
tests for parallel a/v. What if some of them will require both injection points
and wait_for_autovacuum_complete call? If the reloption could override the GUC
parameter, then we could disable parallel a/v globally and turn it on for the
particular table. In this case we can avoid such a problem.

--
Best regards,
Daniil Davydov

---

From: Sami Imseih <[email-masked]>
Date: Tue, 7 Apr 2026 11:33:31 -0500

> and wait_for_autovacuum_complete call?

Yeah, we could use dynamically named injection points, which I don't
see a precedent for. For example, we could construct a name like
"autovacuum-test-<relname>" and have the autovacuum code path
generate the injection point name dynamically based on the relation
being vacuumed. That way, the test only blocks on the specific table
it cares about.

But I don't think we need to do this now, since the test we are dealing
with does not need to wait for autovacuum to complete.

> If the reloption could override the GUC

That is an interesting idea which may be possible since we do reserve
a/v workers at startup with autovacuum_worker_slots. Although I am
not sure if we should be putting a feature that makes disabling
autovacuum globally supported behavior.

--
Sami

---

From: Daniil Davydov <[email-masked]>
Date: Wed, 8 Apr 2026 00:27:32 +0700

Hi,

> being vacuumed. That way, the test only blocks on the specific table

I am afraid that this would be too rough a workaround for this problem..

> with does not need to wait for autovacuum to complete.

Sure! But this test needs to be slightly reworked in the future.

> autovacuum globally supported behavior.

Hm, I think I didn't reveal my thoughts enough.
Let me describe current logic in short : each a/v worker now can take from
bgworkers pool as many parallel workers as the GUC parameter
(autovacuum_max_parallel_workers) allows.

We also have an "autovacuum_parallel_workers" reloption that can additionally
limit the number of parallel workers for the table. Default value of the
reloption is "-1" which means "use the GUC parameter's value". I.e. when we are
setting the GUC parameter to N, then every table automatically allows N
parallel a/v workers. If autovacuum_max_parallel_workers = 0 then no one can
launch parallel workers for autovacuum, even if reloption is > 0. Thus,
autovacuum_max_parallel_workers is the main limiter during the number of
parallel workers calculation.

But I suggest an alternative idea - allow reloption to override GUC parameter.
So even if autovacuum_max_parallel_workers is 0 we still can enable parallel
a/v for a particular table via reloption.

This approach allows us to rework the test as follows :
1) Keep the default value of GUC parameter which means that no table allows
parallel a/v.
2) Set reloption of a particular table to N (allow parallel a/v for this and
only this table).

This approach may also be very useful in large productions. You can read
discussion about it from here [1] up to the end of the thread. Since the
question is still open, all feedback is welcome!

[1] https://www.postgresql.org/message-id/CAJDiXgj3A%3DwNC-S0z3TixmnVUkifs%3D07yLLHJ7_%2BdDsakft1tA%40mail.gmail.com

--
Best regards,
Daniil Davydov

---

From: Sami Imseih <[email-masked]>
Date: Tue, 7 Apr 2026 13:02:49 -0500

> I am afraid that this would be too rough a workaround for this problem..

Perhaps, but I don't see it being unreasonable for injection points.

I guess we can also think about expanding InjectionPointCondition to
handle other types of conditions, maybe OID??, to filter when running
the point.

/*
* Conditions related to injection points. This tracks in shared memory the
* runtime conditions under which an injection point is allowed to run,
* stored as private_data when an injection point is attached, and passed as
* argument to the callback.
*
* If more types of runtime conditions need to be tracked, this structure
* should be expanded.
*/
typedef enum InjectionPointConditionType
{
INJ_CONDITION_ALWAYS = 0, /* always run */
INJ_CONDITION_PID, /* PID restriction */
} InjectionPointConditionType;

typedef struct InjectionPointCondition
{
/* Type of the condition */
InjectionPointConditionType type;

/* ID of the process where the injection point is allowed to run */
int pid;
} InjectionPointCondition;

> parallel workers calculation.

autovacuum_max_parallel_workers being the limiter is a desirable
attribute, otherwise
it will allow users to disable the GUC and set whatever they want on a
per table level,
only guarded by max_parallel_workers. That to me sounds pretty easy to
misconfigure
and manage.

> [1] https://www.postgresql.org/message-id/CAJDiXgj3A%3DwNC-S0z3TixmnVUkifs%3D07yLLHJ7_%2BdDsakft1tA%40mail.gmail.com

Thanks!

--
Sami

---

From: Daniil Davydov <[email-masked]>
Date: Wed, 8 Apr 2026 02:56:48 +0700

Hi,

> handle other types of conditions, maybe OID??, to filter when running

Hm, sounds reasonable.

I am thinking about how to make it less invasive. For example, we can write an
extension for this test (like many other tests do). The extension could allow
us to specify the oid of the table we are interested in. See attached patch
that demonstrates my idea. What do you think?

> only guarded by max_parallel_workers. That to me sounds pretty easy to

Yes, this is the main argument against this idea. However, in the thread that
I mentioned I tried to give arguments why this might be extremely convenient
for users with large databases.

--
Best regards,
Daniil Davydov
From 68a164ff4c44ddac64f8a2c3a1fe7fb3f0f16da3 Mon Sep 17 00:00:00 2001
From: Daniil Davidov <[email-masked]>
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

---

From: Michael Paquier <[email-masked]>
Date: Wed, 8 Apr 2026 09:07:50 +0900

> handle other types of conditions, maybe OID??, to filter when running

Yeah, InjectionPointConditionType was designed with these types of
extensions in mind in terms of conditions you want to assign to a
point name when attaching it, with the check happening in the callback
attached when it is run.  It should not be complicated to extend
injection_points_attach(), just pass down a string that it then
translated to an OID, or you could use a different grammar as well.
One thing that I'd be careful about is to handle that with one
argument in the SQL attach function, with the condition data given as
an input string.  One grammar that Alexander K. designed at some point
for some of the facilities he had proposed was a JSON input, but
that's an implementation artifact.

Doing something as a separate module/library would be also fine.  We
do that for the AIO tests.
--
Michael

---

From: Sami Imseih <[email-masked]>
Date: Wed, 8 Apr 2026 09:52:39 -0500

> do that for the AIO tests.

I think we can enhance these tests using a separate module as Daniil is
suggesting, but we should probably get 0001 committed first and then
have a quick follow-up. What do you think?

--
Sami

---

From: Alexander Lakhin <[email-masked]>
Date: Thu, 9 Apr 2026 21:00:00 +0300

Hello,

> the test that is waiting for "parallel autovacuum worker updated cost params".

I've found another issue with the test manifested on buildfarm, at least
at [1]:
[06:54:07.738](4.121s) not ok 1
[06:54:07.769](0.031s) #   Failed test at 
/home/bf/bf-build/flaviventris/HEAD/pgsql/src/test/modules/test_autovacuum/t/001_parallel_autovacuum.pl line 133.
### Stopping node "main" using mode fast

The corresponding test code:
# Wait until the parallel autovacuum on table is completed. At the same time,
# we check that the required number of parallel workers has been started.
wait_for_autovacuum_complete($node, $av_count);
ok( $node->log_contains(
         qr/parallel workers: index vacuum: 2 planned, 2 launched in total/,
         $log_offset));

but regress_log_001_parallel_autovacuum contains this string:
2026-04-07 06:54:07.736 CEST [1825954][autovacuum worker][102/5:0] LOG:  automatic vacuum of table 
"postgres.public.test_autovac": index scans: 1
...
     parallel workers: index vacuum: 2 planned, 2 launched in total

though the timestamp difference is only 2 ms. I tried the following
modification:
@@ -1222,6 +1222,7 @@ heap_vacuum_rel(Relation rel, const VacuumParams *params,
                               (double) dead_items_max_bytes / (1024 * 1024));
              appendStringInfo(&buf, _("system usage: %s"), pg_rusage_show(&ru0));

+pg_usleep(300000);
              ereport(verbose ? INFO : LOG,
                      (errmsg_internal("%s", buf.data)));
              pfree(buf.data);

and it makes the test fail for me on each run.
Could you please look if this can be fixed too?

[1] https://buildfarm.postgresql.org/cgi-bin/show_log.pl?nm=flaviventris&dt=2026-04-07%2004%3A41%3A09

Best regards,
Alexander

---

From: Masahiko Sawada <[email-masked]>
Date: Thu, 9 Apr 2026 11:18:45 -0700

> have a quick follow-up. What do you think?

+1. I've pushed the fix.

It might also be useful if an injection point is activated on the
particular database rather than cluster-wide.

Regards,

---

From: Sami Imseih <[email-masked]>
Date: Thu, 9 Apr 2026 13:21:37 -0500

Thanks!

> particular database rather than cluster-wide.

Here is something that could be discussed for v20 [1]

[1] https://commitfest.postgresql.org/patch/6663/

--
Sami

---

From: Masahiko Sawada <[email-masked]>
Date: Thu, 9 Apr 2026 11:25:41 -0700

On Thu, Apr 9, 2026 at 11:00 AM Alexander Lakhin <[email-masked]> wrote:
> [06:54:07.769](0.031s) #   Failed test at /home/bf/bf-build/flaviventris/HEAD/pgsql/src/test/modules/test_autovacuum/t/001_parallel_autovacuum.pl line 133.
> 2026-04-07 06:54:07.736 CEST [1825954][autovacuum worker][102/5:0] LOG:  automatic vacuum of table "postgres.public.test_autovac": index scans: 1
> ...

The root cause seems to me that it's not guaranteed that we can see
the autovacuum logs after checking the statistics (i.e.,
pg_stat_user_tables) as we update the statistics and then write the
log.

One way to fix the test is to replace log_contains() with
wait_for_log(). We can also remove wait_for_autovacuum_complete()
logic altogether.

Regards,

---

From: Sami Imseih <[email-masked]>
Date: Thu, 9 Apr 2026 13:32:33 -0500

> wait_for_log(). We can also remove wait_for_autovacuum_complete()

+1. I was going to reply with exactly this. Attached is the fix.

--
Sami

---

From: Daniil Davydov <[email-masked]>
Date: Fri, 10 Apr 2026 02:32:30 +0700

Hi,

> +1. I was going to reply with exactly this. Attached is the fix.

Thank you for the patch! I agree with this approach.

Optionally, we can change the comment above to something like this:
# Wait until the parallel autovacuum on the table completes and reports the
# number of launched workers, which must correspond to the value specified in
# the reloption.

IMHO it better reflects what is going on.

--
Best regards,
Daniil Davydov

---

From: Masahiko Sawada <[email-masked]>
Date: Thu, 9 Apr 2026 12:40:55 -0700

> +1. I was going to reply with exactly this. Attached is the fix.

Thank you for the patch! I agree with the overall idea. Since we
enable autovacuum log only the test_autovac table, just checking
autovacuum log works as expected.

I think we can simplify the test further by removing the logic around
the av_count variable.

Regards,

---

From: Sami Imseih <[email-masked]>
Date: Thu, 9 Apr 2026 15:14:15 -0500

> # number of launched workers, which must correspond to the value specified in

Made the comment less verbose but in the same spirit as the above.

> I think we can simplify the test further by removing the logic around

removed av_count and pg_stat_user_tables query, but hardened the
regexp a bit to ensure that the parallel logging is for the test_autovac
table. It gives the same assurance as counting pg_stat_user_tables
and will be better if we add another parallel test table in the future.

--
Sami

---

From: Masahiko Sawada <[email-masked]>
Date: Thu, 9 Apr 2026 15:11:24 -0700

> and will be better if we add another parallel test table in the future.

I believe that we don't need to worry about the regexp for this test.
Parallel vacuum would be used on all tables having more than one
index, but we enable the autovacuum logs only on the test_autovac
table.

Regards,

---

From: Sami Imseih <[email-masked]>
Date: Thu, 9 Apr 2026 17:54:19 -0500

> index, but we enable the autovacuum logs only on the test_autovac

ah, correct.

```
) WITH (autovacuum_parallel_workers = $autovacuum_parallel_workers,
log_autovacuum_min_duration = 0);
```

see v3 with the reverted regexp string.

Thanks!

--
Sami

---

From: Michael Paquier <[email-masked]>
Date: Fri, 10 Apr 2026 08:15:34 +0900

> particular database rather than cluster-wide.

Indeed.  One can design a lot of conditions like that.  The important
part for me is to find use-cases for them, rather than pile them in
the code for potential "future" uses.
--
Michael