]> granicus.if.org Git - postgresql/commitdiff
Fix commit_ts for standby
authorAlvaro Herrera <alvherre@alvh.no-ip.org>
Thu, 1 Oct 2015 18:06:55 +0000 (15:06 -0300)
committerAlvaro Herrera <alvherre@alvh.no-ip.org>
Thu, 1 Oct 2015 18:06:55 +0000 (15:06 -0300)
Module initialization was still not completely correct after commit
6b61955135e9, per crash report from Takashi Ohnishi.  To fix, instead of
trying to monkey around with the value of the GUC setting directly, add
a separate boolean flag that enables the feature on a standby, but only
for the startup (recovery) process, when it sees that its master server
has the feature enabled.
Discussion: http://www.postgresql.org/message-id/ca44c6c7f9314868bdc521aea4f77cbf@MP-MSGSS-MBX004.msg.nttdata.co.jp

Also change the deactivation routine to delete all segment files rather
than leaving the last one around.  (This doesn't need separate
WAL-logging, because on recovery we execute the same deactivation
routine anyway.)

In passing, clean up the code structure somewhat, particularly so that
xlog.c doesn't know so much about when to activate/deactivate the
feature.

Thanks to Fujii Masao for testing and Petr JelĂ­nek for off-list discussion.

Back-patch to 9.5, where commit_ts was introduced.

src/backend/access/transam/commit_ts.c
src/backend/access/transam/twophase.c
src/backend/access/transam/xact.c
src/backend/access/transam/xlog.c
src/include/access/commit_ts.h

index 78090c5f09c798866735b005fe77607b9545f883..79ca04a6eafd285d44e2db6e6d57fc6a54990ed0 100644 (file)
@@ -93,6 +93,14 @@ CommitTimestampShared *commitTsShared;
 /* GUC variable */
 bool           track_commit_timestamp;
 
+/*
+ * When this is set, commit_ts is force-enabled during recovery.  This is so
+ * that a standby can replay WAL records coming from a master with the setting
+ * enabled.  (Note that this doesn't enable SQL access to the data; it's
+ * effectively write-only until the GUC itself is enabled.)
+ */
+static bool            enable_during_recovery;
+
 static void SetXidCommitTsInPage(TransactionId xid, int nsubxids,
                                         TransactionId *subxids, TimestampTz ts,
                                         RepOriginId nodeid, int pageno);
@@ -100,6 +108,8 @@ static void TransactionIdSetCommitTs(TransactionId xid, TimestampTz ts,
                                                 RepOriginId nodeid, int slotno);
 static int     ZeroCommitTsPage(int pageno, bool writeXlog);
 static bool CommitTsPagePrecedes(int page1, int page2);
