1{
2 pkgs,
3 makeTest,
4 genTests,
5}:
6
7let
8 inherit (pkgs) lib;
9
10 makeTestFor =
11 package:
12 let
13 postgresqlDataDir = "/var/lib/postgresql/${package.psqlSchema}";
14 replicationUser = "wal_receiver_user";
15 replicationSlot = "wal_receiver_slot";
16 replicationConn = "postgresql://${replicationUser}@localhost";
17 baseBackupDir = "/var/cache/wals/pg_basebackup";
18 walBackupDir = "/var/cache/wals/pg_wal";
19 recoveryFile = pkgs.writeTextDir "recovery.signal" "";
20 in
21 makeTest {
22 name = "postgresql-wal-receiver-${package.name}";
23 meta.maintainers = with lib.maintainers; [ euxane ];
24
25 nodes.machine =
26 { ... }:
27 {
28 systemd.tmpfiles.rules = [
29 "d /var/cache/wals 0750 postgres postgres - -"
30 ];
31
32 services.postgresql = {
33 inherit package;
34 enable = true;
35 settings = {
36 max_replication_slots = 10;
37 max_wal_senders = 10;
38 recovery_end_command = "touch recovery.done";
39 restore_command = "cp ${walBackupDir}/%f %p";
40 wal_level = "archive"; # alias for replica on pg >= 9.6
41 };
42 authentication = ''
43 host replication ${replicationUser} all trust
44 '';
45 initialScript = pkgs.writeText "init.sql" ''
46 create user ${replicationUser} replication;
47 select * from pg_create_physical_replication_slot('${replicationSlot}');
48 '';
49 };
50
51 services.postgresqlWalReceiver.receivers.main = {
52 postgresqlPackage = package;
53 connection = replicationConn;
54 slot = replicationSlot;
55 directory = walBackupDir;
56 };
57 # This is only to speedup test, it isn't time racing. Service is set to autorestart always,
58 # default 60sec is fine for real system, but is too much for a test
59 systemd.services.postgresql-wal-receiver-main.serviceConfig.RestartSec = lib.mkForce 5;
60 systemd.services.postgresql.serviceConfig.ReadWritePaths = [ "/var/cache/wals" ];
61 };
62
63 testScript = ''
64 # make an initial base backup
65 machine.wait_for_unit("postgresql")
66 machine.wait_for_unit("postgresql-wal-receiver-main")
67 # WAL receiver healthchecks PG every 5 seconds, so let's be sure they have connected each other
68 # required only for 9.4
69 machine.sleep(5)
70 machine.succeed(
71 "${package}/bin/pg_basebackup --dbname=${replicationConn} --pgdata=${baseBackupDir}"
72 )
73
74 # create a dummy table with 100 records
75 machine.succeed(
76 "sudo -u postgres psql --command='create table dummy as select * from generate_series(1, 100) as val;'"
77 )
78
79 # stop postgres and destroy data
80 machine.systemctl("stop postgresql")
81 machine.systemctl("stop postgresql-wal-receiver-main")
82 machine.succeed("rm -r ${postgresqlDataDir}/{base,global,pg_*}")
83
84 # restore the base backup
85 machine.succeed(
86 "cp -r ${baseBackupDir}/* ${postgresqlDataDir} && chown postgres:postgres -R ${postgresqlDataDir}"
87 )
88
89 # prepare WAL and recovery
90 machine.succeed("chmod a+rX -R ${walBackupDir}")
91 machine.execute(
92 "for part in ${walBackupDir}/*.partial; do mv $part ''${part%%.*}; done"
93 ) # make use of partial segments too
94 machine.succeed(
95 "cp ${recoveryFile}/* ${postgresqlDataDir}/ && chmod 666 ${postgresqlDataDir}/recovery*"
96 )
97
98 # replay WAL
99 machine.systemctl("start postgresql")
100 machine.wait_for_file("${postgresqlDataDir}/recovery.done")
101 machine.systemctl("restart postgresql")
102 machine.wait_for_unit("postgresql")
103
104 # check that our records have been restored
105 machine.succeed(
106 "test $(sudo -u postgres psql --pset='pager=off' --tuples-only --command='select count(distinct val) from dummy;') -eq 100"
107 )
108 '';
109 };
110in
111genTests { inherit makeTestFor; }