nixpkgs/nixos/tests/postgresql/postgresql-wal-receiver.nix
Maximilian Bosch eb96c8dc5b
postgresql: refactor postgresqlVersions attribute & tests
Every postgresql testcase essentially does the following things:

* Filter `postgresqlVersions` for server packages
* Filter postgresql server packages for suitable ones (i.e. extensions
  must support the given version)
* Generate an attribute-set of testcases

The first item became necessary in
7ab1e88833 given that
`postgresql/default.nix` now exposes JIT and non-JIT servers AND a
`libpq` that is not suitable for the tests here.

This changes restructures this a little bit, i.e.:

* Having an attribute-set that contains a bunch of postgresql servers
  and a single client package seems odd (and the sole consumer of
  `postgresqlVersions` in nixpkgs, the test suite, has to take that into
  account). Hence, postgresql's default.nix now provides `libpq` (the client)
  and a `postgresqlVersions` attribute with all supported JIT and non-JIT
  variants of postgresql.

* Each test-case gets a third argument, a function called `genTests`:
  this function sets `recurseForDerivations = true;` and generates an
  attribute-set of tests for each postgresql version given a function
  that returns a testcase or multiple test-cases (`makeTestFor`). The
  argument to `makeTestFor` is a postgresql server package.

  This function also accepts a filter predicate that is passed against
  `filterAttrs` to remove postgresql server packages that are not
  suitable for the test (e.g. because the version isn't supported by the
  extension to test).

I checked by making sure that the `.drv` doesn't change on staging with
this change on top for postgresq, postgresql-jit,
postgresql-wal-receiver, postgresql-tls-client-cert, anonymizer, pgjwt,
pgvecto-rs, timescaledb, tsja and wal2json.
2025-01-26 21:58:57 +01:00

113 lines
4.1 KiB
Nix

{
pkgs,
makeTest,
genTests,
}:
let
inherit (pkgs) lib;
makeTestFor =
package:
let
postgresqlDataDir = "/var/lib/postgresql/${package.psqlSchema}";
replicationUser = "wal_receiver_user";
replicationSlot = "wal_receiver_slot";
replicationConn = "postgresql://${replicationUser}@localhost";
baseBackupDir = "/var/cache/wals/pg_basebackup";
walBackupDir = "/var/cache/wals/pg_wal";
recoveryFile = pkgs.writeTextDir "recovery.signal" "";
in
makeTest {
name = "postgresql-wal-receiver-${package.name}";
meta.maintainers = with lib.maintainers; [ euxane ];
nodes.machine =
{ ... }:
{
systemd.tmpfiles.rules = [
"d /var/cache/wals 0750 postgres postgres - -"
];
services.postgresql = {
inherit package;
enable = true;
enableJIT = lib.hasInfix "-jit-" package.name;
settings = {
max_replication_slots = 10;
max_wal_senders = 10;
recovery_end_command = "touch recovery.done";
restore_command = "cp ${walBackupDir}/%f %p";
wal_level = "archive"; # alias for replica on pg >= 9.6
};
authentication = ''
host replication ${replicationUser} all trust
'';
initialScript = pkgs.writeText "init.sql" ''
create user ${replicationUser} replication;
select * from pg_create_physical_replication_slot('${replicationSlot}');
'';
};
services.postgresqlWalReceiver.receivers.main = {
postgresqlPackage = package;
connection = replicationConn;
slot = replicationSlot;
directory = walBackupDir;
};
# This is only to speedup test, it isn't time racing. Service is set to autorestart always,
# default 60sec is fine for real system, but is too much for a test
systemd.services.postgresql-wal-receiver-main.serviceConfig.RestartSec = lib.mkForce 5;
systemd.services.postgresql.serviceConfig.ReadWritePaths = [ "/var/cache/wals" ];
};
testScript = ''
# make an initial base backup
machine.wait_for_unit("postgresql")
machine.wait_for_unit("postgresql-wal-receiver-main")
# WAL receiver healthchecks PG every 5 seconds, so let's be sure they have connected each other
# required only for 9.4
machine.sleep(5)
machine.succeed(
"${package}/bin/pg_basebackup --dbname=${replicationConn} --pgdata=${baseBackupDir}"
)
# create a dummy table with 100 records
machine.succeed(
"sudo -u postgres psql --command='create table dummy as select * from generate_series(1, 100) as val;'"
)
# stop postgres and destroy data
machine.systemctl("stop postgresql")
machine.systemctl("stop postgresql-wal-receiver-main")
machine.succeed("rm -r ${postgresqlDataDir}/{base,global,pg_*}")
# restore the base backup
machine.succeed(
"cp -r ${baseBackupDir}/* ${postgresqlDataDir} && chown postgres:postgres -R ${postgresqlDataDir}"
)
# prepare WAL and recovery
machine.succeed("chmod a+rX -R ${walBackupDir}")
machine.execute(
"for part in ${walBackupDir}/*.partial; do mv $part ''${part%%.*}; done"
) # make use of partial segments too
machine.succeed(
"cp ${recoveryFile}/* ${postgresqlDataDir}/ && chmod 666 ${postgresqlDataDir}/recovery*"
)
# replay WAL
machine.systemctl("start postgresql")
machine.wait_for_file("${postgresqlDataDir}/recovery.done")
machine.systemctl("restart postgresql")
machine.wait_for_unit("postgresql")
# check that our records have been restored
machine.succeed(
"test $(sudo -u postgres psql --pset='pager=off' --tuples-only --command='select count(distinct val) from dummy;') -eq 100"
)
'';
};
in
genTests { inherit makeTestFor; }