Skip to content

Commit 4f3924d

Browse files
committed
Keep CommitTs module in sync in standby and master
We allow this module to be turned off on restarts, so a restart time check is enough to activate or deactivate the module; however, if there is a standby replaying WAL emitted from a master which is restarted, but the standby isn't, the state in the standby becomes inconsistent and can easily be crashed. Fix by activating and deactivating the module during WAL replay on parameter change as well as on system start. Problem reported by Fujii Masao in http://www.postgresql.org/message-id/CAHGQGwFhJ3CnHo1CELEfay18yg_RA-XZT-7D8NuWUoYSZ90r4Q@mail.gmail.com Author: Petr Jelínek
1 parent e3f1c24 commit 4f3924d

File tree

4 files changed

+99
-24
lines changed

4 files changed

+99
-24
lines changed

src/backend/access/transam/commit_ts.c

+60-21
Original file line numberDiff line numberDiff line change
@@ -557,6 +557,12 @@ StartupCommitTs(void)
557557
TransactionId xid = ShmemVariableCache->nextXid;
558558
int pageno = TransactionIdToCTsPage(xid);
559559

560+
if (track_commit_timestamp)
561+
{
562+
ActivateCommitTs();
563+
return;
564+
}
565+
560566
LWLockAcquire(CommitTsControlLock, LW_EXCLUSIVE);
561567

562568
/*
@@ -569,16 +575,33 @@ StartupCommitTs(void)
569575

570576
/*
571577
* This must be called ONCE during postmaster or standalone-backend startup,
572-
* when commit timestamp is enabled. Must be called after recovery has
573-
* finished.
578+
* when commit timestamp is enabled, after recovery has finished.
579+
*/
580+
void
581+
CompleteCommitTsInitialization(void)
582+
{
583+
if (!track_commit_timestamp)
584+
DeactivateCommitTs(true);
585+
}
586+
587+
/*
588+
* Activate this module whenever necessary.
589+
* This must happen during postmaster or standalong-backend startup,
590+
* or during WAL replay anytime the track_commit_timestamp setting is
591+
* changed in the master.
592+
*
593+
* The reason why this SLRU needs separate activation/deactivation functions is
594+
* that it can be enabled/disabled during start and the activation/deactivation
595+
* on master is propagated to slave via replay. Other SLRUs don't have this
596+
* property and they can be just initialized during normal startup.
574597
*
575598
* This is in charge of creating the currently active segment, if it's not
576599
* already there. The reason for this is that the server might have been
577600
* running with this module disabled for a while and thus might have skipped
578601
* the normal creation point.
579602
*/
580603
void
581-
CompleteCommitTsInitialization(void)
604+
ActivateCommitTs(void)
582605
{
583606
TransactionId xid = ShmemVariableCache->nextXid;
584607
int pageno = TransactionIdToCTsPage(xid);
@@ -590,22 +613,6 @@ CompleteCommitTsInitialization(void)
590613
CommitTsCtl->shared->latest_page_number = pageno;
591614
LWLockRelease(CommitTsControlLock);
592615

593-
/*
594-
* If this module is not currently enabled, make sure we don't hand back
595-
* possibly-invalid data; also remove segments of old data.
596-
*/
597-
if (!track_commit_timestamp)
598-
{
599-
LWLockAcquire(CommitTsLock, LW_EXCLUSIVE);
600-
ShmemVariableCache->oldestCommitTs = InvalidTransactionId;
601-
ShmemVariableCache->newestCommitTs = InvalidTransactionId;
602-
LWLockRelease(CommitTsLock);
603-
604-
TruncateCommitTs(ReadNewTransactionId());
605-
606-
return;
607-
}
608-
609616
/*
610617
* If CommitTs is enabled, but it wasn't in the previous server run, we
611618
* need to set the oldest and newest values to the next Xid; that way, we
@@ -640,6 +647,37 @@ CompleteCommitTsInitialization(void)
640647
}
641648
}
642649

650+
/*
651+
* Deactivate this module.
652+
*
653+
* This must be called when the track_commit_timestamp parameter is turned off.
654+
* This happens during postmaster or standalone-backend startup, or during WAL
655+
* replay.
656+
*
657+
* Resets CommitTs into invalid state to make sure we don't hand back
658+
* possibly-invalid data; also removes segments of old data.
659+
*/
660+
void
661+
DeactivateCommitTs(bool do_wal)
662+
{
663+
TransactionId xid = ShmemVariableCache->nextXid;
664+
int pageno = TransactionIdToCTsPage(xid);
665+
666+
/*
667+
* Re-Initialize our idea of the latest page number.
668+
*/
669+
LWLockAcquire(CommitTsControlLock, LW_EXCLUSIVE);
670+
CommitTsCtl->shared->latest_page_number = pageno;
671+
LWLockRelease(CommitTsControlLock);
672+
673+
LWLockAcquire(CommitTsLock, LW_EXCLUSIVE);
674+
ShmemVariableCache->oldestCommitTs = InvalidTransactionId;
675+
ShmemVariableCache->newestCommitTs = InvalidTransactionId;
676+
LWLockRelease(CommitTsLock);
677+
678+
TruncateCommitTs(ReadNewTransactionId(), do_wal);
679+
}
680+
643681
/*
644682
* This must be called ONCE during postmaster or standalone-backend shutdown
645683
*/
@@ -705,7 +743,7 @@ ExtendCommitTs(TransactionId newestXact)
705743
* Note that we don't need to flush XLOG here.
706744
*/
707745
void
708-
TruncateCommitTs(TransactionId oldestXact)
746+
TruncateCommitTs(TransactionId oldestXact, bool do_wal)
709747
{
710748
int cutoffPage;
711749

@@ -721,7 +759,8 @@ TruncateCommitTs(TransactionId oldestXact)
721759
return; /* nothing to remove */
722760

723761
/* Write XLOG record */
724-
WriteTruncateXlogRec(cutoffPage);
762+
if (do_wal)
763+
WriteTruncateXlogRec(cutoffPage);
725764

726765
/* Now we can remove the old CommitTs segment(s) */
727766
SimpleLruTruncate(CommitTsCtl, cutoffPage);

src/backend/access/transam/xlog.c

+35-1
Original file line numberDiff line numberDiff line change
@@ -5688,6 +5688,19 @@ do { \
56885688
minValue))); \
56895689
} while(0)
56905690

