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 1waTy8-001prU-2z for pgsql-hackers@arkaria.postgresql.org; Fri, 19 Jun 2026 07:48:53 +0000 Received: from localhost ([127.0.0.1] helo=malur.postgresql.org) by malur.postgresql.org with esmtp (Exim 4.96) (envelope-from ) id 1waTy7-00FUUS-25 for pgsql-hackers@arkaria.postgresql.org; Fri, 19 Jun 2026 07:48:51 +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 1waTy7-00FUUJ-0B for pgsql-hackers@lists.postgresql.org; Fri, 19 Jun 2026 07:48:51 +0000 Received: from mail-oo1-xc2a.google.com ([2607:f8b0:4864:20::c2a]) by makus.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (Exim 4.98.2) (envelope-from ) id 1waTy4-000000016kw-2xxd for pgsql-hackers@lists.postgresql.org; Fri, 19 Jun 2026 07:48:49 +0000 Received: by mail-oo1-xc2a.google.com with SMTP id 006d021491bc7-6a0d414f71bso578248eaf.2 for ; Fri, 19 Jun 2026 00:48:48 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1781855327; cv=none; d=google.com; s=arc-20240605; b=RpaPxcyQD/8Isq5L589YsEc3ATBVW+p5vpKEA78HZYUL481i/3FsTLFid1fql/HB8/ 1S7miu9/fnXYqTbb0wCanSzoGPOx4VUWnp3qzPxJxV7KSjVaMQb9SeLKfDTIwKKk6/5w nt84DlctLw+HgFAeXnmvqhhhOF0EDJq3SYLvj4iBQOeOJjAfPkVDgHqgglYVF8Ue/jSP +Pi9SQ1EPSIRnHR9nsa/euxeQu6EtEifMtIVjQvZQjg5iR4inEmmXSjJxjC9UGLv9Rba sUBAJO43TJ5r2DHikVFdkAoZ7sxMdARP5P8jxR2fOY1PKBym9xD3LiY4ipdPGffIBKyZ gWnA== 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=uaENeIA2Mk81F077upz9J8MzOaLFIOSD5g/DbyXvv74=; fh=UZ113Yj8da/vuUifPC3gdQPhqggkvmlqfy3zl5svl2A=; b=faFFYKGO/vACv11cKAbqe65yd0oz2QxBsleUQ8zkf9fmXy89/P+tu+xyCC5J1caSRW CReJVDP2z0uvZM9RF3poq8FAI9nUlkF6QQFC4qU/1ZjeTqgk1pQLEV4RW/RA5lPu0wk9 2IVCbSCCmIfZGwHqnwRJ0WuY+2qojo13/bT4S772YHKDts+uwHugL5nSxh91aKyDG0jJ uf6Wm3lPjUaCxeqcNkLA2ZAvIqKsrPcvDbesvDAZ7xmPk1u2n6fg6ACArW8UuDNcTs4Z YtNF9+dT5zmCqZSMpkxObjIwlPGWTQYk2OHF8nh/hTWgxgLCy/iPSqVNNg41F1eLlp34 r5fQ==; 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=1781855327; x=1782460127; 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=uaENeIA2Mk81F077upz9J8MzOaLFIOSD5g/DbyXvv74=; b=r8AiGaTy09qil88I3fmmdgHrYkrLH+gvO8uVUt5HF+EK149M0IkEljhvBHkgN1gyUg nCCwj3yagjrvlAkcB6XlVPsWqsl31k3BHtLCMjSuvhMSlajQfxDZKsnLDL3d5iSrY3j2 RM3BG/Qiw7rygXaD262mYFyvVycWDdoudtx9GrguU9lmSBNZ7C3xS8+aOZhbOL+ZHNGl fJeWmzq+FwkCWr6z9l+xvrB7oKQz/rUSrbEG/J7MlbQsu9LZ3gpelan0yn69gMh8J7/X MBvmoTPpJmCrBdXY8Md0g4ueDM1wfuZrpXEaTCjh1r1k/Bad3uqPLuRhRLyGyDgSDJku XA2Q== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1781855327; x=1782460127; 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=uaENeIA2Mk81F077upz9J8MzOaLFIOSD5g/DbyXvv74=; b=UtWnUo1lno2pOJS6XNNR44LW0fF0EM/XYxzyy0BZ03uKCV71gk6stp+TJOuc7rs9rl 0gsmFx9Kd/GrmVM7ztU2fZZwPFs99ojnSZQyo9F3a+HA//A3xmqmmBuLUmzWCxdTu6Mk icGHU2hUs90cUW/ULPGka3nhVWX70X0cmAeftTZzW4hJV61a4ElXy88bho8JkZmf3BEX bAmerjbOV1x17AT0DQBSxERbt8nSs6U3kJHd5JVjMtwIOagRJ5mREjpLfGeZ6/6Q/ycj nXogOfBNhjWXy5wvj4zsdyjm8eHK6iqGSBh3I12Ps1Fnl2iF98EKopKLROrkto+/gIxS 01jw== X-Forwarded-Encrypted: i=1; AFNElJ+8SHJzGTyQmUfBqj2o6k+gtI+WEFDrMunaeIBDlk2cz34qj2TFpaM7L5T8ij9sYK9lqVO0DNsWMwSOTZhd@lists.postgresql.org X-Gm-Message-State: AOJu0YzpUmaJWvelB2JMqE7xAvXD4rZtHybYKC4DwdTFIchOILbaDeK4 cLLGB+f5G6rKNo5o2xbswVTK4OXbLg2B9Pi4Cz7d2TxPYuQyDNpuTrX4ryqtWNIcdGft+RYLEWJ 5MuwI3qa/AIbksndfaXPuA6mIJQris+4= X-Gm-Gg: AfdE7cm/50tqzyTmby+DHEpRc+cUVHPAKRlD0pwR3J2SH59DJYFKRWI0RNf3JsLigh/ OYKMaJQFxLtae/UlMcj4xHAApAQcWfHMKm2t95+fDWjpc6YL9BgTD6sq1jAggdRpCz2lAPYv8D3 +nhTDJNLohPry6gc0AXIYsqDo9gE5us3c4LBey/xISYljA2Dwjxo7OEcdpgU0tuNG/Xr8miRT0A lrXNhsi2ht1WHyMi0O8BhJRFrqvqSGjAM2rGAbxq17JjRiX4pRypiCuJlE7ye76bJtzENKQI6U= X-Received: by 2002:a05:6820:222a:b0:69e:b8:ffe8 with SMTP id 006d021491bc7-6a0d89cf24dmr2167494eaf.52.1781855327272; Fri, 19 Jun 2026 00:48:47 -0700 (PDT) MIME-Version: 1.0 References: In-Reply-To: From: Shlok Kyal Date: Fri, 19 Jun 2026 13:18:34 +0530 X-Gm-Features: AVVi8CdYMq6AquqWxNc62VzSp2Z4fN1zliyhej0B5G81KR5Vfi-QbemGG2rYwhM Message-ID: Subject: Re: [PATCH] Preserve replication origin OIDs in pg_upgrade To: Ajin Cherian Cc: 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 On Wed, 17 Jun 2026 at 14:49, Ajin Cherian wrote: > > On Fri, Jun 5, 2026 at 8:10=E2=80=AFPM vignesh C wr= ote: > > > > Few comments: > > 1) pg_dumpall dumps all replication origins, not just subscription > > origins and recreates every origin: > > SELECT o.*, os.remote_lsn > > FROM pg_replication_origin o > > LEFT JOIN pg_replication_origin_status os > > ON o.roident =3D os.local_id > > > > Is pg_upgrade expected to preserve user-created origins? > > If so, you can mention this in the commit message > > > > I explicitly state that all replication origins are migrated in the > commit message, nothing specific about subscription origins. > > > 2) Should the table lock be released here, if so should we mention > > comments similar to how it is mentioned in > > binary_upgarde_add_sub_rel_state: > > + /* Restore the remote_lsn if provided, while still holding the lock *= / > > + if (!PG_ARGISNULL(2)) > > + { > > + XLogRecPtr remote_commit =3D PG_GETARG_LSN(2); > > > > - ReplicationSlotRelease(); > > + replorigin_advance(node, remote_commit, InvalidXLogRecPtr, > > + false /* backward */, > > + false /* WAL log */); > > + } > > + > > + table_close(rel, RowExclusiveLock); > > > > Is that relevant when creating a new row? or only when updating an existi= ng one? > > > 3) We can use new style of ereport to exclude '(' before errcode: > > 3.a) > > + 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))); > > > > 3.b) Similarly here: > > + ereport(ERROR, > > + (errcode(ERRCODE_DUPLICATE_OBJECT), > > + errmsg("replication origin with ID %u already exists", node_oid))); > > > > 3.c) There are many others, you can search other ereport in the patch > > and change it too. > > > > Done. > > > 4) Since only one column will be returned here, can we use > > "PQgetvalue(res, 0, 0) and remove PQfnumber: > > + res =3D executeQueryOrDie(conn, > > + "SELECT count(*) AS nrepl_origins " > > + "FROM pg_catalog.pg_replication_origin"); > > + i_nrepl_origins =3D PQfnumber(res, "nrepl_origins"); > > + cluster->nrepl_origins =3D atoi(PQgetvalue(res, 0, i_nrepl_origins)); > > + PQclear(res); > > > > Done. > > > 5) replorigin_create also has some duplicate code like below, will it > > be possible to have a common function so that both of them can use? > > /* Check for name collision */ > > + ScanKeyInit(&key, > > + Anum_pg_replication_origin_roname, > > + BTEqualStrategyNumber, F_TEXTEQ, > > + roname_d); > > + scan =3D systable_beginscan(rel, ReplicationOriginNameIndex, > > + true /* indexOK */, > > + SnapshotSelf, > > + 1, &key); > > + collides =3D HeapTupleIsValid(systable_getnext(scan)); > > + systable_endscan(scan); > > + > > + if (collides) > > + ereport(ERROR, > > + (errcode(ERRCODE_DUPLICATE_OBJECT), > > + errmsg("replication origin \"%s\" already exists", > > + originname))); > > + > > + memset(&nulls, 0, sizeof(nulls)); > > + memset(&values, 0, sizeof(values)); > > + > > + values[Anum_pg_replication_origin_roident - 1] =3D ObjectIdGetDatum(n= ode); > > + values[Anum_pg_replication_origin_roname - 1] =3D roname_d; > > + > > + tuple =3D heap_form_tuple(RelationGetDescr(rel), values, nulls); > > + CatalogTupleInsert(rel, tuple); > > + heap_freetuple(tuple); > > + CommandCounterIncrement(); > > > > I have created a new function replorigin_create_with_id() which has > some common code and called that from both replorigin_create() and > binary_upgrade_create_replication_origin() > > > On Tue, Jun 9, 2026 at 3:06=E2=80=AFAM Shlok Kyal wrote: > > > > Hi Ajin, > > I have reviewed the patch and here are some comments: > > > > 1. I want to clarify, what is the expected behaviour when we upgrade > > the postgres instance from version <=3D 16 to "HEAD + patch"? > > - ReplicationOriginNameForLogicalRep(subid, InvalidOid, originname, > > sizeof(originname)); > > - replorigin_create(originname); > > + if (!IsBinaryUpgrade) > > + { > > + ReplicationOriginNameForLogicalRep(subid, InvalidOid, > > originname, sizeof(originname)); > > + replorigin_create(originname); > > + } > > and > > + /* Dump replication origins */ > > + if (server_version >=3D 170000 && binary_upgrade && > > archDumpFormat =3D=3D archNull) > > + dumpReplicationOrigins(conn); > > > > When the postgres instance with subscription is updated from PG 16 (or > > less) to HEAD, the replication origin is created as we do not have "if > > (!IsBinaryUpgrade)" check in HEAD, > > whereas when the similar instance is upgraded to "HEAD + patch", no > > replication origin is present after the upgrade. > > Is this difference in the behaviour between HEAD and "HEAD + patch" exp= ected? > > > > I have modified this as option 3 suggested by Shveta and Amit and > removed the PG17 restriction for migrating replication origins, now it > does it for all versions that have replication origins. > > > 2.a) Can we replace: > > + /* Check for OID collision */ > > + ScanKeyInit(&key, > > + Anum_pg_replication_origin_roident, > > + BTEqualStrategyNumber, F_OIDEQ, > > + ObjectIdGetDatum(node)); > > + scan =3D systable_beginscan(rel, ReplicationOriginIdentIndex, > > + true /* indexOK */, > > + SnapshotSelf, > > + 1, &key); > > + collides =3D HeapTupleIsValid(systable_getnext(scan)); > > + systable_endscan(scan); > > + > > + if (collides) > > + ereport(ERROR, > > + (errcode(ERRCODE_DUPLICATE_OBJECT), > > + errmsg("replication origin with ID %u already > > exists", node_oid))); > > > > with: > > if (SearchSysCacheExists1(REPLORIGIDENT, ObjectIdGetDatum(node))) > > ereport(ERROR, errcode(ERRCODE_DUPLICATE_OBJECT), > > errmsg("replication origin with ID %u already > > exists", node_oid)); > > > > 2.b) Similarly, Can we replace: > > + /* Check for name collision */ > > + ScanKeyInit(&key, > > + Anum_pg_replication_origin_roname, > > + BTEqualStrategyNumber, F_TEXTEQ, > > + roname_d); > > + scan =3D systable_beginscan(rel, ReplicationOriginNameIndex, > > + true /* indexOK */, > > + SnapshotSelf, > > + 1, &key); > > + collides =3D HeapTupleIsValid(systable_getnext(scan)); > > + systable_endscan(scan); > > + > > + if (collides) > > + ereport(ERROR, > > + (errcode(ERRCODE_DUPLICATE_OBJECT), > > + errmsg("replication origin \"%s\" already exists", > > + originname))); > > with: > > if (SearchSysCacheExists1(REPLORIGNAME, roname_d)) > > ereport(ERROR, errcode(ERRCODE_DUPLICATE_OBJECT), > > errmsg("replication origin \"%s\" already > > exists", originname)); > > > > This will make the code smaller and easy to read. Thoughts? > > Done > > > On Tue, Jun 9, 2026 at 2:13=E2=80=AFPM shveta malik wrote: > > > > > > Please find a few comments on v007: > > > > 1) > > > > + node =3D (ReplOriginId) node_oid; > > + originname =3D text_to_cstring(PG_GETARG_TEXT_PP(1)); > > + > > + if (strlen(originname) > 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))); > > + > > + roname_d =3D CStringGetTextDatum(originname); > > > > CStringGetTextDatum is: > > #define CStringGetTextDatum(s) PointerGetDatum(cstring_to_text(s)) > > --------- > > > > We are first converting text to C-string , then Cstring to text and > > then getting Datum. Double allocation and double conversion. Can we > > please try this instead and verify if it works: > > > > text *origin_text =3D PG_GETARG_TEXT_PP(1); > > originname =3D text_to_cstring(origin_text); > > roname_d =3D PointerGetDatum(origin_text); > > > > I've restructured the code now, its a bit tough to do this now. > > > 2) > > +check_new_cluster_replication_origins(void) > > +{ > > + PGconn *conn; > > + PGresult *res; > > + int norigins; > > + > > + /* Quick return if there are no replication origins to migrate. */ > > + if (old_cluster.nrepl_origins =3D=3D 0) > > + return; > > > > Don't we need a version check simialr to what we have introduced in > > pg_dumpall for origins? (Similar to > > check_new_cluster_replication_slots as well) > > > > I've removed the version check for PG17 now. > > > 3) > > Since we have introduced check_new_cluster_replication_origins, do we > > even need below checks in binary_upgrade_create_replication_origin()? > > In which conditions will they be hit? > > > > + /* Check for OID collision */ > > + .... > > + if (collides) > > + ereport(ERROR, > > + (errcode(ERRCODE_DUPLICATE_OBJECT), > > + errmsg("replication origin with ID %u already exists", node_oid))); > > + > > + /* Check for name collision */ > > + .... > > + if (collides) > > + ereport(ERROR, > > + (errcode(ERRCODE_DUPLICATE_OBJECT), > > + errmsg("replication origin \"%s\" already exists", > > + originname))); > > > > With the restructured code, its not easy to do this now. > > > > > 4) > > Now since check_new_cluster_subscription_configuration() is purely > > about checking max-origin GUC configuration, I think the name is > > misleading. IMO, we should merge it to > > check_new_cluster_replication_origins(). See how > > check_new_cluster_replication_slots() does it: checking both > > new-cluster's count and the max-configuration for slots. > > > > Done. > > > On Tue, Jun 9, 2026 at 9:32=E2=80=AFPM Shlok Kyal wrote: > > > > > > > One more comment: > > > > 3. In logical-replication.sgml, we have documentation as: > > "Commit timestamps and origin data are not preserved during the upgrade= ." > > Now since the origin related data is preserved after the upgrade, > > should we update the documentation here? > > > > Updated. > > I have addressed the comments in v8 (attached). I've also now taken > out the check for PG17 on old cluster and now all clusters which have > replication origins are migrated. 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 required: + 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 remote_= lsn SELECT pg_catalog.binary_upgrade_create_replication_origin('1'::pg_catalog.= 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' 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 remote_= lsn SELECT pg_catalog.binary_upgrade_create_replication_origin('1'::pg_catalog.= 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? Thanks, Shlok Kyal