logical_decoding_xact_bookkeep.patch

application/octet-stream

Filename: logical_decoding_xact_bookkeep.patch
Type: application/octet-stream
Part: 0
Message: [BUG] Logical replication failure "ERROR: could not map filenode "base/13237/442428" to relation OID" with catalog modifying txns

Patch

Format: unified
File+
src/backend/replication/logical/decode.c 49 1
src/backend/replication/logical/reorderbuffer.c 1 1
src/backend/replication/logical/snapbuild.c 9 5
src/include/replication/reorderbuffer.h 2 0
src/include/replication/snapbuild.h 1 1
diff --git a/src/backend/replication/logical/decode.c b/src/backend/replication/logical/decode.c
index 5f59613..2512912 100644
--- a/src/backend/replication/logical/decode.c
+++ b/src/backend/replication/logical/decode.c
@@ -42,6 +42,7 @@
 #include "replication/reorderbuffer.h"
 #include "replication/snapbuild.h"
 #include "storage/standby.h"
+#include "utils/builtins.h"
 
 typedef struct XLogRecordBuffer
 {
@@ -85,6 +86,9 @@ static bool DecodeTXNNeedSkip(LogicalDecodingContext *ctx,
 							  XLogRecordBuffer *buf, Oid dbId,
 							  RepOriginId origin_id);
 
+/* record previous restart_lsn running xacts */
+xl_running_xacts *last_running = NULL;
+
 /*
  * Take every XLogReadRecord()ed record and perform the actions required to
  * decode it using the output plugin already setup in the logical decoding
@@ -402,6 +406,28 @@ DecodeStandbyOp(LogicalDecodingContext *ctx, XLogRecordBuffer *buf)
 			{
 				xl_running_xacts *running = (xl_running_xacts *) XLogRecGetData(r);
 
+				/* record restart_lsn running xacts */
+				if (MyReplicationSlot && (buf->origptr == MyReplicationSlot->data.restart_lsn))
+				{
+					if (last_running)
+						free(last_running);
+
+					last_running = NULL;
+
+					/*
+					 * xl_running_xacts contains a xids Flexible Array
+					 * and its size is subxcnt + xcnt.
+					 * Take that into account while allocating
+					 * the memory for last_running.
+					 */
+					last_running = (xl_running_xacts *) malloc(sizeof(xl_running_xacts)
+																+ sizeof(TransactionId )
+																* (running->subxcnt + running->xcnt));
+					memcpy(last_running, running, sizeof(xl_running_xacts)
+														 + (sizeof(TransactionId)
+														 * (running->subxcnt + running->xcnt)));
+				}
+
 				SnapBuildProcessRunningXacts(builder, buf->origptr, running);
 
 				/*
@@ -678,6 +704,7 @@ DecodeCommit(LogicalDecodingContext *ctx, XLogRecordBuffer *buf,
 	TimestampTz commit_time = parsed->xact_time;
 	RepOriginId origin_id = XLogRecGetOrigin(buf->record);
 	int			i;
+	bool force_travel_and_snapshot = false;
 
 	if (parsed->xinfo & XACT_XINFO_HAS_ORIGIN)
 	{
@@ -685,8 +712,29 @@ DecodeCommit(LogicalDecodingContext *ctx, XLogRecordBuffer *buf,
 		commit_time = parsed->origin_timestamp;
 	}
 
+	/*
+	 * Check if the commit contain catalog invalidations
+	 * and if the xid was part of the restart_lsn
+	 * running ones
+	 */
+	if ((parsed->xinfo & XACT_XINFO_HAS_INVALS) && last_running)
+	{
+		/* make last_running->xids bsearch()able */
+		qsort(last_running->xids,
+			  last_running->subxcnt + last_running->xcnt,
+			  sizeof(TransactionId), xidComparator);
+
+		/*
+		 * Is this xid part of the known running ones
+		 * in the restart_lsn RUNNING_XACT entry?
+		 */
+		force_travel_and_snapshot = TransactionIdInArray(xid, last_running->xids,
+														 last_running->subxcnt
+														 + last_running->xcnt);
+	}
+
 	SnapBuildCommitTxn(ctx->snapshot_builder, buf->origptr, xid,
-					   parsed->nsubxacts, parsed->subxacts);
+					   parsed->nsubxacts, parsed->subxacts, force_travel_and_snapshot);
 
 	/* ----
 	 * Check whether we are interested in this specific transaction, and tell
diff --git a/src/backend/replication/logical/reorderbuffer.c b/src/backend/replication/logical/reorderbuffer.c
index 91600ac..4a5610b 100644
--- a/src/backend/replication/logical/reorderbuffer.c
+++ b/src/backend/replication/logical/reorderbuffer.c
@@ -4892,7 +4892,7 @@ ApplyLogicalMappingFile(HTAB *tuplecid_data, Oid relid, const char *fname)
 /*
  * Check whether the TransactionId 'xid' is in the pre-sorted array 'xip'.
  */
