]> git.ipfire.org Git - thirdparty/postgresql.git/commitdiff
aio: Refactor tests in preparation for more tests
authorAndres Freund <andres@anarazel.de>
Thu, 26 Mar 2026 14:07:59 +0000 (10:07 -0400)
committerAndres Freund <andres@anarazel.de>
Thu, 26 Mar 2026 14:38:56 +0000 (10:38 -0400)
In a future commit more AIO related tests are due to be introduced. However
001_aio.pl already is fairly large.

This commit introduces a new TestAio package with helpers for writing AIO
related tests. Then it uses the new helpers to simplify the existing
001_aio.pl by iterating over all supported io_methods. This will be
particularly helpful because additional methods already have been submitted.

Additionally this commit splits out testing of initdb using a non-default
method into its own test. While that test is somewhat important, it's fairly
slow and doesn't break that often. For development velocity it's helpful for
001_aio.pl to be faster.

While particularly the latter could benefit from being its own commit, it
seems to introduce more back-and-forth than it's worth.

Author: Andres Freund <andres@anarazel.de>
Reviewed-by: Nazir Bilal Yavuz <byavuz81@gmail.com>
Reviewed-by: Melanie Plageman <melanieplageman@gmail.com>
Discussion: https://postgr.es/m/zljergweqti7x67lg5ije2rzjusie37nslsnkjkkby4laqqbfw@3p3zu522yykv

src/test/modules/test_aio/meson.build
src/test/modules/test_aio/t/001_aio.pl
src/test/modules/test_aio/t/003_initdb.pl [new file with mode: 0644]
src/test/modules/test_aio/t/TestAio.pm [new file with mode: 0644]

index fefa25bc5ab082cf73a2a2c11ee468188d114804..18a797f3a3b5d20135ff429b4c123702e72e052c 100644 (file)
@@ -32,6 +32,7 @@ tests += {
     'tests': [
       't/001_aio.pl',
       't/002_io_workers.pl',
+      't/003_initdb.pl',
     ],
   },
 }
index 5c634ec3ca9265b7129693dbc329b3fdb169eaaa..e18b2a2b8ae3f2ca0558b360e9087320df8117a7 100644 (file)
@@ -7,126 +7,56 @@ use PostgreSQL::Test::Cluster;
 use PostgreSQL::Test::Utils;
 use Test::More;
 
+use FindBin;
+use lib $FindBin::RealBin;
 
-###
-# Test io_method=worker
-###
-my $node_worker = create_node('worker');
-$node_worker->start();
-
-test_generic('worker', $node_worker);
-SKIP:
-{
-       skip 'Injection points not supported by this build', 1
-         unless $ENV{enable_injection_points} eq 'yes';
-       test_inject_worker('worker', $node_worker);
-}
+use TestAio;
 
-$node_worker->stop();
+my @methods = TestAio::supported_io_methods();
+my %nodes;
 
 
 ###
-# Test io_method=io_uring
+# Create and configure one instance for each io_method
 ###
 
-if (have_io_uring())
+foreach my $method (@methods)
 {
-       my $node_uring = create_node('io_uring');
-       $node_uring->start();
-       test_generic('io_uring', $node_uring);
-       $node_uring->stop();
-}
-
-
-###
-# Test io_method=sync
-###
-
-my $node_sync = create_node('sync');
+       my $node = PostgreSQL::Test::Cluster->new($method);
 
-# just to have one test not use the default auto-tuning
+       $nodes{$method} = $node;
+       $node->init();
+       $node->append_conf('postgresql.conf', "io_method=$method");
+       TestAio::configure($node);
+}
 
