Author: movead
Date:   Fri Jun 12 17:20:21 2020 +0800
doc/src/sgml/config.sgml                              |  50 +++++++++++++++++++++++++++++++++++++++++++++++++-
src/backend/access/transam/csn_log.c                  | 159 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------------------------------------------
src/backend/access/transam/csn_snapshot.c             |  56 ++++++++++++++++++++++++++++++++------------------------
src/backend/access/transam/xlog.c                     |  10 ++++++++--
src/backend/storage/ipc/procarray.c                   |   2 +-
src/backend/utils/misc/guc.c                          |   2 +-
src/backend/utils/misc/postgresql.conf.sample         |   2 ++
src/backend/utils/time/snapmgr.c                      |   3 ++-
src/include/access/csn_log.h                          |   9 ++++++++-
src/test/modules/Makefile                             |   1 +
src/test/modules/csnsnapshot/Makefile                 |  18 ++++++++++++++++++
src/test/modules/csnsnapshot/csn_snapshot.conf        |   1 +
src/test/modules/csnsnapshot/expected/csnsnapshot.out |   1 +
src/test/modules/csnsnapshot/sql/csnsnapshot.sql      |   1 +
src/test/modules/csnsnapshot/t/001_base.pl            | 102 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
src/test/modules/csnsnapshot/t/002_standby.pl         |  66 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
src/test/regress/expected/sysviews.out                |   2 +-
17 files changed, 410 insertions(+), 75 deletions(-)
diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml
index 2908821560..a50ad8bfbe 100644
--- a/doc/src/sgml/config.sgml
+++ b/doc/src/sgml/config.sgml
@@ -9083,8 +9083,56 @@ dynamic_library_path = 'C:\tools\postgresql;H:\my_project\lib;$libdir'
      </varlistentry>
 
      </variablelist>
-   </sect1>
 
+    <sect2 id="runtime-config-CSN-base-snapshot">
+     <title>CSN Based Snapshot</title>
+
+      <para>
+       By default, The snapshots in <productname>PostgreSQL</productname> uses the
+       XID (TransactionID) to identify the status of the transaction, the in-progress
+       transactions, and the future transactions for all its visibility calculations.
+      </para>
+
+      <para>
+       <productname>PostgreSQL</productname> also provides the CSN (commit-sequence-number)
+        based mechanism to identify the past-transactions and the ones that are yet to
+        be started/committed.
+      </para>
+
+     <variablelist>
+      <varlistentry id="guc-enable-csn-snapshot" xreflabel="enable_csn_snapshot">
+       <term><varname>enable_csn_snapshot</varname> (<type>boolean</type>)
+        <indexterm>
+         <primary><varname>enable_csn_snapshot</varname> configuration parameter</primary>
+        </indexterm>
+       </term>
+       <listitem>
+
+        <para>
+        Enable/disable the CSN based transaction visibility tracking for the snapshot.
+        </para>
+
+        <para>
+        <productname>PostgreSQL</productname> uses the clock timestamp as a CSN,
+        so enabling the CSN based snapshots can be useful for implementing the global
+        snapshots and global transaction visibility.
+        </para>
+
+        <para>
+         when enabled <productname>PostgreSQL</productname> creates
+         <filename>pg_csn</filename> directory under <envar>PGDATA</envar> to keep
+         the track of CSN and XID mappings.
+        </para>
+
+        <para>
+         The default value is off.
+        </para>
+       </listitem>
+      </varlistentry>
+
+     </variablelist>
+    </sect2>
+   </sect1>
    <sect1 id="runtime-config-compatible">
     <title>Version and Platform Compatibility</title>
 
diff --git a/src/backend/access/transam/csn_log.c b/src/backend/access/transam/csn_log.c
index 4577e61fc3..319e89c805 100644
--- a/src/backend/access/transam/csn_log.c
+++ b/src/backend/access/transam/csn_log.c
@@ -30,9 +30,28 @@
 #include "miscadmin.h"
 #include "pg_trace.h"
 #include "utils/snapmgr.h"
+#include "storage/shmem.h"
 
 bool enable_csn_snapshot;
 
