Re: Proposal: Conflict log history table for Logical Replication

From: Dilip Kumar <dilipbalaut(at)gmail(dot)com>
To: Amit Kapila <amit(dot)kapila16(at)gmail(dot)com>
Cc: vignesh C <vignesh21(at)gmail(dot)com>, Nisha Moond <nisha(dot)moond412(at)gmail(dot)com>, Peter Smith <smithpb2250(at)gmail(dot)com>, shveta malik <shveta(dot)malik(at)gmail(dot)com>, Masahiko Sawada <sawada(dot)mshk(at)gmail(dot)com>, Bharath Rupireddy <bharath(dot)rupireddyforpostgres(at)gmail(dot)com>, PostgreSQL Hackers <pgsql-hackers(at)lists(dot)postgresql(dot)org>
Subject: Re: Proposal: Conflict log history table for Logical Replication
Date: 2026-04-28 14:23:02
Message-ID: CAFiTN-vPDqrQ2rHykNgd+groFxqwBYFQF97R-Co2EmtUkV6MTg@mail.gmail.com
Views: Whole Thread | Raw Message | Download mbox | Resend email
Thread:
Lists: pgsql-hackers

On Tue, Apr 28, 2026 at 9:45 AM Amit Kapila <amit(dot)kapila16(at)gmail(dot)com> wrote:
>
> On Thu, Apr 23, 2026 at 7:36 PM vignesh C <vignesh21(at)gmail(dot)com> wrote:
> >
> > On Tue, 21 Apr 2026 at 15:21, Dilip Kumar <dilipbalaut(at)gmail(dot)com> wrote:
> > >
> Some comments on 0001:
> ====================

Thanks for the review, I will send the updated patch along with other
open comments and bug fixes.

> 1.
> postgres=# create subscription sub1 connection 'dbname=postgres'
> publication pub1 WITH (conflict_log_destination='all');
> NOTICE: created replication slot "sub1" on publisher
> CREATE SUBSCRIPTION
>
> Should there be a NOTICE for internally created table similar to slot creation?

+1

>
> 2.
> +typedef enum ConflictLogDest
> +{
> + /* Log conflicts to the server logs */
> + CONFLICT_LOG_DEST_LOG = 1 << 0, /* 0x01 */
> +
> + /* Log conflicts to an internally managed conflict log table */
> + CONFLICT_LOG_DEST_TABLE = 1 << 1, /* 0x02 */
> +
> + /* Convenience bitmask for all supported destinations */
> + CONFLICT_LOG_DEST_ALL = (CONFLICT_LOG_DEST_LOG | CONFLICT_LOG_DEST_TABLE)
> +} ConflictLogDest;
> +
> +/*
> + * Array mapping for converting internal enum to string.
> + */
> +static const char *const ConflictLogDestNames[] = {
> + [CONFLICT_LOG_DEST_LOG] = "log",
> + [CONFLICT_LOG_DEST_TABLE] = "table",
> + [CONFLICT_LOG_DEST_ALL] = "all"
> +};
>
> Defining an array this way could be an Array size issue. Actually the
> array has just three elements so the last element should be at
> ConflictLogDestNames[2] but if we go by the above definition, it will
> be ConflictLogDestNames[3]. Can we define by referring the following
> existing way:
>
> typedef enum
> {
> XLOG_FROM_ANY = 0, /* request to read WAL from any source */
> XLOG_FROM_ARCHIVE, /* restored using restore_command */
> XLOG_FROM_PG_WAL, /* existing file in pg_wal */
> XLOG_FROM_STREAM, /* streamed from primary */
> } XLogSource;
>
> /* human-readable names for XLogSources, for debugging output */
> static const char *const xlogSourceNames[] = {"any", "archive",
> "pg_wal", "stream"};
>
> In this case, _ANY is similar to your 'all' case and won't have the
> array size issue.

I will analyze this and respond by tomorrow.

> 3. create_conflict_log_table_tupdesc() calls BlessTupleDesc, which is
> intended for set-returning functions as indicated by a comment atop it
> (BlessTupleDesc - make a completed tuple descriptor useful for SRFs).
> Is there a reason that the patch uses it for
> heap_create_with_catalog()? If so, can we write a comment for the
> same?

BlessTupleDesc are used in other cases as well other then SRF as well,
I will add a one liner comment.

> 4. ConflictLogDestNames[] and ConflictLogSchema[] are static const
> arrays defined in conflict.h, which is included by conflict.c,
> worker.c, and execReplication.c. Each translation unit gets its own
> copy. It is better to define them in .c and the declared extern in the
> header. See how forkNames is used in the code.

Will fix

> 5.
> We allow DELETE to permit users to manually
> + * prune or truncate these logs, but manual data insertion or modification
> + * (INSERT, UPDATE, MERGE) is prohibited to maintain the integrity of the
> + * system-generated logs.
> + */
> + if (IsConflictNamespace(RelationGetNamespace(resultRel)) &&
> + operation != CMD_DELETE)
> + ereport(ERROR,
> + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
> + errmsg("cannot modify or insert data into conflict log table \"%s\"",
> + RelationGetRelationName(resultRel)),
> + errdetail("Conflict log tables are system-managed and only support
> cleanup via DELETE or TRUNCATE.")));
>
> The comment and code doesn't match completely because errdetail has
> reference to TRUNCATE but comment says only DELETE probably because
> this function doesn't accept TRUNCATE as operation type. I think we
> can slightly update the comments to reflect it.

I will check and fix this.

> 6. Commit message states:
> "If the user chooses to enable logging to a table (by selecting
> 'table' or 'all'),
> an internal logging table named conflict_log_table_<subid> is automatically
> created .."
>
> I see that the patch creates a table with the name
> pg_conflict_<subid>. Apart from updating the commit message, how about
> naming the table as pg_conflict_log_<subid> or pg_subconflict_<subid>.
> In the first alternative pg_conflict_log_<subid>, the table name in
> isolation without schema name describes its purpose, the second
> alternative breaks the repetitiveness of pg_conflict when used with
> schema like pg_conflict.pg_conflict_16394.

I prefer pg_conflict_log_<subid> over pg_subconflict_<subid>

> 7. The commit message can contain why we didn't go for a single global
> table to store conflicts for each subscription.

Make sense

--
Regards,
Dilip Kumar
Google

In response to

Browse pgsql-hackers by date

  From Date Subject
Next Message Henrik TJ 2026-04-28 14:25:43 Fix background writer processing locking in README
Previous Message Bharath Rupireddy 2026-04-28 14:17:00 Re: Fix race condition in pg_get_publication_tables with concurrent DROP TABLE