From 1f835482eca07460e34ed7f40a58dc0e2a7c006a Mon Sep 17 00:00:00 2001
From: Shveta Malik <shveta.malik@gmail.com>
Date: Thu, 4 Apr 2024 14:18:51 +0530
Subject: [PATCH v2] Correct sanity check to compare confirmed_lsn.

This patch attempts to fix a BF failure introduced by commit 2ec005b.

It is seen in BF failure logs, that during the first call of the
SQL function pg_sync_replication_slots(), restart_lsn of the synced
slot is advanced to a lsn which is > than remote slot's restart_lsn.
And when sync SQL function is executed the second time, without any
further change on primary, it hits the error:
  ERROR:  cannot synchronize local slot "lsub1_slot" LSN(0/3000060) to
remote slot's LSN(0/3000028) as synchronization would move it
backwards

Issue:
Issue is that we are having a wrong sanity check based on
'restart_lsn' in synchronize_one_slot():

if (remote_slot->restart_lsn < slot->data.restart_lsn)
    elog(ERROR, ...);

Prior to commit 2ec005b, this check was okay, as we did not expect
restart_lsn of the synced slot to be ahead of remote since we were
directly copying the lsns. But now since we use 'advance slot'
functionality to reach to a consistent state while syncing a slot,
there is a possibility that for a synced slot, while consuming
changes from remote's slot restart_lsn till confirmed_flush, slot
sync find running xacts record after reaching consistent point,
for which it serializes the snapshot and thus end up advancing
restart_lsn of standby ahead of remote-slot.

In order to fix this, corrected sanity check to compare confirmed_lsn
rather than restart_lsn.

Other changes are:
a)
Now we attempt sync in update_local_synced_slot() if one of confirmed_lsn,
restart_lsn, and catalog_xmin for remote slot is ahead of local slot, instead
of them just being inequal.

b)
A log has been added after LogicalSlotAdvanceAndCheckSnapState()
to report the case when the local and remote slots' confirmed-lsn
were not found to be the same after sync.
---
 src/backend/replication/logical/slotsync.c | 35 ++++++++++++++++++----
 1 file changed, 29 insertions(+), 6 deletions(-)

diff --git a/src/backend/replication/logical/slotsync.c b/src/backend/replication/logical/slotsync.c
index 9ac847b780..1540407a0d 100644
--- a/src/backend/replication/logical/slotsync.c
+++ b/src/backend/replication/logical/slotsync.c
@@ -174,9 +174,14 @@ update_local_synced_slot(RemoteSlot *remote_slot, Oid remote_dbid,
 	if (found_consistent_snapshot)
 		*found_consistent_snapshot = false;
 
-	if (remote_slot->confirmed_lsn != slot->data.confirmed_flush ||
-		remote_slot->restart_lsn != slot->data.restart_lsn ||
-		remote_slot->catalog_xmin != slot->data.catalog_xmin)
+	/*
+	 * Attempt to sync lsns and xmins only if remote slot is ahead of local
+	 * slot.
+	 */
+	if (remote_slot->confirmed_lsn > slot->data.confirmed_flush ||
+		remote_slot->restart_lsn > slot->data.restart_lsn ||
+		TransactionIdFollows(remote_slot->catalog_xmin,
+							 slot->data.catalog_xmin))
 	{
 		/*
 		 * We can't directly copy the remote slot's LSN or xmin unless there
@@ -207,6 +212,14 @@ update_local_synced_slot(RemoteSlot *remote_slot, Oid remote_dbid,
 		{
 			LogicalSlotAdvanceAndCheckSnapState(remote_slot->confirmed_lsn,
 												found_consistent_snapshot);
+
+			if (slot->data.confirmed_flush != remote_slot->confirmed_lsn)
+				elog(LOG,
+					 "could not synchronize local slot \"%s\" LSN(%X/%X)"
+					 " to remote slot's LSN(%X/%X) ",
+					 remote_slot->name,
+					 LSN_FORMAT_ARGS(slot->data.confirmed_flush),
+					 LSN_FORMAT_ARGS(remote_slot->confirmed_lsn));
 		}
 
 		ReplicationSlotsComputeRequiredXmin(false);
@@ -627,14 +640,24 @@ synchronize_one_slot(RemoteSlot *remote_slot, Oid remote_dbid)
 			/*
 			 * Sanity check: As long as the invalidations are handled
 			 * appropriately as above, this should never happen.
+			 *
+			 * We do not intend to check restart_lsn here, as restart_lsn of
+			 * the synced slot could be ahead of the remote slot. Since we use
+			 * advance slot functionality to reach to a consistent state while
+			 * syncing a slot, there is a possibility that for a synced slot,
+			 * while consuming changes from remote's slot restart_lsn till
+			 * confirmed_flush, slot sync find running xacts record after
+			 * reaching consistent point, for which it serializes the snapshot
+			 * and thus end up advancing restart_lsn of standby ahead of
+			 * remote-slot.
 			 */
-			if (remote_slot->restart_lsn < slot->data.restart_lsn)
+			if (remote_slot->confirmed_lsn < slot->data.confirmed_flush)
 				elog(ERROR,
 					 "cannot synchronize local slot \"%s\" LSN(%X/%X)"
 					 " to remote slot's LSN(%X/%X) as synchronization"
 					 " would move it backwards", remote_slot->name,
-					 LSN_FORMAT_ARGS(slot->data.restart_lsn),
-					 LSN_FORMAT_ARGS(remote_slot->restart_lsn));
+					 LSN_FORMAT_ARGS(slot->data.confirmed_flush),
+					 LSN_FORMAT_ARGS(remote_slot->confirmed_lsn));
 
 			/* Make sure the slot changes persist across server restart */
 			if (update_local_synced_slot(remote_slot, remote_dbid, NULL))
-- 
2.34.1

