summary refs log tree commit diff
path: root/nixos/tests/postgresql-wal-receiver.nix
blob: c50746aa838e4d18e55dfcf8abfc401d8416686e (plain) (blame)
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
{ system ? builtins.currentSystem
, config ? { }
, pkgs ? import ../.. { inherit system config; } }:

with import ../lib/testing.nix { inherit system pkgs; };
with pkgs.lib;

let
  makePostgresqlWalReceiverTest = subTestName: postgresqlPackage: let

  postgresqlDataDir = "/var/db/postgresql/test";
  replicationUser = "wal_receiver_user";
  replicationSlot = "wal_receiver_slot";
  replicationConn = "postgresql://${replicationUser}@localhost";
  baseBackupDir = "/tmp/pg_basebackup";
  walBackupDir = "/tmp/pg_wal";
  atLeast12 = versionAtLeast postgresqlPackage.version "12.0";
  restoreCommand = ''
    restore_command = 'cp ${walBackupDir}/%f %p'
  '';

  recoveryFile = if atLeast12
      then pkgs.writeTextDir "recovery.signal" ""
      else pkgs.writeTextDir "recovery.conf" "${restoreCommand}";

  in makeTest {
    name = "postgresql-wal-receiver-${subTestName}";
    meta.maintainers = with maintainers; [ pacien ];

    machine = { ... }: {
      # Needed because this test uses a non-default 'services.postgresql.dataDir'.
      systemd.tmpfiles.rules = [
        "d /var/db/postgresql 0700 postgres postgres"
      ];
      services.postgresql = {
        package = postgresqlPackage;
        enable = true;
        dataDir = postgresqlDataDir;
        extraConfig = ''
          wal_level = archive # alias for replica on pg >= 9.6
          max_wal_senders = 10
          max_replication_slots = 10
        '' + optionalString atLeast12 ''
          ${restoreCommand}
          recovery_end_command = 'touch recovery.done'
        '';
        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 = {
        inherit postgresqlPackage;
        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 = mkForce 5;
    };

    testScript = ''
      # make an initial base backup
      $machine->waitForUnit('postgresql');
      $machine->waitForUnit('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('${postgresqlPackage}/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->waitForFile('${postgresqlDataDir}/recovery.done');
      $machine->systemctl('restart postgresql');
      $machine->waitForUnit('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 mapAttrs makePostgresqlWalReceiverTest (import ../../pkgs/servers/sql/postgresql pkgs)