src/backend/access/rmgrdesc/Makefile      |    1 
src/backend/access/rmgrdesc/csnlogdesc.c  |   95 +++++++++++++++
src/backend/access/rmgrdesc/xlogdesc.c    |    6 +
src/backend/access/transam/csn_log.c      |  187 ++++++++++++++++++++++-------
src/backend/access/transam/csn_snapshot.c |   72 ++++++++++-
src/backend/access/transam/rmgr.c         |    1
src/backend/access/transam/xlog.c         |   12 +-
src/backend/commands/vacuum.c             |    3
src/backend/storage/ipc/procarray.c       |    2
src/backend/utils/time/snapmgr.c          |    2
src/bin/pg_controldata/pg_controldata.c   |    2
src/bin/pg_upgrade/pg_upgrade.c           |    5 +
src/bin/pg_upgrade/pg_upgrade.h           |    2
src/bin/pg_waldump/rmgrdesc.c             |    1
src/include/access/csn_log.h              |   29 ++++
src/include/access/rmgrlist.h             |    1
src/include/access/xlog_internal.h        |    1
src/include/catalog/pg_control.h          |    1
18 files changed, 359 insertions(+), 64 deletions(-)
diff --git a/src/backend/access/rmgrdesc/Makefile b/src/backend/access/rmgrdesc/Makefile
index f88d72fd86..15fc36f7b4 100644
--- a/src/backend/access/rmgrdesc/Makefile
+++ b/src/backend/access/rmgrdesc/Makefile
@@ -11,6 +11,7 @@ include $(top_builddir)/src/Makefile.global
 OBJS = \
 	brindesc.o \
 	clogdesc.o \
+	csnlogdesc.o \
 	committsdesc.o \
 	dbasedesc.o \
 	genericdesc.o \