+/*
+ * We use csnSnapshotActive to judge if csn snapshot enabled instead of by
+ * enable_csn_snapshot, this design is similar to 'track_commit_timestamp'.
+ *
+ * Because in process of replication if master change 'enable_csn_snapshot'
+ * in a database restart, standby should apply wal record for GUC changed,
+ * then it's difficult to notice all backends about that. So they can get
+ * the message by 'csnSnapshotActive' which in share buffer. It will not
+ * acquire a lock, so without performance issue.
+ *
+ */
+typedef struct CSNshapshotShared
+{
+	bool		csnSnapshotActive;
+} CSNshapshotShared;
+
+CSNshapshotShared *csnShared = NULL;
+
 /*
  * Defines for CSNLog page sizes.  A page is the same BLCKSZ as is used
  * everywhere else in Postgres.
@@ -94,9 +113,6 @@ CSNLogSetCSN(TransactionId xid, int nsubxids,
 	int			i = 0;
 	int			offset = 0;
 
-	/* Callers of CSNLogSetCSN() must check GUC params */
-	Assert(enable_csn_snapshot);
-
 	Assert(TransactionIdIsValid(xid));
 
 	pageno = TransactionIdToPage(xid);		/* get page of parent */
@@ -167,7 +183,7 @@ static void
 CSNLogSetCSNInSlot(TransactionId xid, XidCSN csn, int slotno)
 {
 	int			entryno = TransactionIdToPgIndex(xid);
-	XidCSN *ptr;
+	XidCSN 		*ptr;
 
 	Assert(LWLockHeldByMe(CSNLogControlLock));
 
@@ -192,9 +208,6 @@ CSNLogGetCSNByXid(TransactionId xid)
 	XidCSN *ptr;
 	XidCSN	xid_csn;
 
-	/* Callers of CSNLogGetCSNByXid() must check GUC params */
-	Assert(enable_csn_snapshot);
-
 	/* lock is acquired by SimpleLruReadPage_ReadOnly */
 	slotno = SimpleLruReadPage_ReadOnly(CsnlogCtl, pageno, xid);
 	ptr = (XidCSN *) (CsnlogCtl->shared->page_buffer[slotno] + entryno * sizeof(XLogRecPtr));
@@ -220,9 +233,6 @@ CSNLogShmemBuffers(void)
 Size
 CSNLogShmemSize(void)
 {
-	if (!enable_csn_snapshot)
-		return 0;
-
 	return SimpleLruShmemSize(CSNLogShmemBuffers(), 0);
 }
 
@@ -232,37 +242,25 @@ CSNLogShmemSize(void)
 void
 CSNLogShmemInit(void)
 {
-	if (!enable_csn_snapshot)
-		return;
+	bool		found;
+
 
 	CsnlogCtl->PagePrecedes = CSNLogPagePrecedes;
 	SimpleLruInit(CsnlogCtl, "CSNLog Ctl", CSNLogShmemBuffers(), 0,
 				  CSNLogControlLock, "pg_csn", LWTRANCHE_CSN_LOG_BUFFERS);
+
+	csnShared = ShmemInitStruct("CSNlog shared",
+									 sizeof(CSNshapshotShared),
+									 &found);
 }
 
 /*
- * This func must be called ONCE on system install.  It creates the initial
- * CSNLog segment.  The pg_csn directory is assumed to have been
- * created by initdb, and CSNLogShmemInit must have been called already.
+ * See ActivateCSNlog
  */
 void
 BootStrapCSNLog(void)
 {
-	int			slotno;
-
-	if (!enable_csn_snapshot)
-		return;
-
-	LWLockAcquire(CSNLogControlLock, LW_EXCLUSIVE);
-
-	/* Create and zero the first page of the commit log */
-	slotno = ZeroCSNLogPage(0, false);
-
-	/* Make sure it's written out */
-	SimpleLruWritePage(CsnlogCtl, slotno);
-	Assert(!CsnlogCtl->shared->page_dirty[slotno]);
-
-	LWLockRelease(CSNLogControlLock);
+	return;
 }
 
 /*
@@ -290,13 +288,94 @@ ZeroTruncateCSNLogPage(int pageno, bool write_xlog)
 	SimpleLruTruncate(CsnlogCtl, pageno);
 }
 
+void
+ActivateCSNlog(void)
+{
+	int				startPage;
+	TransactionId	nextXid = InvalidTransactionId;
+
+	if (csnShared->csnSnapshotActive)
+		return;
+
+
+	nextXid = XidFromFullTransactionId(ShmemVariableCache->nextFullXid);
+	startPage = TransactionIdToPage(nextXid);
+
+	/* Create the current segment file, if necessary */
+	if (!SimpleLruDoesPhysicalPageExist(CsnlogCtl, startPage))
+	{
+		int			slotno;
+		LWLockAcquire(CSNLogControlLock, LW_EXCLUSIVE);
+		slotno = ZeroCSNLogPage(startPage, false);
+		SimpleLruWritePage(CsnlogCtl, slotno);
+		LWLockRelease(CSNLogControlLock);
+	}
+	csnShared->csnSnapshotActive = true;
+}
+
+bool
+get_csnlog_status(void)
+{
+	if(!csnShared)
+	{
+		/* Should not arrived */
+		elog(ERROR, "We do not have csnShared point");
+	}
+	return csnShared->csnSnapshotActive;
+}
+
+void
+DeactivateCSNlog(void)
+{
+	csnShared->csnSnapshotActive = false;
+	LWLockAcquire(CSNLogControlLock, LW_EXCLUSIVE);
+	(void) SlruScanDirectory(CsnlogCtl, SlruScanDirCbDeleteAll, NULL);
+	LWLockRelease(CSNLogControlLock);
+}
+
+void
+StartupCSN(void)
+{
+	ActivateCSNlog();
+}
+
+void
+CompleteCSNInitialization(void)
+{
+	/*
+	 * If the feature is not enabled, turn it off for good.  This also removes
+	 * any leftover data.
+	 *
+	 * Conversely, we activate the module if the feature is enabled.  This is
+	 * necessary for primary and standby as the activation depends on the
+	 * control file contents at the beginning of recovery or when a
+	 * XLOG_PARAMETER_CHANGE is replayed.
+	 */
+	if (!get_csnlog_status())
+		DeactivateCSNlog();
+	else
+		ActivateCSNlog();
+}
+
+void
+CSNlogParameterChange(bool newvalue, bool oldvalue)
+{
+	if (newvalue)
+	{
+		if (!csnShared->csnSnapshotActive)
+			ActivateCSNlog();
+	}
+	else if (csnShared->csnSnapshotActive)
+		DeactivateCSNlog();
+}
+
 /*
  * This must be called ONCE during postmaster or standalone-backend shutdown
  */
 void
 ShutdownCSNLog(void)
 {
-	if (!enable_csn_snapshot)
+	if (!get_csnlog_status())
 		return;
 
 	/*
@@ -316,7 +395,7 @@ ShutdownCSNLog(void)
 void
 CheckPointCSNLog(void)
 {
-	if (!enable_csn_snapshot)
+	if (!get_csnlog_status())
 		return;
 
 	/*
@@ -344,7 +423,7 @@ ExtendCSNLog(TransactionId newestXact)
 {
 	int			pageno;
 
-	if (!enable_csn_snapshot)
+	if (!get_csnlog_status())
 		return;
 
 	/*
@@ -375,9 +454,9 @@ ExtendCSNLog(TransactionId newestXact)
 void
 TruncateCSNLog(TransactionId oldestXact)
 {
-	int			cutoffPage;
+	int				cutoffPage;
 
-	if (!enable_csn_snapshot)
+	if (!get_csnlog_status())
 		return;
 
 	/*
@@ -390,7 +469,6 @@ TruncateCSNLog(TransactionId oldestXact)
 	 */
 	TransactionIdRetreat(oldestXact);
 	cutoffPage = TransactionIdToPage(oldestXact);
-
 	ZeroTruncateCSNLogPage(cutoffPage, true);
 }
 