5691+
#define RecoveryRequiresBoolParameter(param_name, currValue, masterValue) \
5692+
do { \
5693+
bool _currValue = (currValue); \
5694+
bool _masterValue = (masterValue); \
5695+
if (_currValue != _masterValue) \
5696+
ereport(ERROR, \
5697+
(errcode(ERRCODE_INVALID_PARAMETER_VALUE), \
5698+
errmsg("hot standby is not possible because it requires \"%s\" to be same on master and standby (master has \"%s\", standby has \"%s\")", \
5699+
param_name, \
5700+
_masterValue ? "true" : "false", \
5701+
_currValue ? "true" : "false"))); \
5702+
} while(0)
5703+
56915704
/*
56925705
* Check to see if required parameters are set high enough on this server
56935706
* for various aspects of recovery operation.
@@ -5730,6 +5743,9 @@ CheckRequiredParameterValues(void)
57305743
RecoveryRequiresIntParameter("max_locks_per_transaction",
57315744
max_locks_per_xact,
57325745
ControlFile->max_locks_per_xact);
5746+
RecoveryRequiresBoolParameter("track_commit_timestamp",
5747+
track_commit_timestamp,
5748+
ControlFile->track_commit_timestamp);
57335749
}
57345750
}
57355751

@@ -9118,7 +9134,6 @@ xlog_redo(XLogReaderState *record)
91189134
ControlFile->max_locks_per_xact = xlrec.max_locks_per_xact;
91199135
ControlFile->wal_level = xlrec.wal_level;
91209136
ControlFile->wal_log_hints = xlrec.wal_log_hints;
9121-
ControlFile->track_commit_timestamp = xlrec.track_commit_timestamp;
91229137

91239138
/*
91249139
* Update minRecoveryPoint to ensure that if recovery is aborted, we
@@ -9136,6 +9151,25 @@ xlog_redo(XLogReaderState *record)
91369151
ControlFile->minRecoveryPointTLI = ThisTimeLineID;
91379152
}
91389153

9154+
/*
9155+
* Update the commit timestamp tracking. If there was a change
9156+
* it needs to be activated or deactivated accordingly.
9157+
*/
9158+
if (track_commit_timestamp != xlrec.track_commit_timestamp)
9159+
{
9160+
track_commit_timestamp = xlrec.track_commit_timestamp;
9161+
ControlFile->track_commit_timestamp = track_commit_timestamp;
9162+
if (track_commit_timestamp)
9163+
ActivateCommitTs();
9164+
else
9165+
/*
9166+
* We can't create a new WAL record here, but that's OK as
9167+
* master did the WAL logging already and we will replay the
9168+
* record from master in case we crash.
9169+
*/
9170+
DeactivateCommitTs(false);
9171+
}
9172+
91399173
UpdateControlFile();
91409174
LWLockRelease(ControlFileLock);
91419175

src/backend/commands/vacuum.c

+1-1
Original file line numberDiff line numberDiff line change
@@ -1087,7 +1087,7 @@ vac_truncate_clog(TransactionId frozenXID,
10871087
* checkpoint.
10881088
*/
10891089
TruncateCLOG(frozenXID);
1090-
TruncateCommitTs(frozenXID);
1090+
TruncateCommitTs(frozenXID, true);
10911091

10921092
/*
10931093
* Update the wrap limit for GetNewTransactionId and creation of new

src/include/access/commit_ts.h

+3-1
Original file line numberDiff line numberDiff line change
@@ -39,11 +39,13 @@ extern Size CommitTsShmemSize(void);
3939
extern void CommitTsShmemInit(void);
4040
extern void BootStrapCommitTs(void);
4141
extern void StartupCommitTs(void);
42+
extern void ActivateCommitTs(void);
43+
extern void DeactivateCommitTs(bool do_wal);
4244
extern void CompleteCommitTsInitialization(void);
4345
extern void ShutdownCommitTs(void);
4446
extern void CheckPointCommitTs(void);
4547
extern void ExtendCommitTs(TransactionId newestXact);
46-
extern void TruncateCommitTs(TransactionId oldestXact);
48+
extern void TruncateCommitTs(TransactionId oldestXact, bool do_wal);
4749
extern void SetCommitTsLimit(TransactionId oldestXact,
4850
TransactionId newestXact);
4951
extern void AdvanceOldestCommitTs(TransactionId oldestXact);

0 commit comments

Comments
 (0)