postgresql: add backup and restore

This commit is contained in:
Jörg Thalheim
2024-06-05 18:37:31 +02:00
parent c550261d40
commit 46530a0a9c
6 changed files with 171 additions and 75 deletions

View File

@@ -8,32 +8,56 @@ let
createDatatbaseState =
db:
let
folder = "/var/backup/postgresql/${db.name}";
curFile = "${folder}/dump.sql.zstd";
prevFile = "${folder}/dump.sql.prev.zstd";
inProgressFile = "${folder}/dump.sql.in-progress.zstd";
folder = "/var/backup/postgres/${db.name}";
current = "${folder}/dump.sql.zstd";
in
{
folders = [ folder ];
preBackupCommand = ''
(
umask 0077 # ensure backup is only readable by postgres user
if [ -e ${curFile} ]; then
mv ${curFile} ${prevFile}
fi
while [[ "$(systemctl is-active postgres)" == activating ]]; then
sleep 1
done
systemctl is-active postgres
pg_dump -C ${db.name} | \
${pkgs.zstd}/bin/zstd --rsyncable | \
> ${inProgressFile}
mv ${inProgressFile} ${curFile}
)
export PATH=${
lib.makeBinPath [
config.services.postgresql.package
config.systemd.package
pkgs.coreutils
pkgs.util-linux
pkgs.zstd
]
}
while [[ "$(systemctl is-active postgresql)" == activating ]]; do
sleep 1
done
mkdir -p "${folder}"
runuser -u postgres -- pg_dump --compress=zstd --dbname=${db.name} -Fc -c > "${current}.tmp"
mv "${current}.tmp" ${current}
'';
postRestoreCommand = ''
if [[ -f ${prevFile} ]]; then
zstd --decompress --stdout ${prevFile} | psql -d ${db.name}
export PATH=${
lib.makeBinPath [
config.services.postgresql.package
config.systemd.package
pkgs.coreutils
pkgs.util-linux
pkgs.zstd
]
}
while [[ "$(systemctl is-active postgresql)" == activating ]]; do
sleep 1
done
echo "Waiting for postgres to be ready..."
while ! runuser -u postgres -- psql --port=${builtins.toString config.services.postgresql.settings.port} -d postgres -c "" ; do
if ! systemctl is-active postgresql; then exit 1; fi
sleep 0.1
done
if [[ -e "${current}" ]]; then
umask 077 # only root can read the backup
mkdir -p "${folder}"
runuser -u postgres -- dropdb "${db.name}"
runuser -u postgres -- pg_restore -C -d postgres "${current}"
else
echo No database backup found, skipping restore
fi
'';
};
@@ -41,23 +65,24 @@ let
createDatabase = db: ''
CREATE DATABASE "${db.name}" ${
lib.concatStringsSep " " (
lib.mapAttrsToList (name: value: "${name} = ':${value}'") db.createOptions
lib.mapAttrsToList (name: value: "${name} = '${value}'") db.create.options
)
}
'';
cfg = config.clan.postgresql;
userClauses = lib.mapAttrsToList (
_: user: ""
_: user:
''$PSQL -tAc "SELECT 1 FROM pg_roles WHERE rolname='${user.name}'" | grep -q 1 || $PSQL -tAc 'CREATE USER "${user.name}"' ''
) cfg.users;
databaseClauses = lib.mapAttrsToList (
name: db:
lib.optionalString (db.create) ''$PSQL -d postgres -c "SELECT 1 FROM pg_database WHERE datname = '${name}'" | grep -q 1 || $PSQL -d postgres -c ${lib.escapeShellArg (createDatabase db)} ${createDatabaseArgs db}''
lib.optionalString db.create.enable ''$PSQL -d postgres -c "SELECT 1 FROM pg_database WHERE datname = '${name}'" | grep -q 1 || $PSQL -d postgres -c ${lib.escapeShellArg (createDatabase db)} ''
) cfg.databases;
in
{
options.clan.postgresl = {
options.clan.postgresql = {
# we are reimplemeting ensureDatabase and ensureUser options here to allow to create databases with options
databases = lib.mkOption {
default = { };
type = lib.types.attrsOf (
@@ -70,11 +95,12 @@ in
default = name;
};
# set to false, in case the upstream module uses ensureDatabase option
create = lib.mkOption {
create.enable = lib.mkOption {
type = lib.types.bool;
default = true;
description = "Create the database if it does not exist.";
};
createOptions = lib.mkOption {
create.options = lib.mkOption {
type = lib.types.lazyAttrsOf lib.types.str;
default = { };
example = {
@@ -85,6 +111,11 @@ in
OWNER = "foo";
};
};
backup.enable = lib.mkOption {
type = lib.types.bool;
default = true;
description = "Backup the database.";
};
};
}
)
@@ -106,18 +137,24 @@ in
};
};
config = {
services.postgresql.settings = {
wal_level = "replica";
max_wal_senders = 3;
};
services.postgresql.enable = true;
# We are duplicating a bit the upstream module but allow to create databases with options
systemd.services.postgresql.postStart = ''
PSQL="psql --port=${builtins.toString config.services.postgresql.settings.port}"
while ! $PSQL -d postgres -c "" 2> /dev/null; do
if ! kill -0 "$MAINPID"; then exit 1; fi
sleep 0.1
if ! kill -0 "$MAINPID"; then exit 1; fi
sleep 0.1
done
${lib.concatStringsSep "\n" userClauses}
${lib.concatStringsSep "\n" databaseClauses}
'';
clanCore.state = lib.mapAttrs' (
_: db: lib.nameValuePair "postgresql-${db.name}" (createDatatbaseState db)
) config.clan.postgresql.databases;