Hello, Álvaro!

On Thu, Jan 8, 2026 at 11:08 AM Mihail Nikalayeu
<[email protected]> wrote:
> I'll try to prepare next set of fixes today/tomorrow:
>
> * In wait_for_idle exit early if we see backend stuck into injection
> point (no sense to wait more, my bad - should be added from the
> start), also reduce timeout to 1/2 of TAP
> * In wait_for_injection_point use 1/2 of TAP timeout.
> * Try to detect install_check in tests and skip instead of whole pack
> * Change year to 2026 :)

Fixes for the fixes are in attachment.

I am not very confident about the second commit, because I am not an
expert in meson/Makefiles.
Some additional explanations are available in commit messages.

Tested with -DCLOBBER_CACHE_ALWAYS=1

I hope this is the last one.

Best regards,
Mikhail.
From 538a20ec9abab8642e0770cd52a3adeb458c7d42 Mon Sep 17 00:00:00 2001
From: nkey <[email protected]>
Date: Thu, 8 Jan 2026 20:25:41 +0300
Subject: [PATCH v1 1/2] Fix wait_for_idle() to properly handle
 CLOBBER_CACHE_ALWAYS builds
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

In commit e1c971945d62, the test 010_index_concurrently_upsert.pl was added with logic to handle CLOBBER_CACHE_ALWAYS builds, where a backend can hit an injection point while attaching it due to catalog cache invalidation. The wait_for_idle() function detects whether the attachment completed or the backend became stuck waiting on an injection point.

However, using the full TAP framework timeout causes the test to wait indefinitely when a backend is stuck, until the test harness terminates it. This prevents the recovery logic from executing.

Fix by reducing the timeout to half the TAP default, and adding early exit when the backend is detected waiting on an injection point. This allows the test to promptly detect stuck backends and wake them up.

Also reduce wait_for_injection_point() timeout to half the TAP default, ensuring time remains for diagnostic output if the test gets stuck.

Author: Mihail Nikalayeu <[email protected]>
Reviewed-by: Álvaro Herrera <[email protected]>
Discussion: https://postgr.es/m/CADzfLwWOVyJygX6BFuyuhTKkJ7uw2e8OcVCDnf6iqnOFhMPE%2BA%40mail.gmail.com
---
 .../test_misc/t/010_index_concurrently_upsert.pl | 16 ++++++++++------
 1 file changed, 10 insertions(+), 6 deletions(-)

diff --git a/src/test/modules/test_misc/t/010_index_concurrently_upsert.pl b/src/test/modules/test_misc/t/010_index_concurrently_upsert.pl
index 5953af11e9e..1f3dcd80886 100644
--- a/src/test/modules/test_misc/t/010_index_concurrently_upsert.pl
+++ b/src/test/modules/test_misc/t/010_index_concurrently_upsert.pl
@@ -1,5 +1,5 @@
 
-# Copyright (c) 2025, PostgreSQL Global Development Group
+# Copyright (c) 2026, PostgreSQL Global Development Group
 
 # Test INSERT ON CONFLICT DO UPDATE behavior concurrent with
 # CREATE INDEX CONCURRENTLY and REINDEX CONCURRENTLY.
