Skip to content

Commit

Permalink
nixos/pgbouncer: rework RFC42 integration
Browse files Browse the repository at this point in the history
Commit bfb9d18 added RFC42 support
which is a good thing in general, but this implementation has the
following flaws:

* `services.pgbouncer.logFile` was not renamed to `[...].log_file`, but
  to `[...].logfile`. Also the use of `mkRenamedOptionModule` is
  inappropriate here because the two options are not equivalent: the old
  option took a path relative to the home directory, the new an absolute
  path.

* Using `mkRenamedOptionModule` with options that don't exist (but are
  keys in a freeform attr-set or an `attrsOf X`), you get the following
  error when referencing an option you didn't declare:

    error: evaluation aborted with the following error message: 'Renaming error: option `services.pgbouncer.settings.pgbouncer.listen_port' does not exist.'

  This error is pretty bad because it's not actionable for an end-user of
  the module. A possible use-case is doing

    networking.firewall.allowedTCPPorts = [ config.services.pgbouncer.listenPort ];

  without specifying a custom listen port. This is an example of why you
  want to keep options, they already contain defaults and you can re-use
  those defaults in other parts of your system configuration.

  I decided to re-add a bunch of options where I figured that it's
  either useful to be able to address those in the NixOS configuration
  or having documentation directly in the options' reference in the
  NixOS manual.

  I didn't add all options, I'll leave that to the maintainers of
  pgbouncer.
  • Loading branch information
Ma27 committed Nov 22, 2024
1 parent 6306bb7 commit 2995b38
Showing 1 changed file with 184 additions and 4 deletions.
188 changes: 184 additions & 4 deletions nixos/modules/services/databases/pgbouncer.nix
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,20 @@ let
cfg = config.services.pgbouncer;

