@@ -1828,7 +1828,19 @@ LogicalConfirmReceivedLocation(XLogRecPtr lsn)
18281828
18291829 SpinLockAcquire (& MyReplicationSlot -> mutex );
18301830
1831- MyReplicationSlot -> data .confirmed_flush = lsn ;
1831+ /*
1832+ * Prevent moving the confirmed_flush backwards, as this could lead to
1833+ * data duplication issues caused by replicating already replicated
1834+ * changes.
1835+ *
1836+ * This can happen when a client acknowledges an LSN it doesn't have
1837+ * to do anything for, and thus didn't store persistently. After a
1838+ * restart, the client can send the prior LSN that it stored
1839+ * persistently as an acknowledgement, but we need to ignore such an
1840+ * LSN. See similar case handling in CreateDecodingContext.
1841+ */
1842+ if (lsn > MyReplicationSlot -> data .confirmed_flush )
1843+ MyReplicationSlot -> data .confirmed_flush = lsn ;
18321844
18331845 /* if we're past the location required for bumping xmin, do so */
18341846 if (MyReplicationSlot -> candidate_xmin_lsn != InvalidXLogRecPtr &&
@@ -1893,7 +1905,14 @@ LogicalConfirmReceivedLocation(XLogRecPtr lsn)
18931905 else
18941906 {
18951907 SpinLockAcquire (& MyReplicationSlot -> mutex );
1896- MyReplicationSlot -> data .confirmed_flush = lsn ;
1908+
1909+ /*
1910+ * Prevent moving the confirmed_flush backwards. See comments above
1911+ * for the details.
1912+ */
1913+ if (lsn > MyReplicationSlot -> data .confirmed_flush )
1914+ MyReplicationSlot -> data .confirmed_flush = lsn ;
1915+
18971916 SpinLockRelease (& MyReplicationSlot -> mutex );
18981917 }
18991918}
0 commit comments