On Fri, Mar 21, 2025 at 12:50 PM Nisha Moond wrote: > Thanks, Hou-san, for the review and fix patches. I’ve incorporated > your suggestions. > Attached are the v7 patches, including patch 002, which implements > stats collection for 'multiple_unique_conflicts' in > pg_stat_subscription_stats.
Thanks for updating the patches. Here are some more comments: 1. The comments atop of ReportApplyConflict() should be updated to reflect the updates made to the input parameters. 2. Add ConflictTupleInfo in typedefs.list. 3. Few typos exiting => existing 4. $node_subscriber->wait_for_log( qr/ERROR: conflict detected on relation \"public.conf_tab\": conflict=multiple_unique_conflicts/, We should avoid adding the elevel('ERROR') body here as the format could be changed depending on the log_error_verbosity. Please refer to d13ff82 for details. Please see attachment 0001 for the proposed changes. 5. ok( $node_subscriber->log_contains( qr/Key already exists in unique index \"conf_tab_pkey\".*\n.*Key \(a\)=\(2\); existing local tuple \(2, 2, 2\); remote tuple \(2, 3, 4\)./, $log_offset), 'multiple_unique_conflicts detected during insertion for conf_tab_pkey (a) = (2)' ); ... ok( $node_subscriber->log_contains( qr/Key already exists in unique index \"conf_tab_b_key\".*\n.*Key \(b\)=\(3\); existing local tuple \(3, 3, 3\); remote tuple \(2, 3, 4\)./, $log_offset), 'multiple_unique_conflicts detected during insertion for conf_tab_b_key (b) = (3)' ); Currently, different detail lines are checked in separate test cases. It would be clearer to merge these checks, ensuring comprehensive validation including line breaks. See attachment 0002 for the proposed changes. 6. The document related to the conflict log format should be updated. E.g., all the places that mentioned insert|update_exists might need to mention the new multi-key conflict as well. And it would be better to mention there would be multiple detail lines in the new conflict. Please see attachment 0003 for the proposed changes. Best Regards, Hou zj
From 20e2d08ba853a61523b6356b12cd600cc31217ad Mon Sep 17 00:00:00 2001 From: Hou Zhijie <houzj.f...@cn.fujitsu.com> Date: Fri, 21 Mar 2025 13:50:27 +0800 Subject: [PATCH v2 1/3] fix comments and tests --- src/backend/replication/logical/conflict.c | 9 ++------- src/include/replication/conflict.h | 15 ++++++--------- src/test/subscription/t/035_conflicts.pl | 2 +- src/tools/pgindent/typedefs.list | 1 + 4 files changed, 10 insertions(+), 17 deletions(-) diff --git a/src/backend/replication/logical/conflict.c b/src/backend/replication/logical/conflict.c index b1614d3aaf6..9295c8ef6c1 100644 --- a/src/backend/replication/logical/conflict.c +++ b/src/backend/replication/logical/conflict.c @@ -91,15 +91,10 @@ GetTupleTransactionInfo(TupleTableSlot *localslot, TransactionId *xmin, * 'searchslot' should contain the tuple used to search the local tuple to be * updated or deleted. * - * 'conflictslots' list contains the existing local tuples, if any, that - * conflicts with the remote tuple. 'localxmins', 'localorigins', and 'localts' - * provide the transaction information related to the existing local tuples. - * * 'remoteslot' should contain the remote new tuple, if any. * - * The 'conflictindexes' list represents the OIDs of the unique index that - * triggered the constraint violation error. We use this to report the key - * values for conflicting tuple. + * 'conflicttuples' should be a list composed of ConflictTupleInfo pointer. + * Refer to the ConflictTupleInfo structure comments for details. * * The caller must ensure that the index with the OID 'indexoid' is locked so * that we can fetch and display the conflicting key value. diff --git a/src/include/replication/conflict.h b/src/include/replication/conflict.h index 06d5d05c560..4d0bab0dd28 100644 --- a/src/include/replication/conflict.h +++ b/src/include/replication/conflict.h @@ -55,18 +55,15 @@ typedef enum /* - * Information for the exiting local tuple that caused the conflict. + * Information for the existing local tuple that caused the conflict. */ typedef struct ConflictTupleInfo { - TupleTableSlot *slot; - Oid indexoid; /* conflicting index */ - TransactionId xmin; /* transaction ID that modified the existing - * local tuple */ - RepOriginId origin; /* which origin modified the exiting local - * tuple */ - TimestampTz ts; /* when the exiting local tuple was modified - * by the origin */ + TupleTableSlot *slot; /* tuple slot holding the conflicting local tuple */ + Oid indexoid; /* OID of the index where the conflict occurred */ + TransactionId xmin; /* transaction ID of the modification causing the conflict */ + RepOriginId origin; /* origin identifier of the modification */ + TimestampTz ts; /* timestamp of when the modification on the conflicting local tuple occurred */ } ConflictTupleInfo; extern bool GetTupleTransactionInfo(TupleTableSlot *localslot, diff --git a/src/test/subscription/t/035_conflicts.pl b/src/test/subscription/t/035_conflicts.pl index f1417e313db..37f8af07995 100644 --- a/src/test/subscription/t/035_conflicts.pl +++ b/src/test/subscription/t/035_conflicts.pl @@ -109,7 +109,7 @@ $node_publisher->safe_psql('postgres', # Confirm that this causes an error on the subscriber $node_subscriber->wait_for_log( - qr/ERROR: conflict detected on relation \"public.conf_tab\": conflict=multiple_unique_conflicts/, + qr/conflict detected on relation \"public.conf_tab\": conflict=multiple_unique_conflicts/, $log_offset); ok( $node_subscriber->log_contains( diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index bfa276d2d35..3fbf5a4c212 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -480,6 +480,7 @@ ConditionVariableMinimallyPadded ConditionalStack ConfigData ConfigVariable +ConflictTupleInfo ConflictType ConnCacheEntry ConnCacheKey -- 2.30.0.windows.2
From 12bd20f5d0deb1b31520a641df884034f8adf044 Mon Sep 17 00:00:00 2001 From: Hou Zhijie <houzj.f...@cn.fujitsu.com> Date: Fri, 21 Mar 2025 14:09:01 +0800 Subject: [PATCH v2 2/3] merge tests --- src/test/subscription/t/035_conflicts.pl | 52 ++++++++---------------- 1 file changed, 16 insertions(+), 36 deletions(-) diff --git a/src/test/subscription/t/035_conflicts.pl b/src/test/subscription/t/035_conflicts.pl index 37f8af07995..d06e64747cb 100644 --- a/src/test/subscription/t/035_conflicts.pl +++ b/src/test/subscription/t/035_conflicts.pl @@ -67,26 +67,16 @@ $node_publisher->safe_psql('postgres', # Confirm that this causes an error on the subscriber $node_subscriber->wait_for_log( - qr/ERROR: conflict detected on relation \"public.conf_tab\": conflict=multiple_unique_conflicts/, + qr/conflict detected on relation \"public.conf_tab\": conflict=multiple_unique_conflicts.* +.*Key already exists in unique index \"conf_tab_pkey\".* +.*Key \(a\)=\(2\); existing local tuple \(2, 2, 2\); remote tuple \(2, 3, 4\).* +.*Key already exists in unique index \"conf_tab_b_key\".* +.*Key \(b\)=\(3\); existing local tuple \(3, 3, 3\); remote tuple \(2, 3, 4\).* +.*Key already exists in unique index \"conf_tab_c_key\".* +.*Key \(c\)=\(4\); existing local tuple \(4, 4, 4\); remote tuple \(2, 3, 4\)./, $log_offset); -ok( $node_subscriber->log_contains( - qr/Key already exists in unique index \"conf_tab_pkey\".*\n.*Key \(a\)=\(2\); existing local tuple \(2, 2, 2\); remote tuple \(2, 3, 4\)./, - $log_offset), - 'multiple_unique_conflicts detected during insertion for conf_tab_pkey (a) = (2)' -); - -ok( $node_subscriber->log_contains( - qr/Key already exists in unique index \"conf_tab_b_key\".*\n.*Key \(b\)=\(3\); existing local tuple \(3, 3, 3\); remote tuple \(2, 3, 4\)./, - $log_offset), - 'multiple_unique_conflicts detected during insertion for conf_tab_b_key (b) = (3)' -); - -ok( $node_subscriber->log_contains( - qr/Key already exists in unique index \"conf_tab_c_key\".*\n.*Key \(c\)=\(4\); existing local tuple \(4, 4, 4\); remote tuple \(2, 3, 4\)./, - $log_offset), - 'multiple_unique_conflicts detected during insertion for conf_tab_c_key (c) = (4)' -); +pass('multiple_unique_conflicts detected during update'); # Truncate table to get rid of the error $node_subscriber->safe_psql('postgres', "TRUNCATE conf_tab;"); @@ -109,25 +99,15 @@ $node_publisher->safe_psql('postgres', # Confirm that this causes an error on the subscriber $node_subscriber->wait_for_log( - qr/conflict detected on relation \"public.conf_tab\": conflict=multiple_unique_conflicts/, + qr/conflict detected on relation \"public.conf_tab\": conflict=multiple_unique_conflicts.* +.*Key already exists in unique index \"conf_tab_pkey\".* +.*Key \(a\)=\(6\); existing local tuple \(6, 6, 6\); remote tuple \(6, 7, 8\).* +.*Key already exists in unique index \"conf_tab_b_key\".* +.*Key \(b\)=\(7\); existing local tuple \(7, 7, 7\); remote tuple \(6, 7, 8\).* +.*Key already exists in unique index \"conf_tab_c_key\".* +.*Key \(c\)=\(8\); existing local tuple \(8, 8, 8\); remote tuple \(6, 7, 8\)./, $log_offset); -ok( $node_subscriber->log_contains( - qr/Key already exists in unique index \"conf_tab_pkey\".*\n.*Key \(a\)=\(6\); existing local tuple \(6, 6, 6\); remote tuple \(6, 7, 8\)./, - $log_offset), - 'multiple_unique_conflicts detected during update for conf_tab_pkey (a) = (6)' -); - -ok( $node_subscriber->log_contains( - qr/Key already exists in unique index \"conf_tab_b_key\".*\n.*Key \(b\)=\(7\); existing local tuple \(7, 7, 7\); remote tuple \(6, 7, 8\)./, - $log_offset), - 'multiple_unique_conflicts detected during update for conf_tab_b_key (b) = (7)' -); - -ok( $node_subscriber->log_contains( - qr/Key already exists in unique index \"conf_tab_c_key\".*\n.*Key \(c\)=\(8\); existing local tuple \(8, 8, 8\); remote tuple \(6, 7, 8\)./, - $log_offset), - 'multiple_unique_conflicts detected during update for conf_tab_c_key (c) = (8)' -); +pass('multiple_unique_conflicts detected during insert'); done_testing(); -- 2.30.0.windows.2
From d4b7282365b7112953bcf3769f705bb4994ddd8d Mon Sep 17 00:00:00 2001 From: Hou Zhijie <houzj.f...@cn.fujitsu.com> Date: Fri, 21 Mar 2025 14:27:23 +0800 Subject: [PATCH v2 3/3] add documents --- doc/src/sgml/logical-replication.sgml | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/doc/src/sgml/logical-replication.sgml b/doc/src/sgml/logical-replication.sgml index 4637e898b9f..abade232473 100644 --- a/doc/src/sgml/logical-replication.sgml +++ b/doc/src/sgml/logical-replication.sgml @@ -1948,8 +1948,8 @@ DETAIL: <replaceable class="parameter">detailed_explanation</replaceable>. <para> The <literal>Key</literal> section includes the key values of the local tuple that violated a unique constraint for - <literal>insert_exists</literal> or <literal>update_exists</literal> - conflicts. + <literal>insert_exists</literal>, <literal>update_exists</literal> or + <literal>multiple_unique_conflicts</literal> conflicts. </para> </listitem> <listitem> @@ -1958,8 +1958,8 @@ DETAIL: <replaceable class="parameter">detailed_explanation</replaceable>. tuple if its origin differs from the remote tuple for <literal>update_origin_differs</literal> or <literal>delete_origin_differs</literal> conflicts, or if the key value conflicts with the remote tuple for - <literal>insert_exists</literal> or <literal>update_exists</literal> - conflicts. + <literal>insert_exists</literal>, <literal>update_exists</literal> or + <literal>multiple_unique_conflicts</literal> conflicts. </para> </listitem> <listitem> @@ -1995,6 +1995,16 @@ DETAIL: <replaceable class="parameter">detailed_explanation</replaceable>. The large column values are truncated to 64 bytes. </para> </listitem> + <listitem> + <para> + Note that in case of <literal>multiple_unique_conflicts</literal> conflict, + multiple <replaceable class="parameter">detailed_explanation</replaceable> + and <replaceable class="parameter">detail_values</replaceable> lines + will be generated, each detailing the conflict information associated + with distinct unique + constraints. + </para> + </listitem> </itemizedlist> </listitem> </varlistentry> -- 2.30.0.windows.2