Re: Proposal: Conflict log history table for Logical Replication

shveta malik <shveta.malik@gmail.com>

From: shveta malik <shveta.malik@gmail.com>
To: Dilip Kumar <dilipbalaut@gmail.com>
Cc: Amit Kapila <amit.kapila16@gmail.com>, Peter Smith <smithpb2250@gmail.com>, vignesh C <vignesh21@gmail.com>, Nisha Moond <nisha.moond412@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-07-06T09:29:46Z
Lists: pgsql-hackers
On Mon, Jul 6, 2026 at 12:52 PM Dilip Kumar <dilipbalaut@gmail.com> wrote:
>
>
> Attached patch fixes comments raised by Nisha, Shaveta and separates
> out the logical-replication.sgml changes to a new patch.
>

Thanks. Please find a few comments for the review done so far (0001 alone):


1)
docs of v64-001 does not compile alone unless we install 002 as well.

Perhaps thes sections are probelematic:
+              Conflict details are recorded in the server log. This is
+              the default behavior. See
+              <xref linkend="logical-replication-conflict-file-based-logging"/>
+              for details.

+              querying and analysis of conflicts. See
+              <xref
linkend="logical-replication-conflict-table-based-logging"/>
+              for details.

These try to point to 002's new sections. We can move these lines as
well to 002.

2)
+ if (pending_conflict_log.tuple != NULL)
+ {
+ heap_freetuple(pending_conflict_log.tuple);
+ pending_conflict_log.tuple = NULL;
+ }
+ if (pending_conflict_log.errcontext_str != NULL)

We can leave a blank line between 2 if-blocks to make these more
readbale, else they give a feeling of if-else block.

3)
ProcessPendingConflictLogTuple, InsertConflictLogTuple and
ReportApplyConflict: all uses cleanup similar to below:

if (pending_conflict_log.tuple != NULL)
{
        heap_freetuple(pending_conflict_log.tuple);
        pending_conflict_log.tuple = NULL;
}
if (pending_conflict_log.errcontext_str != NULL)
{
        pfree(pending_conflict_log.errcontext_str);
        pending_conflict_log.errcontext_str = NULL;
}

A small helper (say ResetPendingConflictLog()) would remove
duplication and make future maintenance easier.

4)
In build_local_conflicts_json_array(), we are first building a list of
JSON datums and then copying them into an array. This is okay, but
since num_conflicts can be computed upfront, do you think we could
directly allocate the array, avoiding the temproary list.

Instead of below steps:

+ foreach_ptr(ConflictTupleInfo, conflicttuple, conflicttuples)
+ {
....
+ json_datum = DirectFunctionCall1(row_to_json, datum);
...
+ /* Add to the array element. */
+ json_datums = lappend(json_datums, (void *) json_datum);
+ }

+ num_conflicts = list_length(json_datums);
+
+ json_datum_array = palloc_array(Datum, num_conflicts);
+
+ i = 0;
+ foreach(lc, json_datums)
+ {
+ json_datum_array[i] = (Datum) lfirst(lc);
+ i++;
+ }


can we do:

num_conflicts = list_length(conflicttuples);
json_datum_array = palloc_array(Datum, num_conflicts);

foreach_ptr(ConflictTupleInfo, conflicttuple, conflicttuples)
{
....
 json_datum_array[i++] = DirectFunctionCall1(row_to_json, datum);
}

thanks
Shveta



Commits

Same data as JSON: GET /api/v1/messages/:b64id/commits the thread's linked commits as JSON, with link sources. API reference →
  1. Allow logical replication conflicts to be logged to a table.

  2. Avoid orphaned objects dependencies