Re: Proposal: Conflict log history table for Logical Replication

vignesh C <vignesh21@gmail.com>

From: vignesh C <vignesh21@gmail.com>
To: Nisha Moond <nisha.moond412@gmail.com>
Cc: Peter Smith <smithpb2250@gmail.com>, Dilip Kumar <dilipbalaut@gmail.com>, Amit Kapila <amit.kapila16@gmail.com>, shveta malik <shveta.malik@gmail.com>, Masahiko Sawada <sawada.mshk@gmail.com>, Bharath Rupireddy <bharath.rupireddyforpostgres@gmail.com>, PostgreSQL Hackers <pgsql-hackers@lists.postgresql.org>, shveta malik <shvetamalik@gmail.com>
Date: 2026-05-25T04:42:53Z
Lists: pgsql-hackers

Attachments

On Fri, 22 May 2026 at 15:42, Nisha Moond <nisha.moond412@gmail.com> wrote:
>
> Here are few more review comments -
> 1) Patch-0001 + 0002:
> In subscription.sql:
>  -- Verify the table OID for reap check
>  SELECT 'pg_conflict_log_for_subid_' || oid AS internal_tablename FROM
> pg_subscription WHERE subname = 'regress_conflict_test1' \gset
>  SET client_min_messages = WARNING;
>  DROP SUBSCRIPTION regress_conflict_test1;
>  -- should return NULL, meaning the conflict log table was reaped via dependency
>  SELECT to_regclass(:'internal_tablename');
>
> Here, internal_tablename becomes "pg_conflict_log_*" without the
> pg_conflict. schema prefix. So, "SELECT
> to_regclass(:'internal_tablename');" will always return NULL even if
> the table still exists in the pg_conflict schema, which skips the
> actual drop verification scenario.
> Should we instead use:
>    "SELECT 'pg_conflict.pg_conflict_log_' || oid AS internal_tablename..."
> ~~~
>
> For Patch-0007:
> 2)
> @@ -2067,9 +2095,31 @@ selectDumpableNamespace(NamespaceInfo *nsinfo,
> Archive *fout)
>  static void
>  selectDumpableTable(TableInfo *tbinfo, Archive *fout)
> ....
> + if (strcmp(tbinfo->dobj.namespace->dobj.name, "pg_conflict") == 0)
> ...
> + * Dump pg_conflict tables only during binary upgrade. The schema
> + * is assumed to already exist.
> + */
> + tbinfo->dobj.dump = DUMP_COMPONENT_DEFINITION;
> ....
> + else
> + tbinfo->dobj.dump = DUMP_COMPONENT_NONE;
> + }
> +
>
> For conflict log tables during binary upgrade, we set:
>    tbinfo->dobj.dump = DUMP_COMPONENT_DEFINITION;
>
> but then execution falls through to the later logic:
> ...
>   else
>     tbinfo->dobj.dump = tbinfo->dobj.namespace->dobj.dump_contains;
>
> which seems to overwrite the earlier 'dobj.dump' value. So it looks
> like DUMP_COMPONENT_DEFINITION may never actually survive here.
> Should we return from this block instead of continuing further?
>
> 3)
> @@ -5656,6 +5757,11 @@ dumpSubscription(Archive *fout, const
> SubscriptionInfo *subinfo)
>
>   appendPQExpBufferStr(query, ");\n");
>
> + appendPQExpBuffer(query,
> +   "\n\nALTER SUBSCRIPTION %s SET (conflict_log_destination = %s);\n",
> +   qsubname,
> +   subinfo->subconflictlogdest);
> +
>
> The above ALTER SUBSCRIPTION command seems to be dumped
> unconditionally for every subscription.
> Since the default value during subscription creation is already
> "subconflictlogdest = 'log' ", should we emit this command only when
> subconflictlogdest is non-NULL and not 'log'?
>
> 4)
> + if (PQgetisnull(res, i, i_sublogdestination))
> + subinfo[i].subconflictlogdest = NULL;
> + else
> + subinfo[i].subconflictlogdest =
> + pg_strdup(PQgetvalue(res, i, i_sublogdestination));
> +
> + if (PQgetisnull(res, i, i_sublogdestination))
> + subinfo[i].subconflictlogdest = NULL;
> + else
> + subinfo[i].subconflictlogdest =
> + pg_strdup(PQgetvalue(res, i, i_sublogdestination));
> +
>   /* Decide whether we want to dump it */
>
> Looks like the same if-else block is repeated twice here.

Thanks for the comments, the attached v39 version patch has the
changes for the same.

Regards,
Vignesh