at 25.11-pre 7.9 kB view raw
1{ 2 config, 3 lib, 4 pkgs, 5 ... 6}: 7let 8 receiverSubmodule = { 9 options = { 10 postgresqlPackage = lib.mkPackageOption pkgs "postgresql" { 11 example = "postgresql_15"; 12 }; 13 14 directory = lib.mkOption { 15 type = lib.types.path; 16 example = lib.literalExpression "/mnt/pg_wal/main/"; 17 description = '' 18 Directory to write the output to. 19 ''; 20 }; 21 22 statusInterval = lib.mkOption { 23 type = lib.types.int; 24 default = 10; 25 description = '' 26 Specifies the number of seconds between status packets sent back to the server. 27 This allows for easier monitoring of the progress from server. 28 A value of zero disables the periodic status updates completely, 29 although an update will still be sent when requested by the server, to avoid timeout disconnect. 30 ''; 31 }; 32 33 slot = lib.mkOption { 34 type = lib.types.str; 35 default = ""; 36 example = "some_slot_name"; 37 description = '' 38 Require {command}`pg_receivewal` to use an existing replication slot (see 39 [Section 26.2.6 of the PostgreSQL manual](https://www.postgresql.org/docs/current/warm-standby.html#STREAMING-REPLICATION-SLOTS)). 40 When this option is used, {command}`pg_receivewal` will report a flush position to the server, 41 indicating when each segment has been synchronized to disk so that the server can remove that segment if it is not otherwise needed. 42 43 When the replication client of {command}`pg_receivewal` is configured on the server as a synchronous standby, 44 then using a replication slot will report the flush position to the server, but only when a WAL file is closed. 45 Therefore, that configuration will cause transactions on the primary to wait for a long time and effectively not work satisfactorily. 46 The option {option}`synchronous` must be specified in addition to make this work correctly. 47 ''; 48 }; 49 50 synchronous = lib.mkOption { 51 type = lib.types.bool; 52 default = false; 53 description = '' 54 Flush the WAL data to disk immediately after it has been received. 55 Also send a status packet back to the server immediately after flushing, regardless of {option}`statusInterval`. 56 57 This option should be specified if the replication client of {command}`pg_receivewal` is configured on the server as a synchronous standby, 58 to ensure that timely feedback is sent to the server. 59 ''; 60 }; 61 62 compress = lib.mkOption { 63 type = lib.types.ints.between 0 9; 64 default = 0; 65 description = '' 66 Enables gzip compression of write-ahead logs, and specifies the compression level 67 (`0` through `9`, `0` being no compression and `9` being best compression). 68 The suffix `.gz` will automatically be added to all filenames. 69 70 This option requires PostgreSQL >= 10. 71 ''; 72 }; 73 74 connection = lib.mkOption { 75 type = lib.types.str; 76 example = "postgresql://user@somehost"; 77 description = '' 78 Specifies parameters used to connect to the server, as a connection string. 79 See [Section 34.1.1 of the PostgreSQL manual](https://www.postgresql.org/docs/current/libpq-connect.html#LIBPQ-CONNSTRING) for more information. 80 81 Because {command}`pg_receivewal` doesn't connect to any particular database in the cluster, 82 database name in the connection string will be ignored. 83 ''; 84 }; 85 86 extraArgs = lib.mkOption { 87 type = with lib.types; listOf str; 88 default = [ ]; 89 example = lib.literalExpression '' 90 [ 91 "--no-sync" 92 ] 93 ''; 94 description = '' 95 A list of extra arguments to pass to the {command}`pg_receivewal` command. 96 ''; 97 }; 98 99 environment = lib.mkOption { 100 type = with lib.types; attrsOf str; 101 default = { }; 102 example = lib.literalExpression '' 103 { 104 PGPASSFILE = "/private/passfile"; 105 PGSSLMODE = "require"; 106 } 107 ''; 108 description = '' 109 Environment variables passed to the service. 110 Usable parameters are listed in [Section 34.14 of the PostgreSQL manual](https://www.postgresql.org/docs/current/libpq-envars.html). 111 ''; 112 }; 113 }; 114 }; 115 116in 117{ 118 options = { 119 services.postgresqlWalReceiver = { 120 receivers = lib.mkOption { 121 type = with lib.types; attrsOf (submodule receiverSubmodule); 122 default = { }; 123 example = lib.literalExpression '' 124 { 125 main = { 126 postgresqlPackage = pkgs.postgresql_15; 127 directory = /mnt/pg_wal/main/; 128 slot = "main_wal_receiver"; 129 connection = "postgresql://user@somehost"; 130 }; 131 } 132 ''; 133 description = '' 134 PostgreSQL WAL receivers. 135 Stream write-ahead logs from a PostgreSQL server using {command}`pg_receivewal` (formerly {command}`pg_receivexlog`). 136 See [the man page](https://www.postgresql.org/docs/current/app-pgreceivewal.html) for more information. 137 ''; 138 }; 139 }; 140 }; 141 142 config = 143 let 144 receivers = config.services.postgresqlWalReceiver.receivers; 145 in 146 lib.mkIf (receivers != { }) { 147 users = { 148 users.postgres = { 149 uid = config.ids.uids.postgres; 150 group = "postgres"; 151 description = "PostgreSQL server user"; 152 }; 153 154 groups.postgres = { 155 gid = config.ids.gids.postgres; 156 }; 157 }; 158 159 assertions = lib.concatLists ( 160 lib.attrsets.mapAttrsToList (name: config: [ 161 { 162 assertion = config.compress > 0 -> lib.versionAtLeast config.postgresqlPackage.version "10"; 163 message = "Invalid configuration for WAL receiver \"${name}\": compress requires PostgreSQL version >= 10."; 164 } 165 ]) receivers 166 ); 167 168 systemd.tmpfiles.rules = lib.mapAttrsToList (name: config: '' 169 d ${lib.escapeShellArg config.directory} 0750 postgres postgres - - 170 '') receivers; 171 172 systemd.services = lib.mapAttrs' ( 173 name: config: 174 lib.nameValuePair "postgresql-wal-receiver-${name}" { 175 description = "PostgreSQL WAL receiver (${name})"; 176 wantedBy = [ "multi-user.target" ]; 177 startLimitIntervalSec = 0; # retry forever, useful in case of network disruption 178 179 serviceConfig = { 180 User = "postgres"; 181 Group = "postgres"; 182 KillSignal = "SIGINT"; 183 Restart = "always"; 184 RestartSec = 60; 185 }; 186 187 inherit (config) environment; 188 189 script = 190 let 191 receiverCommand = 192 postgresqlPackage: 193 if (lib.versionAtLeast postgresqlPackage.version "10") then 194 "${postgresqlPackage}/bin/pg_receivewal" 195 else 196 "${postgresqlPackage}/bin/pg_receivexlog"; 197 in 198 '' 199 ${receiverCommand config.postgresqlPackage} \ 200 --no-password \ 201 --directory=${lib.escapeShellArg config.directory} \ 202 --status-interval=${toString config.statusInterval} \ 203 --dbname=${lib.escapeShellArg config.connection} \ 204 ${lib.optionalString (config.compress > 0) "--compress=${toString config.compress}"} \ 205 ${lib.optionalString (config.slot != "") "--slot=${lib.escapeShellArg config.slot}"} \ 206 ${lib.optionalString config.synchronous "--synchronous"} \ 207 ${lib.concatStringsSep " " config.extraArgs} 208 ''; 209 } 210 ) receivers; 211 }; 212 213 meta.maintainers = with lib.maintainers; [ euxane ]; 214}