+static void ActivateCommitTs(void);
+static void DeactivateCommitTs(bool do_wal);
 static void WriteZeroPageXlogRec(int pageno);
 static void WriteTruncateXlogRec(int pageno);
 static void WriteSetTimestampXlogRec(TransactionId mainxid, int nsubxids,
@@ -122,10 +132,6 @@ static void WriteSetTimestampXlogRec(TransactionId mainxid, int nsubxids,
  * subtrans implementation changes in the future, we might want to revisit the
  * decision of storing timestamp info for each subxid.
  *
- * The replaying_xlog parameter indicates whether the module should execute
- * its write even if the feature is nominally disabled, because we're replaying
- * a record generated from a master where the feature is enabled.
- *
  * The write_xlog parameter tells us whether to include an XLog record of this
  * or not.  Normally, this is called from transaction commit routines (both
  * normal and prepared) and the information will be stored in the transaction
@@ -136,18 +142,17 @@ static void WriteSetTimestampXlogRec(TransactionId mainxid, int nsubxids,
 void
 TransactionTreeSetCommitTsData(TransactionId xid, int nsubxids,
                                                           TransactionId *subxids, TimestampTz timestamp,
-                                                          RepOriginId nodeid,
-                                                          bool replaying_xlog, bool write_xlog)
+                                                          RepOriginId nodeid, bool write_xlog)
 {
        int                     i;
        TransactionId headxid;
        TransactionId newestXact;
 
-       /* We'd better not try to write xlog during replay */
-       Assert(!(write_xlog && replaying_xlog));
-
-       /* No-op if feature not enabled, unless replaying WAL */
-       if (!track_commit_timestamp && !replaying_xlog)
+       /*
+        * No-op if the module is not enabled, but allow writes in a standby
+        * during recovery.
+        */
+       if (!track_commit_timestamp && !enable_during_recovery)
                return;
 
        /*
@@ -534,40 +539,61 @@ ZeroCommitTsPage(int pageno, bool writeXlog)
 /*
  * This must be called ONCE during postmaster or standalone-backend startup,
  * after StartupXLOG has initialized ShmemVariableCache->nextXid.
+ *
+ * Caller may choose to enable the feature even when it is turned off in the
+ * configuration.
  */
 void
-StartupCommitTs(void)
+StartupCommitTs(bool force_enable)
 {
-       TransactionId xid = ShmemVariableCache->nextXid;
-       int                     pageno = TransactionIdToCTsPage(xid);
-
-       if (track_commit_timestamp)
-       {
-               ActivateCommitTs();
-               return;
-       }
-
-       LWLockAcquire(CommitTsControlLock, LW_EXCLUSIVE);
-
        /*
-        * Initialize our idea of the latest page number.
+        * If the module is not enabled, there's nothing to do here.  The module
+        * could still be activated from elsewhere.
         */
-       CommitTsCtl->shared->latest_page_number = pageno;
-
-       LWLockRelease(CommitTsControlLock);
+       if (track_commit_timestamp || force_enable)
+               ActivateCommitTs();
 }
 
 /*
  * This must be called ONCE during postmaster or standalone-backend startup,
- * when commit timestamp is enabled, after recovery has finished.
+ * after recovery has finished.
  */
 void
 CompleteCommitTsInitialization(void)
 {
+       /*
+        * If the feature is not enabled, turn it off for good.  This also removes
+        * any leftover data.
+        */
        if (!track_commit_timestamp)
                DeactivateCommitTs(true);
 }
 
+/*
+ * Activate or deactivate CommitTs' upon reception of a XLOG_PARAMETER_CHANGE
+ * XLog record in a standby.
+ */
+void
+CommitTsParameterChange(bool newvalue, bool oldvalue)
+{
+       /*
+        * If the commit_ts module is disabled in this server and we get word from
+        * the master server that it is enabled there, activate it so that we can
+        * replay future WAL records involving it; also mark it as active on
+        * pg_control.  If the old value was already set, we already did this, so
+        * don't do anything.
+        *
+        * If the module is disabled in the master, disable it here too.
+        */
+       if (newvalue)
+       {
+               if (!track_commit_timestamp && !oldvalue)
+                       ActivateCommitTs();
+       }
+       else if (oldvalue)
+               DeactivateCommitTs(false);
+}
+
 /*
  * Activate this module whenever necessary.
  *             This must happen during postmaster or standalong-backend startup,
@@ -584,7 +610,7 @@ CompleteCommitTsInitialization(void)
  * running with this module disabled for a while and thus might have skipped
  * the normal creation point.
  */
-void
+static void
 ActivateCommitTs(void)
 {
        TransactionId xid = ShmemVariableCache->nextXid;
@@ -629,6 +655,9 @@ ActivateCommitTs(void)
                Assert(!CommitTsCtl->shared->page_dirty[slotno]);
                LWLockRelease(CommitTsControlLock);
        }
+
+       /* We can now replay xlog records from this module */
+       enable_during_recovery = true;
 }
 
 /*
@@ -641,7 +670,7 @@ ActivateCommitTs(void)
  * Resets CommitTs into invalid state to make sure we don't hand back
  * possibly-invalid data; also removes segments of old data.
  */
-void
+static void
 DeactivateCommitTs(bool do_wal)
 {
        TransactionId xid = ShmemVariableCache->nextXid;
@@ -659,7 +688,18 @@ DeactivateCommitTs(bool do_wal)
        ShmemVariableCache->newestCommitTs = InvalidTransactionId;
        LWLockRelease(CommitTsLock);
 
-       TruncateCommitTs(ReadNewTransactionId(), do_wal);
+       /*
+        * Remove *all* files.  This is necessary so that there are no leftover
+        * files; in the case where this feature is later enabled after running
+        * with it disabled for some time there may be a gap in the file sequence.
+        * (We can probably tolerate out-of-sequence files, as they are going to
+        * be overwritten anyway when we wrap around, but it seems better to be
+        * tidy.)
+        */
+       (void) SlruScanDirectory(CommitTsCtl, SlruScanDirCbDeleteAll, NULL);
+
+       /* No longer enabled on recovery */
+       enable_during_recovery = false;
 }
 
 /*
@@ -699,7 +739,7 @@ ExtendCommitTs(TransactionId newestXact)
        int                     pageno;
 
        /* nothing to do if module not enabled */
-       if (!track_commit_timestamp)
+       if (!track_commit_timestamp && !enable_during_recovery)
                return;
 
        /*
@@ -916,8 +956,7 @@ commit_ts_redo(XLogReaderState *record)
                        subxids = NULL;
 
                TransactionTreeSetCommitTsData(setts->mainxid, nsubxids, subxids,
-                                                                          setts->timestamp, setts->nodeid, false,
-                                                                          true);
+                                                                          setts->timestamp, setts->nodeid, true);
                if (subxids)
                        pfree(subxids);
        }
index e005cc558abdc719b6a1be18c807e7bdb7187e2d..8c47e0f707698de7253a39f6c2a2be9c7d5bcb55 100644 (file)
@@ -2131,7 +2131,7 @@ RecordTransactionCommitPrepared(TransactionId xid,
 
        TransactionTreeSetCommitTsData(xid, nchildren, children,
                                                                   replorigin_session_origin_timestamp,
-                                                                  replorigin_session_origin, false, false);
+                                                                  replorigin_session_origin, false);
 
        /*
         * We don't currently try to sleep before flush here ... nor is there any
index 8f56a44d06e2c709f7180dcabcaa1a037f977cb3..e8aafbac508e72031d3bde24de0b4eba171cc7fb 100644 (file)
@@ -1237,8 +1237,7 @@ RecordTransactionCommit(void)
 
                TransactionTreeSetCommitTsData(xid, nchildren, children,
                                                                           replorigin_session_origin_timestamp,
-                                                                          replorigin_session_origin,
-                                                                          false, false);
+                                                                          replorigin_session_origin, false);
        }
 
        /*
@@ -5333,8 +5332,7 @@ xact_redo_commit(xl_xact_parsed_commit *parsed,
 
        /* Set the transaction commit timestamp and metadata */
        TransactionTreeSetCommitTsData(xid, parsed->nsubxacts, parsed->subxacts,
-                                                                  commit_time, origin_id,
-                                                                  true, false);
+                                                                  commit_time, origin_id, false);
 
        if (standbyState == STANDBY_DISABLED)
        {
index 0266d61bbdb7b74eb4274d72cb8bf13d403f2fbe..08d16823ed105374023c405dcfd8afcb3bd83bcf 100644 (file)
@@ -6567,7 +6567,7 @@ StartupXLOG(void)
                         * maintained during recovery and need not be started yet.
                         */
                        StartupCLOG();
-                       StartupCommitTs();
+                       StartupCommitTs(ControlFile->track_commit_timestamp);
                        StartupSUBTRANS(oldestActiveXID);
 
                        /*
@@ -7336,7 +7336,7 @@ StartupXLOG(void)
        if (standbyState == STANDBY_DISABLED)
        {
                StartupCLOG();
-               StartupCommitTs();
+               StartupCommitTs(false);
                StartupSUBTRANS(oldestActiveXID);
        }
 
@@ -9456,25 +9456,9 @@ xlog_redo(XLogReaderState *record)
                        ControlFile->minRecoveryPointTLI = ThisTimeLineID;
                }
 
-               /*
-                * Update the commit timestamp tracking. If there was a change it
-                * needs to be activated or deactivated accordingly.
-                */
-               if (track_commit_timestamp != xlrec.track_commit_timestamp)
-               {
-                       track_commit_timestamp = xlrec.track_commit_timestamp;
-                       ControlFile->track_commit_timestamp = track_commit_timestamp;
-                       if (track_commit_timestamp)
-                               ActivateCommitTs();
-                       else
-
-                               /*
-                                * We can't create a new WAL record here, but that's OK as
-                                * master did the WAL logging already and we will replay the
-                                * record from master in case we crash.
-                                */
-                               DeactivateCommitTs(false);
-               }
+               CommitTsParameterChange(xlrec.track_commit_timestamp,
+                                                               ControlFile->track_commit_timestamp);
+               ControlFile->track_commit_timestamp = xlrec.track_commit_timestamp;
 
                UpdateControlFile();
                LWLockRelease(ControlFileLock);
index dc865d1bc3d389acd095b9163eb8e27a62ef459a..1b95b5837ea31dcc6433d371a1fb39b384b374c2 100644 (file)
@@ -24,8 +24,7 @@ extern bool check_track_commit_timestamp(bool *newval, void **extra,
 
 extern void TransactionTreeSetCommitTsData(TransactionId xid, int nsubxids,
                                                           TransactionId *subxids, TimestampTz timestamp,
-                                                          RepOriginId nodeid,
-                                                          bool replaying_xlog, bool write_xlog);
+                                                          RepOriginId nodeid, bool write_xlog);
 extern bool TransactionIdGetCommitTsData(TransactionId xid,
                                                         TimestampTz *ts, RepOriginId *nodeid);
 extern TransactionId GetLatestCommitTsData(TimestampTz *ts,
@@ -35,9 +34,8 @@ extern Size CommitTsShmemBuffers(void);
 extern Size CommitTsShmemSize(void);
 extern void CommitTsShmemInit(void);
 extern void BootStrapCommitTs(void);
-extern void StartupCommitTs(void);
-extern void ActivateCommitTs(void);
-extern void DeactivateCommitTs(bool do_wal);
+extern void StartupCommitTs(bool force_enable);
+extern void CommitTsParameterChange(bool xlrecvalue, bool pgcontrolvalue);
 extern void CompleteCommitTsInitialization(void);
 extern void ShutdownCommitTs(void);
 extern void CheckPointCommitTs(void);