-$node_sync->append_conf(
+# Just to have one test not use the default auto-tuning
+$nodes{'sync'}->append_conf(
        'postgresql.conf', qq(
-io_max_concurrency=4
+ io_max_concurrency=4
 ));
 
-$node_sync->start();
-test_generic('sync', $node_sync);
-$node_sync->stop();
-
-done_testing();
-
 
 ###
-# Test Helpers
+# Execute the tests for each io_method
 ###
 
-sub create_node
+foreach my $method (@methods)
 {
-       local $Test::Builder::Level = $Test::Builder::Level + 1;
-
-       my $io_method = shift;
+       my $node = $nodes{$method};
 
-       my $node = PostgreSQL::Test::Cluster->new($io_method);
-
-       # Want to test initdb for each IO method, otherwise we could just reuse
-       # the cluster.
-       #
-       # Unfortunately Cluster::init() puts PG_TEST_INITDB_EXTRA_OPTS after the
-       # options specified by ->extra, if somebody puts -c io_method=xyz in
-       # PG_TEST_INITDB_EXTRA_OPTS it would break this test. Fix that up if we
-       # detect it.
-       local $ENV{PG_TEST_INITDB_EXTRA_OPTS} = $ENV{PG_TEST_INITDB_EXTRA_OPTS};
-       if (defined $ENV{PG_TEST_INITDB_EXTRA_OPTS}
-               && $ENV{PG_TEST_INITDB_EXTRA_OPTS} =~ m/io_method=/)
-       {
-               $ENV{PG_TEST_INITDB_EXTRA_OPTS} .= " -c io_method=$io_method";
-       }
-
-       $node->init(extra => [ '-c', "io_method=$io_method" ]);
-
-       $node->append_conf(
-               'postgresql.conf', qq(
-shared_preload_libraries=test_aio
-log_min_messages = 'DEBUG3'
-log_statement=all
-log_error_verbosity=default
-restart_after_crash=false
-temp_buffers=100
-));
+       $node->start();
+       test_io_method($method, $node);
+       $node->stop();
+}
 
-       # Even though we used -c io_method=... above, if TEMP_CONFIG sets
-       # io_method, it'd override the setting persisted at initdb time. While
-       # using (and later verifying) the setting from initdb provides some
-       # verification of having used the io_method during initdb, it's probably
-       # not worth the complication of only appending if the variable is set in
-       # in TEMP_CONFIG.
-       $node->append_conf(
-               'postgresql.conf', qq(
-io_method=$io_method
-));
+done_testing();
 
-       ok(1, "$io_method: initdb");
 
-       return $node;
-}
+###
+# Test Helpers
+###
 
-sub have_io_uring
-{
-       # To detect if io_uring is supported, we look at the error message for
-       # assigning an invalid value to an enum GUC, which lists all the valid
-       # options. We need to use -C to deal with running as administrator on
-       # windows, the superuser check is omitted if -C is used.
-       my ($stdout, $stderr) =
-         run_command [qw(postgres -C invalid -c io_method=invalid)];
-       die "can't determine supported io_method values"
-         unless $stderr =~ m/Available values: ([^\.]+)\./;
-       my $methods = $1;
-       note "supported io_method values are: $methods";
-
-       return ($methods =~ m/io_uring/) ? 1 : 0;
-}
 
 sub psql_like
 {
@@ -1490,8 +1420,8 @@ SELECT read_rel_block_ll('tbl_cs_fail', 3, nblocks=>1, zero_on_error=>true);),
 }
 
 
-# Run all tests that are supported for all io_methods
-sub test_generic
+# Run all tests that for the specified node / io_method
+sub test_io_method
 {
        my $io_method = shift;
        my $node = shift;
@@ -1526,10 +1456,23 @@ CHECKPOINT;
        test_ignore_checksum($io_method, $node);
        test_checksum_createdb($io_method, $node);
 
+       # generic injection tests
   SKIP:
        {
                skip 'Injection points not supported by this build', 1
                  unless $ENV{enable_injection_points} eq 'yes';
                test_inject($io_method, $node);
        }
+
+       # worker specific injection tests
+       if ($io_method eq 'worker')
+       {
+         SKIP:
+               {
+                       skip 'Injection points not supported by this build', 1
+                         unless $ENV{enable_injection_points} eq 'yes';
+
+                       test_inject_worker($io_method, $node);
+               }
+       }
 }
