Thread

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

    Nisha Moond <nisha.moond412@gmail.com> — 2026-05-15T10:29:23Z

    On Thu, May 14, 2026 at 12:45 PM vignesh C <vignesh21@gmail.com> wrote:
    >
    > I have fixed the other comments except this one. I will think more
    > about this and reply separately. The attached patch has the changes
    > for the rest of the comments. The patch also addresses comments from
    > [1].
    >
    
    Thanks for the patches. Please find below comments for v34 patch-set.
    
    1) Bug report:
    When disable_on_error = true for a subscription, and an ERROR-level
    conflict such as insert_exists occurs, the subscription gets disabled
    without logging the conflict into the CLT.
    
    patch-001:
    2)  execMain.c:
    + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
    + errmsg("cannot modify or insert data into conflict log table \"%s\"",
    + RelationGetRelationName(resultRel)),
    
    Is ERRCODE_INSUFFICIENT_PRIVILEGE the right error code here? It gives
    the impression that the operation might succeed with higher
    privileges. Should we instead use ERRCODE_WRONG_OBJECT_TYPE, similar
    to nearby restrictions?
    
    3) No notice is shown when the conflict log table is removed after
    changing conflict_log_destination from table/all to log.
    Example:
    postgres=# alter subscription sub1 set (conflict_log_destination = table);
    NOTICE:  created conflict log table
    "pg_conflict.pg_conflict_log_16400" for subscription "sub1"
    ALTER SUBSCRIPTION
    
    postgres=# alter subscription sub1 set (conflict_log_destination = log);
    ALTER SUBSCRIPTION
    
    We already show a notice when changing from log to table/all. Should
    we add a similar notice as in DROP SUBSCRIPTION for above case?
    
    patch-003:
    4) conflict.c: ReportApplyConflict()
    + bool log_dest_clt = false;
    + bool log_dest_logfile;
    
    log_dest_logfile should also be initialized to false, since for dest
    == CONFLICT_LOG_DEST_TABLE, it is never assigned.
    
    5) worker_internal.h
     extern PGDLLIMPORT List *table_states_not_ready;
    
    +extern XLogRecPtr remote_final_lsn;
    +extern TimestampTz remote_commit_ts;
    +extern TransactionId remote_xid;
    
    Should these new declarations also use PGDLLIMPORT?
    
    6) worker.c: apply_handle_stream_start()
    + remote_xid = stream_xid;
    + remote_final_lsn = InvalidXLogRecPtr;
    + remote_commit_ts = 0;
    +
      if (!TransactionIdIsValid(stream_xid))
      ereport(ERROR,
      (errcode(ERRCODE_PROTOCOL_VIOLATION),
    
    Should the remote_xid assignment be moved after the validity check? We
    could move all three assignments below the check.
    
    patch-005:
    7) subscriptioncmds.c: DropSubscription()
    + if (OidIsValid(form->subconflictlogrelid))
    + {
    + char *conflictrelname = get_rel_name(form->subconflictlogrelid);
    ....
    "form" is being used here after the tuple it points to has already been deleted:
    
      /* Remove the tuple from catalog. */
      CatalogTupleDelete(rel, &tup->t_self);
    
      ReleaseSysCache(tup);
    
    I think form->subconflictlogrelid should be saved beforehand and then
    used later, similar to subid.
    
    --
    Thanks,
    Nisha