diff --git a/src/backend/access/rmgrdesc/csnlogdesc.c b/src/backend/access/rmgrdesc/csnlogdesc.c
new file mode 100644
index 0000000000..e96b056325
--- /dev/null
+++ b/src/backend/access/rmgrdesc/csnlogdesc.c
@@ -0,0 +1,95 @@
+/*-------------------------------------------------------------------------
+ *
+ * clogdesc.c
+ *	  rmgr descriptor routines for access/transam/csn_log.c
+ *
+ * Portions Copyright (c) 1996-2020, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *	  src/backend/access/rmgrdesc/csnlogdesc.c
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "access/csn_log.h"
+
+
+void
+csnlog_desc(StringInfo buf, XLogReaderState *record)
+{
+	char	   *rec = XLogRecGetData(record);
+	uint8		info = XLogRecGetInfo(record) & ~XLR_INFO_MASK;
+
+	if (info == XLOG_CSN_ZEROPAGE)
+	{
+		int pageno;
+
+		memcpy(&pageno, XLogRecGetData(record), sizeof(int));
+		appendStringInfo(buf, "pageno %d", pageno);
+	}
+	else if (info == XLOG_CSN_TRUNCATE)
+	{
+		int pageno;
+
+		memcpy(&pageno, XLogRecGetData(record), sizeof(int));
+		appendStringInfo(buf, "pageno %d", pageno);
+	}
+	else if (info == XLOG_CSN_ASSIGNMENT)
+	{
+		XidCSN csn;
+
+		memcpy(&csn, XLogRecGetData(record), sizeof(XidCSN));
+		appendStringInfo(buf, "assign "INT64_FORMAT"", csn);
+	}
+	else if (info == XLOG_CSN_SETXIDCSN)
+	{
+		xl_xidcsn_set *xlrec = (xl_xidcsn_set *) rec;
+		int			  nsubxids;
+
+		appendStringInfo(buf, "set "INT64_FORMAT" for: %u",
+						 xlrec->xidcsn,
+						 xlrec->xtop);
+		nsubxids = ((XLogRecGetDataLen(record) - MinSizeOfXidCSNSet) /
+					sizeof(TransactionId));
+		if (nsubxids > 0)
+		{
+			int			i;
+			TransactionId *subxids;
+
+			subxids = palloc(sizeof(TransactionId) * nsubxids);
+			memcpy(subxids,
+				   XLogRecGetData(record) + MinSizeOfXidCSNSet,
+				   sizeof(TransactionId) * nsubxids);
+			for (i = 0; i < nsubxids; i++)
+				appendStringInfo(buf, ", %u", subxids[i]);
+			pfree(subxids);
+		}
+	}
+}
+
+const char *
+csnlog_identify(uint8 info)
+{
+	const char *id = NULL;
+
+	switch (info & ~XLR_INFO_MASK)
+	{
+		case XLOG_CSN_ASSIGNMENT:
+			id = "ASSIGNMENT";
+			break;
+		case XLOG_CSN_SETXIDCSN:
+			id = "SETXIDCSN";
+			break;
+		case XLOG_CSN_ZEROPAGE:
+			id = "ZEROPAGE";
+			break;
+		case XLOG_CSN_TRUNCATE:
+			id = "TRUNCATE";
+			break;
+	}
+
+	return id;
+}
diff --git a/src/backend/access/rmgrdesc/xlogdesc.c b/src/backend/access/rmgrdesc/xlogdesc.c
index 1cd97852e8..44e2e8ecec 100644
--- a/src/backend/access/rmgrdesc/xlogdesc.c
+++ b/src/backend/access/rmgrdesc/xlogdesc.c
@@ -114,7 +114,8 @@ xlog_desc(StringInfo buf, XLogReaderState *record)
 		appendStringInfo(buf, "max_connections=%d max_worker_processes=%d "
 						 "max_wal_senders=%d max_prepared_xacts=%d "
 						 "max_locks_per_xact=%d wal_level=%s "
-						 "wal_log_hints=%s track_commit_timestamp=%s",
+						 "wal_log_hints=%s track_commit_timestamp=%s "
+						 "enable_csn_snapshot=%s",
 						 xlrec.MaxConnections,
 						 xlrec.max_worker_processes,
 						 xlrec.max_wal_senders,
@@ -122,7 +123,8 @@ xlog_desc(StringInfo buf, XLogReaderState *record)
 						 xlrec.max_locks_per_xact,
 						 wal_level_str,
 						 xlrec.wal_log_hints ? "on" : "off",
-						 xlrec.track_commit_timestamp ? "on" : "off");
+						 xlrec.track_commit_timestamp ? "on" : "off",
+						 xlrec.enable_csn_snapshot ? "on" : "off");
 	}
 	else if (info == XLOG_FPW_CHANGE)
 	{
diff --git a/src/backend/access/transam/csn_log.c b/src/backend/access/transam/csn_log.c
index 4e0b8d64e4..4577e61fc3 100644
--- a/src/backend/access/transam/csn_log.c
+++ b/src/backend/access/transam/csn_log.c
@@ -9,6 +9,11 @@
  * transactions.  Because of same lifetime and persistancy requirements
  * this module is quite similar to subtrans.c
  *
+ * If we switch database from CSN-base snapshot to xid-base snapshot then,
+ * nothing wrong. But if we switch xid-base snapshot to CSN-base snapshot
+ * it should decide a new xid whwich begin csn-base check. It can not be
+ * oldestActiveXID because of prepared transaction.
+ *
  * Portions Copyright (c) 1996-2018, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
@@ -52,7 +57,8 @@ bool enable_csn_snapshot;
 static SlruCtlData CSNLogCtlData;
 #define CsnlogCtl (&CSNLogCtlData)
 
-static int	ZeroCSNLogPage(int pageno);
+static int	ZeroCSNLogPage(int pageno, bool write_xlog);
+static void ZeroTruncateCSNLogPage(int pageno, bool write_xlog);
 static bool CSNLogPagePrecedes(int page1, int page2);
 static void CSNLogSetPageStatus(TransactionId xid, int nsubxids,
 									  TransactionId *subxids,
@@ -60,6 +66,11 @@ static void CSNLogSetPageStatus(TransactionId xid, int nsubxids,
 static void CSNLogSetCSNInSlot(TransactionId xid, XidCSN csn,
 									  int slotno);
 
+static void WriteXidCsnXlogRec(TransactionId xid, int nsubxids,
+					 TransactionId *subxids, XidCSN csn);
+static void WriteZeroCSNPageXlogRec(int pageno);
+static void WriteTruncateCSNXlogRec(int pageno);
+
 /*
  * CSNLogSetCSN
  *
@@ -77,7 +88,7 @@ static void CSNLogSetCSNInSlot(TransactionId xid, XidCSN csn,
  */
 void
 CSNLogSetCSN(TransactionId xid, int nsubxids,
-					 TransactionId *subxids, XidCSN csn)
+					 TransactionId *subxids, XidCSN csn, bool write_xlog)
 {
 	int			pageno;
 	int			i = 0;
@@ -89,6 +100,10 @@ CSNLogSetCSN(TransactionId xid, int nsubxids,
 	Assert(TransactionIdIsValid(xid));
 
 	pageno = TransactionIdToPage(xid);		/* get page of parent */
+
+	if(write_xlog)
+		WriteXidCsnXlogRec(xid, nsubxids, subxids, csn);
+
 	for (;;)
 	{
 		int			num_on_page = 0;
@@ -180,11 +195,7 @@ CSNLogGetCSNByXid(TransactionId xid)
 	/* Callers of CSNLogGetCSNByXid() must check GUC params */
 	Assert(enable_csn_snapshot);
 
-	/* Can't ask about stuff that might not be around anymore */
-	Assert(TransactionIdFollowsOrEquals(xid, TransactionXmin));
-
 	/* lock is acquired by SimpleLruReadPage_ReadOnly */
-
 	slotno = SimpleLruReadPage_ReadOnly(CsnlogCtl, pageno, xid);
 	ptr = (XidCSN *) (CsnlogCtl->shared->page_buffer[slotno] + entryno * sizeof(XLogRecPtr));
 	xid_csn = *ptr;
@@ -245,7 +256,7 @@ BootStrapCSNLog(void)
 	LWLockAcquire(CSNLogControlLock, LW_EXCLUSIVE);
 
 	/* Create and zero the first page of the commit log */
-	slotno = ZeroCSNLogPage(0);
+	slotno = ZeroCSNLogPage(0, false);
 
 	/* Make sure it's written out */
 	SimpleLruWritePage(CsnlogCtl, slotno);
@@ -263,50 +274,20 @@ BootStrapCSNLog(void)
  * Control lock must be held at entry, and will be held at exit.
  */
 static int
-ZeroCSNLogPage(int pageno)
+ZeroCSNLogPage(int pageno, bool write_xlog)
 {
 	Assert(LWLockHeldByMe(CSNLogControlLock));
+	if(write_xlog)
+		WriteZeroCSNPageXlogRec(pageno);
 	return SimpleLruZeroPage(CsnlogCtl, pageno);
 }
 
-/*
- * This must be called ONCE during postmaster or standalone-backend startup,
- * after StartupXLOG has initialized ShmemVariableCache->nextXid.
- *
- * oldestActiveXID is the oldest XID of any prepared transaction, or nextXid
- * if there are none.
- */
-void
-StartupCSNLog(TransactionId oldestActiveXID)
+static void
+ZeroTruncateCSNLogPage(int pageno, bool write_xlog)
 {
-	int			startPage;
-	int			endPage;
-
-	if (!enable_csn_snapshot)
-		return;
-
-	/*
-	 * Since we don't expect pg_csn to be valid across crashes, we
-	 * initialize the currently-active page(s) to zeroes during startup.
-	 * Whenever we advance into a new page, ExtendCSNLog will likewise
-	 * zero the new page without regard to whatever was previously on disk.
-	 */
-	LWLockAcquire(CSNLogControlLock, LW_EXCLUSIVE);
-
-	startPage = TransactionIdToPage(oldestActiveXID);
-	endPage = TransactionIdToPage(XidFromFullTransactionId(ShmemVariableCache->nextFullXid));
-
-	while (startPage != endPage)
-	{
-		(void) ZeroCSNLogPage(startPage);
-		startPage++;
-		/* must account for wraparound */
-		if (startPage > TransactionIdToPage(MaxTransactionId))
-			startPage = 0;
-	}
-	(void) ZeroCSNLogPage(startPage);
-
-	LWLockRelease(CSNLogControlLock);
+	if(write_xlog)
+		WriteTruncateCSNXlogRec(pageno);
+	SimpleLruTruncate(CsnlogCtl, pageno);
 }
 
 /*
@@ -379,7 +360,7 @@ ExtendCSNLog(TransactionId newestXact)
 	LWLockAcquire(CSNLogControlLock, LW_EXCLUSIVE);
 
 	/* Zero the page and make an XLOG entry about it */
-	ZeroCSNLogPage(pageno);
+	ZeroCSNLogPage(pageno, !InRecovery);
 
 	LWLockRelease(CSNLogControlLock);
 }
@@ -410,7 +391,7 @@ TruncateCSNLog(TransactionId oldestXact)
 	TransactionIdRetreat(oldestXact);
 	cutoffPage = TransactionIdToPage(oldestXact);
 
-	SimpleLruTruncate(CsnlogCtl, cutoffPage);
+	ZeroTruncateCSNLogPage(cutoffPage, true);
 }
 
 /*
@@ -436,3 +417,115 @@ CSNLogPagePrecedes(int page1, int page2)
 
 	return TransactionIdPrecedes(xid1, xid2);
 }
+
+void
+WriteAssignCSNXlogRec(XidCSN xidcsn)
+{
+	XidCSN log_csn = 0;
+
+	if(xidcsn > get_last_log_wal_csn())
+	{
+		log_csn = CSNAddByNanosec(xidcsn, 20);
+		set_last_log_wal_csn(log_csn);
+	}
+	else
+	{
+		return;
+	}
+
+	XLogBeginInsert();
+	XLogRegisterData((char *) (&log_csn), sizeof(XidCSN));
+	XLogInsert(RM_CSNLOG_ID, XLOG_CSN_ASSIGNMENT);
+}
+
+static void
+WriteXidCsnXlogRec(TransactionId xid, int nsubxids,
+					 TransactionId *subxids, XidCSN csn)
+{
+	xl_xidcsn_set 	xlrec;
+	XLogRecPtr		recptr;
+
+	xlrec.xtop = xid;
+	xlrec.nsubxacts = nsubxids;
+	xlrec.xidcsn = csn;
+
+	XLogBeginInsert();
+	XLogRegisterData((char *) &xlrec, MinSizeOfXidCSNSet);
+	XLogRegisterData((char *) subxids, nsubxids * sizeof(TransactionId));
+	recptr = XLogInsert(RM_CSNLOG_ID, XLOG_CSN_SETXIDCSN);
+	XLogFlush(recptr);
+}
+
+/*
+ * Write a ZEROPAGE xlog record
+ */
+static void
+WriteZeroCSNPageXlogRec(int pageno)
+{
+	XLogBeginInsert();
+	XLogRegisterData((char *) (&pageno), sizeof(int));
+	(void) XLogInsert(RM_CSNLOG_ID, XLOG_CSN_ZEROPAGE);
+}
+
+/*
+ * Write a TRUNCATE xlog record
+ */
+static void
+WriteTruncateCSNXlogRec(int pageno)
+{
+	XLogRecPtr	recptr;
+	return;
+	XLogBeginInsert();
+	XLogRegisterData((char *) (&pageno), sizeof(int));
+	recptr = XLogInsert(RM_CSNLOG_ID, XLOG_CSN_TRUNCATE);
+	XLogFlush(recptr);
+}
+
+
+void
+csnlog_redo(XLogReaderState *record)
+{
+	uint8		info = XLogRecGetInfo(record) & ~XLR_INFO_MASK;
+
+	/* Backup blocks are not used in csnlog records */
+	Assert(!XLogRecHasAnyBlockRefs(record));
+
+	if (info == XLOG_CSN_ASSIGNMENT)
+	{
+		XidCSN csn;
+
+		memcpy(&csn, XLogRecGetData(record), sizeof(XidCSN));
+		LWLockAcquire(CSNLogControlLock, LW_EXCLUSIVE);
+		set_last_max_csn(csn);
+		LWLockRelease(CSNLogControlLock);
+
+	}
+	else if (info == XLOG_CSN_SETXIDCSN)
+	{
+		xl_xidcsn_set *xlrec = (xl_xidcsn_set *) XLogRecGetData(record);
+		CSNLogSetCSN(xlrec->xtop, xlrec->nsubxacts, xlrec->xsub, xlrec->xidcsn, false);
+	}
+	else if (info == XLOG_CSN_ZEROPAGE)
+	{
+		int			pageno;
+		int			slotno;
+
+		memcpy(&pageno, XLogRecGetData(record), sizeof(int));
+		LWLockAcquire(CSNLogControlLock, LW_EXCLUSIVE);
+		slotno = ZeroCSNLogPage(pageno, false);
+		SimpleLruWritePage(CsnlogCtl, slotno);
+		LWLockRelease(CSNLogControlLock);
+		Assert(!CsnlogCtl->shared->page_dirty[slotno]);
+
+	}
+	else if (info == XLOG_CSN_TRUNCATE)
+	{
+		int			pageno;
+
+		memcpy(&pageno, XLogRecGetData(record), sizeof(int));
+		CsnlogCtl->shared->latest_page_number = pageno;
+		ZeroTruncateCSNLogPage(pageno, false);
+	}
+	else
+		elog(PANIC, "csnlog_redo: unknown op code %u", info);
+}
diff --git a/src/backend/access/transam/csn_snapshot.c b/src/backend/access/transam/csn_snapshot.c
index e2d4d2649e..a3d164d77e 100644
--- a/src/backend/access/transam/csn_snapshot.c
+++ b/src/backend/access/transam/csn_snapshot.c
@@ -31,6 +31,8 @@
 /* Raise a warning if imported snapshot_csn exceeds ours by this value. */
 #define SNAP_DESYNC_COMPLAIN (1*NSECS_PER_SEC) /* 1 second */
 
+TransactionId 	 xmin_for_csn = InvalidTransactionId;
+
 /*
  * CSNSnapshotState
  *
@@ -40,7 +42,9 @@
  */
 typedef struct
 {
-	SnapshotCSN		 last_max_csn;
+	SnapshotCSN		 last_max_csn;		/* Record the max csn till now */
+	XidCSN			 last_csn_log_wal;	/* for interval we log the assign csn to wal */
+	TransactionId 	 xmin_for_csn; 		/*'xmin_for_csn' for when turn xid-snapshot to csn-snapshot*/
 	volatile slock_t lock;
 } CSNSnapshotState;
 
@@ -80,6 +84,7 @@ CSNSnapshotShmemInit()
 		if (!found)
 		{
 			csnState->last_max_csn = 0;
+			csnState->last_csn_log_wal = 0;
 			SpinLockInit(&csnState->lock);
 		}
 	}
