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.96) (envelope-from ) id 1wc3f3-0033bQ-2z for pgsql-hackers@arkaria.postgresql.org; Tue, 23 Jun 2026 16:07:42 +0000 Received: from localhost ([127.0.0.1] helo=malur.postgresql.org) by malur.postgresql.org with esmtp (Exim 4.96) (envelope-from ) id 1wc3f2-00Ctvo-2Y for pgsql-hackers@arkaria.postgresql.org; Tue, 23 Jun 2026 16:07:40 +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.96) (envelope-from ) id 1wc3f2-00Ctvg-0l for pgsql-hackers@lists.postgresql.org; Tue, 23 Jun 2026 16:07:40 +0000 Received: from mail-ej1-x635.google.com ([2a00:1450:4864:20::635]) by makus.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (Exim 4.98.2) (envelope-from ) id 1wc3f0-00000001lja-0qDH for pgsql-hackers@lists.postgresql.org; Tue, 23 Jun 2026 16:07:39 +0000 Received: by mail-ej1-x635.google.com with SMTP id a640c23a62f3a-c0c964d6470so444417866b.3 for ; Tue, 23 Jun 2026 09:07:37 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1782230856; cv=none; d=google.com; s=arc-20240605; b=UjTnzmmMjldRJk19NPMQcVsZJ5fhb5KoCuJ6BudS3TC8ol+uEAff0PNyreTO3OCiwX WbXoH3K4eAUy9VnYFRkHw7iFI25t/QUxcKLaDSc9qizRBRhW+cKUeMCuC3pOIK+fVl0y kSLR1vR4HWNsVUXc0qIitybzdf7c/twffH/G8hsNBz4ghsEg8YDPxurgnGPHY1taG2hG zhujNfSPwV0EPUbYCfVga5IZicll5zmgXidKdg+nGYNhLO6/OHFffLp8surE6NjvQ5iE Tp75FJCwLYzjd7Tv4CbAy86SZGfk1Kgchj9F9LlOntDWcEul42YV539+qiuIwW6cci0p z6Cg== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20240605; h=content-transfer-encoding:cc:to:subject:message-id:date:from :in-reply-to:references:mime-version:dkim-signature; bh=S7DAoD2SsLnc70zfsNRie+81rFyKDCqWXylFMB0g/04=; fh=NCHQHvisl2vdN1YJl0TIZXHjs7AkMJJz9LGzhnRxiLk=; b=Zu8pTQBUd+Dm9+PrKzTmp6Hlgm6CAOOXqbb9idXNdR6qNtWdtzfdAqwisr568ywakt VeAJBEzuKv5c4xJFE9LMDT/OPocHop5ASfWifaqkXiuu8B0gpWm1bTVv75t55Qfyw4/A c8xuVkJcadMSP7Zfq0X36T9BTOvLv8B1w2OdL9mijoN5d0ZX4QS1X82bkecPx/4Z6IBZ rs0rffCagZ8TjiOQFPJ3/MCAH8f+P7gs3vf2VCbPKF3sLy5/KOM++E23XxZT52d3o/Ma qwn1jG6fB9SIWtqb3p92EC1qpipKxob7J8/zzDLaoCZM+dq37eErR4+bwuUPkQk2iCSN 4trQ==; darn=lists.postgresql.org ARC-Authentication-Results: i=1; mx.google.com; arc=none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1782230856; x=1782835656; darn=lists.postgresql.org; h=content-transfer-encoding:cc:to:subject:message-id:date:from :in-reply-to:references:mime-version:from:to:cc:subject:date :message-id:reply-to; bh=S7DAoD2SsLnc70zfsNRie+81rFyKDCqWXylFMB0g/04=; b=psxQxg2VjBEy7Nbr/g4Ou5izSApwVZVOwq/MhPqsQDfXFKJVG5COxjMhUkvMQjqus8 A9rwRrVGq47EZm/xAQXpJAPxkzMYm290tjo/+PXTT1Zi+uuqgh5niZVj+L9h0uEyFXjX eoIZtV7IwodwMwMTqtIPtJQbzBokXPq9MvOhOqx8v8QnmM+VGfHW+5uvzHrAJtWKGh0W a6SVYnW2kowlhc0w2ioRgNBdLwRjpas8bcngnfiL74OGrRUPOrOTOQJ+yGRJ0MntrTFY 6+KJ9Q44ZxdWgGBxenNarWDcCBY4V5JEAajN3NJky20JPT3A4akvlRvOPbzRwGByhUeQ hA9g== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1782230856; x=1782835656; h=content-transfer-encoding:cc:to:subject:message-id:date:from :in-reply-to:references:mime-version:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=S7DAoD2SsLnc70zfsNRie+81rFyKDCqWXylFMB0g/04=; b=gLxDuhqaw/U6EkeusfGynv2kLSjznkpdfSUTdewELEymo7HREWfufbUZgMnXroDm94 tK6QJf9nYPLjV3SkxkdiDwm1KdjetmwK73YbfexiRPCStKpqj+f/AfW8eVhka2fBcQQP sIzg4vhFVQIgatRApVOB3BfsEsFkP39e0kX2v95tjDSTCk0giz9foM7deuDqVCQlcO3O DU/3KFRqoss+C6iH8f5obJ4nssU6jEPu5RHA1im/l+HaH0ruDZc9oXMeOayCobnE2gVY H4LOUIEWKQLkikVGAPGwm6u7MyGKvtKV5L8fA8F3kvnPlNj18u+G+fefNk7BfebjKIAO IeBg== X-Forwarded-Encrypted: i=1; AFNElJ+25oQGsogWYe7jDxriMg6YtaTI4ybmNbiJn1FJGoHaxaHf1YPRMk1CNRm5diPka3ijQWYYzXKE9+yzVTXa@lists.postgresql.org X-Gm-Message-State: AOJu0Ywu75z57TbabzCCyW529O63Rmd5bi+pyzT6Yx/bgQlfPv1O74rG /X8DX/uohsPhOUKMev2E2fVgMHG7jLa3SPqDrf9OHL168HPTONl06Tfeh35VHwPu42zpE3iiQX0 4zWhAwdp8fVcoTgy+cUu5xO5vtiqri/c= X-Gm-Gg: AfdE7cmgVmrkUByIoE6OQU8JTDrYdayMoxIddkU3i/zM6L1shsSlTgn6H6+pmPac1SK Wgi323mTW4IKty2n5nZriD5qyBsOFG6iRZ8rYhkxZi7ETGZrBUUPZeLckJ9Jc3dPm4VQLKY9MqO sQuSVjriAUFC5bO2nZKLd8E2z81/onwrZhielOyXpVeyKhS8Ql/GBrPK6YWDDapeksy8whuLgcm ISuqEtgitrjM3msXFH1/VkFHkS900fBC8pGur/PR11TR2c1I8MYR+h/SFyXdc4hdBgYcRJvSewN WGU0J4PSS1JQ1ma20GvS2/ZnZKwjoMPkVKE0A8s= X-Received: by 2002:a17:907:9447:b0:c0f:d848:75de with SMTP id a640c23a62f3a-c10908b9259mr179560266b.48.1782230855935; Tue, 23 Jun 2026 09:07:35 -0700 (PDT) MIME-Version: 1.0 References: In-Reply-To: From: Rui Zhao Date: Wed, 24 Jun 2026 00:07:23 +0800 X-Gm-Features: AVVi8Ccf9toFpN8oVUB7iAG46kbvB4jH44Tyfpk69Ycv_5_WyswNKhTAcT2kwkg Message-ID: Subject: Re: [PATCH] Preserve replication origin OIDs in pg_upgrade To: Ajin Cherian Cc: Shlok Kyal , shveta malik , Zsolt Parragi , vignesh C , "Hayato Kuroda (Fujitsu)" , PostgreSQL Hackers , shveta malik Content-Type: text/plain; charset="UTF-8" Content-Transfer-Encoding: quoted-printable List-Id: List-Help: List-Subscribe: List-Post: List-Owner: List-Archive: Archived-At: Precedence: bulk Hi Ajin, Thanks for v9, and for addressing both my earlier comments. I also went through the rest of v9 -- the other reviewers' comments and the corresponding fixes (including the replorigin_create() locking rework) look good to me; I didn't find any issues there. A follow-up on the version gating, though: I think it now moved a bit too far. get_replication_origin_info() is called unconditionally (outside the >=3D 1700 block): /* Get replication origin information */ get_replication_origin_info(&old_cluster); and it runs "SELECT count(*) FROM pg_catalog.pg_replication_origin". pg_dumpall's dumpReplicationOrigins() queries the same catalog with no version guard either. But pg_replication_origin only exists from 9.5 (commit 5aa2350426), while pg_upgrade still accepts old clusters back to 9.2: if (GET_MAJOR_VERSION(old_cluster.major_version) < 902) pg_fatal("This utility can only upgrade from PostgreSQL version %s and later.", "9.2"); So upgrading from 9.2/9.3/9.4 now fails: the query hits a non-existent catalog and pg_fatal()s (and dumpReplicationOrigins would too). It should be gated on the version where origins were introduced, e.g. if (GET_MAJOR_VERSION(old_cluster.major_version) >=3D 905) get_replication_origin_info(&old_cluster); with the matching server_version >=3D 90500 check on the pg_dumpall side. That keeps the < 17 case I flagged working while not breaking 9.2-9.4 (which have no origins to migrate anyway). One nit while here: there is an extra blank line after the check_new_cluster_replication_origins() call in check_new_cluster(). Thanks, Rui Ajin Cherian =E4=BA=8E2026=E5=B9=B46=E6=9C=8823=E6=97= =A5=E5=91=A8=E4=BA=8C 19:50=E5=86=99=E9=81=93=EF=BC=9A > > On Wed, Jun 17, 2026 at 8:59=E2=80=AFPM Nisha Moond wrote: > > > > Hi Ajin, Thanks for the patches. > > I have started reviewing the patches. Please find a few initial > > comments below (from v7 review, still applicable to v8): > > > > 1) Patch-002: pg_dump.c > > + if (dopt->binary_upgrade && subinfo->subenabled && > > fout->remoteVersion >=3D 170000) > > { > > - if (subinfo->suboriginremotelsn) > > - { > > - /* > > - * Preserve the remote_lsn for the subscriber's replication > > > > I could not find any usage of subinfo[i].suboriginremotelsn anymore. > > It seems to be dead code now. Can we remove it? > > Removed it. > > > > ~~~ > > > > 2) Patch-002 : binary_upgrade_create_replication_origin() > > - PG_RETURN_VOID(); > > + node_oid =3D PG_GETARG_OID(0); > > + > > + if (node_oid =3D=3D InvalidOid || node_oid > PG_UINT16_MAX) > > + ereport(ERROR, > > + errcode(ERRCODE_INVALID_PARAMETER_VALUE), > > + errmsg("replication origin ID %u is out of range", node_oid)); > > + > > > > The above range check allows PG_UINT16_MAX, but that value is not part > > of the valid replication origin ID range. > > > > In origin.h: > > #define DoNotReplicateId PG_UINT16_MAX > > > > And we already ensure "ReplOriginId !=3D DoNotReplicateId" everywhere > > else. Would something like the below be more appropriate? > > if (node_oid =3D=3D InvalidReplOriginId || node_oid >=3D DoNotReplica= teId) > > ereport(ERROR, ... > > ~~~ > > Done. > > > > > > 3) I think we should add some additional details in > > logical-replication.sgml, especially under Prepare for > > Subscriber Upgrades. > > Some suggestions: > > 3a) Since all replication origins are now migrated, this: > > "...greater than or equal to the number of subscriptions present in > > the old cluster." > > could become: > > "...greater than or equal to the number of replication origins > > present in the old cluster." > > > > 3b) We should also mention: > > The new cluster must contain no replication origins. > > > > Done. > > > > On Wed, Jun 17, 2026 at 9:42=E2=80=AFPM Nisha Moond wrote: > > > > 4) I was able to build successfully without all three new header > > inclusions below, so we may not need them. > > --- a/src/backend/utils/adt/pg_upgrade_support.c > > +++ b/src/backend/utils/adt/pg_upgrade_support.c > > @@ -11,6 +11,7 @@ > > +#include "access/genam.h" > > ... > > +#include "utils/fmgroids.h" > > ... > > +#include "utils/snapmgr.h" > > ~~~ > > > > Removed. > > > > 5) Race condition in origin.c: replorigin_create() > > - heap_freetuple(tuple); > > + table_close(rel, ExclusiveLock); > > + > > + replorigin_create_with_id(roident, roname, InvalidXLogRecPtr); > > + > > > > Due to the above refactoring, CREATE SUBSCRIPTION can now fail because > > of a race between two concurrent CREATE SUBSCRIPTION commands: > > > > postgres=3D# create subscription sub2 connection 'dbname=3Dpostgres > > host=3Dlocalhost port=3D7733' publication pub1; > > ERROR: replication origin with ID 2 already exists > > > > I think this happens because the table lock is released, allowing > > another session to create a pg_origin with the next available roident, > > which replorigin_create_with_id() later attempts to use. > > > > Would it make sense to call replorigin_create_with_id() before > > table_close() so the lock is still held? I tested this and it works, > > though I'm not fully sure whether self-locking could be a concern > > here, since replorigin_create_with_id() again acquires a > > RowExclusiveLock on the same table. > > ~~~ > > > > I have fixed this as suggested by Shveta > > ~~~~~~~~~~ > > > > On Thu, Jun 18, 2026 at 7:17=E2=80=AFPM shveta malik wrote: > > > > > I think this happens because the table lock is released, allowing > > > another session to create a pg_origin with the next available roident= , > > > which replorigin_create_with_id() later attempts to use. > > > > > > Would it make sense to call replorigin_create_with_id() before > > > table_close() so the lock is still held? I tested this and it works, > > > though I'm not fully sure whether self-locking could be a concern > > > here, since replorigin_create_with_id() again acquires a > > > RowExclusiveLock on the same table. > > > > We can handle this race-condition by moving the table-open to callers > > rather than internal call replorigin_create_with_id(). But we also > > need the following changes first: > > > > Yes, did it that way. > > > > 1) > > replorigin_create_with_id has: > > > > + /* > > + * To avoid needing a TOAST table for pg_replication_origin, we limit > > + * replication origin names to 512 bytes. This should be more than en= ough > > + * for all practical use. > > + */ > > + if (strlen(roname) > MAX_RONAME_LEN) > > + ereport(ERROR, > > + errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), > > + errmsg("replication origin name is too long"), > > + errdetail("Replication origin names must be no longer than %d bytes."= , > > + MAX_RONAME_LEN)); > > > > replorigin_create_with_id() is called by replorigin_create() and > > binary_upgrade_create_replication_origin(). replorigin_create() > > already has this check. We should move above check to > > binary_upgrade_create_replication_origin() and get rid of it from > > internal function replorigin_create_with_id(). > > Moved. > > > > > 2) > > > > Also the checks: > > + if (SearchSysCacheExists1(REPLORIGIDENT, ObjectIdGetDatum(roident))) > > + ereport(ERROR, > > + errcode(ERRCODE_DUPLICATE_OBJECT), > > + errmsg("replication origin with ID %u already exists", > > + (Oid) roident)); > > + > > + if (SearchSysCacheExists1(REPLORIGNAME, roname_d)) > > + ereport(ERROR, > > + errcode(ERRCODE_DUPLICATE_OBJECT), > > + errmsg("replication origin \"%s\" already exists", roname)); > > > > should be moved to caller binary_upgrade_create_replication_origin(). > > IMO, these checks are not needed for replorigin_create(), it alread > > has this, which should suffice. > > > > ereport(ERROR, > > (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED= ), > > errmsg("could not find free > > replication origin ID"))); > > > > ~~ > > > > Once we correctly place these checks, we can move the 'table_open with > > lock' to caller (before name and id collision checks) instead of > > replorigin_create_with_id(), which will take care of race-conditions > > reported by Nisha. > > > > I have moved the roident check out of replorigin_create_with_id(), but > kept the roname check there as that is not there in > replorigin_create() > > On Fri, Jun 19, 2026 at 5:48=E2=80=AFPM Shlok Kyal wrote: > > Hi Ajin, > > > > Thanks for providing the updated patch, I reviewed the patch and I > > have some comments: > > > > 1. patch is not applying on HEAD and needs a rebase. > > > > 2. New line after 'check_new_cluster_replication_origins' is not requir= ed: > > + check_new_cluster_replication_origins(); > > + > > > > 3. pg_dumpall with option '--roles-only' also dumps replication origins > > eg: > > ./pg_dumpall -p 9000 --binary-upgrade --roles-only > op.sql > > output contains: > > -- For binary upgrade, must preserve replication origin roident and rem= ote_lsn > > SELECT pg_catalog.binary_upgrade_create_replication_origin('1'::pg_cata= log.oid, > > 'pg_16387'::pg_catalog.text, '0/00000000'::pg_catalog.pg_lsn); > > > > I think in this case we should not dump the replication origins as we > > explicitly gave the option of '--roles-only' > > > > I have moved the dump_replication_origins under !roles_only and > !tablespaces_only checks, I think "globals only" is valid for > replication origins. > > > > 4. pg_dumpall with --globals-only option also dumps replication origins= . > > eg: > > ./pg_dumpall -p 9000 --binary-upgrade --globals-only> op.sql and > > output contains: > > -- For binary upgrade, must preserve replication origin roident and rem= ote_lsn > > SELECT pg_catalog.binary_upgrade_create_replication_origin('1'::pg_cata= log.oid, > > 'pg_16387'::pg_catalog.text, '0/00000000'::pg_catalog.pg_lsn); > > > > Is this behaviour intentional? Other logical replication objects > > (e.g., publications and subscriptions) are not included in pg_dumpall > > --globals-only, so is it okay to dump replication origin? > > > > This is expected as publications and subscriptions are database > specific and not globals, while replication origins are global. > > > > On Mon, Jun 22, 2026 at 2:22=E2=80=AFAM Rui Zhao w= rote: > >> > >> Hi Ajin, > >> > >> Thanks for v8. A couple of comments: > >> > >> 1) The PG17 gating seems inconsistent between the dump and the check n= ow. > >> dumpReplicationOrigins() is called unconditionally: > >> > >> if (binary_upgrade && archDumpFormat =3D=3D archNull) > >> dumpReplicationOrigins(conn); > >> > >> but get_subscription_info(), which sets nrepl_origins, is still only > >> called for old clusters >=3D 17 in check_new_cluster(): > >> > >> if (GET_MAJOR_VERSION(old_cluster.major_version) >=3D 1700) > >> ... > >> get_subscription_info(&old_cluster); > >> > >> and check_new_cluster_replication_origins() returns early on it: > >> > >> if (old_cluster.nrepl_origins =3D=3D 0) > >> return; > >> > >> For a < 17 old cluster get_subscription_info() is never called, so > >> nrepl_origins stays 0 and this returns early. So when such a cluster h= as a > >> (user-created) origin, the origin still gets dumped and recreated, whi= le > >> the "new cluster must have no origins" and max_active_replication_orig= ins > >> checks are skipped, and the upgrade fails during restore instead of at= the > >> check step. nrepl_origins is just a count(*) on pg_replication_origin,= so > >> moving its collection out of the >=3D 1700 block should be enough. > >> > > Yes, good catch. I have separated out the replication origin speciifc > checks into a different function and moved it oustide the >=3D 1700 > block > > >> 2) 004_subscription.pl only checks subscription origins (pg_). Si= nce > >> the patch migrates all origins, should we also create a user origin > >> (pg_replication_origin_create()) and verify its roident/remote_lsn aft= er > >> the upgrade? That would also cover the case in (1). > > > Added. > > All the above changes have been addressed in patch v9. > > regards, > Ajin Cherian > Fujitsu Australia