205 lines
		
	
	
		
			7.6 KiB
		
	
	
	
		
			Nix
		
	
	
	
	
	
			
		
		
	
	
			205 lines
		
	
	
		
			7.6 KiB
		
	
	
	
		
			Nix
		
	
	
	
	
	
| { config, lib, pkgs, ... }:
 | |
| 
 | |
| with lib;
 | |
| 
 | |
| let
 | |
|   receiverSubmodule = {
 | |
|     options = {
 | |
|       postgresqlPackage = mkOption {
 | |
|         type = types.package;
 | |
|         example = literalExample "pkgs.postgresql_11";
 | |
|         description = ''
 | |
|           PostgreSQL package to use.
 | |
|         '';
 | |
|       };
 | |
| 
 | |
|       directory = mkOption {
 | |
|         type = types.path;
 | |
|         example = literalExample "/mnt/pg_wal/main/";
 | |
|         description = ''
 | |
|           Directory to write the output to.
 | |
|         '';
 | |
|       };
 | |
| 
 | |
|       statusInterval = mkOption {
 | |
|         type = types.int;
 | |
|         default = 10;
 | |
|         description = ''
 | |
|           Specifies the number of seconds between status packets sent back to the server.
 | |
|           This allows for easier monitoring of the progress from server.
 | |
|           A value of zero disables the periodic status updates completely,
 | |
|           although an update will still be sent when requested by the server, to avoid timeout disconnect.
 | |
|         '';
 | |
|       };
 | |
| 
 | |
|       slot = mkOption {
 | |
|         type = types.str;
 | |
|         default = "";
 | |
|         example = "some_slot_name";
 | |
|         description = ''
 | |
|           Require <command>pg_receivewal</command> to use an existing replication slot (see
 | |
|           <link xlink:href="https://www.postgresql.org/docs/current/warm-standby.html#STREAMING-REPLICATION-SLOTS">Section 26.2.6 of the PostgreSQL manual</link>).
 | |
|           When this option is used, <command>pg_receivewal</command> will report a flush position to the server,
 | |
|           indicating when each segment has been synchronized to disk so that the server can remove that segment if it is not otherwise needed.
 | |
| 
 | |
|           When the replication client of <command>pg_receivewal</command> is configured on the server as a synchronous standby,
 | |
|           then using a replication slot will report the flush position to the server, but only when a WAL file is closed.
 | |
|           Therefore, that configuration will cause transactions on the primary to wait for a long time and effectively not work satisfactorily.
 | |
|           The option <option>synchronous</option> must be specified in addition to make this work correctly.
 | |
|         '';
 | |
|       };
 | |
| 
 | |
|       synchronous = mkOption {
 | |
|         type = types.bool;
 | |
|         default = false;
 | |
|         description = ''
 | |
|           Flush the WAL data to disk immediately after it has been received.
 | |
|           Also send a status packet back to the server immediately after flushing, regardless of <option>statusInterval</option>.
 | |
| 
 | |
|           This option should be specified if the replication client of <command>pg_receivewal</command> is configured on the server as a synchronous standby,
 | |
|           to ensure that timely feedback is sent to the server.
 | |
|         '';
 | |
|       };
 | |
| 
 | |
|       compress = mkOption {
 | |
|         type = types.ints.between 0 9;
 | |
|         default = 0;
 | |
|         description = ''
 | |
|           Enables gzip compression of write-ahead logs, and specifies the compression level
 | |
|           (<literal>0</literal> through <literal>9</literal>, <literal>0</literal> being no compression and <literal>9</literal> being best compression).
 | |
|           The suffix <literal>.gz</literal> will automatically be added to all filenames.
 | |
| 
 | |
|           This option requires PostgreSQL >= 10.
 | |
|         '';
 | |
|       };
 | |
| 
 | |
|       connection = mkOption {
 | |
|         type = types.str;
 | |
|         example = "postgresql://user@somehost";
 | |
|         description = ''
 | |
|           Specifies parameters used to connect to the server, as a connection string.
 | |
|           See <link xlink:href="https://www.postgresql.org/docs/current/libpq-connect.html#LIBPQ-CONNSTRING">Section 34.1.1 of the PostgreSQL manual</link> for more information.
 | |
| 
 | |
|           Because <command>pg_receivewal</command> doesn't connect to any particular database in the cluster,
 | |
|           database name in the connection string will be ignored.
 | |
|         '';
 | |
|       };
 | |
| 
 | |
|       extraArgs = mkOption {
 | |
|         type = with types; listOf str;
 | |
|         default = [ ];
 | |
|         example = literalExample ''
 | |
|           [
 | |
|             "--no-sync"
 | |
|           ]
 | |
|         '';
 | |
|         description = ''
 | |
|           A list of extra arguments to pass to the <command>pg_receivewal</command> command.
 | |
|         '';
 | |
|       };
 | |
| 
 | |
|       environment = mkOption {
 | |
|         type = with types; attrsOf str;
 | |
|         default = { };
 | |
|         example = literalExample ''
 | |
|           {
 | |
|             PGPASSFILE = "/private/passfile";
 | |
|             PGSSLMODE = "require";
 | |
|           }
 | |
|         '';
 | |
|         description = ''
 | |
|           Environment variables passed to the service.
 | |
|           Usable parameters are listed in <link xlink:href="https://www.postgresql.org/docs/current/libpq-envars.html">Section 34.14 of the PostgreSQL manual</link>.
 | |
|         '';
 | |
|       };
 | |
|     };
 | |
|   };
 | |
