From 3a8b4b0205099bcd6e56a1321fd624643d01f15a Mon Sep 17 00:00:00 2001 From: Alex Vandiver Date: Tue, 10 Oct 2023 20:53:31 +0000 Subject: [PATCH] backup: Call the pg_dump binary whose version we are running. `/usr/bin/pg_dump` on Ubuntu and Debian is actually a tool which attempts to choose which `pg_dump` binary from all of the `postgresql-client-*` packages that are installed to run. However, its logic is confused by passing empty `--host` and `--port` options -- instead of looking at the running server instance on the server, it instead assumes some remote host and chooses the highest versioned `pg_dump` which is installed. Because Zulip writes binary database backups, they are sensitive to the version of the client `pg_dump` binary is used -- and the output may not be backwards compatible. Using a PostgreSQL 16 `pg_dump` writes archive format 1.15, which cannot be read by a PostgreSQL 15 `pg_restore`. Zulip does not currently support PostgreSQL 16 as a server. This means that backups on servers with `postgresql-client-16` installed did not successfully round-trip Zulip backups -- their backups are written using PostgreSQL 16's client, and the `pg_restore` chosen on restore was correctly chosen as the one whose version matched the server (PostgreSQL 15 or below), and thus did not understand the new archive format. Existing `./manage.py backups` taken since `postgresql-client-16` were installed are thus not directly usable by the `restore-backup` script. They are not useless, however, since they can theoretically be converted into a format readable by PostgreSQL 15 -- by importing into a PostgreSQL 16 instance, and re-dumping with a PostgreSQL 15 `pg_dump`. Fix this issue by hard-coding path to the binary whose version matches the version of the server we are connected to. This may theoretically fail if we are connected to a remote PostgreSQL instance and we do not have a `postgresql-client` package locally installed which matches the remote PostgreSQL server's version. However, choosing a matching version is the only way to ensure that it will be able to be imported cleanly -- and it is preferable that we fail the backup process rather than write backups that we cannot easily restore from. Fixes: #27160. --- zerver/management/commands/backup.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/zerver/management/commands/backup.py b/zerver/management/commands/backup.py index e5e9573943..eac9a7d8c4 100644 --- a/zerver/management/commands/backup.py +++ b/zerver/management/commands/backup.py @@ -52,6 +52,7 @@ class Command(ZulipBaseCommand): with open(os.path.join(tmp, "zulip-backup", "postgres-version"), "w") as f: pg_server_version = connection.cursor().connection.server_version + major_pg_version = pg_server_version // 10000 print(pg_server_version, file=f) members.append("zulip-backup/postgres-version") @@ -68,7 +69,7 @@ class Command(ZulipBaseCommand): if not options["skip_db"]: pg_dump_command = [ - "pg_dump", + f"/usr/lib/postgresql/{major_pg_version}/bin/pg_dump", "--format=directory", "--file=" + os.path.join(tmp, "zulip-backup", "database"), "--host=" + settings.DATABASES["default"]["HOST"],