summaryrefslogtreecommitdiffstats
path: root/nixos/tests/postgresql/postgresql-wal-receiver.nix
blob: f929596cf6141c5004d9325d516ef10bee108ed1 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
{
  runTest,
  genTests,
  ...
}:

let
  makeTestFor =
    package:
    runTest (
      { lib, pkgs, ... }:
      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
      {
        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;
              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.target")
          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.target")

          # 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; }