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 */