diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c index 9d3ca4dc7e..eb86bb4420 100644 --- a/src/backend/access/transam/xlog.c +++ b/src/backend/access/transam/xlog.c @@ -93,6 +93,7 @@ #include "storage/proc.h" #include "storage/procarray.h" #include "storage/reinit.h" +#include "storage/sinvaladt.h" #include "storage/spin.h" #include "storage/sync.h" #include "utils/guc_hooks.h" @@ -6040,6 +6041,30 @@ StartupXLOG(void) XLogCtl->LogwrtRqst.Write = EndOfLog; XLogCtl->LogwrtRqst.Flush = EndOfLog; + /* + * Invalidate all sinval-managed caches before READ WRITE transactions + * begin. The xl_heap_inplace WAL record doesn't store sufficient data + * for invalidations. The commit record, if any, has the invalidations. + * However, the inplace update is permanent, whether or not we reach a + * commit record. Fortunately, read-only transactions tolerate caches not + * reflecting the latest inplace updates. Read-only transactions + * experience the notable inplace updates as follows: + * + * - relhasindex=true affects readers only after the CREATE INDEX + * transaction commit makes an index fully available to them. + * + * - datconnlimit=DATCONNLIMIT_INVALID_DB affects readers only at + * InitPostgres() time, and that read does not use a cache. + * + * - relfrozenxid, datfrozenxid, relminmxid, and datminmxid have no effect + * on readers. + * + * Hence, hot standby queries (all READ ONLY) function correctly without + * the missing invalidations. This avoided changing the WAL format in + * back branches. + */ + SIResetAll(); + /* * Preallocate additional log files, if wanted. */ diff --git a/src/backend/storage/ipc/sinvaladt.c b/src/backend/storage/ipc/sinvaladt.c index b486d8ddd1..271212987e 100644 --- a/src/backend/storage/ipc/sinvaladt.c +++ b/src/backend/storage/ipc/sinvaladt.c @@ -683,6 +683,48 @@ SICleanupQueue(bool callerHasWriteLock, int minFree) } } +/* + * SIResetAll + * Mark all active backends as "reset" + * + * Use this when we don't know what needs to be invalidated. It's a + * cluster-wide InvalidateSystemCaches(). This was a back-branch-only remedy + * to avoid a WAL format change. + * + * The implementation is like SICleanupQueue(false, MAXNUMMESSAGES + 1), with + * one addition. SICleanupQueue() assumes minFree << MAXNUMMESSAGES, so it + * assumes hasMessages==true for any backend it resets. We're resetting even + * fully-caught-up backends, so we set hasMessages. + */ +void +SIResetAll(void) +{ + SISeg *segP = shmInvalBuffer; + int i; + + LWLockAcquire(SInvalWriteLock, LW_EXCLUSIVE); + LWLockAcquire(SInvalReadLock, LW_EXCLUSIVE); + + for (i = 0; i < segP->numProcs; i++) + { + ProcState *stateP = &segP->procState[segP->pgprocnos[i]]; + + Assert(stateP->procPid != 0); + if (stateP->sendOnly) + continue; + + /* Consuming the reset will update "nextMsgNum" and "signaled". */ + stateP->resetState = true; + stateP->hasMessages = true; + } + + segP->minMsgNum = segP->maxMsgNum; + segP->nextThreshold = CLEANUP_MIN; + + LWLockRelease(SInvalReadLock); + LWLockRelease(SInvalWriteLock); +} + /* * GetNextLocalTransactionId --- allocate a new LocalTransactionId diff --git a/src/include/storage/sinvaladt.h b/src/include/storage/sinvaladt.h index c3c97b3f8b..7c8f450db7 100644 --- a/src/include/storage/sinvaladt.h +++ b/src/include/storage/sinvaladt.h @@ -35,6 +35,7 @@ extern void SharedInvalBackendInit(bool sendOnly); extern void SIInsertDataEntries(const SharedInvalidationMessage *data, int n); extern int SIGetDataEntries(SharedInvalidationMessage *data, int datasize); extern void SICleanupQueue(bool callerHasWriteLock, int minFree); +extern void SIResetAll(void); extern LocalTransactionId GetNextLocalTransactionId(void);