@@ -116,6 +121,8 @@ GenerateCSN(bool locked)
 	else
 		csnState->last_max_csn = csn;
 
+	WriteAssignCSNXlogRec(csn);
+
 	if (!locked)
 		SpinLockRelease(&csnState->lock);
 
@@ -131,7 +138,7 @@ GenerateCSN(bool locked)
 XidCSN
 TransactionIdGetXidCSN(TransactionId xid)
 {
-	XidCSN xid_csn;
+	XidCSN 			 xid_csn;
 
 	Assert(enable_csn_snapshot);
 
@@ -145,13 +152,35 @@ TransactionIdGetXidCSN(TransactionId xid)
 		Assert(false); /* Should not happend */
 	}
 
+	/*
+	 * If we just switch a xid-snapsot to a csn_snapshot, we should handle a start
+	 * xid for csn basse check. Just in case we have prepared transaction which
+	 * hold the TransactionXmin but without CSN.
+	 */
+	if(InvalidTransactionId == xmin_for_csn)
+	{
+		SpinLockAcquire(&csnState->lock);
+		if(InvalidTransactionId != csnState->xmin_for_csn)
+			xmin_for_csn = csnState->xmin_for_csn;
+		else
+			xmin_for_csn = FrozenTransactionId;
+
+		SpinLockRelease(&csnState->lock);
+	}
+
+	if ( FrozenTransactionId != xmin_for_csn ||
+					TransactionIdPrecedes(xmin_for_csn, TransactionXmin))
+	{
+		xmin_for_csn = TransactionXmin;
+	}
+
 	/*
 	 * For xids which less then TransactionXmin CSNLog can be already
 	 * trimmed but we know that such transaction is definetly not concurrently
 	 * running according to any snapshot including timetravel ones. Callers
 	 * should check TransactionDidCommit after.
 	 */
