v30-0004-Tests-for-parallel-autovacuum.patch
text/x-patch
Filename: v30-0004-Tests-for-parallel-autovacuum.patch
Type: text/x-patch
Part: 4
Patch
Same data as JSON:
GET /api/v1/attachments/:id/patch
the parsed metadata as JSON — format, series position, per-file stats; never the diff bytes.
API reference →
Format: format-patch
Series: patch v30-0004
Subject: Tests for parallel autovacuum
| File | + | − |
|---|---|---|
| src/backend/access/heap/vacuumlazy.c | 9 | 0 |
| src/backend/commands/vacuumparallel.c | 18 | 0 |
| src/test/modules/Makefile | 1 | 0 |
| src/test/modules/meson.build | 1 | 0 |
| src/test/modules/test_autovacuum/.gitignore | 2 | 0 |
| src/test/modules/test_autovacuum/Makefile | 20 | 0 |
| src/test/modules/test_autovacuum/meson.build | 15 | 0 |
| src/test/modules/test_autovacuum/t/001_parallel_autovacuum.pl | 191 | 0 |
From 8becbcbb62c89537955de3edb5e7a568c244b631 Mon Sep 17 00:00:00 2001
From: Daniil Davidov <d.davydov@postgrespro.ru>
Date: Tue, 17 Mar 2026 02:50:23 +0700
Subject: [PATCH v30 4/5] Tests for parallel autovacuum
---
src/backend/access/heap/vacuumlazy.c | 9 +
src/backend/commands/vacuumparallel.c | 18 ++
src/test/modules/Makefile | 1 +
src/test/modules/meson.build | 1 +
src/test/modules/test_autovacuum/.gitignore | 2 +
src/test/modules/test_autovacuum/Makefile | 20 ++
src/test/modules/test_autovacuum/meson.build | 15 ++
.../t/001_parallel_autovacuum.pl | 191 ++++++++++++++++++
8 files changed, 257 insertions(+)
create mode 100644 src/test/modules/test_autovacuum/.gitignore
create mode 100644 src/test/modules/test_autovacuum/Makefile
create mode 100644 src/test/modules/test_autovacuum/meson.build
create mode 100644 src/test/modules/test_autovacuum/t/001_parallel_autovacuum.pl
diff --git a/src/backend/access/heap/vacuumlazy.c b/src/backend/access/heap/vacuumlazy.c
index c57432670e7..8d2980f3ef0 100644
--- a/src/backend/access/heap/vacuumlazy.c
+++ b/src/backend/access/heap/vacuumlazy.c
@@ -152,6 +152,7 @@
#include "storage/latch.h"
#include "storage/lmgr.h"
#include "storage/read_stream.h"
+#include "utils/injection_point.h"
#include "utils/lsyscache.h"
#include "utils/pg_rusage.h"
#include "utils/timestamp.h"
@@ -873,6 +874,14 @@ heap_vacuum_rel(Relation rel, const VacuumParams params,
lazy_check_wraparound_failsafe(vacrel);
dead_items_alloc(vacrel, params.nworkers);
+#ifdef USE_INJECTION_POINTS
+ /*
+ * Trigger injection point, if parallel autovacuum is about to be started.
+ */
+ if (AmAutoVacuumWorkerProcess() && ParallelVacuumIsActive(vacrel))
+ INJECTION_POINT("autovacuum-start-parallel-vacuum", NULL);
+#endif
+
/*
* Call lazy_scan_heap to perform all required heap pruning, index
* vacuuming, and heap vacuuming (plus related processing)
diff --git a/src/backend/commands/vacuumparallel.c b/src/backend/commands/vacuumparallel.c
index 98aeb66eec4..62b6f50b538 100644
--- a/src/backend/commands/vacuumparallel.c
+++ b/src/backend/commands/vacuumparallel.c
@@ -46,6 +46,7 @@
#include "storage/bufmgr.h"
#include "storage/proc.h"
#include "tcop/tcopprot.h"
+#include "utils/injection_point.h"
#include "utils/lsyscache.h"
#include "utils/rel.h"
@@ -653,6 +654,14 @@ parallel_vacuum_update_shared_delay_params(void)
VacuumUpdateCosts();
shared_params_generation_local = params_generation;
+
+ elog(DEBUG2,
+ "parallel autovacuum worker updated cost params: cost_limit=%d, cost_delay=%g, cost_page_miss=%d, cost_page_dirty=%d, cost_page_hit=%d",
+ vacuum_cost_limit,
+ vacuum_cost_delay,
+ VacuumCostPageMiss,
+ VacuumCostPageDirty,
+ VacuumCostPageHit);
}
/*
@@ -895,6 +904,15 @@ parallel_vacuum_process_all_indexes(ParallelVacuumState *pvs, int num_index_scan
pvs->pcxt->nworkers_launched, nworkers)));
}
+#ifdef USE_INJECTION_POINTS
+ /*
+ * This injection point is used to wait until parallel autovacuum workers
+ * finishes their part of index processing.
+ */
+ if (nworkers > 0)
+ INJECTION_POINT("autovacuum-leader-before-indexes-processing", NULL);
+#endif
+
/* Vacuum the indexes that can be processed by only leader process */
parallel_vacuum_process_unsafe_indexes(pvs);
diff --git a/src/test/modules/Makefile b/src/test/modules/Makefile
index 28ce3b35eda..336a212faf4 100644
--- a/src/test/modules/Makefile
+++ b/src/test/modules/Makefile
@@ -16,6 +16,7 @@ SUBDIRS = \
plsample \
spgist_name_ops \
test_aio \
+ test_autovacuum \
test_binaryheap \
test_bitmapset \
test_bloomfilter \
diff --git a/src/test/modules/meson.build b/src/test/modules/meson.build
index 3ac291656c1..929659956cb 100644
--- a/src/test/modules/meson.build
+++ b/src/test/modules/meson.build
@@ -16,6 +16,7 @@ subdir('plsample')
subdir('spgist_name_ops')
subdir('ssl_passphrase_callback')
subdir('test_aio')
+subdir('test_autovacuum')
subdir('test_binaryheap')
subdir('test_bitmapset')
subdir('test_bloomfilter')
diff --git a/src/test/modules/test_autovacuum/.gitignore b/src/test/modules/test_autovacuum/.gitignore
new file mode 100644
index 00000000000..716e17f5a2a
--- /dev/null
+++ b/src/test/modules/test_autovacuum/.gitignore
@@ -0,0 +1,2 @@
+# Generated subdirectories
+/tmp_check/
diff --git a/src/test/modules/test_autovacuum/Makefile b/src/test/modules/test_autovacuum/Makefile
new file mode 100644
index 00000000000..188ec9f96a2
--- /dev/null
+++ b/src/test/modules/test_autovacuum/Makefile
@@ -0,0 +1,20 @@
+# src/test/modules/test_autovacuum/Makefile
+
+PGFILEDESC = "test_autovacuum - test code for parallel autovacuum"
+
+TAP_TESTS = 1
+
+EXTRA_INSTALL = src/test/modules/injection_points
+
+export enable_injection_points
+
+ifdef USE_PGXS
+PG_CONFIG = pg_config
+PGXS := $(shell $(PG_CONFIG) --pgxs)
+include $(PGXS)
+else
+subdir = src/test/modules/test_autovacuum
+top_builddir = ../../../..
+include $(top_builddir)/src/Makefile.global
+include $(top_srcdir)/contrib/contrib-global.mk
+endif
diff --git a/src/test/modules/test_autovacuum/meson.build b/src/test/modules/test_autovacuum/meson.build
new file mode 100644
index 00000000000..86e392bc0de
--- /dev/null
+++ b/src/test/modules/test_autovacuum/meson.build
@@ -0,0 +1,15 @@
+# Copyright (c) 2024-2026, PostgreSQL Global Development Group
+
+tests += {
+ 'name': 'test_autovacuum',
+ 'sd': meson.current_source_dir(),
+ 'bd': meson.current_build_dir(),
+ 'tap': {
+ 'env': {
+ 'enable_injection_points': get_option('injection_points') ? 'yes' : 'no',
+ },
+ 'tests': [
+ 't/001_parallel_autovacuum.pl',
+ ],
+ },
+}
diff --git a/src/test/modules/test_autovacuum/t/001_parallel_autovacuum.pl b/src/test/modules/test_autovacuum/t/001_parallel_autovacuum.pl
new file mode 100644
index 00000000000..0364019d5f0
--- /dev/null
+++ b/src/test/modules/test_autovacuum/t/001_parallel_autovacuum.pl
@@ -0,0 +1,191 @@
+# Test parallel autovacuum behavior
+
+use warnings FATAL => 'all';
+use PostgreSQL::Test::Cluster;
+use PostgreSQL::Test::Utils;
+use Test::More;
+
+if ($ENV{enable_injection_points} ne 'yes')
+{
+ plan skip_all => 'Injection points not supported by this build';
+}
+
+# Before each test we should disable autovacuum for 'test_autovac' table and
+# generate some dead tuples in it. Returns the current autovacuum_count of
+# the table tset_autovac.
+sub prepare_for_next_test
+{
+ my ($node, $test_number) = @_;
+
+ $node->safe_psql(
+ 'postgres', qq{
+ ALTER TABLE test_autovac SET (autovacuum_enabled = false);
+ UPDATE test_autovac SET col_1 = $test_number;
+ });
+
+ my $count = $node->safe_psql(
+ 'postgres', qq{
+ SELECT autovacuum_count FROM pg_stat_user_tables WHERE relname = 'test_autovac'
+ });
+
+ return $count;
+}
+
+# Wait for the table to be vacuumed by an autovacuum worker.
+sub wait_for_autovacuum_complete
+{
+ my ($node, $old_count) = @_;
+
+ $node->poll_query_until(
+ 'postgres', qq{
+ SELECT autovacuum_count > $old_count FROM pg_stat_user_tables WHERE relname = 'test_autovac'
+ });
+}
+
+my $psql_out;
+
+my $node = PostgreSQL::Test::Cluster->new('main');
+$node->init;
+
+# Configure postgres, so it can launch parallel autovacuum workers, log all
+# information we are interested in and autovacuum works frequently
+$node->append_conf(
+ 'postgresql.conf', qq{
+ max_worker_processes = 20
+ max_parallel_workers = 20
+ autovacuum_max_parallel_workers = 4
+ log_min_messages = debug2
+ autovacuum_naptime = '1s'
+ min_parallel_index_scan_size = 0
+});
+$node->start;
+
+# Check if the extension injection_points is available, as it may be
+# possible that this script is run with installcheck, where the module
+# would not be installed by default.
+if (!$node->check_extension('injection_points'))
+{
+ plan skip_all => 'Extension injection_points not installed';
+}
+
+# Create all functions needed for testing
+$node->safe_psql(
+ 'postgres', qq{
+ CREATE EXTENSION injection_points;
+});
+
+my $indexes_num = 3;
+my $initial_rows_num = 10_000;
+my $autovacuum_parallel_workers = 2;
+
+# Create table and fill it with some data
+$node->safe_psql(
+ 'postgres', qq{
+ CREATE TABLE test_autovac (
+ id SERIAL PRIMARY KEY,
+ col_1 INTEGER, col_2 INTEGER, col_3 INTEGER, col_4 INTEGER
+ ) WITH (autovacuum_parallel_workers = $autovacuum_parallel_workers,
+ log_autovacuum_min_duration = 0);
+
+ INSERT INTO test_autovac
+ SELECT
+ g AS col1,
+ g + 1 AS col2,
+ g + 2 AS col3,
+ g + 3 AS col4
+ FROM generate_series(1, $initial_rows_num) AS g;
+});
+
+# Create specified number of b-tree indexes on the table
+$node->safe_psql(
+ 'postgres', qq{
+ DO \$\$
+ DECLARE
+ i INTEGER;
+ BEGIN
+ FOR i IN 1..$indexes_num LOOP
+ EXECUTE format('CREATE INDEX idx_col_\%s ON test_autovac (col_\%s);', i, i);
+ END LOOP;
+ END \$\$;
+});
+
+# Test 1 :
+# Our table has enough indexes and appropriate reloptions, so autovacuum must
+# be able to process it in parallel mode. Just check if it can do it.
+
+my $av_count = prepare_for_next_test($node, 1);
+my $log_offset = -s $node->logfile;
+
+$node->safe_psql(
+ 'postgres', qq{
+ ALTER TABLE test_autovac SET (autovacuum_enabled = true);
+});
+
+# 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));
+
+# Test 2:
+# Check whether parallel autovacuum leader can propagate cost-based parameters
+# to the parallel workers.
+
+$av_count = prepare_for_next_test($node, 2);
+$log_offset = -s $node->logfile;
+
+$node->safe_psql(
+ 'postgres', qq{
+ SELECT injection_points_attach('autovacuum-start-parallel-vacuum', 'wait');
+ SELECT injection_points_attach('autovacuum-leader-before-indexes-processing', 'wait');
+
+ 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');
+
+# Update the shared cost-based delay parameters.
+$node->safe_psql(
+ 'postgres', qq{
+ ALTER SYSTEM SET vacuum_cost_limit = 500;
+ ALTER SYSTEM SET vacuum_cost_page_miss = 10;
+ ALTER SYSTEM SET vacuum_cost_page_dirty = 10;
+ ALTER SYSTEM SET vacuum_cost_page_hit = 10;
+ SELECT pg_reload_conf();
+});
+
+# Resume the leader process to update the shared parameters during heap scan (i.e.
+# vacuum_delay_point() is called) and launch a parallel vacuum worker, but it stops
+# before vacuuming indexes due to the injection point.
+$node->safe_psql(
+ 'postgres', qq{
+ SELECT injection_points_wakeup('autovacuum-start-parallel-vacuum');
+});
+$node->wait_for_event('autovacuum worker',
+ 'autovacuum-leader-before-indexes-processing');
+
+# Check whether parallel worker successfully updated all parameters during
+# index processing
+$node->wait_for_log(
+ qr/parallel autovacuum worker updated cost params: cost_limit=500, cost_delay=2, cost_page_miss=10, cost_page_dirty=10, cost_page_hit=10/,
+ $log_offset);
+
+$node->safe_psql(
+ 'postgres', qq{
+ SELECT injection_points_wakeup('autovacuum-leader-before-indexes-processing');
+});
+
+wait_for_autovacuum_complete($node, $av_count);
+
+# Cleanup
+$node->safe_psql(
+ 'postgres', qq{
+ SELECT injection_points_detach('autovacuum-start-parallel-vacuum');
+ SELECT injection_points_detach('autovacuum-leader-before-indexes-processing');
+});
+
+$node->stop;
+done_testing();
--
2.43.0