@@ -443,7 +521,6 @@ WriteXidCsnXlogRec(TransactionId xid, int nsubxids,
 					 TransactionId *subxids, XidCSN csn)
 {
 	xl_xidcsn_set 	xlrec;
-	XLogRecPtr		recptr;
 
 	xlrec.xtop = xid;
 	xlrec.nsubxacts = nsubxids;
@@ -452,8 +529,7 @@ WriteXidCsnXlogRec(TransactionId xid, int nsubxids,
 	XLogBeginInsert();
 	XLogRegisterData((char *) &xlrec, MinSizeOfXidCSNSet);
 	XLogRegisterData((char *) subxids, nsubxids * sizeof(TransactionId));
-	recptr = XLogInsert(RM_CSNLOG_ID, XLOG_CSN_SETXIDCSN);
-	XLogFlush(recptr);
+	XLogInsert(RM_CSNLOG_ID, XLOG_CSN_SETXIDCSN);
 }
 
 /*
@@ -473,12 +549,9 @@ WriteZeroCSNPageXlogRec(int pageno)
 static void
 WriteTruncateCSNXlogRec(int pageno)
 {
-	XLogRecPtr	recptr;
-	return;
 	XLogBeginInsert();
 	XLogRegisterData((char *) (&pageno), sizeof(int));
-	recptr = XLogInsert(RM_CSNLOG_ID, XLOG_CSN_TRUNCATE);
-	XLogFlush(recptr);
+	XLogInsert(RM_CSNLOG_ID, XLOG_CSN_TRUNCATE);
 }
 
 
diff --git a/src/backend/access/transam/csn_snapshot.c b/src/backend/access/transam/csn_snapshot.c
index ec090fd499..d7d0b5e90f 100644
--- a/src/backend/access/transam/csn_snapshot.c
+++ b/src/backend/access/transam/csn_snapshot.c
@@ -62,10 +62,7 @@ CSNSnapshotShmemSize(void)
 {
 	Size	size = 0;
 
-	if (enable_csn_snapshot)
-	{
-		size += MAXALIGN(sizeof(CSNSnapshotState));
-	}
+	size += MAXALIGN(sizeof(CSNSnapshotState));
 
 	return size;
 }
@@ -76,17 +73,14 @@ CSNSnapshotShmemInit()
 {
 	bool found;
 
-	if (enable_csn_snapshot)
+	csnState = ShmemInitStruct("csnState",
+							sizeof(CSNSnapshotState),
+							&found);
+	if (!found)
 	{
-		csnState = ShmemInitStruct("csnState",
-								sizeof(CSNSnapshotState),
-								&found);
-		if (!found)
-		{
-			csnState->last_max_csn = 0;
-			csnState->last_csn_log_wal = 0;
-			SpinLockInit(&csnState->lock);
-		}
+		csnState->last_max_csn = 0;
+		csnState->last_csn_log_wal = 0;
+		SpinLockInit(&csnState->lock);
 	}
 }
 
@@ -104,7 +98,7 @@ GenerateCSN(bool locked)
 	instr_time	current_time;
 	SnapshotCSN	csn;
 
-	Assert(enable_csn_snapshot);
+	Assert(get_csnlog_status());
 
 	/*
 	 * TODO: create some macro that add small random shift to current time.
@@ -140,7 +134,7 @@ TransactionIdGetXidCSN(TransactionId xid)
 {
 	XidCSN 			 xid_csn;
 
-	Assert(enable_csn_snapshot);
+	Assert(get_csnlog_status());
 
 	/* Handle permanent TransactionId's for which we don't have mapping */
 	if (!TransactionIdIsNormal(xid))
@@ -222,7 +216,7 @@ XidInvisibleInCSNSnapshot(TransactionId xid, Snapshot snapshot)
 {
 	XidCSN csn;
 
-	Assert(enable_csn_snapshot);
+	Assert(get_csnlog_status());
 
 	csn = TransactionIdGetXidCSN(xid);
 
@@ -277,7 +271,7 @@ void
 CSNSnapshotAbort(PGPROC *proc, TransactionId xid,
 					int nsubxids, TransactionId *subxids)
 {
-	if (!enable_csn_snapshot)
+	if (!get_csnlog_status())
 		return;
 
 	CSNLogSetCSN(xid, nsubxids, subxids, AbortedXidCSN, true);
@@ -310,7 +304,7 @@ CSNSnapshotPrecommit(PGPROC *proc, TransactionId xid,
 	XidCSN oldassignedXidCsn = InProgressXidCSN;
 	bool in_progress;
 
-	if (!enable_csn_snapshot)
+	if (!get_csnlog_status())
 		return;
 
 	/* Set InDoubt status if it is local transaction */
@@ -348,7 +342,7 @@ CSNSnapshotCommit(PGPROC *proc, TransactionId xid,
 {
 	volatile XidCSN assigned_xid_csn;
 
-	if (!enable_csn_snapshot)
+	if (!get_csnlog_status())
 		return;
 
 	if (!TransactionIdIsValid(xid))
@@ -391,10 +385,24 @@ get_last_log_wal_csn(void)
 }
 
 /*
- * 'xmin_for_csn' for when turn xid-snapshot to csn-snapshot
+ *
  */
 void
-set_xmin_for_csn(void)
+prepare_csn_env(bool enable)
 {
-	csnState->xmin_for_csn = XidFromFullTransactionId(ShmemVariableCache->nextFullXid);
-}
+	TransactionId		nextxid = InvalidTransactionId;
+
+	if(enable)
+	{
+		nextxid = XidFromFullTransactionId(ShmemVariableCache->nextFullXid);
+		/* 'xmin_for_csn' for when turn xid-snapshot to csn-snapshot */
+		csnState->xmin_for_csn = nextxid;
+		/* produce the csnlog segment we want now and seek to current page */
+		ActivateCSNlog();
+	}
+	else
+	{
+		/* Try to drop all csnlog seg */
+		DeactivateCSNlog();
+	}
+}
\ No newline at end of file
diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index a6e6c760b1..e69085fdc0 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -79,6 +79,7 @@
 #include "utils/relmapper.h"
 #include "utils/snapmgr.h"
 #include "utils/timestamp.h"
+#include "access/csn_log.h"
 
 extern uint32 bootstrap_data_checksum_version;
 
@@ -6802,6 +6803,9 @@ StartupXLOG(void)
 	if (ControlFile->track_commit_timestamp)
 		StartupCommitTs();
 
+	if(ControlFile->enable_csn_snapshot)
+		StartupCSN();
+
 	/*
 	 * Recover knowledge about replay progress of known replication partners.
 	 */
@@ -7918,6 +7922,7 @@ StartupXLOG(void)
 	 * commit timestamp.
 	 */
 	CompleteCommitTsInitialization();
+	CompleteCSNInitialization();
 
 	/*
 	 * All done with end-of-recovery actions.
@@ -9758,8 +9763,7 @@ XLogReportParameters(void)
 		LWLockAcquire(ControlFileLock, LW_EXCLUSIVE);
 
 		if (enable_csn_snapshot != ControlFile->enable_csn_snapshot)
-			set_xmin_for_csn();
-
+			prepare_csn_env(enable_csn_snapshot);
 		ControlFile->MaxConnections = MaxConnections;
 		ControlFile->max_worker_processes = max_worker_processes;
 		ControlFile->max_wal_senders = max_wal_senders;
@@ -10201,6 +10205,8 @@ xlog_redo(XLogReaderState *record)
 		CommitTsParameterChange(xlrec.track_commit_timestamp,
 								ControlFile->track_commit_timestamp);
 		ControlFile->track_commit_timestamp = xlrec.track_commit_timestamp;
+		CSNlogParameterChange(xlrec.enable_csn_snapshot,
+								ControlFile->enable_csn_snapshot);
 		ControlFile->enable_csn_snapshot = xlrec.enable_csn_snapshot;
 
 		UpdateControlFile();
diff --git a/src/backend/storage/ipc/procarray.c b/src/backend/storage/ipc/procarray.c
index f8db77ccd7..e066801933 100644
--- a/src/backend/storage/ipc/procarray.c
+++ b/src/backend/storage/ipc/procarray.c
@@ -1734,7 +1734,7 @@ GetSnapshotData(Snapshot snapshot)
 	 * Take XidCSN under ProcArrayLock so the snapshot stays
 	 * synchronized.
 	 */
-	if (!snapshot->takenDuringRecovery && enable_csn_snapshot)
+	if (!snapshot->takenDuringRecovery && get_csnlog_status())
 		xid_csn = GenerateCSN(false);
 
 	LWLockRelease(ProcArrayLock);
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index 6634804de6..e458ef6a09 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -1170,7 +1170,7 @@ static struct config_bool ConfigureNamesBool[] =
 			gettext_noop("Used to achieve REPEATEBLE READ isolation level for postgres_fdw transactions.")
 		},
 		&enable_csn_snapshot,
-		true, /* XXX: set true to simplify tesing. XXX2: Seems that RESOURCES_MEM isn't the best catagory */
+		false,
 		NULL, NULL, NULL
 	},
 	{
diff --git a/src/backend/utils/misc/postgresql.conf.sample b/src/backend/utils/misc/postgresql.conf.sample
index 3a25287a39..092a2743cd 100644
--- a/src/backend/utils/misc/postgresql.conf.sample
+++ b/src/backend/utils/misc/postgresql.conf.sample
@@ -296,6 +296,8 @@
 				# (change requires restart)
 #track_commit_timestamp = off	# collect timestamp of transaction commit
 				# (change requires restart)
+#enable_csn_snapshot = off	# enable csn base snapshot
+				# (change requires restart)
 
 # - Master Server -
 
diff --git a/src/backend/utils/time/snapmgr.c b/src/backend/utils/time/snapmgr.c
index 218f32e8ec..b50b8cbd30 100644
--- a/src/backend/utils/time/snapmgr.c
+++ b/src/backend/utils/time/snapmgr.c
@@ -52,6 +52,7 @@
 #include "access/transam.h"
 #include "access/xact.h"
 #include "access/xlog.h"
+#include "access/csn_log.h"
 #include "catalog/catalog.h"
 #include "lib/pairingheap.h"
 #include "miscadmin.h"
@@ -2244,7 +2245,7 @@ XidInMVCCSnapshot(TransactionId xid, Snapshot snapshot)
 
 	in_snapshot = XidInLocalMVCCSnapshot(xid, snapshot);
 
-	if (!enable_csn_snapshot)
+	if (!get_csnlog_status())
 	{
 		Assert(XidCSNIsFrozen(snapshot->snapshot_csn));
 		return in_snapshot;
diff --git a/src/include/access/csn_log.h b/src/include/access/csn_log.h
index b973e0c2ce..5838028a30 100644
--- a/src/include/access/csn_log.h
+++ b/src/include/access/csn_log.h
@@ -50,6 +50,13 @@ 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);
+extern void prepare_csn_env(bool enable_csn_snapshot);
+extern void CatchCSNLog(void);
+extern void ActivateCSNlog(void);
+extern void DeactivateCSNlog(void);
+extern void StartupCSN(void);
+extern void CompleteCSNInitialization(void);
+extern void CSNlogParameterChange(bool newvalue, bool oldvalue);
+extern bool get_csnlog_status(void);
 
 #endif   /* CSNLOG_H */
\ No newline at end of file
diff --git a/src/test/modules/Makefile b/src/test/modules/Makefile
index 29de73c060..86e114e934 100644
--- a/src/test/modules/Makefile
+++ b/src/test/modules/Makefile
@@ -7,6 +7,7 @@ include $(top_builddir)/src/Makefile.global
 SUBDIRS = \
 		  brin \
 		  commit_ts \
+		  csnsnapshot \
 		  dummy_index_am \
 		  dummy_seclabel \
 		  snapshot_too_old \
diff --git a/src/test/modules/csnsnapshot/Makefile b/src/test/modules/csnsnapshot/Makefile
new file mode 100644
index 0000000000..45c4221cd0
--- /dev/null
+++ b/src/test/modules/csnsnapshot/Makefile
@@ -0,0 +1,18 @@
+# src/test/modules/csnsnapshot/Makefile
+
+REGRESS = csnsnapshot
+REGRESS_OPTS = --temp-config=$(top_srcdir)/src/test/modules/csnsnapshot/csn_snapshot.conf
+NO_INSTALLCHECK = 1
+
+TAP_TESTS = 1
+
+ifdef USE_PGXS
+PG_CONFIG = pg_config
+PGXS := $(shell $(PG_CONFIG) --pgxs)
+include $(PGXS)
+else
+subdir = src/test/modules/csnsnapshot
+top_builddir = ../../../..
+include $(top_builddir)/src/Makefile.global
+include $(top_srcdir)/contrib/contrib-global.mk
+endif
diff --git a/src/test/modules/csnsnapshot/csn_snapshot.conf b/src/test/modules/csnsnapshot/csn_snapshot.conf
new file mode 100644
index 0000000000..e9d3c35756
--- /dev/null
+++ b/src/test/modules/csnsnapshot/csn_snapshot.conf
@@ -0,0 +1 @@
+track_commit_timestamp = on
diff --git a/src/test/modules/csnsnapshot/expected/csnsnapshot.out b/src/test/modules/csnsnapshot/expected/csnsnapshot.out
new file mode 100644
index 0000000000..ac28e417b6
--- /dev/null
+++ b/src/test/modules/csnsnapshot/expected/csnsnapshot.out
@@ -0,0 +1 @@
+create table t1(i int, j int, k varchar);
diff --git a/src/test/modules/csnsnapshot/sql/csnsnapshot.sql b/src/test/modules/csnsnapshot/sql/csnsnapshot.sql
new file mode 100644
index 0000000000..91539b8c30
--- /dev/null
+++ b/src/test/modules/csnsnapshot/sql/csnsnapshot.sql
@@ -0,0 +1 @@
+create table t1(i int, j int, k varchar);
\ No newline at end of file
diff --git a/src/test/modules/csnsnapshot/t/001_base.pl b/src/test/modules/csnsnapshot/t/001_base.pl
new file mode 100644
index 0000000000..1c91f4d9f7
--- /dev/null
+++ b/src/test/modules/csnsnapshot/t/001_base.pl
@@ -0,0 +1,102 @@
+# Single-node test: value can be set, and is still present after recovery
+
+use strict;
+use warnings;
+
+use TestLib;
+use Test::More tests => 5;
+use PostgresNode;
+
+my $node = get_new_node('csntest');
+$node->init;
+$node->append_conf('postgresql.conf', qq{
+					enable_csn_snapshot = on
+					csn_snapshot_defer_time = 10
+					max_prepared_transactions = 10
+					});
+$node->start;
+
+my $test_1 = 1;
+
+# Create a table
+$node->safe_psql('postgres', 'create table t1(i int, j int)');
+
+# insert test record
+$node->safe_psql('postgres', 'insert into t1 values(1,1)');
+# export csn snapshot
+my $test_snapshot = $node->safe_psql('postgres', 'select pg_csn_snapshot_export()');
+# insert test record
+$node->safe_psql('postgres', 'insert into t1 values(2,1)');
+
+my $count1 = $node->safe_psql('postgres', "select count(*) from t1");
+is($count1, '2', 'Get right number in nomal query');
+my $count2 = $node->safe_psql('postgres', "
+			begin transaction isolation level repeatable read;
+			select pg_csn_snapshot_import($test_snapshot);
+			select count(*) from t1;
+			commit;"
+			);
+
+is($count2, '
+1', 'Get right number in csn import query');
+
+#prepare transaction test
+$node->safe_psql('postgres', "
+						begin;
+						insert into t1 values(3,1);
+						insert into t1 values(3,2);
+						prepare	transaction 'pt3';
+						");
+$node->safe_psql('postgres', "
+						begin;
+						insert into t1 values(4,1);
+						insert into t1 values(4,2);
+						prepare	transaction 'pt4';
+						");
+$node->safe_psql('postgres', "
+						begin;
+						insert into t1 values(5,1);
+						insert into t1 values(5,2);
+						prepare	transaction 'pt5';
+						");
+$node->safe_psql('postgres', "
+						begin;
+						insert into t1 values(6,1);
+						insert into t1 values(6,2);
+						prepare	transaction 'pt6';
+						");
+$node->safe_psql('postgres', "commit prepared 'pt4';");
+
+# restart with enable_csn_snapshot off
+$node->append_conf('postgresql.conf', "enable_csn_snapshot = off");
+$node->restart;
+$node->safe_psql('postgres', "
+						insert into t1 values(7,1);
+						insert into t1 values(7,2);
+						");
+$node->safe_psql('postgres', "commit prepared 'pt3';");
+$count1 = $node->safe_psql('postgres', "select count(*) from t1");
+is($count1, '8', 'Get right number in nomal query');
+
+
+# restart with enable_csn_snapshot on
+$node->append_conf('postgresql.conf', "enable_csn_snapshot = on");
+$node->restart;
+$node->safe_psql('postgres', "
+						insert into t1 values(8,1);
+						insert into t1 values(8,2);
+						");
+$node->safe_psql('postgres', "commit prepared 'pt5';");
+$count1 = $node->safe_psql('postgres', "select count(*) from t1");
+is($count1, '12', 'Get right number in nomal query');
+
+# restart with enable_csn_snapshot off
+$node->append_conf('postgresql.conf', "enable_csn_snapshot = on");
+$node->restart;
+$node->safe_psql('postgres', "
+						insert into t1 values(9,1);
+						insert into t1 values(9,2);
+						");
+$node->safe_psql('postgres', "commit prepared 'pt6';");
+$count1 = $node->safe_psql('postgres', "select count(*) from t1");
+is($count1, '16', 'Get right number in nomal query');
diff --git a/src/test/modules/csnsnapshot/t/002_standby.pl b/src/test/modules/csnsnapshot/t/002_standby.pl
new file mode 100644
index 0000000000..b7c4ea93b2
--- /dev/null
+++ b/src/test/modules/csnsnapshot/t/002_standby.pl
@@ -0,0 +1,66 @@
+# Test simple scenario involving a standby
+
+use strict;
+use warnings;
+
+use TestLib;
+use Test::More tests => 6;
+use PostgresNode;
+
+my $bkplabel = 'backup';
+my $master   = get_new_node('master');
+$master->init(allows_streaming => 1);
+
+$master->append_conf(
+	'postgresql.conf', qq{
+	enable_csn_snapshot = on
+	max_wal_senders = 5
+	});
+$master->start;
+$master->backup($bkplabel);
+
+my $standby = get_new_node('standby');
+$standby->init_from_backup($master, $bkplabel, has_streaming => 1);
+$standby->start;
+
+$master->safe_psql('postgres', "create table t1(i int, j int)");
+
+my $guc_on_master = $master->safe_psql('postgres', 'show enable_csn_snapshot');
+is($guc_on_master, 'on', "GUC on master");
+
+my $guc_on_standby = $standby->safe_psql('postgres', 'show enable_csn_snapshot');
+is($guc_on_standby, 'on', "GUC on standby");
+
+$master->append_conf('postgresql.conf', 'enable_csn_snapshot = off');
+$master->restart;
+
+$guc_on_master = $master->safe_psql('postgres', 'show enable_csn_snapshot');
+is($guc_on_master, 'off', "GUC off master");
+
+$guc_on_standby = $standby->safe_psql('postgres', 'show enable_csn_snapshot');
+is($guc_on_standby, 'on', "GUC on standby");
+
+# We consume a large number of transaction,for skip page
+for my $i (1 .. 4096) #4096
+{
+	$master->safe_psql('postgres', "insert into t1 values(1,$i)");
+}
+$master->safe_psql('postgres', "select pg_sleep(2)");
+$master->append_conf('postgresql.conf', 'enable_csn_snapshot = on');
+$master->restart;
+
+my $count_standby = $standby->safe_psql('postgres', 'select count(*) from t1');
+is($count_standby, '4096', "Ok for siwtch xid-base > csn-base"); #4096
+
+# We consume a large number of transaction,for skip page
+for my $i (1 .. 4096) #4096
+{
+	$master->safe_psql('postgres', "insert into t1 values(1,$i)");
+}
+$master->safe_psql('postgres', "select pg_sleep(2)");
+
+$master->append_conf('postgresql.conf', 'enable_csn_snapshot = off');
+$master->restart;
+
+$count_standby = $standby->safe_psql('postgres', 'select count(*) from t1');
+is($count_standby, '8192', "Ok for siwtch csn-base > xid-base"); #8192
\ No newline at end of file
diff --git a/src/test/regress/expected/sysviews.out b/src/test/regress/expected/sysviews.out
index 86a5df0cba..c9118db2b0 100644
--- a/src/test/regress/expected/sysviews.out
+++ b/src/test/regress/expected/sysviews.out
@@ -73,7 +73,7 @@ select name, setting from pg_settings where name like 'enable%';
               name              | setting 
 --------------------------------+---------
  enable_bitmapscan              | on
- enable_csn_snapshot            | on
+ enable_csn_snapshot            | off
  enable_gathermerge             | on
  enable_groupingsets_hash_disk  | off
  enable_hashagg                 | on
