diff --git a/src/backend/postmaster/bgwriter.c b/src/backend/postmaster/bgwriter.c index 6f5d937af9..1ec66c221f 100644 --- a/src/backend/postmaster/bgwriter.c +++ b/src/backend/postmaster/bgwriter.c @@ -54,9 +54,11 @@ #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. * diff --git a/src/backend/storage/ipc/standby.c b/src/backend/storage/ipc/standby.c index 437d64b787..fb5f18edfc 100644 --- a/src/backend/storage/ipc/standby.c +++ b/src/backend/storage/ipc/standby.c @@ -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; } /* diff --git a/src/include/storage/standby.h b/src/include/storage/standby.h index 1eb10c454f..89ab704699 100644 --- a/src/include/storage/standby.h +++ b/src/include/storage/standby.h @@ -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 */