Thread

  1. Re: Proposal: Conflict log history table for Logical Replication

    Nisha Moond <nisha.moond412@gmail.com> — 2026-05-22T10:12:20Z

    On Fri, May 22, 2026 at 10:21 AM Nisha Moond <nisha.moond412@gmail.com> wrote:
    >
    > On Wed, May 20, 2026 at 3:05 PM vignesh C <vignesh21@gmail.com> wrote:
    > >
    > > Rest of the comments were fixed.
    > > The attached v37 version patch has the changes for the same. Also
    > > Peter's comments on the documentation patch from [1] and Shveta's
    > > comments from [2] are addressed in the attached patch.
    > >
    >
    > Here are few comments based on v37 testing:
    >
    
    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,
    Nisha