@@ -793,7 +793,7 @@ done_testing();
 sub wait_for_injection_point
 {
 	my ($node, $point_name, $timeout) = @_;
-	$timeout //= $PostgreSQL::Test::Utils::timeout_default;
+	$timeout //= $PostgreSQL::Test::Utils::timeout_default / 2;
 
 	for (my $elapsed = 0; $elapsed < $timeout * 10; $elapsed++)
 	{
@@ -833,19 +833,23 @@ sub ok_injection_point
 }
 
 # Helper: Wait for a specific backend to become idle.
-# Returns true if idle, false if timeout.
+# Returns true if idle, false if waiting for injection point or timeout.
 sub wait_for_idle
 {
 	my ($node, $pid, $timeout) = @_;
-	$timeout //= $PostgreSQL::Test::Utils::timeout_default;
+	$timeout //= $PostgreSQL::Test::Utils::timeout_default / 2;
 
 	for (my $elapsed = 0; $elapsed < $timeout * 10; $elapsed++)
 	{
-		my $state = $node->safe_psql(
+		my $result = $node->safe_psql(
 			'postgres', qq[
-			SELECT state FROM pg_stat_activity WHERE pid = $pid;
+			SELECT state, wait_event_type FROM pg_stat_activity WHERE pid = $pid;
 		]);
+		my ($state, $wait_event_type) = split(/\|/, $result, 2);
+		$state //= '';
+		$wait_event_type //= '';
 		return 1 if $state eq 'idle';
+		return 0 if $wait_event_type eq 'InjectionPoint';
 		sleep(0.1);
 	}
 	return 0;
-- 
2.43.0

From 7f0aa3ae4b6be42b0797f757faedf2d43a02ca72 Mon Sep 17 00:00:00 2001
From: nkey <[email protected]>
Date: Thu, 8 Jan 2026 21:09:07 +0300
Subject: [PATCH v1 2/2] Allow test_misc TAP tests to run during installcheck
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Commit e1c971945d62 disabled installcheck for the entire test_misc module because injection points are cluster-wide and incompatible with a concurrently running test execution. However, this also prevents other tests in the module (001-009) from running during installcheck.

Instead of disabling the whole module, introduce an 'is_installcheck' environment variable set by both Meson and Make build systems.

Individual tests can check this variable and skip themselves when running under installcheck. The injection point test now skips itself when is_installcheck='yes', while other tests in test_misc continue to run normally.

Author: Mihail Nikalayeu <[email protected]>
Reviewed-by: Álvaro Herrera <[email protected]>
Discussion: https://postgr.es/m/CADzfLwWOVyJygX6BFuyuhTKkJ7uw2e8OcVCDnf6iqnOFhMPE%2BA%40mail.gmail.com
---
 meson.build                                                | 6 ++++--
 src/Makefile.global.in                                     | 7 +++++++
 src/test/modules/test_misc/Makefile                        | 3 ---
 src/test/modules/test_misc/meson.build                     | 2 --
 .../modules/test_misc/t/010_index_concurrently_upsert.pl   | 5 +++++
 5 files changed, 16 insertions(+), 7 deletions(-)

diff --git a/meson.build b/meson.build
index 2064d1b0a8d..91d6bed7d46 100644
--- a/meson.build
+++ b/meson.build
@@ -3811,9 +3811,11 @@ endforeach # directories with tests
 # repeat condition so meson realizes version dependency
 add_test_setup('tmp_install',
   is_default: true,
-  exclude_suites: running_suites)
+  exclude_suites: running_suites,
+  env: {'is_installcheck': 'no'})
 add_test_setup('running',
-  exclude_suites: ['setup'] + install_suites)
+  exclude_suites: ['setup'] + install_suites,
+  env: {'is_installcheck': 'yes'})
 
 
 
diff --git a/src/Makefile.global.in b/src/Makefile.global.in
index 371cd7eba2c..2879310a92e 100644
--- a/src/Makefile.global.in
+++ b/src/Makefile.global.in
@@ -469,6 +469,7 @@ cd $(srcdir) && \
    TESTLOGDIR='$(CURDIR)/tmp_check/log' \
    TESTDATADIR='$(CURDIR)/tmp_check' \
    PATH="$(bindir):$(CURDIR):$$PATH" \
+   is_installcheck='yes' \
    PGPORT='6$(DEF_PGPORT)' top_builddir='$(CURDIR)/$(top_builddir)' \
    PG_REGRESS='$(CURDIR)/$(top_builddir)/src/test/regress/pg_regress' \
    share_contrib_dir='$(DESTDIR)$(datadir)/$(datamoduledir)' \
@@ -483,6 +484,7 @@ cd $(srcdir) && \
    TESTLOGDIR='$(CURDIR)/tmp_check/log' \
    TESTDATADIR='$(CURDIR)/tmp_check' \
    PATH="$(bindir):$(CURDIR):$$PATH" \
+   is_installcheck='yes' \
    PGPORT='6$(DEF_PGPORT)' \
    PG_REGRESS='$(top_builddir)/src/test/regress/pg_regress' \
    $(PROVE) $(PG_PROVE_FLAGS) $(PROVE_FLAGS) $(if $(PROVE_TESTS),$(PROVE_TESTS),t/*.pl)
@@ -497,6 +499,7 @@ cd $(srcdir) && \
    TESTLOGDIR='$(CURDIR)/tmp_check/log' \
    TESTDATADIR='$(CURDIR)/tmp_check' \
    $(with_temp_install) \
+   is_installcheck='no' \
    PGPORT='6$(DEF_PGPORT)' top_builddir='$(CURDIR)/$(top_builddir)' \
    PG_REGRESS='$(CURDIR)/$(top_builddir)/src/test/regress/pg_regress' \
    share_contrib_dir='$(abs_top_builddir)/tmp_install$(datadir)/$(datamoduledir)' \
@@ -705,6 +708,7 @@ pg_regress_clean_files = results/ regression.diffs regression.out tmp_check/ tmp
 pg_regress_check = \
     echo "\# +++ regress check in $(subdir) +++" && \
     $(with_temp_install) \
+    is_installcheck='no' \
     $(top_builddir)/src/test/regress/pg_regress \
     --temp-instance=./tmp_check \
     --inputdir=$(srcdir) \
@@ -713,6 +717,7 @@ pg_regress_check = \
     $(pg_regress_locale_flags) $(EXTRA_REGRESS_OPTS)
 pg_regress_installcheck = \
     echo "\# +++ regress install-check in $(subdir) +++" && \
+    is_installcheck='yes' \
     $(top_builddir)/src/test/regress/pg_regress \
     --inputdir=$(srcdir) \
     --bindir='$(bindir)' \
@@ -721,6 +726,7 @@ pg_regress_installcheck = \
 pg_isolation_regress_check = \
     echo "\# +++ isolation check in $(subdir) +++" && \
     $(with_temp_install) \
+    is_installcheck='no' \
     $(top_builddir)/src/test/isolation/pg_isolation_regress \
     --temp-instance=./tmp_check_iso \
     --inputdir=$(srcdir) --outputdir=output_iso \
@@ -729,6 +735,7 @@ pg_isolation_regress_check = \
     $(pg_regress_locale_flags) $(EXTRA_REGRESS_OPTS)
 pg_isolation_regress_installcheck = \
     echo "\# +++ isolation install-check in $(subdir) +++" && \
+    is_installcheck='yes' \
     $(top_builddir)/src/test/isolation/pg_isolation_regress \
     --inputdir=$(srcdir) --outputdir=output_iso \
     --bindir='$(bindir)' \
diff --git a/src/test/modules/test_misc/Makefile b/src/test/modules/test_misc/Makefile
index fedbef071ef..399b9094a38 100644
--- a/src/test/modules/test_misc/Makefile
+++ b/src/test/modules/test_misc/Makefile
@@ -5,9 +5,6 @@ TAP_TESTS = 1
 EXTRA_INSTALL=src/test/modules/injection_points \
 	contrib/test_decoding
 
-# The injection points are cluster-wide, so disable installcheck
-NO_INSTALLCHECK = 1
-
 export enable_injection_points
 
 ifdef USE_PGXS
diff --git a/src/test/modules/test_misc/meson.build b/src/test/modules/test_misc/meson.build
index 6e8db1621a7..423f66d8bdc 100644
--- a/src/test/modules/test_misc/meson.build
+++ b/src/test/modules/test_misc/meson.build
@@ -20,7 +20,5 @@ tests += {
       't/009_log_temp_files.pl',
       't/010_index_concurrently_upsert.pl',
     ],
-    # The injection points are cluster-wide, so disable installcheck
-    'runningcheck': false,
   },
 }
diff --git a/src/test/modules/test_misc/t/010_index_concurrently_upsert.pl b/src/test/modules/test_misc/t/010_index_concurrently_upsert.pl
index 1f3dcd80886..dbf04c6e345 100644
--- a/src/test/modules/test_misc/t/010_index_concurrently_upsert.pl
+++ b/src/test/modules/test_misc/t/010_index_concurrently_upsert.pl
@@ -19,6 +19,11 @@ use Test::More;
 plan skip_all => 'Injection points not supported by this build'
   unless $ENV{enable_injection_points} eq 'yes';
 
+plan skip_all => 'The injection points are cluster-wide, so skip for installcheck'
+    unless ($ENV{is_installcheck} eq 'no');
+
+is($ENV{is_installcheck}, 'no', 'should not be executed during installcheck');
+
 # Node initialization
 my $node = PostgreSQL::Test::Cluster->new('node');
 $node->init();
-- 
2.43.0

Reply via email to