-static bool
+bool
 TransactionIdInArray(TransactionId xid, TransactionId *xip, Size num)
 {
 	return bsearch(&xid, xip, num,
diff --git a/src/backend/replication/logical/snapbuild.c b/src/backend/replication/logical/snapbuild.c
index ed3acad..6be39d1 100644
--- a/src/backend/replication/logical/snapbuild.c
+++ b/src/backend/replication/logical/snapbuild.c
@@ -910,15 +910,19 @@ SnapBuildPurgeCommittedTxn(SnapBuild *builder)
  */
 void
 SnapBuildCommitTxn(SnapBuild *builder, XLogRecPtr lsn, TransactionId xid,
-				   int nsubxacts, TransactionId *subxacts)
+				   int nsubxacts, TransactionId *subxacts, bool force_travel_and_snapshot)
 {
 	int			nxact;
-
-	bool		needs_snapshot = false;
-	bool		needs_timetravel = false;
-	bool		sub_needs_timetravel = false;
+	bool		needs_snapshot;
+	bool		needs_timetravel;
+	bool		sub_needs_timetravel;
 
 	TransactionId xmax = xid;
+	/*
+	 * if force_travel_and_snapshot is set to true
+	 * then proceed as if there are any catalog modifying subxacts.
+	 */
+	needs_snapshot = needs_timetravel = sub_needs_timetravel = force_travel_and_snapshot;
 
 	/*
 	 * Transactions preceding BUILDING_SNAPSHOT will neither be decoded, nor
diff --git a/src/include/replication/reorderbuffer.h b/src/include/replication/reorderbuffer.h
index 565a961..7fc79f3 100644
--- a/src/include/replication/reorderbuffer.h
+++ b/src/include/replication/reorderbuffer.h
@@ -685,4 +685,6 @@ void		ReorderBufferSetRestartPoint(ReorderBuffer *, XLogRecPtr ptr);
 
 void		StartupReorderBuffer(void);
 
+extern bool TransactionIdInArray(TransactionId xid, TransactionId *xip, Size num);
+
 #endif
diff --git a/src/include/replication/snapbuild.h b/src/include/replication/snapbuild.h
index fbabce6..46f7df0 100644
--- a/src/include/replication/snapbuild.h
+++ b/src/include/replication/snapbuild.h
@@ -80,7 +80,7 @@ extern XLogRecPtr SnapBuildInitialConsistentPoint(SnapBuild *builder);
 
 extern void SnapBuildCommitTxn(SnapBuild *builder, XLogRecPtr lsn,
 							   TransactionId xid, int nsubxacts,
-							   TransactionId *subxacts);
+							   TransactionId *subxacts, bool force_travel_and_snapshot);
 extern bool SnapBuildProcessChange(SnapBuild *builder, TransactionId xid,
 								   XLogRecPtr lsn);
 extern void SnapBuildProcessNewCid(SnapBuild *builder, TransactionId xid,