]> begriffs open source - ai-pg/blob - full-docs/html/logical-replication-conflicts.html
Include links to all subsection html pages, with shorter paths too
[ai-pg] / full-docs / html / logical-replication-conflicts.html
1 <?xml version="1.0" encoding="UTF-8" standalone="no"?>
2 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><html xmlns="http://www.w3.org/1999/xhtml"><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /><title>29.7. Conflicts</title><link rel="stylesheet" type="text/css" href="stylesheet.css" /><link rev="made" href="pgsql-docs@lists.postgresql.org" /><meta name="generator" content="DocBook XSL Stylesheets Vsnapshot" /><link rel="prev" href="logical-replication-gencols.html" title="29.6. Generated Column Replication" /><link rel="next" href="logical-replication-restrictions.html" title="29.8. Restrictions" /></head><body id="docContent" class="container-fluid col-10"><div class="navheader"><table width="100%" summary="Navigation header"><tr><th colspan="5" align="center">29.7. Conflicts</th></tr><tr><td width="10%" align="left"><a accesskey="p" href="logical-replication-gencols.html" title="29.6. Generated Column Replication">Prev</a> </td><td width="10%" align="left"><a accesskey="u" href="logical-replication.html" title="Chapter 29. Logical Replication">Up</a></td><th width="60%" align="center">Chapter 29. Logical Replication</th><td width="10%" align="right"><a accesskey="h" href="index.html" title="PostgreSQL 18.0 Documentation">Home</a></td><td width="10%" align="right"> <a accesskey="n" href="logical-replication-restrictions.html" title="29.8. Restrictions">Next</a></td></tr></table><hr /></div><div class="sect1" id="LOGICAL-REPLICATION-CONFLICTS"><div class="titlepage"><div><div><h2 class="title" style="clear: both">29.7. Conflicts <a href="#LOGICAL-REPLICATION-CONFLICTS" class="id_link">#</a></h2></div></div></div><p>
3    Logical replication behaves similarly to normal DML operations in that
4    the data will be updated even if it was changed locally on the subscriber
5    node.  If incoming data violates any constraints the replication will
6    stop.  This is referred to as a <em class="firstterm">conflict</em>.  When
7    replicating <code class="command">UPDATE</code> or <code class="command">DELETE</code>
8    operations, missing data is also considered as a
9    <em class="firstterm">conflict</em>, but does not result in an error and such
10    operations will simply be skipped.
11   </p><p>
12    Additional logging is triggered, and the conflict statistics are collected (displayed in the
13    <a class="link" href="monitoring-stats.html#MONITORING-PG-STAT-SUBSCRIPTION-STATS" title="27.2.9. pg_stat_subscription_stats"><code class="structname">pg_stat_subscription_stats</code></a> view)
14    in the following <em class="firstterm">conflict</em> cases:
15    </p><div class="variablelist"><dl class="variablelist"><dt id="CONFLICT-INSERT-EXISTS"><span class="term"><code class="literal">insert_exists</code></span> <a href="#CONFLICT-INSERT-EXISTS" class="id_link">#</a></dt><dd><p>
16        Inserting a row that violates a <code class="literal">NOT DEFERRABLE</code>
17        unique constraint. Note that to log the origin and commit
18        timestamp details of the conflicting key,
19        <a class="link" href="runtime-config-replication.html#GUC-TRACK-COMMIT-TIMESTAMP"><code class="varname">track_commit_timestamp</code></a>
20        should be enabled on the subscriber. In this case, an error will be
21        raised until the conflict is resolved manually.
22       </p></dd><dt id="CONFLICT-UPDATE-ORIGIN-DIFFERS"><span class="term"><code class="literal">update_origin_differs</code></span> <a href="#CONFLICT-UPDATE-ORIGIN-DIFFERS" class="id_link">#</a></dt><dd><p>
23        Updating a row that was previously modified by another origin.
24        Note that this conflict can only be detected when
25        <a class="link" href="runtime-config-replication.html#GUC-TRACK-COMMIT-TIMESTAMP"><code class="varname">track_commit_timestamp</code></a>
26        is enabled on the subscriber. Currently, the update is always applied
27        regardless of the origin of the local row.
28       </p></dd><dt id="CONFLICT-UPDATE-EXISTS"><span class="term"><code class="literal">update_exists</code></span> <a href="#CONFLICT-UPDATE-EXISTS" class="id_link">#</a></dt><dd><p>
29        The updated value of a row violates a <code class="literal">NOT DEFERRABLE</code>
30        unique constraint. Note that to log the origin and commit
31        timestamp details of the conflicting key,
32        <a class="link" href="runtime-config-replication.html#GUC-TRACK-COMMIT-TIMESTAMP"><code class="varname">track_commit_timestamp</code></a>
33        should be enabled on the subscriber. In this case, an error will be
34        raised until the conflict is resolved manually. Note that when updating a
35        partitioned table, if the updated row value satisfies another partition
36        constraint resulting in the row being inserted into a new partition, the
37        <code class="literal">insert_exists</code> conflict may arise if the new row
38        violates a <code class="literal">NOT DEFERRABLE</code> unique constraint.
39       </p></dd><dt id="CONFLICT-UPDATE-MISSING"><span class="term"><code class="literal">update_missing</code></span> <a href="#CONFLICT-UPDATE-MISSING" class="id_link">#</a></dt><dd><p>
40        The row to be updated was not found. The update will simply be
41        skipped in this scenario.
42       </p></dd><dt id="CONFLICT-DELETE-ORIGIN-DIFFERS"><span class="term"><code class="literal">delete_origin_differs</code></span> <a href="#CONFLICT-DELETE-ORIGIN-DIFFERS" class="id_link">#</a></dt><dd><p>
43        Deleting a row that was previously modified by another origin. Note that
44        this conflict can only be detected when
45        <a class="link" href="runtime-config-replication.html#GUC-TRACK-COMMIT-TIMESTAMP"><code class="varname">track_commit_timestamp</code></a>
46        is enabled on the subscriber. Currently, the delete is always applied
47        regardless of the origin of the local row.
48       </p></dd><dt id="CONFLICT-DELETE-MISSING"><span class="term"><code class="literal">delete_missing</code></span> <a href="#CONFLICT-DELETE-MISSING" class="id_link">#</a></dt><dd><p>
49        The row to be deleted was not found. The delete will simply be
50        skipped in this scenario.
51       </p></dd><dt id="CONFLICT-MULTIPLE-UNIQUE-CONFLICTS"><span class="term"><code class="literal">multiple_unique_conflicts</code></span> <a href="#CONFLICT-MULTIPLE-UNIQUE-CONFLICTS" class="id_link">#</a></dt><dd><p>
52        Inserting or updating a row violates multiple
53        <code class="literal">NOT DEFERRABLE</code> unique constraints. Note that to log
54        the origin and commit timestamp details of conflicting keys, ensure
55        that <a class="link" href="runtime-config-replication.html#GUC-TRACK-COMMIT-TIMESTAMP"><code class="varname">track_commit_timestamp</code></a>
56        is enabled on the subscriber. In this case, an error will be raised until
57        the conflict is resolved manually.
58       </p></dd></dl></div><p>
59     Note that there are other conflict scenarios, such as exclusion constraint
60     violations. Currently, we do not provide additional details for them in the
61     log.
62   </p><p>
63    The log format for logical replication conflicts is as follows:
64 </p><pre class="synopsis">
65 LOG:  conflict detected on relation "<em class="replaceable"><code>schemaname</code></em>.<em class="replaceable"><code>tablename</code></em>": conflict=<em class="replaceable"><code>conflict_type</code></em>
66 DETAIL:  <em class="replaceable"><code>detailed_explanation</code></em>.
67 {<em class="replaceable"><code>detail_values</code></em> [; ... ]}.
68
69 <span class="phrase">where <em class="replaceable"><code>detail_values</code></em> is one of:</span>
70
71     <code class="literal">Key</code> (<em class="replaceable"><code>column_name</code></em> [<span class="optional">, ...</span>])=(<em class="replaceable"><code>column_value</code></em> [<span class="optional">, ...</span>])
72     <code class="literal">existing local row</code> [<span class="optional">(<em class="replaceable"><code>column_name</code></em> [<span class="optional">, ...</span>])=</span>](<em class="replaceable"><code>column_value</code></em> [<span class="optional">, ...</span>])
73     <code class="literal">remote row</code> [<span class="optional">(<em class="replaceable"><code>column_name</code></em> [<span class="optional">, ...</span>])=</span>](<em class="replaceable"><code>column_value</code></em> [<span class="optional">, ...</span>])
74     <code class="literal">replica identity</code> {(<em class="replaceable"><code>column_name</code></em> [<span class="optional">, ...</span>])=(<em class="replaceable"><code>column_value</code></em> [<span class="optional">, ...</span>]) | full [<span class="optional">(<em class="replaceable"><code>column_name</code></em> [<span class="optional">, ...</span>])=</span>](<em class="replaceable"><code>column_value</code></em> [<span class="optional">, ...</span>])}
75 </pre><p>
76
77    The log provides the following information:
78    </p><div class="variablelist"><dl class="variablelist"><dt><span class="term"><code class="literal">LOG</code></span></dt><dd><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><p>
79          <em class="replaceable"><code>schemaname</code></em>.<em class="replaceable"><code>tablename</code></em>
80          identifies the local relation involved in the conflict.
81          </p></li><li class="listitem"><p>
82          <em class="replaceable"><code>conflict_type</code></em> is the type of conflict that occurred
83          (e.g., <code class="literal">insert_exists</code>, <code class="literal">update_exists</code>).
84          </p></li></ul></div></dd><dt><span class="term"><code class="literal">DETAIL</code></span></dt><dd><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><p>
85          <em class="replaceable"><code>detailed_explanation</code></em> includes
86          the origin, transaction ID, and commit timestamp of the transaction that
87          modified the existing local row, if available.
88         </p></li><li class="listitem"><p>
89          The <code class="literal">Key</code> section includes the key values of the local
90          row that violated a unique constraint for
91          <code class="literal">insert_exists</code>, <code class="literal">update_exists</code> or
92          <code class="literal">multiple_unique_conflicts</code> conflicts.
93         </p></li><li class="listitem"><p>
94          The <code class="literal">existing local row</code> section includes the local
95          row if its origin differs from the remote row for
96          <code class="literal">update_origin_differs</code> or <code class="literal">delete_origin_differs</code>
97          conflicts, or if the key value conflicts with the remote row for
98          <code class="literal">insert_exists</code>, <code class="literal">update_exists</code> or
99          <code class="literal">multiple_unique_conflicts</code> conflicts.
100         </p></li><li class="listitem"><p>
101          The <code class="literal">remote row</code> section includes the new row from
102          the remote insert or update operation that caused the conflict. Note that
103          for an update operation, the column value of the new row will be null
104          if the value is unchanged and toasted.
105         </p></li><li class="listitem"><p>
106          The <code class="literal">replica identity</code> section includes the replica
107          identity key values that were used to search for the existing local
108          row to be updated or deleted. This may include the full row value
109          if the local relation is marked with
110          <a class="link" href="sql-altertable.html#SQL-ALTERTABLE-REPLICA-IDENTITY-FULL"><code class="literal">REPLICA IDENTITY FULL</code></a>.
111         </p></li><li class="listitem"><p>
112          <em class="replaceable"><code>column_name</code></em> is the column name.
113          For <code class="literal">existing local row</code>, <code class="literal">remote row</code>,
114          and <code class="literal">replica identity full</code> cases, column names are
115          logged only if the user lacks the privilege to access all columns of
116          the table. If column names are present, they appear in the same order
117          as the corresponding column values.
118         </p></li><li class="listitem"><p>
119          <em class="replaceable"><code>column_value</code></em> is the column value.
120          The large column values are truncated to 64 bytes.
121         </p></li><li class="listitem"><p>
122          Note that in case of <code class="literal">multiple_unique_conflicts</code> conflict,
123          multiple <em class="replaceable"><code>detailed_explanation</code></em>
124          and <em class="replaceable"><code>detail_values</code></em> lines
125          will be generated, each detailing the conflict information associated
126          with distinct unique
127          constraints.
128         </p></li></ul></div></dd></dl></div><p>
129   </p><p>
130    Logical replication operations are performed with the privileges of the role
131    which owns the subscription.  Permissions failures on target tables will
132    cause replication conflicts, as will enabled
133    <a class="link" href="ddl-rowsecurity.html" title="5.9. Row Security Policies">row-level security</a> on target tables
134    that the subscription owner is subject to, without regard to whether any
135    policy would ordinarily reject the <code class="command">INSERT</code>,
136    <code class="command">UPDATE</code>, <code class="command">DELETE</code> or
137    <code class="command">TRUNCATE</code> which is being replicated.  This restriction on
138    row-level security may be lifted in a future version of
139    <span class="productname">PostgreSQL</span>.
140   </p><p>
141    A conflict that produces an error will stop the replication; it must be
142    resolved manually by the user.  Details about the conflict can be found in
143    the subscriber's server log.
144   </p><p>
145    The resolution can be done either by changing data or permissions on the subscriber so
146    that it does not conflict with the incoming change or by skipping the
147    transaction that conflicts with the existing data.  When a conflict produces
148    an error, the replication won't proceed, and the logical replication worker will
149    emit the following kind of message to the subscriber's server log:
150 </p><pre class="screen">
151 ERROR:  conflict detected on relation "public.test": conflict=insert_exists
152 DETAIL:  Key already exists in unique index "t_pkey", which was modified locally in transaction 740 at 2024-06-26 10:47:04.727375+08.
153 Key (c)=(1); existing local row (1, 'local'); remote row (1, 'remote').
154 CONTEXT:  processing remote data for replication origin "pg_16395" during "INSERT" for replication target relation "public.test" in transaction 725 finished at 0/14C0378
155 </pre><p>
156    The LSN of the transaction that contains the change violating the constraint and
157    the replication origin name can be found from the server log (LSN 0/14C0378 and
158    replication origin <code class="literal">pg_16395</code> in the above case).  The
159    transaction that produced the conflict can be skipped by using
160    <a class="link" href="sql-altersubscription.html#SQL-ALTERSUBSCRIPTION-PARAMS-SKIP"><code class="command">ALTER SUBSCRIPTION ... SKIP</code></a>
161    with the finish LSN
162    (i.e., LSN 0/14C0378).  The finish LSN could be an LSN at which the transaction
163    is committed or prepared on the publisher.  Alternatively, the transaction can
164    also be skipped by calling the <a class="link" href="functions-admin.html#PG-REPLICATION-ORIGIN-ADVANCE">
165    <code class="function">pg_replication_origin_advance()</code></a> function.
166    Before using this function, the subscription needs to be disabled temporarily
167    either by <a class="link" href="sql-altersubscription.html#SQL-ALTERSUBSCRIPTION-PARAMS-DISABLE">
168    <code class="command">ALTER SUBSCRIPTION ... DISABLE</code></a> or, the
169    subscription can be used with the
170    <a class="link" href="sql-createsubscription.html#SQL-CREATESUBSCRIPTION-PARAMS-WITH-DISABLE-ON-ERROR"><code class="literal">disable_on_error</code></a>
171    option. Then, you can use <code class="function">pg_replication_origin_advance()</code>
172    function with the <em class="parameter"><code>node_name</code></em> (i.e., <code class="literal">pg_16395</code>)
173    and the next LSN of the finish LSN (i.e., 0/14C0379).  The current position of
174    origins can be seen in the <a class="link" href="view-pg-replication-origin-status.html" title="53.19. pg_replication_origin_status">
175    <code class="structname">pg_replication_origin_status</code></a> system view.
176    Please note that skipping the whole transaction includes skipping changes that
177    might not violate any constraint.  This can easily make the subscriber
178    inconsistent.
179    The additional details regarding conflicting rows, such as their origin and
180    commit timestamp can be seen in the <code class="literal">DETAIL</code> line of the
181    log. But note that this information is only available when
182    <a class="link" href="runtime-config-replication.html#GUC-TRACK-COMMIT-TIMESTAMP"><code class="varname">track_commit_timestamp</code></a>
183    is enabled on the subscriber. Users can use this information to decide
184    whether to retain the local change or adopt the remote alteration. For
185    instance, the <code class="literal">DETAIL</code> line in the above log indicates that
186    the existing row was modified locally. Users can manually perform a
187    remote-change-win.
188   </p><p>
189    When the
190    <a class="link" href="sql-createsubscription.html#SQL-CREATESUBSCRIPTION-PARAMS-WITH-STREAMING"><code class="literal">streaming</code></a>
191    mode is <code class="literal">parallel</code>, the finish LSN of failed transactions
192    may not be logged. In that case, it may be necessary to change the streaming
193    mode to <code class="literal">on</code> or <code class="literal">off</code> and cause the same
194    conflicts again so the finish LSN of the failed transaction will be written
195    to the server log. For the usage of finish LSN, please refer to <a class="link" href="sql-altersubscription.html" title="ALTER SUBSCRIPTION"><code class="command">ALTER SUBSCRIPTION ...
196    SKIP</code></a>.
197   </p></div><div class="navfooter"><hr /><table width="100%" summary="Navigation footer"><tr><td width="40%" align="left"><a accesskey="p" href="logical-replication-gencols.html" title="29.6. Generated Column Replication">Prev</a> </td><td width="20%" align="center"><a accesskey="u" href="logical-replication.html" title="Chapter 29. Logical Replication">Up</a></td><td width="40%" align="right"> <a accesskey="n" href="logical-replication-restrictions.html" title="29.8. Restrictions">Next</a></td></tr><tr><td width="40%" align="left" valign="top">29.6. Generated Column Replication </td><td width="20%" align="center"><a accesskey="h" href="index.html" title="PostgreSQL 18.0 Documentation">Home</a></td><td width="40%" align="right" valign="top"> 29.8. Restrictions</td></tr></table></div></body></html>