From 73eba5004a06a744b6b8570e42432b9e9f75997b Mon Sep 17 00:00:00 2001 From: Amit Kapila Date: Mon, 24 Mar 2025 12:30:44 +0530 Subject: Detect and Log multiple_unique_conflicts type conflict. Introduce a new conflict type, multiple_unique_conflicts, to handle cases where an incoming row during logical replication violates multiple UNIQUE constraints. Previously, the apply worker detected and reported only the first encountered key conflict (insert_exists/update_exists), causing repeated failures as each constraint violation needs to be handled one by one making the process slow and error-prone. With this patch, the apply worker checks all unique constraints upfront once the first key conflict is detected and reports multiple_unique_conflicts if multiple violations exist. This allows users to resolve all conflicts at once by deleting all conflicting tuples rather than dealing with them individually or skipping the transaction. In the future, this will also allow us to specify different resolution handlers for such a conflict type. Add the stats for this conflict type in pg_stat_subscription_stats. Author: Nisha Moond Author: Zhijie Hou Reviewed-by: Amit Kapila Reviewed-by: Peter Smith Reviewed-by: Dilip Kumar Discussion: https://postgr.es/m/CABdArM7FW-_dnthGkg2s0fy1HhUB8C3ELA0gZX1kkbs1ZZoV3Q@mail.gmail.com --- src/backend/executor/execReplication.c | 30 +++++++++++++++++++----------- 1 file changed, 19 insertions(+), 11 deletions(-) (limited to 'src/backend/executor') diff --git a/src/backend/executor/execReplication.c b/src/backend/executor/execReplication.c index 0a9b880d250..ede89ea3cf9 100644 --- a/src/backend/executor/execReplication.c +++ b/src/backend/executor/execReplication.c @@ -493,25 +493,33 @@ CheckAndReportConflict(ResultRelInfo *resultRelInfo, EState *estate, ConflictType type, List *recheckIndexes, TupleTableSlot *searchslot, TupleTableSlot *remoteslot) { - /* Check all the unique indexes for a conflict */ + List *conflicttuples = NIL; + TupleTableSlot *conflictslot; + + /* Check all the unique indexes for conflicts */ foreach_oid(uniqueidx, resultRelInfo->ri_onConflictArbiterIndexes) { - TupleTableSlot *conflictslot; - if (list_member_oid(recheckIndexes, uniqueidx) && FindConflictTuple(resultRelInfo, estate, uniqueidx, remoteslot, &conflictslot)) { - RepOriginId origin; - TimestampTz committs; - TransactionId xmin; - - GetTupleTransactionInfo(conflictslot, &xmin, &origin, &committs); - ReportApplyConflict(estate, resultRelInfo, ERROR, type, - searchslot, conflictslot, remoteslot, - uniqueidx, xmin, origin, committs); + ConflictTupleInfo *conflicttuple = palloc0_object(ConflictTupleInfo); + + conflicttuple->slot = conflictslot; + conflicttuple->indexoid = uniqueidx; + + GetTupleTransactionInfo(conflictslot, &conflicttuple->xmin, + &conflicttuple->origin, &conflicttuple->ts); + + conflicttuples = lappend(conflicttuples, conflicttuple); } } + + /* Report the conflict, if found */ + if (conflicttuples) + ReportApplyConflict(estate, resultRelInfo, ERROR, + list_length(conflicttuples) > 1 ? CT_MULTIPLE_UNIQUE_CONFLICTS : type, + searchslot, remoteslot, conflicttuples); } /* -- cgit v1.2.3