Logging running transactions every 15 seconds.
authorRobert Haas <rhaas@postgresql.org>
Wed, 15 Jan 2014 17:41:20 +0000 (12:41 -0500)
committerRobert Haas <rhaas@postgresql.org>
Wed, 15 Jan 2014 17:41:20 +0000 (12:41 -0500)
Previously, we did this just once per checkpoint, but that could make
Hot Standby take a long time to initialize.  To avoid busying an
otherwise-idle system, we don't do this if no WAL has been written
since we did it last.

Andres Freund

src/backend/postmaster/bgwriter.c
src/backend/storage/ipc/standby.c
src/include/storage/standby.h

index 6f5d937af9f4544b51d95e1b5c8f6df3f2034a22..1ec66c221fbd9dff5901a19d03a00382b78a6150 100644 (file)
 #include "storage/shmem.h"
 #include "storage/smgr.h"
 #include "storage/spin.h"
+#include "storage/standby.h"
 #include "utils/guc.h"
 #include "utils/memutils.h"
 #include "utils/resowner.h"
+#include "utils/timestamp.h"
 
 
 /*
@@ -70,6 +72,20 @@ int          BgWriterDelay = 200;
  */
 #define HIBERNATE_FACTOR           50
 
+/*
+ * Interval in which standby snapshots are logged into the WAL stream, in
+ * milliseconds.
+ */
+#define LOG_SNAPSHOT_INTERVAL_MS 15000
+
+/*
+ * LSN and timestamp at which we last issued a LogStandbySnapshot(), to avoid
+ * doing so too often or repeatedly if there has been no other write activity
+ * in the system.
+ */
+static TimestampTz last_snapshot_ts;
+static XLogRecPtr last_snapshot_lsn = InvalidXLogRecPtr;
+
 /*
  * Flags set by interrupt handlers for later service in the main loop.
  */
@@ -141,6 +157,12 @@ BackgroundWriterMain(void)
     */
    CurrentResourceOwner = ResourceOwnerCreate(NULL, "Background Writer");
 
+   /*
+    * We just started, assume there has been either a shutdown or
+    * end-of-recovery snapshot.
+    */
+   last_snapshot_ts = GetCurrentTimestamp();
+
    /*
     * Create a memory context that we will do all our work in.  We do this so
     * that we can reset the context during error recovery and thereby avoid
@@ -275,6 +297,46 @@ BackgroundWriterMain(void)
            smgrcloseall();
        }
 
+       /*
+        * Log a new xl_running_xacts every now and then so replication can get
+        * into a consistent state faster (think of suboverflowed snapshots)
+        * and clean up resources (locks, KnownXids*) more frequently. The
+        * costs of this are relatively low, so doing it 4 times
+        * (LOG_SNAPSHOT_INTERVAL_MS) a minute seems fine.
+        *
+        * We assume the interval for writing xl_running_xacts is
+        * significantly bigger than BgWriterDelay, so we don't complicate the
+        * overall timeout handling but just assume we're going to get called
+        * often enough even if hibernation mode is active. It's not that
+        * important that log_snap_interval_ms is met strictly. To make sure
+        * we're not waking the disk up unneccesarily on an idle system we
+        * check whether there has been any WAL inserted since the last time
+        * we've logged a running xacts.
+        *
+        * We do this logging in the bgwriter as its the only process thats
+        * run regularly and returns to its mainloop all the
+        * time. E.g. Checkpointer, when active, is barely ever in its
+        * mainloop and thus makes it hard to log regularly.
+        */
+       if (XLogStandbyInfoActive() && !RecoveryInProgress())
+       {
+           TimestampTz timeout = 0;
+           TimestampTz now = GetCurrentTimestamp();
+           timeout = TimestampTzPlusMilliseconds(last_snapshot_ts,
+                                                 LOG_SNAPSHOT_INTERVAL_MS);
+
+           /*
+            * only log if enough time has passed and some xlog record has been
+            * inserted.
+            */
+           if (now >= timeout &&
+               last_snapshot_lsn != GetXLogInsertRecPtr())
+           {
+               last_snapshot_lsn = LogStandbySnapshot();
+               last_snapshot_ts = now;
+           }
+       }
+
        /*
         * Sleep until we are signaled or BgWriterDelay has elapsed.
         *
index 437d64b7873f5d89494f16de71741756660b703d..fb5f18edfc74b80142c4d40928675c267857396a 100644 (file)
@@ -42,7 +42,7 @@ static void ResolveRecoveryConflictWithVirtualXIDs(VirtualTransactionId *waitlis
                                       ProcSignalReason reason);
 static void ResolveRecoveryConflictWithLock(Oid dbOid, Oid relOid);
 static void SendRecoveryConflictWithBufferPin(ProcSignalReason reason);
-static void LogCurrentRunningXacts(RunningTransactions CurrRunningXacts);
+static XLogRecPtr LogCurrentRunningXacts(RunningTransactions CurrRunningXacts);
 static void LogAccessExclusiveLocks(int nlocks, xl_standby_lock *locks);
 
 
@@ -853,10 +853,13 @@ standby_redo(XLogRecPtr lsn, XLogRecord *record)
  * currently running xids, performed by StandbyReleaseOldLocks().
  * Zero xids should no longer be possible, but we may be replaying WAL
  * from a time when they were possible.
+ *
+ * Returns the RecPtr of the last inserted record.
  */