| 
 | |
| in {
 | |
|   options = {
 | |
|     services.postgresqlWalReceiver = {
 | |
|       receivers = mkOption {
 | |
|         type = with types; attrsOf (submodule receiverSubmodule);
 | |
|         default = { };
 | |
|         example = literalExample ''
 | |
|           {
 | |
|             main = {
 | |
|               postgresqlPackage = pkgs.postgresql_11;
 | |
|               directory = /mnt/pg_wal/main/;
 | |
|               slot = "main_wal_receiver";
 | |
|               connection = "postgresql://user@somehost";
 | |
|             };
 | |
|           }
 | |
|         '';
 | |
|         description = ''
 | |
|           PostgreSQL WAL receivers.
 | |
|           Stream write-ahead logs from a PostgreSQL server using <command>pg_receivewal</command> (formerly <command>pg_receivexlog</command>).
 | |
|           See <link xlink:href="https://www.postgresql.org/docs/current/app-pgreceivewal.html">the man page</link> for more information.
 | |
|         '';
 | |
|       };
 | |
|     };
 | |
|   };
 | |
| 
 | |
|   config = let
 | |
|     receivers = config.services.postgresqlWalReceiver.receivers;
 | |
|   in mkIf (receivers != { }) {
 | |
|     users = {
 | |
|       users.postgres = {
 | |
|         uid = config.ids.uids.postgres;
 | |
|         group = "postgres";
 | |
|         description = "PostgreSQL server user";
 | |
|       };
 | |
| 
 | |
|       groups.postgres = {
 | |
|         gid = config.ids.gids.postgres;
 | |
|       };
 | |
|     };
 | |
| 
 | |
|     assertions = concatLists (attrsets.mapAttrsToList (name: config: [
 | |
|       {
 | |
|         assertion = config.compress > 0 -> versionAtLeast config.postgresqlPackage.version "10";
 | |
|         message = "Invalid configuration for WAL receiver \"${name}\": compress requires PostgreSQL version >= 10.";
 | |
|       }
 | |
|     ]) receivers);
 | |
| 
 | |
|     systemd.tmpfiles.rules = mapAttrsToList (name: config: ''
 | |
|       d ${escapeShellArg config.directory} 0750 postgres postgres - -
 | |
|     '') receivers;
 | |
| 
 | |
|     systemd.services = with attrsets; mapAttrs' (name: config: nameValuePair "postgresql-wal-receiver-${name}" {
 | |
|       description = "PostgreSQL WAL receiver (${name})";
 | |
|       wantedBy = [ "multi-user.target" ];
 | |
|       startLimitIntervalSec = 0; # retry forever, useful in case of network disruption
 | |
| 
 | |
|       serviceConfig = {
 | |
|         User = "postgres";
 | |
|         Group = "postgres";
 | |
|         KillSignal = "SIGINT";
 | |
|         Restart = "always";
 | |
|         RestartSec = 60;
 | |
|       };
 | |
| 
 | |
|       inherit (config) environment;
 | |
| 
 | |
|       script = let
 | |
|         receiverCommand = postgresqlPackage:
 | |
|          if (versionAtLeast postgresqlPackage.version "10")
 | |
|            then "${postgresqlPackage}/bin/pg_receivewal"
 | |
|            else "${postgresqlPackage}/bin/pg_receivexlog";
 | |
|       in ''
 | |
|         ${receiverCommand config.postgresqlPackage} \
 | |
|           --no-password \
 | |
|           --directory=${escapeShellArg config.directory} \
 | |
|           --status-interval=${toString config.statusInterval} \
 | |
|           --dbname=${escapeShellArg config.connection} \
 | |
|           ${optionalString (config.compress > 0) "--compress=${toString config.compress}"} \
 | |
|           ${optionalString (config.slot != "") "--slot=${escapeShellArg config.slot}"} \
 | |
|           ${optionalString config.synchronous "--synchronous"} \
 | |
|           ${concatStringsSep " " config.extraArgs}
 | |
|       '';
 | |
|     }) receivers;
 | |
|   };
 | |
| 
 | |
|   meta.maintainers = with maintainers; [ pacien ];
 | |
| }
 | 