diff --git a/src/test/modules/test_aio/t/003_initdb.pl b/src/test/modules/test_aio/t/003_initdb.pl
new file mode 100644 (file)
index 0000000..c03ae58
--- /dev/null
@@ -0,0 +1,71 @@
+# Copyright (c) 2024-2025, PostgreSQL Global Development Group
+#
+# Test initdb for each IO method. This is done separately from 001_aio.pl, as
+# it isn't fast. This way the more commonly failing / hacked-on 001_aio.pl can
+# be iterated on more quickly.
+
+use strict;
+use warnings FATAL => 'all';
+use PostgreSQL::Test::Cluster;
+use PostgreSQL::Test::Utils;
+use Test::More;
+
+use FindBin;
+use lib $FindBin::RealBin;
+
+use TestAio;
+
+
+foreach my $method (TestAio::supported_io_methods())
+{
+       test_create_node($method);
+}
+
+done_testing();
+
+
+sub test_create_node
+{
+       local $Test::Builder::Level = $Test::Builder::Level + 1;
+
+       my $io_method = shift;
+
+       my $node = PostgreSQL::Test::Cluster->new($io_method);
+
+       # Want to test initdb for each IO method, otherwise we could just reuse
+       # the cluster.
+       #
+       # Unfortunately Cluster::init() puts PG_TEST_INITDB_EXTRA_OPTS after the
+       # options specified by ->extra, if somebody puts -c io_method=xyz in
+       # PG_TEST_INITDB_EXTRA_OPTS it would break this test. Fix that up if we
+       # detect it.
+       local $ENV{PG_TEST_INITDB_EXTRA_OPTS} = $ENV{PG_TEST_INITDB_EXTRA_OPTS};
+       if (defined $ENV{PG_TEST_INITDB_EXTRA_OPTS}
+               && $ENV{PG_TEST_INITDB_EXTRA_OPTS} =~ m/io_method=/)
+       {
+               $ENV{PG_TEST_INITDB_EXTRA_OPTS} .= " -c io_method=$io_method";
+       }
+
+       $node->init(extra => [ '-c', "io_method=$io_method" ]);
+
+       TestAio::configure($node);
+
+       # Even though we used -c io_method=... above, if TEMP_CONFIG sets
+       # io_method, it'd override the setting persisted at initdb time. While
+       # using (and later verifying) the setting from initdb provides some
+       # verification of having used the io_method during initdb, it's probably
+       # not worth the complication of only appending if the variable is set in
+       # in TEMP_CONFIG.
+       $node->append_conf(
+               'postgresql.conf', qq(
+io_method=$io_method
+));
+
+       ok(1, "$io_method: initdb");
+
+       $node->start();
+       $node->stop();
+       ok(1, "$io_method: start & stop");
+
+       return $node;
+}
diff --git a/src/test/modules/test_aio/t/TestAio.pm b/src/test/modules/test_aio/t/TestAio.pm
new file mode 100644 (file)
index 0000000..5bc80a9
--- /dev/null
@@ -0,0 +1,90 @@
+# Copyright (c) 2024-2025, PostgreSQL Global Development Group
+
+=pod
+
+=head1 NAME
+
+TestAio - helpers for writing AIO related tests
+
+=cut
+
+package TestAio;
+
+use PostgreSQL::Test::Cluster;
+use PostgreSQL::Test::Utils;
+use Test::More;
+
+
+=pod
+
+=head1 METHODS
+
+=over
+
+=item TestAio::supported_io_methods()
+
+Return an array of all the supported values for the io_method GUC
+
+=cut
+
+sub supported_io_methods()
+{
+       my @io_methods = ('worker');
+
+       push(@io_methods, "io_uring") if have_io_uring();
+
+       # Return sync last, as it will least commonly fail
+       push(@io_methods, "sync");
+
+       return @io_methods;
+}
+
+
+=item TestAio::configure()
+
+Prepare a cluster for AIO test
+
+=cut
+
+sub configure
+{
+       my $node = shift;
+
+       $node->append_conf(
+               'postgresql.conf', qq(
+shared_preload_libraries=test_aio
+log_min_messages = 'DEBUG3'
+log_statement=all
+log_error_verbosity=default
+restart_after_crash=false
+temp_buffers=100
+));
+
+}
+
+
+=pod
+
+=item TestAio::have_io_uring()
+
+Return if io_uring is supported
+
+=cut
+
+sub have_io_uring
+{
+       # To detect if io_uring is supported, we look at the error message for
+       # assigning an invalid value to an enum GUC, which lists all the valid
+       # options. We need to use -C to deal with running as administrator on
+       # windows, the superuser check is omitted if -C is used.
+       my ($stdout, $stderr) =
+         run_command [qw(postgres -C invalid -c io_method=invalid)];
+       die "can't determine supported io_method values"
+         unless $stderr =~ m/Available values: ([^\.]+)\./;
+       my $methods = $1;
+       note "supported io_method values are: $methods";
+
+       return ($methods =~ m/io_uring/) ? 1 : 0;
+}
+
+1;