-	if (TransactionIdPrecedes(xid, TransactionXmin))
+	if (TransactionIdPrecedes(xid, xmin_for_csn))
 		return FrozenXidCSN;
 
 	/* Read XidCSN from SLRU */
@@ -251,7 +280,7 @@ CSNSnapshotAbort(PGPROC *proc, TransactionId xid,
 	if (!enable_csn_snapshot)
 		return;
 
-	CSNLogSetCSN(xid, nsubxids, subxids, AbortedXidCSN);
+	CSNLogSetCSN(xid, nsubxids, subxids, AbortedXidCSN, true);
 
 	/*
 	 * Clean assignedXidCsn anyway, as it was possibly set in
@@ -292,7 +321,7 @@ CSNSnapshotPrecommit(PGPROC *proc, TransactionId xid,
 	{
 		Assert(XidCSNIsInProgress(oldassignedXidCsn));
 		CSNLogSetCSN(xid, nsubxids,
-						   subxids, InDoubtXidCSN);
+						   subxids, InDoubtXidCSN, true);
 	}
 	else
 	{
@@ -333,8 +362,39 @@ CSNSnapshotCommit(PGPROC *proc, TransactionId xid,
 	assigned_xid_csn = pg_atomic_read_u64(&proc->assignedXidCsn);
 	Assert(XidCSNIsNormal(assigned_xid_csn));
 	CSNLogSetCSN(xid, nsubxids,
-						   subxids, assigned_xid_csn);
+						   subxids, assigned_xid_csn, true);
 
 	/* Reset for next transaction */
 	pg_atomic_write_u64(&proc->assignedXidCsn, InProgressXidCSN);
 }
