Re: Proposal: Conflict log history table for Logical Replication
vignesh C <vignesh21@gmail.com>
From: vignesh C <vignesh21@gmail.com>
To: Peter Smith <smithpb2250@gmail.com>
Cc: shveta malik <shveta.malik@gmail.com>,
Dilip Kumar <dilipbalaut@gmail.com>, Nisha Moond <nisha.moond412@gmail.com>,
Amit Kapila <amit.kapila16@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-26T05:53:51Z
Lists: pgsql-hackers
Attachments
- v40-0001-Add-configurable-conflict-log-table-for-Logical-.patch (application/octet-stream)
- v40-0005-Implement-the-conflict-insertion-infrastructure-.patch (application/octet-stream)
- v40-0004-Review-comment-fixes-for-transfer-ownership-patc.patch (application/octet-stream)
- v40-0003-transfer-ownership.patch (application/octet-stream)
- v40-0002-Review-comment-fixes-for-Add-configurable-confli.patch (application/octet-stream)
- v40-0006-Review-comment-fixes-for-Implement-the-conflict-.patch (application/octet-stream)
- v40-0007-Preserve-conflict-log-destination-and-subscripti.patch (application/octet-stream)
- v40-0008-Documentation-patch.patch (application/octet-stream)
- v40-0009-Review-comment-fixes-for-Documentation-patch.patch (application/octet-stream)
- v40-0010-Add-conflict-log-table-information-to-describe-s.patch (application/octet-stream)
On Mon, 25 May 2026 at 07:07, Peter Smith <smithpb2250@gmail.com> wrote:
>
> Hi Vignesh,
>
> Some review comments for v38-0001/0002 combined.
>
> ======
> src/backend/commands/subscriptioncmds.c
>
> AlterSubscriptionConflictLogDestination:
>
> 1.
> * Update the conflict log table associated with a subscription when its
> * conflict log destination is changed.
>
> Somehow, that 'its' sounded awkward to me.
>
> SUGGESTION.
> When the subscription's 'conflict_log_destination' is changed, update
> the conflict log table if required.
>
> ~~~
>
> 2.
> + * If the new destination requires a conflict log table and none was previously
> + * required, this function validates an existing conflict log table identified
> + * by the subscription specific naming convention or creates a new one.
>
> What does this mean: "validates an existing conflict log table". How
> is there an "existing" CLT when you already said "none was previously
> required". Maybe this needs more explanation. If it is talking about
> "not already associated with another subscription", then it should
> just say that.
>
> Anyway, it seems validation that the comment claims this function is
> doing is not done here at all, but is really done by
> 'create_conflict_log_table'.
>
> ~~~
>
> 3.
> +static bool
> +AlterSubscriptionConflictLogDestination(Subscription *sub,
> + ConflictLogDest logdest,
> + Oid *conflicttablerelid)
>
> 3a.
> There was no forward declaration of this static function, but there
> was for all the others.
>
> ~
>
> 3b.
> Static functions should use snake-case names.
>
> ~~~
>
> 4.
> Personally, I think it is more natural to read LEFT-TO-RIGHT,
> OLD-THEN-NEW, etc., so I felt that the has_oldtable check should
> always come before want_table.
>
> Also, the 'ifs' seemed tricky because it's not obvious what
> has/need_table combinations are missing. e.g. The following seems
> easier to me. And probably lots of comments could be moved to here in
> the code as well, instead of in the function comment.
>
> SUGGESTION
> if (has_old_table)
> {
> /* There is a CLT already. */
>
> if (!want_table)
> {
> /* Remove it. */
> drop_subscription_dependencies(sub->oid, sub->name, sub->conflictlogrelid);
> update_relid = true;
> }
> }
> else
> {
> /* There was no previous CLT. */
>
> if (want_table)
> {
> /* Create one. */
> relid = create_conflict_log_table(sub->oid, sub->name, sub->owner);
> update_relid = true;
> }
> }
>
> ~~~
>
> 5.
> +static void
> +drop_subscription_dependencies(Oid subid, char *subname,
> + Oid subconflictlogrelid)
> +{
> + ObjectAddress object;
> + char *conflictrelname;
> +
> + conflictrelname = get_rel_name(subconflictlogrelid);
> +
> + /*
> + * By using PERFORM_DELETION_SKIP_ORIGINAL, we ensure that only the
> + * conflict log table is deleted while the subscription remains.
> + */
> + ObjectAddressSet(object, SubscriptionRelationId, subid);
> + performDeletion(&object, DROP_CASCADE,
> + PERFORM_DELETION_INTERNAL |
> + PERFORM_DELETION_SKIP_ORIGINAL);
> +
> + ereport(NOTICE,
> + errmsg("dropped conflict log table \"%s\" for subscription \"%s\"",
> + get_qualified_objname(PG_CONFLICT_NAMESPACE, conflictrelname),
> + subname));
> +}
> +
>
> One day, this function might do more than just remove the CLT, so IMO
> all this function body should be within a check:
>
> if (OidIsValid(subconflictlogrelid))
> {
> /* Drop any dependent CLT */
> ...
> }
>
> ~~~
>
> DropSubscription
>
> 6.
> + if (OidIsValid(subconflictlogrelid))
> + drop_subscription_dependencies(subid, subname, subconflictlogrelid);
>
> Make it unconditional. Instead, add the condition inside the
> 'drop_subscription_dependencies', per the previous review comment #5.
>
> ======
> src/test/regress/sql/subscription.sql
>
> 7.
> +--
> +-- PUBLICATION: Verify conflict log tables are not publishable
> +--
> +-- pg_relation_is_publishable should return false for internal conflict log
> +-- tables to prevent them from being accidentally included in publications
> +--
>
> Everywhere else, you had removed the word "internal", but this one
> (maybe others?) was missed.
Thanks for the comments, these are addressed in the v40 version patch attached.
Regards,
Vignesh