Received: from malur.postgresql.org ([217.196.149.56]) by arkaria.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.94.2) (envelope-from ) id 1ubLyY-00HQ6S-GN for pgsql-hackers@arkaria.postgresql.org; Mon, 14 Jul 2025 16:24:22 +0000 Received: from localhost ([127.0.0.1] helo=malur.postgresql.org) by malur.postgresql.org with esmtp (Exim 4.94.2) (envelope-from ) id 1ubLyW-009qtQ-Ia for pgsql-hackers@arkaria.postgresql.org; Mon, 14 Jul 2025 16:24:21 +0000 Received: from makus.postgresql.org ([2001:4800:3e1:1::229]) by malur.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.94.2) (envelope-from ) id 1ubLyW-009qtH-4l for pgsql-hackers@lists.postgresql.org; Mon, 14 Jul 2025 16:24:20 +0000 Received: from mail-io1-xd32.google.com ([2607:f8b0:4864:20::d32]) by makus.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (Exim 4.96) (envelope-from ) id 1ubLyU-007GMO-0s for pgsql-hackers@lists.postgresql.org; Mon, 14 Jul 2025 16:24:19 +0000 Received: by mail-io1-xd32.google.com with SMTP id ca18e2360f4ac-86d00726631so123748939f.1 for ; Mon, 14 Jul 2025 09:24:18 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1752510257; x=1753115057; darn=lists.postgresql.org; h=in-reply-to:content-disposition:mime-version:references:message-id :subject:cc:to:from:date:from:to:cc:subject:date:message-id:reply-to; bh=erTefBWjselJQ1Rk8/7Nh3XmNiFoPFEl0KKGrUGOmRg=; b=W8VvXLiSoiU1MMURytBQgQE3pYqQ9MP2ermdPiBby9NQ0HGDIY5bThELrBstV2bPei ETz1UtmgC3jZLYibNNYop55bSCzNCshRFRBfY/04c312vk27icdIQ5RXfsKgdP4Xja5u 9nLHn5x6GwKAqAgOhoblSCBc4xfLot0t1Bucd6N6D1rM4UXR6tDHhcRgVve667xMtqyE r+oywaglOh6Mz65jD0aKP86TP0eImEE/vheqVGnSfy+n5LYiXKqxNcX9in0pfPbjC9Q/ 7THnu7rvZmK6PBfRIpkqkfiDNwlMYDoKUUTghMhf2IPqKppEoBbhLpfPLCwf+AmbfFLy 1CcQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1752510257; x=1753115057; h=in-reply-to:content-disposition:mime-version:references:message-id :subject:cc:to:from:date:x-gm-message-state:from:to:cc:subject:date :message-id:reply-to; bh=erTefBWjselJQ1Rk8/7Nh3XmNiFoPFEl0KKGrUGOmRg=; b=ihCNdbIzgGPnWpIHRZqWPF7uBkRDsQsXr77I+cl+cr+UYHrZiZHMJDe5Cof+Cmqa/J 6NhDJ2V812GYd1qu9ON0mTPRcGwr0hBXuwV/SXNEILbho5DEM2IJPqxG4RxMtb+CaHLF INIMhZ6KZ82UcKgIN4PoTaoYS9hn1PTiD9Ja4u1a3jfzym6WmwqVHIgeBXdmzyKmLR7F pNVE5F7Pl7l5ixlKIRZxKhb5r8lIsYuG9qutRvLR+JbFFd5Lj2n+aYPeO1JtNz3r5VJg JU+390Ml2hoQjSo04sHWUsj5K0V79HUriZhV/KaULSh6ufDPXBdRecAYT4wW46OS6QIG IIqA== X-Forwarded-Encrypted: i=1; AJvYcCXZJ89Kv1R8fuXiiWk8yix7q7WsRWsSVUliqkul/o9+r2P7KE41zEOvtnxFX5rOA83U0WHVUr6BObf7GHFc@lists.postgresql.org X-Gm-Message-State: AOJu0Ywb4ZbsomxoJa4ySU8TerEK2W8wskgySgCgg7ADJPsRbkFA03pC IiR/tvZHaK/fpoPX8w3/Q79+Fu7xIMAbnamg2A92DU7BWFMTIIly/Ihv+x/ICA== X-Gm-Gg: ASbGncufpnOGYIhjxDvkjODQWsWGH/Vv9h4jX8brGSaiiEhcFEXmGjb40Bnb02/6pg9 uS/D6rdqO8EAdSWxpd0Fr1HNYMWMJ90KNYUWQe+61ItYnWQpKwXo9GdP5cjfSGt31f97SNe2xrm vA/om4qm9rsb5In6o/IMesS22L49oVFJbTqRNW2tFhX5Tgv5X/zLkUO5lVXCtR7jqDk0tdOiK76 CIK/PsAYEB0ovne8pbxD3COVl3I86h2DURvqJFUg/L1kvbBPwz6z6vH35/6aF58ucGxgZqhC3b0 LSCibEqwZEXxCo0LgmXEIGA4POMPJXX9pv4tY4iiZ0WwjL/CaLKMdzb000GbiLGy/u4r0J9ilt+ nhuypZCiyOHtJdn3rtj4l6TT6ps1xuQXsEzQrNnA+hhLyltFmR7x8Ey0JqmiIbYGfF3LvmKZ/yh Hjc/OCb7qs7BTW X-Google-Smtp-Source: AGHT+IH9g/IgIE1xQWCQ2qdMC739s6InTxXJWb2ipvvWyotBqKVxdZr/8mxUGu9PnHoVbzRGPjyKBA== X-Received: by 2002:a05:6602:3941:b0:86d:9ec7:267e with SMTP id ca18e2360f4ac-879787e9615mr1649378039f.4.1752510257235; Mon, 14 Jul 2025 09:24:17 -0700 (PDT) Received: from nathan (162-195-168-172.lightspeed.stlsmo.sbcglobal.net. [162.195.168.172]) by smtp.gmail.com with ESMTPSA id 8926c6da1cb9f-50556538b13sm2096939173.14.2025.07.14.09.24.15 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 14 Jul 2025 09:24:16 -0700 (PDT) Date: Mon, 14 Jul 2025 11:24:14 -0500 From: Nathan Bossart To: Nitin Motiani Cc: Hannu Krosing , Tom Lane , PostgreSQL Hackers Subject: Re: Horribly slow pg_upgrade performance with many Large Objects Message-ID: References: MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="9qD3eLYy6j4n6wEu" Content-Disposition: inline In-Reply-To: List-Id: List-Help: List-Subscribe: List-Post: List-Owner: List-Archive: Archived-At: Precedence: bulk --9qD3eLYy6j4n6wEu Content-Type: text/plain; charset=us-ascii Content-Disposition: inline Here is what I have staged for commit, which (barring feedback or objections) I am planning to do towards the end of the week. -- nathan --9qD3eLYy6j4n6wEu Content-Type: text/plain; charset=us-ascii Content-Disposition: attachment; filename=v4-0001-pg_upgrade-Use-COPY-for-large-object-metadata.patch From 9501f31cdf3ce25012cec8f9e2d7c433df749979 Mon Sep 17 00:00:00 2001 From: Nathan Bossart Date: Mon, 14 Jul 2025 11:17:47 -0500 Subject: [PATCH v4 1/1] pg_upgrade: Use COPY for large object metadata. Presently, pg_dump generates commands like SELECT pg_catalog.lo_create('5432'); ALTER LARGE OBJECT 5432 OWNER TO alice; GRANT SELECT ON LARGE OBJECT 5432 TO bob; for each large object. This is particularly slow at restore time, especially when there are tens or hundreds of millions of large objects. From reports and personal experience, such slow restores are primarily encountered at pg_upgrade time. This commit teaches pg_dump to instead dump pg_largeobject_metadata and the corresponding pg_shdepend rows when in binary upgrade mode, i.e., pg_dump now generates commands like COPY pg_catalog.pg_largeobject_metadata (oid, lomowner, lomacl) FROM stdin; 5432 16384 {alice=rw/alice,bob=r/alice} \. COPY pg_catalog.pg_shdepend (dbid, classid, objid, objsubid, refclassid, refobjid, deptype) FROM stdin; 5 2613 5432 0 1260 16384 o 5 2613 5432 0 1260 16385 a \. Testing indicates the COPY approach can be significantly faster. To do any better, we'd probably need to find a way to copy/link pg_largeobject_metadata's files during pg_upgrade, which would be complicated and limited to upgrades from >= v16 (since commit 7b378237aa changed the storage format for aclitem, which is used for pg_largeobject_metadata.lomacl). Note that this change only applies to binary upgrade mode (i.e., dumps initiated by pg_upgrade) since it inserts rows directly into catalogs, bypassing the ordinary privilege checks. Also, this optimization can only be used for upgrades from >= v12 because pg_largeobject_metadata was created WITH OIDS in older versions, which prevents pg_dump from handling pg_largeobject_metadata.oid properly. With some extra effort, it might be possible to support upgrades from older versions, but the added complexity didn't seem worth it to support versions that will have been out-of-support for nearly 3 years by the time this change is released. The astute hacker may remember that prior to v12, pg_upgrade copied/linked pg_largeobject_metadata's files (see commit 12a53c732c). Besides the aforementioned storage format issues, this approach failed to transfer the relevant pg_shdepend rows, and pg_dump still had to generate an lo_create() command per large object so that creating the dependent comments and security labels worked. We could perhaps adopt a hybrid approach for upgrades from v16 and newer (i.e., generate lo_create() commands for each large object, copy/link pg_largeobject_metadata's files, and COPY the relevant pg_shdepend rows), but further testing is needed. Reported-by: Hannu Krosing Suggested-by: Tom Lane Reviewed-by: Hannu Krosing Reviewed-by: Nitin Motiani Discussion: https://postgr.es/m/CAMT0RQSS-6qLH%2BzYsOeUbAYhop3wmQTkNmQpo5--QRDUR%2BqYmQ%40mail.gmail.com --- src/bin/pg_dump/pg_backup_archiver.c | 15 +++++ src/bin/pg_dump/pg_dump.c | 90 ++++++++++++++++++++++++++-- src/bin/pg_dump/t/002_pg_dump.pl | 4 +- 3 files changed, 103 insertions(+), 6 deletions(-) diff --git a/src/bin/pg_dump/pg_backup_archiver.c b/src/bin/pg_dump/pg_backup_archiver.c index 197c1295d93..1b1c61e5ce5 100644 --- a/src/bin/pg_dump/pg_backup_archiver.c +++ b/src/bin/pg_dump/pg_backup_archiver.c @@ -31,6 +31,8 @@ #endif #include "catalog/pg_class_d.h" +#include "catalog/pg_largeobject_metadata.h" +#include "catalog/pg_shdepend.h" #include "common/string.h" #include "compress_io.h" #include "dumputils.h" @@ -2974,6 +2976,19 @@ _tocEntryRequired(TocEntry *te, teSection curSection, ArchiveHandle *AH) int res = REQ_SCHEMA | REQ_DATA; RestoreOptions *ropt = AH->public.ropt; + /* + * For binary upgrade mode, dump pg_largeobject_metadata and the + * associated pg_shdepend rows. This is faster to restore than the + * equivalent set of large object commands. We can only do this for + * upgrades from v12 and newer; in older versions, pg_largeobject_metadata + * was created WITH OIDS, so the OID column is hidden and won't be dumped. + */ + if (ropt->binary_upgrade && AH->public.remoteVersion >= 120000 && + strcmp(te->desc, "TABLE DATA") == 0 && + (te->catalogId.oid == LargeObjectMetadataRelationId || + te->catalogId.oid == SharedDependRelationId)) + return REQ_DATA; + /* These items are treated specially */ if (strcmp(te->desc, "ENCODING") == 0 || strcmp(te->desc, "STDSTRINGS") == 0 || diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index 1937997ea67..89a4927a25a 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -49,8 +49,10 @@ #include "catalog/pg_class_d.h" #include "catalog/pg_default_acl_d.h" #include "catalog/pg_largeobject_d.h" +#include "catalog/pg_largeobject_metadata_d.h" #include "catalog/pg_proc_d.h" #include "catalog/pg_publication_d.h" +#include "catalog/pg_shdepend.h" #include "catalog/pg_subscription_d.h" #include "catalog/pg_type_d.h" #include "common/connect.h" @@ -209,6 +211,12 @@ static int nbinaryUpgradeClassOids = 0; static SequenceItem *sequences = NULL; static int nsequences = 0; +/* + * For binary upgrade, the dump ID of pg_largeobject_metadata is saved for use + * as a dependency for pg_shdepend and any large object comments/seclabels. + */ +static DumpId lo_metadata_dumpId; + /* Maximum number of relations to fetch in a fetchAttributeStats() call. */ #define MAX_ATTR_STATS_RELS 64 @@ -1085,6 +1093,36 @@ main(int argc, char **argv) if (!dopt.dumpData && dopt.sequence_data) getTableData(&dopt, tblinfo, numTables, RELKIND_SEQUENCE); + /* + * For binary upgrade mode, dump pg_largeobject_metadata and the + * associated pg_shdepend rows. This is faster to restore than the + * equivalent set of large object commands. We can only do this for + * upgrades from v12 and newer; in older versions, pg_largeobject_metadata + * was created WITH OIDS, so the OID column is hidden and won't be dumped. + */ + if (dopt.binary_upgrade && fout->remoteVersion >= 120000) + { + TableInfo *lo_metadata = findTableByOid(LargeObjectMetadataRelationId); + TableInfo *shdepend = findTableByOid(SharedDependRelationId); + + makeTableDataInfo(&dopt, lo_metadata); + makeTableDataInfo(&dopt, shdepend); + + /* + * Save pg_largeobject_metadata's dump ID for use as a dependency on + * pg_shdepend and any large object comments/seclabels. + */ + lo_metadata_dumpId = lo_metadata->dataObj->dobj.dumpId; + addObjectDependency(&shdepend->dataObj->dobj, lo_metadata_dumpId); + + /* + * Only dump large object shdepend rows for this database. + */ + shdepend->dataObj->filtercond = "WHERE classid = 'pg_largeobject'::regclass " + "AND dbid = (SELECT oid FROM pg_database " + " WHERE datname = current_database())"; + } + /* * In binary-upgrade mode, we do not have to worry about the actual LO * data or the associated metadata that resides in the pg_largeobject and @@ -3924,10 +3962,37 @@ getLOs(Archive *fout) * as it will be copied by pg_upgrade, which simply copies the * pg_largeobject table. We *do* however dump out anything but the * data, as pg_upgrade copies just pg_largeobject, but not - * pg_largeobject_metadata, after the dump is restored. + * pg_largeobject_metadata, after the dump is restored. In versions + * before v12, this is done via proper large object commands. In + * newer versions, we dump the content of pg_largeobject_metadata and + * any associated pg_shdepend rows, which is faster to restore. (On + *