Skip to content

Commit ed46758

Browse files
committed
Logging running transactions every 15 seconds.
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
1 parent d02c0dd commit ed46758

File tree

3 files changed

+86
-5
lines changed

3 files changed

+86
-5
lines changed

src/backend/postmaster/bgwriter.c

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,9 +54,11 @@
5454
#include "storage/shmem.h"
5555
#include "storage/smgr.h"
5656
#include "storage/spin.h"
57+
#include "storage/standby.h"
5758
#include "utils/guc.h"
5859
#include "utils/memutils.h"
5960
#include "utils/resowner.h"
61+
#include "utils/timestamp.h"
6062

6163

6264
/*
@@ -70,6 +72,20 @@ int BgWriterDelay = 200;
7072
*/
7173
#define HIBERNATE_FACTOR 50
7274

75+
/*
76+
* Interval in which standby snapshots are logged into the WAL stream, in
77+
* milliseconds.
78+
*/
79+
#define LOG_SNAPSHOT_INTERVAL_MS 15000
80+
81+
/*
82+
* LSN and timestamp at which we last issued a LogStandbySnapshot(), to avoid
83+
* doing so too often or repeatedly if there has been no other write activity
84+
* in the system.
85+
*/
86+
static TimestampTz last_snapshot_ts;
87+
static XLogRecPtr last_snapshot_lsn = InvalidXLogRecPtr;
88+
7389
/*
7490
* Flags set by interrupt handlers for later service in the main loop.
7591
*/
@@ -141,6 +157,12 @@ BackgroundWriterMain(void)
141157
*/
142158
CurrentResourceOwner = ResourceOwnerCreate(NULL, "Background Writer");
143159

160+
/*
161+
* We just started, assume there has been either a shutdown or
162+
* end-of-recovery snapshot.
163+
*/
164+
last_snapshot_ts = GetCurrentTimestamp();
165+
144166
/*
145167
* Create a memory context that we will do all our work in. We do this so
146168
* that we can reset the context during error recovery and thereby avoid
@@ -275,6 +297,46 @@ BackgroundWriterMain(void)
275297
smgrcloseall();
276298
}
277299

300+
/*
301+
* Log a new xl_running_xacts every now and then so replication can get
302+
* into a consistent state faster (think of suboverflowed snapshots)
303+
* and clean up resources (locks, KnownXids*) more frequently. The
304+
* costs of this are relatively low, so doing it 4 times
305+
* (LOG_SNAPSHOT_INTERVAL_MS) a minute seems fine.
306+
*
307+
* We assume the interval for writing xl_running_xacts is
308+
* significantly bigger than BgWriterDelay, so we don't complicate the
309+
* overall timeout handling but just assume we're going to get called
310+
* often enough even if hibernation mode is active. It's not that
311+
* important that log_snap_interval_ms is met strictly. To make sure
312+
* we're not waking the disk up unneccesarily on an idle system we
313+
* check whether there has been any WAL inserted since the last time
314+
* we've logged a running xacts.
315+
*
316+
* We do this logging in the bgwriter as its the only process thats
317+
* run regularly and returns to its mainloop all the
318+
* time. E.g. Checkpointer, when active, is barely ever in its
319+
* mainloop and thus makes it hard to log regularly.
320+
*/
321+
if (XLogStandbyInfoActive() && !RecoveryInProgress())
322+
{
323+
TimestampTz timeout = 0;
324+
TimestampTz now = GetCurrentTimestamp();
325+
timeout = TimestampTzPlusMilliseconds(last_snapshot_ts,
326+
LOG_SNAPSHOT_INTERVAL_MS);
327+
328+
/*
329+
* only log if enough time has passed and some xlog record has been
330+
* inserted.
331+
*/
332+
if (now >= timeout &&
333+
last_snapshot_lsn != GetXLogInsertRecPtr())
334+
{
335+
last_snapshot_lsn = LogStandbySnapshot();
336+
last_snapshot_ts = now;
337+
}
338+
}
339+
278340
/*
279341
* Sleep until we are signaled or BgWriterDelay has elapsed.
280342
*

src/backend/storage/ipc/standby.c

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ static void ResolveRecoveryConflictWithVirtualXIDs(VirtualTransactionId *waitlis
4242
ProcSignalReason reason);
4343
static void ResolveRecoveryConflictWithLock(Oid dbOid, Oid relOid);
4444
static void SendRecoveryConflictWithBufferPin(ProcSignalReason reason);
45-
static void LogCurrentRunningXacts(RunningTransactions CurrRunningXacts);
45+
static XLogRecPtr LogCurrentRunningXacts(RunningTransactions CurrRunningXacts);
4646
static void LogAccessExclusiveLocks(int nlocks, xl_standby_lock *locks);
4747

4848

@@ -853,10 +853,13 @@ standby_redo(XLogRecPtr lsn, XLogRecord *record)
853853
* currently running xids, performed by StandbyReleaseOldLocks().
854854
* Zero xids should no longer be possible, but we may be replaying WAL
855855
* from a time when they were possible.
856+
*
857+
* Returns the RecPtr of the last inserted record.
856858
*/
857-
void
859+
XLogRecPtr
858860
LogStandbySnapshot(void)
859861
{
862+
XLogRecPtr recptr;
860863
RunningTransactions running;
861864
xl_standby_lock *locks;
862865
int nlocks;
@@ -876,9 +879,12 @@ LogStandbySnapshot(void)
876879
* record we write, because standby will open up when it sees this.
877880
*/
878881
running = GetRunningTransactionData();
879-
LogCurrentRunningXacts(running);
882+
recptr = LogCurrentRunningXacts(running);
883+
880884
/* GetRunningTransactionData() acquired XidGenLock, we must release it */
881885
LWLockRelease(XidGenLock);
886+
887+
return recptr;
882888
}
883889

884890
/*
@@ -889,7 +895,7 @@ LogStandbySnapshot(void)
889895
* is a contiguous chunk of memory and never exists fully until it is
890896
* assembled in WAL.
891897
*/
892-
static void
898+
static XLogRecPtr
893899
LogCurrentRunningXacts(RunningTransactions CurrRunningXacts)
894900
{
895901
xl_running_xacts xlrec;
@@ -939,6 +945,19 @@ LogCurrentRunningXacts(RunningTransactions CurrRunningXacts)
939945
CurrRunningXacts->oldestRunningXid,
940946
CurrRunningXacts->latestCompletedXid,
941947
CurrRunningXacts->nextXid);
948+
949+
/*
950+
* Ensure running_xacts information is synced to disk not too far in the
951+
* future. We don't want to stall anything though (i.e. use XLogFlush()),
952+
* so we let the wal writer do it during normal
953+
* operation. XLogSetAsyncXactLSN() conveniently will mark the LSN as
954+
* to-be-synced and nudge the WALWriter into action if sleeping. Check
955+
* XLogBackgroundFlush() for details why a record might not be flushed
956+
* without it.
957+
*/
958+
XLogSetAsyncXactLSN(recptr);
959+
960+
return recptr;
942961
}
943962

944963
/*

src/include/storage/standby.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,6 @@ typedef RunningTransactionsData *RunningTransactions;
113113
extern void LogAccessExclusiveLock(Oid dbOid, Oid relOid);
114114
extern void LogAccessExclusiveLockPrepare(void);
115115

116-
extern void LogStandbySnapshot(void);
116+
extern XLogRecPtr LogStandbySnapshot(void);
117117

118118
#endif /* STANDBY_H */

0 commit comments

Comments
 (0)