settingsFormat = pkgs.formats.ini { };
configFile = settingsFormat.generate "pgbouncer.ini" cfg.settings;
configFile = settingsFormat.generate "pgbouncer.ini"
(lib.filterAttrsRecursive (_: v: v != null) cfg.settings);
configPath = "pgbouncer/pgbouncer.ini";
in
{
imports = [
(lib.mkRenamedOptionModule
(lib.mkRemovedOptionModule
[ "services" "pgbouncer" "logFile" ]
[ "services" "pgbouncer" "settings" "pgbouncer" "log_file" ])
''
`services.pgbouncer.logFile` has been removed, use `services.pgbouncer.settings.pgbouncer.logfile`
instead.
Please note that the new option expects an absolute path
whereas the old option accepted paths relative to pgbouncer's home dir.
'')
(lib.mkRenamedOptionModule
[ "services" "pgbouncer" "listenAddress" ]
[ "services" "pgbouncer" "settings" "pgbouncer" "listen_addr" ])
Expand Down Expand Up @@ -128,7 +134,181 @@ in
};

settings = lib.mkOption {
type = settingsFormat.type;
type = lib.types.submodule {
freeformType = settingsFormat.type;
options = {
pgbouncer = {
listen_port = lib.mkOption {
type = lib.types.port;
default = 6432;
description = ''
Which port to listen on. Applies to both TCP and Unix sockets.
'';
};

listen_addr = lib.mkOption {
type = lib.types.nullOr lib.types.commas;
example = "*";
default = null;
description = ''
Specifies a list (comma-separated) of addresses where to listen for TCP connections.
You may also use * meaning “listen on all addresses”.
When not set, only Unix socket connections are accepted.
Addresses can be specified numerically (IPv4/IPv6) or by name.
'';
};

pool_mode = lib.mkOption {
type = lib.types.enum [ "session" "transaction" "statement" ];
default = "session";
description = ''
Specifies when a server connection can be reused by other clients.
session
Server is released back to pool after client disconnects. Default.
transaction
Server is released back to pool after transaction finishes.
statement
Server is released back to pool after query finishes.
Transactions spanning multiple statements are disallowed in this mode.
'';
};

max_client_conn = lib.mkOption {
type = lib.types.int;
default = 100;
description = ''
Maximum number of client connections allowed.
When this setting is increased, then the file descriptor limits in the operating system
might also have to be increased. Note that the number of file descriptors potentially
used is more than maxClientConn. If each user connects under its own user name to the server,
the theoretical maximum used is:
maxClientConn + (max pool_size * total databases * total users)
If a database user is specified in the connection string (all users connect under the same user name),
the theoretical maximum is:
maxClientConn + (max pool_size * total databases)
The theoretical maximum should never be reached, unless somebody deliberately crafts a special load for it.
Still, it means you should set the number of file descriptors to a safely high number.
'';
};

default_pool_size = lib.mkOption {
type = lib.types.int;
default = 20;
description = ''
How many server connections to allow per user/database pair.
Can be overridden in the per-database configuration.
'';
};

max_db_connections = lib.mkOption {
type = lib.types.int;
default = 0;
description = ''
Do not allow more than this many server connections per database (regardless of user).
This considers the PgBouncer database that the client has connected to,
not the PostgreSQL database of the outgoing connection.
This can also be set per database in the [databases] section.
Note that when you hit the limit, closing a client connection to one pool will
not immediately allow a server connection to be established for another pool,
because the server connection for the first pool is still open.
Once the server connection closes (due to idle timeout),
a new server connection will immediately be opened for the waiting pool.
0 = unlimited
'';
};

max_user_connections = lib.mkOption {
type = lib.types.int;
default = 0;
description = ''
Do not allow more than this many server connections per user (regardless of database).
This considers the PgBouncer user that is associated with a pool,
which is either the user specified for the server connection
or in absence of that the user the client has connected as.
This can also be set per user in the [users] section.
Note that when you hit the limit, closing a client connection to one pool
will not immediately allow a server connection to be established for another pool,
because the server connection for the first pool is still open.
Once the server connection closes (due to idle timeout), a new server connection
will immediately be opened for the waiting pool.
0 = unlimited
'';
};

ignore_startup_parameters = lib.mkOption {
type = lib.types.nullOr lib.types.commas;
example = "extra_float_digits";
default = null;
description = ''
By default, PgBouncer allows only parameters it can keep track of in startup packets:
client_encoding, datestyle, timezone and standard_conforming_strings.
All others parameters will raise an error.
To allow others parameters, they can be specified here, so that PgBouncer knows that
they are handled by the admin and it can ignore them.
If you need to specify multiple values, use a comma-separated list.
IMPORTANT: When using prometheus-pgbouncer-exporter, you need:
extra_float_digits
<https://github.com/prometheus-community/pgbouncer_exporter#pgbouncer-configuration>
'';
};
};
databases = lib.mkOption {
type = lib.types.attrsOf lib.types.str;
default = {};
example = {
exampledb = "host=/run/postgresql/ port=5432 auth_user=exampleuser dbname=exampledb sslmode=require";
bardb = "host=localhost dbname=bazdb";
foodb = "host=host1.example.com port=5432";
};
description = ''
Detailed information about PostgreSQL database definitions:
<https://www.pgbouncer.org/config.html#section-databases>
'';
};
users = lib.mkOption {
type = lib.types.attrsOf lib.types.str;
default = {};
example = {
user1 = "pool_mode=session";
};
description = ''
Optional.
Detailed information about PostgreSQL user definitions:
<https://www.pgbouncer.org/config.html#section-users>
'';
};

peers = lib.mkOption {
type = lib.types.attrsOf lib.types.str;
default = {};
example = {
"1" = "host=host1.example.com";
"2" = "host=/tmp/pgbouncer-2 port=5555";
};
description = ''
Optional.
Detailed information about PostgreSQL database definitions:
<https://www.pgbouncer.org/config.html#section-peers>
'';
};
};
};
default = { };
description = ''
Configuration for PgBouncer, see <https://www.pgbouncer.org/config.html>
Expand Down

0 comments on commit 2995b38

Please sign in to comment.