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