-void
+XLogRecPtr
 LogStandbySnapshot(void)
 {
+   XLogRecPtr recptr;
    RunningTransactions running;
    xl_standby_lock *locks;
    int         nlocks;
@@ -876,9 +879,12 @@ LogStandbySnapshot(void)
     * record we write, because standby will open up when it sees this.
     */
    running = GetRunningTransactionData();
-   LogCurrentRunningXacts(running);
+   recptr = LogCurrentRunningXacts(running);
+
    /* GetRunningTransactionData() acquired XidGenLock, we must release it */
    LWLockRelease(XidGenLock);
+
+   return recptr;
 }
 
 /*
@@ -889,7 +895,7 @@ LogStandbySnapshot(void)
  * is a contiguous chunk of memory and never exists fully until it is
  * assembled in WAL.
  */
-static void
+static XLogRecPtr
 LogCurrentRunningXacts(RunningTransactions CurrRunningXacts)
 {
    xl_running_xacts xlrec;
@@ -939,6 +945,19 @@ LogCurrentRunningXacts(RunningTransactions CurrRunningXacts)
             CurrRunningXacts->oldestRunningXid,
             CurrRunningXacts->latestCompletedXid,
             CurrRunningXacts->nextXid);
+
+   /*
+    * Ensure running_xacts information is synced to disk not too far in the
+    * future. We don't want to stall anything though (i.e. use XLogFlush()),
+    * so we let the wal writer do it during normal
+    * operation. XLogSetAsyncXactLSN() conveniently will mark the LSN as
+    * to-be-synced and nudge the WALWriter into action if sleeping. Check
+    * XLogBackgroundFlush() for details why a record might not be flushed
+    * without it.
+    */
+   XLogSetAsyncXactLSN(recptr);
+
+   return recptr;
 }
 
 /*
index 1eb10c454f2c5991c28e7dfd85b132146b51442a..89ab704699ba32dedca81d4d0ddfba6ef77f1318 100644 (file)
@@ -113,6 +113,6 @@ typedef RunningTransactionsData *RunningTransactions;
 extern void LogAccessExclusiveLock(Oid dbOid, Oid relOid);
 extern void LogAccessExclusiveLockPrepare(void);
 
-extern void LogStandbySnapshot(void);
+extern XLogRecPtr LogStandbySnapshot(void);
 
 #endif   /* STANDBY_H */