+
+void
+set_last_max_csn(XidCSN xidcsn)
+{
+	csnState->last_max_csn = xidcsn;
+}
+
+void
+set_last_log_wal_csn(XidCSN xidcsn)
+{
+	csnState->last_csn_log_wal = xidcsn;
+}
+
+XidCSN
+get_last_log_wal_csn(void)
+{
+	XidCSN			 last_csn_log_wal;
+
+	last_csn_log_wal = csnState->last_csn_log_wal;
+
+	return last_csn_log_wal;
+}
+
+/*
+ * 'xmin_for_csn' for when turn xid-snapshot to csn-snapshot
+ */
+void
+set_xmin_for_csn(void)
+{
+	csnState->xmin_for_csn = XidFromFullTransactionId(ShmemVariableCache->nextFullXid);
+}
diff --git a/src/backend/access/transam/rmgr.c b/src/backend/access/transam/rmgr.c
index 58091f6b52..b1e5ec350e 100644
--- a/src/backend/access/transam/rmgr.c
+++ b/src/backend/access/transam/rmgr.c
@@ -28,6 +28,7 @@
 #include "replication/origin.h"
 #include "storage/standby.h"
 #include "utils/relmapper.h"
+#include "access/csn_log.h"
 
 /* must be kept in sync with RmgrData definition in xlog_internal.h */
 #define PG_RMGR(symname,name,redo,desc,identify,startup,cleanup,mask) \
diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index b7350249da..7187bb0be3 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -4604,6 +4604,7 @@ InitControlFile(uint64 sysidentifier)
 	ControlFile->wal_level = wal_level;
 	ControlFile->wal_log_hints = wal_log_hints;
 	ControlFile->track_commit_timestamp = track_commit_timestamp;
+	ControlFile->enable_csn_snapshot = enable_csn_snapshot;
 	ControlFile->data_checksum_version = bootstrap_data_checksum_version;
 }
 
@@ -7056,7 +7057,6 @@ StartupXLOG(void)
 			 * maintained during recovery and need not be started yet.
 			 */
 			StartupCLOG();
-			StartupCSNLog(oldestActiveXID);
 			StartupSUBTRANS(oldestActiveXID);
 
 			/*
@@ -7874,7 +7874,6 @@ StartupXLOG(void)
 	if (standbyState == STANDBY_DISABLED)
 	{
 		StartupCLOG();
-		StartupCSNLog(oldestActiveXID);
 		StartupSUBTRANS(oldestActiveXID);
 	}
 
@@ -9097,7 +9096,6 @@ CreateCheckPoint(int flags)
 	if (!RecoveryInProgress())
 	{
 		TruncateSUBTRANS(GetOldestXmin(NULL, PROCARRAY_FLAGS_DEFAULT));
-		TruncateCSNLog(GetOldestXmin(NULL, PROCARRAY_FLAGS_DEFAULT));
 	}
 
 	/* Real work is done, but log and update stats before releasing lock. */
@@ -9720,7 +9718,8 @@ XLogReportParameters(void)
 		max_wal_senders != ControlFile->max_wal_senders ||
 		max_prepared_xacts != ControlFile->max_prepared_xacts ||
 		max_locks_per_xact != ControlFile->max_locks_per_xact ||
