Skip to content

Commit 3ff2a1f

Browse files
author
Amit Kapila
committed
Fix assertion failure during decoding from synced slots.
The slot synchronization skips updating the confirmed_flush LSN of the local slot if the local slot has a newer catalog_xmin or restart_lsn, but still allows updating the two_phase and two_phase_at fields of the slot. This opens up a window for the prepared transactions between old confirmed_flush LSN and two_phase_at to unexpectedly get decoded and sent to the downstream after promotion. Then, while decoding the commit prepared the assert will fail, which expects that the prepare hasn't been sent to the downstream. The fix is to skip updating the other slot fields when we are skipping to update the confirmed_flush LSN of the slot. We didn't backpatch this commit as two_phase_at was not synced in back branches, which means prepared transactions won't be unexpectedly sent to downstream. We discovered this problem while analyzing BF failure reported in the discussion link. Reliably reproducing this issue without a debugger is difficult. Given its rarity, adding specific injection point to test it doesn't seem worthwhile, so we won't be adding a dedicated test case. Author: Zhijie Hou <houzj.fnst@fujitsu.com> Reviewed-by: shveta malik <shveta.malik@gmail.com> Reviewed-by: Masahiko Sawada <sawada.mshk@gmail.com> Reviewed-by: Amit Kapila <amit.kapila16@gmail.com> Discussion: https://postgr.es/m/OS0PR01MB5716B44052000EB91EFAE60E94BC2@OS0PR01MB5716.jpnprd01.prod.outlook.com
1 parent ef1811a commit 3ff2a1f

File tree

1 file changed

+27
-12
lines changed

1 file changed

+27
-12
lines changed

src/backend/replication/logical/slotsync.c

Lines changed: 27 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -196,14 +196,14 @@ update_local_synced_slot(RemoteSlot *remote_slot, Oid remote_dbid,
196196
* restart_lsn or the initial xmin_horizon computed for the local slot
197197
* is ahead of the remote slot.
198198
*
199-
* If the slot is persistent, restart_lsn of the synced slot could
200-
* still be ahead of the remote slot. Since we use slot advance
201-
* functionality to keep snapbuild/slot updated, it is possible that
202-
* the restart_lsn is advanced to a later position than it has on the
203-
* primary. This can happen when slot advancing machinery finds
204-
* running xacts record after reaching the consistent state at a later
205-
* point than the primary where it serializes the snapshot and updates
206-
* the restart_lsn.
199+
* If the slot is persistent, both restart_lsn and catalog_xmin of the
200+
* synced slot could still be ahead of the remote slot. Since we use
201+
* slot advance functionality to keep snapbuild/slot updated, it is
202+
* possible that the restart_lsn and catalog_xmin are advanced to a
203+
* later position than it has on the primary. This can happen when
204+
* slot advancing machinery finds running xacts record after reaching
205+
* the consistent state at a later point than the primary where it
206+
* serializes the snapshot and updates the restart_lsn.
207207
*
208208
* We LOG the message if the slot is temporary as it can help the user
209209
* to understand why the slot is not sync-ready. In the case of a
@@ -221,16 +221,25 @@ update_local_synced_slot(RemoteSlot *remote_slot, Oid remote_dbid,
221221

222222
if (remote_slot_precedes)
223223
*remote_slot_precedes = true;
224+
225+
/*
226+
* Skip updating the configuration. This is required to avoid syncing
227+
* two_phase_at without syncing confirmed_lsn. Otherwise, the prepared
228+
* transaction between old confirmed_lsn and two_phase_at will
229+
* unexpectedly get decoded and sent to the downstream after
230+
* promotion. See comments in ReorderBufferFinishPrepared.
231+
*/
232+
return false;
224233
}
225234

226235
/*
227236
* Attempt to sync LSNs and xmins only if remote slot is ahead of local
228237
* slot.
229238
*/
230-
else if (remote_slot->confirmed_lsn > slot->data.confirmed_flush ||
231-
remote_slot->restart_lsn > slot->data.restart_lsn ||
232-
TransactionIdFollows(remote_slot->catalog_xmin,
233-
slot->data.catalog_xmin))
239+
if (remote_slot->confirmed_lsn > slot->data.confirmed_flush ||
240+
remote_slot->restart_lsn > slot->data.restart_lsn ||
241+
TransactionIdFollows(remote_slot->catalog_xmin,
242+
slot->data.catalog_xmin))
234243
{
235244
/*
236245
* We can't directly copy the remote slot's LSN or xmin unless there
@@ -294,6 +303,12 @@ update_local_synced_slot(RemoteSlot *remote_slot, Oid remote_dbid,
294303
SpinLockRelease(&slot->mutex);
295304

296305
updated_config = true;
306+
307+
/*
308+
* Ensure that there is no risk of sending prepared transactions
309+
* unexpectedly after the promotion.
310+
*/
311+
Assert(slot->data.two_phase_at <= slot->data.confirmed_flush);
297312
}
298313

299314
/*

0 commit comments

Comments
 (0)