diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml
index b353c61683..7cd0d5e49f 100644
--- a/doc/src/sgml/config.sgml
+++ b/doc/src/sgml/config.sgml
@@ -9068,8 +9068,56 @@ dynamic_library_path = 'C:\tools\postgresql;H:\my_project\lib;$libdir'
-
+
+ CSN Based Snapshot
+
+
+ By default, The snapshots in PostgreSQL uses the
+ XID (TransactionID) to identify the status of the transaction, the in-progress
+ transactions, and the future transactions for all its visibility calculations.
+
+
+
+ PostgreSQL also provides the CSN (commit-sequence-number)
+ based mechanism to identify the past-transactions and the ones that are yet to
+ be started/committed.
+
+
+
+
+ enable_csn_snapshot (boolean)
+
+ enable_csn_snapshot configuration parameter
+
+
+
+
+
+ Enable/disable the CSN based transaction visibility tracking for the snapshot.
+
+
+
+ PostgreSQL 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.
+
+
+
+ when enabled PostgreSQL creates
+ pg_csn directory under PGDATA to keep
+ the track of CSN and XID mappings.
+
+
+
+ The default value is off.
+
+
+
+
+
+
+
Version and Platform Compatibility
diff --git a/src/backend/access/transam/csn_log.c b/src/backend/access/transam/csn_log.c
index 9a42c5ba60..16c6856758 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 */
@@ -191,9 +207,6 @@ CSNLogGetCSNByXid(TransactionId xid)
int slotno;
XidCSN csn;
- /* Callers of CSNLogGetCSNByXid() must check GUC params */
- Assert(enable_csn_snapshot);
-
/* lock is acquired by SimpleLruReadPage_ReadOnly */
slotno = SimpleLruReadPage_ReadOnly(CsnlogCtl, pageno, xid);
csn = *(XidCSN *) (CsnlogCtl->shared->page_buffer[slotno] + entryno * sizeof(XLogRecPtr));
@@ -218,9 +231,6 @@ CSNLogShmemBuffers(void)
Size
CSNLogShmemSize(void)
{
- if (!enable_csn_snapshot)
- return 0;
-
return SimpleLruShmemSize(CSNLogShmemBuffers(), 0);
}
@@ -230,37 +240,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;
}
/*
@@ -288,13 +286,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;
/*
@@ -314,7 +393,7 @@ ShutdownCSNLog(void)
void
CheckPointCSNLog(void)
{
- if (!enable_csn_snapshot)
+ if (!get_csnlog_status())
return;
/*
@@ -342,7 +421,7 @@ ExtendCSNLog(TransactionId newestXact)
{
int pageno;
- if (!enable_csn_snapshot)
+ if (!get_csnlog_status())
return;
/*
@@ -373,9 +452,9 @@ ExtendCSNLog(TransactionId newestXact)
void
TruncateCSNLog(TransactionId oldestXact)
{
- int cutoffPage;
+ int cutoffPage;
- if (!enable_csn_snapshot)
+ if (!get_csnlog_status())
return;
/*
@@ -388,7 +467,6 @@ TruncateCSNLog(TransactionId oldestXact)
*/
TransactionIdRetreat(oldestXact);
cutoffPage = TransactionIdToPage(oldestXact);
-
ZeroTruncateCSNLogPage(cutoffPage, true);
}
@@ -447,7 +525,6 @@ WriteXidCsnXlogRec(TransactionId xid, int nsubxids,
TransactionId *subxids, XidCSN csn)
{
xl_xidcsn_set xlrec;
- XLogRecPtr recptr;
xlrec.xtop = xid;
xlrec.nsubxacts = nsubxids;
@@ -456,8 +533,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);
}
/*
@@ -477,12 +553,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 99e4a2f1ed..cedce60a6f 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,15 @@ 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;
+ csnState->xmin_for_csn = InvalidTransactionId;
+ SpinLockInit(&csnState->lock);
}
}
@@ -104,7 +99,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 +135,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))
@@ -168,12 +163,20 @@ TransactionIdGetXidCSN(TransactionId xid)
SpinLockRelease(&csnState->lock);
}
- if ( FrozenTransactionId != xmin_for_csn ||
+ if (FrozenTransactionId == xmin_for_csn ||
TransactionIdPrecedes(xmin_for_csn, TransactionXmin))
{
xmin_for_csn = TransactionXmin;
}
+ /*
+ * For the xid with 'xid >= TransactionXmin and xid < xmin_for_csn',
+ * it defined as unclear csn which follow xid-snapshot result.
+ */
+ if(!TransactionIdPrecedes(xid, TransactionXmin) &&
+ TransactionIdPrecedes(xid, xmin_for_csn))
+ return UnclearCSN;
+
/*
* For xids which less then TransactionXmin CSNLog can be already
* trimmed but we know that such transaction is definetly not concurrently
@@ -222,7 +225,7 @@ XidInvisibleInCSNSnapshot(TransactionId xid, Snapshot snapshot)
{
XidCSN csn;
- Assert(enable_csn_snapshot);
+ Assert(get_csnlog_status());
csn = TransactionIdGetXidCSN(xid);
@@ -238,6 +241,14 @@ XidInvisibleInCSNSnapshot(TransactionId xid, Snapshot snapshot)
/* It is bootstrap or frozen transaction */
return false;
}
+ else if(CSNIsUnclear(csn))
+ {
+ /*
+ * Some xid can not figure out csn because of snapshot switch,
+ * and we can follow xid-base result.
+ */
+ return true;
+ }
else
{
/* It is aborted or in-progress */
@@ -277,7 +288,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 +321,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 +359,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 +402,42 @@ get_last_log_wal_csn(void)
}
/*
- * 'xmin_for_csn' for when turn xid-snapshot to csn-snapshot
+ * Rely on different value of enable and same we have different action.
*/
void
-set_xmin_for_csn(void)
+prepare_csn_env(bool enable, bool same, TransactionId *xmin_for_csn_in_control)
{
- csnState->xmin_for_csn = XidFromFullTransactionId(ShmemVariableCache->nextFullXid);
+ TransactionId nextxid = InvalidTransactionId;
+
+ if(enable)
+ {
+ if(same)
+ {
+ /*
+ * Database startup with no enable_csn_snapshot change and value is true,
+ * it can just transmit xmin_for_csn from pg_control to csnState->xmin_for_csn.
+ */
+ csnState->xmin_for_csn = *xmin_for_csn_in_control;
+ }
+ else
+ {
+ /*
+ * Last time database is xid-base snapshot, and now startup as csn-base snapshot,
+ * we should redifine a xmin_for_csn, and store it in both pg_control and
+ * csnState->xmin_for_csn.
+ */
+ nextxid = XidFromFullTransactionId(ShmemVariableCache->nextFullXid);
+ csnState->xmin_for_csn = nextxid;
+ *xmin_for_csn_in_control = nextxid;
+ /* produce the csnlog segment we want now and seek to current page */
+ ActivateCSNlog();
+ }
+ }
+ else
+ {
+ /* Try to drop all csnlog seg */
+ DeactivateCSNlog();
+ /* Clear xmin_for_csn in pg_control because we are xid-base snaposhot now. */
+ *xmin_for_csn_in_control = InvalidTransactionId;
+ }
}
diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index dc2e9ae874..32f1e614b4 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;
@@ -4609,6 +4610,7 @@ InitControlFile(uint64 sysidentifier)
ControlFile->track_commit_timestamp = track_commit_timestamp;
ControlFile->enable_csn_snapshot = enable_csn_snapshot;
ControlFile->data_checksum_version = bootstrap_data_checksum_version;
+ ControlFile->xmin_for_csn = InvalidTransactionId;
}
static void
@@ -6805,6 +6807,9 @@ StartupXLOG(void)
if (ControlFile->track_commit_timestamp)
StartupCommitTs();
+ if(ControlFile->enable_csn_snapshot)
+ StartupCSN();
+
/*
* Recover knowledge about replay progress of known replication partners.
*/
@@ -7921,6 +7926,7 @@ StartupXLOG(void)
* commit timestamp.
*/
CompleteCommitTsInitialization();
+ CompleteCSNInitialization();
/*
* All done with end-of-recovery actions.
@@ -9727,6 +9733,9 @@ XLogRestorePoint(const char *rpName)
static void
XLogReportParameters(void)
{
+ TransactionId xmin_for_csn = InvalidTransactionId;
+
+ xmin_for_csn = ControlFile->xmin_for_csn;
if (wal_level != ControlFile->wal_level ||
wal_log_hints != ControlFile->wal_log_hints ||
MaxConnections != ControlFile->MaxConnections ||
@@ -9766,11 +9775,12 @@ XLogReportParameters(void)
XLogFlush(recptr);
}
+ prepare_csn_env(enable_csn_snapshot,
+ enable_csn_snapshot == ControlFile->enable_csn_snapshot,
+ &xmin_for_csn);
LWLockAcquire(ControlFileLock, LW_EXCLUSIVE);
- if (enable_csn_snapshot != ControlFile->enable_csn_snapshot)
- set_xmin_for_csn();
-
+ ControlFile->xmin_for_csn = xmin_for_csn;
ControlFile->MaxConnections = MaxConnections;
ControlFile->max_worker_processes = max_worker_processes;
ControlFile->max_wal_senders = max_wal_senders;
@@ -9784,6 +9794,16 @@ XLogReportParameters(void)
LWLockRelease(ControlFileLock);
}
+ else
+ {
+ /*
+ * When no GUC change, but for xmin_for_csn it should transmit the xmin_for_csn
+ * from pg_control to csnState->xmin_for_csn. Or it will cause issue when prepare
+ * transaction exixts and with 'xid-snapshot start -> csn-snapshot start ->
+ * csn-snapshot start' sequence.
+ */
+ prepare_csn_env(enable_csn_snapshot, true, &xmin_for_csn);
+ }
}
/*
@@ -10212,6 +10232,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 9283021c7b..e326b431c2 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 1e9bcc7aee..4527dda0ee 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -1160,7 +1160,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 e430e33c7b..8b02ec8200 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)
# - Primary Server -
diff --git a/src/backend/utils/time/snapmgr.c b/src/backend/utils/time/snapmgr.c
index 5fa195b913..2a31366930 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 d7feca0b38..5d508caef5 100644
--- a/src/include/access/csn_log.h
+++ b/src/include/access/csn_log.h
@@ -67,6 +67,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, bool same, TransactionId *xmin_for_csn_in_control);
+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/include/access/csn_snapshot.h b/src/include/access/csn_snapshot.h
index 1894586204..a768f054f5 100644
--- a/src/include/access/csn_snapshot.h
+++ b/src/include/access/csn_snapshot.h
@@ -24,17 +24,19 @@
*/
typedef pg_atomic_uint64 CSN_atomic;
-#define InProgressXidCSN UINT64CONST(0x0)
-#define AbortedXidCSN UINT64CONST(0x1)
+#define InProgressXidCSN UINT64CONST(0x0)
+#define AbortedXidCSN UINT64CONST(0x1)
#define FrozenXidCSN UINT64CONST(0x2)
-#define InDoubtXidCSN UINT64CONST(0x3)
-#define FirstNormalXidCSN UINT64CONST(0x4)
+#define InDoubtXidCSN UINT64CONST(0x3)
+#define UnclearCSN UINT64CONST(0x4)
+#define FirstNormalXidCSN UINT64CONST(0x5)
-#define XidCSNIsInProgress(csn) ((csn) == InProgressXidCSN)
+#define XidCSNIsInProgress(csn) ((csn) == InProgressXidCSN)
#define XidCSNIsAborted(csn) ((csn) == AbortedXidCSN)
-#define XidCSNIsFrozen(csn) ((csn) == FrozenXidCSN)
+#define XidCSNIsFrozen(csn) ((csn) == FrozenXidCSN)
#define XidCSNIsInDoubt(csn) ((csn) == InDoubtXidCSN)
-#define XidCSNIsNormal(csn) ((csn) >= FirstNormalXidCSN)
+#define CSNIsUnclear(csn) ((csn) == UnclearCSN)
+#define XidCSNIsNormal(csn) ((csn) >= FirstNormalXidCSN)
diff --git a/src/include/catalog/pg_control.h b/src/include/catalog/pg_control.h
index 9e5d4b0fc0..3ff7371a92 100644
--- a/src/include/catalog/pg_control.h
+++ b/src/include/catalog/pg_control.h
@@ -183,6 +183,12 @@ typedef struct ControlFileData
bool track_commit_timestamp;
bool enable_csn_snapshot;
+ /*
+ * Used to record a xmin when database startup with a snapshot-switch to csn snapshot,
+ * and will hold the value until it switch to xid-snapshot.
+ */
+ TransactionId xmin_for_csn;
+
/*
* This data is used to check for hardware-architecture compatibility of
* the database and the backend executable. We need not check endianness
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 da2e5aa38b..cc169a1999 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_hashagg | on
enable_hashjoin | on