-		track_commit_timestamp != ControlFile->track_commit_timestamp)
+		track_commit_timestamp != ControlFile->track_commit_timestamp ||
+		enable_csn_snapshot != ControlFile->enable_csn_snapshot)
 	{
 		/*
 		 * The change in number of backend slots doesn't need to be WAL-logged
@@ -9742,6 +9741,7 @@ XLogReportParameters(void)
 			xlrec.wal_level = wal_level;
 			xlrec.wal_log_hints = wal_log_hints;
 			xlrec.track_commit_timestamp = track_commit_timestamp;
+			xlrec.enable_csn_snapshot = enable_csn_snapshot;
 
 			XLogBeginInsert();
 			XLogRegisterData((char *) &xlrec, sizeof(xlrec));
@@ -9750,6 +9750,8 @@ XLogReportParameters(void)
 			XLogFlush(recptr);
 		}
 
+		if (enable_csn_snapshot != ControlFile->enable_csn_snapshot)
+				set_xmin_for_csn();
 		ControlFile->MaxConnections = MaxConnections;
 		ControlFile->max_worker_processes = max_worker_processes;
 		ControlFile->max_wal_senders = max_wal_senders;
@@ -9758,6 +9760,7 @@ XLogReportParameters(void)
 		ControlFile->wal_level = wal_level;
 		ControlFile->wal_log_hints = wal_log_hints;
 		ControlFile->track_commit_timestamp = track_commit_timestamp;
+		ControlFile->enable_csn_snapshot = enable_csn_snapshot;
 		UpdateControlFile();
 	}
 }
@@ -10184,6 +10187,7 @@ xlog_redo(XLogReaderState *record)
 		CommitTsParameterChange(xlrec.track_commit_timestamp,
 								ControlFile->track_commit_timestamp);
 		ControlFile->track_commit_timestamp = xlrec.track_commit_timestamp;
+		ControlFile->enable_csn_snapshot = xlrec.enable_csn_snapshot;
 
 		UpdateControlFile();
 		LWLockRelease(ControlFileLock);
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index 5a110edb07..0f301b1db0 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -53,7 +53,7 @@
 #include "utils/memutils.h"
 #include "utils/snapmgr.h"
 #include "utils/syscache.h"
-
+#include "access/csn_log.h"
 
 /*
  * GUC parameters
@@ -1632,6 +1632,7 @@ vac_truncate_clog(TransactionId frozenXID,
 	 */
 	TruncateCLOG(frozenXID, oldestxid_datoid);
 	TruncateCommitTs(frozenXID);
+	TruncateCSNLog(frozenXID);
 	TruncateMultiXact(minMulti, minmulti_datoid);
 
 	/*
diff --git a/src/backend/storage/ipc/procarray.c b/src/backend/storage/ipc/procarray.c
index 158fb9d31f..c671d92ead 100644
--- a/src/backend/storage/ipc/procarray.c
+++ b/src/backend/storage/ipc/procarray.c
@@ -1736,7 +1736,7 @@ GetSnapshotData(Snapshot snapshot)
 	 * Take XidCSN under ProcArrayLock so the snapshot stays
 	 * synchronized.
 	 */
-	if (enable_csn_snapshot)
+	if (!snapshot->takenDuringRecovery && enable_csn_snapshot)
 		xid_csn = GenerateCSN(false);
 
 	LWLockRelease(ProcArrayLock);
diff --git a/src/backend/utils/time/snapmgr.c b/src/backend/utils/time/snapmgr.c
index e2baeb9222..218f32e8ec 100644
--- a/src/backend/utils/time/snapmgr.c
+++ b/src/backend/utils/time/snapmgr.c
@@ -2265,7 +2265,7 @@ XidInMVCCSnapshot(TransactionId xid, Snapshot snapshot)
 		if (XidInvisibleInCSNSnapshot(xid, snapshot))
 		{
 			XidCSN gcsn = TransactionIdGetXidCSN(xid);
-			Assert(XidCSNIsAborted(gcsn));
+			Assert(XidCSNIsAborted(gcsn) || XidCSNIsInProgress(gcsn));
 		}
 #endif
 		return false;
diff --git a/src/bin/pg_controldata/pg_controldata.c b/src/bin/pg_controldata/pg_controldata.c
index e73639df74..e7194124c7 100644
--- a/src/bin/pg_controldata/pg_controldata.c
+++ b/src/bin/pg_controldata/pg_controldata.c
@@ -306,6 +306,8 @@ main(int argc, char *argv[])
 		   ControlFile->max_locks_per_xact);
 	printf(_("track_commit_timestamp setting:       %s\n"),
 		   ControlFile->track_commit_timestamp ? _("on") : _("off"));
+	printf(_("enable_csn_snapshot setting:    	    %s\n"),
+		   ControlFile->enable_csn_snapshot ? 	 _("on") : _("off"));
 	printf(_("Maximum data alignment:               %u\n"),
 		   ControlFile->maxAlign);
 	/* we don't print floatFormat since can't say much useful about it */
diff --git a/src/bin/pg_upgrade/pg_upgrade.c b/src/bin/pg_upgrade/pg_upgrade.c
index 70194eb096..863ee73d24 100644
--- a/src/bin/pg_upgrade/pg_upgrade.c
+++ b/src/bin/pg_upgrade/pg_upgrade.c
@@ -545,6 +545,11 @@ copy_xact_xlog_xid(void)
 		check_ok();
 	}
 
+	if(old_cluster.controldata.cat_ver > CSN_BASE_SNAPSHOT_ADD_VER)
+	{
+		copy_subdir_files("pg_csn", "pg_csn");
+	}
+
 	/* now reset the wal archives in the new cluster */
 	prep_status("Resetting WAL archives");
 	exec_prog(UTILITY_LOG_FILE, NULL, true, true,
diff --git a/src/bin/pg_upgrade/pg_upgrade.h b/src/bin/pg_upgrade/pg_upgrade.h
index 8b90cefbe0..f35860dfc5 100644
--- a/src/bin/pg_upgrade/pg_upgrade.h
+++ b/src/bin/pg_upgrade/pg_upgrade.h
@@ -123,6 +123,8 @@ extern char *output_files[];
  */
 #define JSONB_FORMAT_CHANGE_CAT_VER 201409291
 
+#define	CSN_BASE_SNAPSHOT_ADD_VER	202002010
+
 
 /*
  * Each relation is represented by a relinfo structure.
diff --git a/src/bin/pg_waldump/rmgrdesc.c b/src/bin/pg_waldump/rmgrdesc.c
index 852d8ca4b1..282bae882a 100644
--- a/src/bin/pg_waldump/rmgrdesc.c
+++ b/src/bin/pg_waldump/rmgrdesc.c
@@ -31,6 +31,7 @@
 #include "rmgrdesc.h"
 #include "storage/standbydefs.h"
 #include "utils/relmapper.h"
+#include "access/csn_log.h"
 
 #define PG_RMGR(symname,name,redo,desc,identify,startup,cleanup,mask) \
 	{ name, desc, identify},
diff --git a/src/include/access/csn_log.h b/src/include/access/csn_log.h
index 9b9611127d..b973e0c2ce 100644
--- a/src/include/access/csn_log.h
+++ b/src/include/access/csn_log.h
@@ -14,17 +14,42 @@
 #include "access/xlog.h"
 #include "utils/snapshot.h"
 
+/* XLOG stuff */
+#define XLOG_CSN_ASSIGNMENT         0x00
+#define XLOG_CSN_SETXIDCSN       0x10
+#define XLOG_CSN_ZEROPAGE           0x20
+#define XLOG_CSN_TRUNCATE           0x30
+
+typedef struct xl_xidcsn_set
+{
+	XidCSN		  xidcsn;
+	TransactionId xtop;			/* XID's top-level XID */
+	int			nsubxacts;		/* number of subtransaction XIDs */
+	TransactionId xsub[FLEXIBLE_ARRAY_MEMBER];	/* assigned subxids */
+} xl_xidcsn_set;
+
+#define MinSizeOfXidCSNSet offsetof(xl_xidcsn_set, xsub)
+#define	CSNAddByNanosec(csn,second) (csn + second * 1000000000L)
+
 extern void CSNLogSetCSN(TransactionId xid, int nsubxids,
-							   TransactionId *subxids, XidCSN csn);
+							   TransactionId *subxids, XidCSN csn, bool write_xlog);
 extern XidCSN CSNLogGetCSNByXid(TransactionId xid);
 
 extern Size CSNLogShmemSize(void);
 extern void CSNLogShmemInit(void);
 extern void BootStrapCSNLog(void);
-extern void StartupCSNLog(TransactionId oldestActiveXID);
 extern void ShutdownCSNLog(void);
 extern void CheckPointCSNLog(void);
 extern void ExtendCSNLog(TransactionId newestXact);
 extern void TruncateCSNLog(TransactionId oldestXact);
 
+extern void csnlog_redo(XLogReaderState *record);
+extern void csnlog_desc(StringInfo buf, XLogReaderState *record);
+extern const char *csnlog_identify(uint8 info);
+extern void WriteAssignCSNXlogRec(XidCSN xidcsn);
+extern void set_last_max_csn(XidCSN xidcsn);
+extern void set_last_log_wal_csn(XidCSN xidcsn);
+extern XidCSN get_last_log_wal_csn(void);
+extern void set_xmin_for_csn(void);
+
 #endif   /* CSNLOG_H */
\ No newline at end of file
diff --git a/src/include/access/rmgrlist.h b/src/include/access/rmgrlist.h
index 6c15df7e70..b2d12bfb27 100644
--- a/src/include/access/rmgrlist.h
+++ b/src/include/access/rmgrlist.h
@@ -47,3 +47,4 @@ PG_RMGR(RM_COMMIT_TS_ID, "CommitTs", commit_ts_redo, commit_ts_desc, commit_ts_i
 PG_RMGR(RM_REPLORIGIN_ID, "ReplicationOrigin", replorigin_redo, replorigin_desc, replorigin_identify, NULL, NULL, NULL)
 PG_RMGR(RM_GENERIC_ID, "Generic", generic_redo, generic_desc, generic_identify, NULL, NULL, generic_mask)
 PG_RMGR(RM_LOGICALMSG_ID, "LogicalMessage", logicalmsg_redo, logicalmsg_desc, logicalmsg_identify, NULL, NULL, NULL)
+PG_RMGR(RM_CSNLOG_ID, "CSN", csnlog_redo, csnlog_desc, csnlog_identify, NULL, NULL, NULL)
diff --git a/src/include/access/xlog_internal.h b/src/include/access/xlog_internal.h
index c8869d5226..729cf5bc56 100644
--- a/src/include/access/xlog_internal.h
+++ b/src/include/access/xlog_internal.h
@@ -236,6 +236,7 @@ typedef struct xl_parameter_change
 	int			wal_level;
 	bool		wal_log_hints;
 	bool		track_commit_timestamp;
+	bool		enable_csn_snapshot;
 } xl_parameter_change;
 
 /* logs restore point */
diff --git a/src/include/catalog/pg_control.h b/src/include/catalog/pg_control.h
index de5670e538..9e5d4b0fc0 100644
--- a/src/include/catalog/pg_control.h
+++ b/src/include/catalog/pg_control.h
@@ -181,6 +181,7 @@ typedef struct ControlFileData
 	int			max_prepared_xacts;
 	int			max_locks_per_xact;
 	bool		track_commit_timestamp;
+	bool		enable_csn_snapshot;
 
 	/*
 	 * This data is used to check for hardware-architecture compatibility of
