Skip to content

Commit da99504

Browse files
committed
At end of recovery, reset all sinval-managed caches.
An inplace update's invalidation messages are part of its transaction's commit record. However, the update survives even if its transaction aborts or we stop recovery before replaying its transaction commit. After recovery, a backend that started in recovery could update the row without incorporating the inplace update. That could result in a table with an index, yet relhasindex=f. That is a source of index corruption. This bulk invalidation avoids the functional consequences. A future change can fix the !RecoveryInProgress() scenario without changing the WAL format. Back-patch to v17 - v12 (all supported versions). v18 will instead add invalidations to WAL. Discussion: https://postgr.es/m/20240618152349.7f.nmisch@google.com
1 parent a0c0078 commit da99504

File tree

3 files changed

+67
-0
lines changed

3 files changed

+67
-0
lines changed

src/backend/access/transam/xlog.c

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@
6464
#include "storage/proc.h"
6565
#include "storage/procarray.h"
6666
#include "storage/reinit.h"
67+
#include "storage/sinvaladt.h"
6768
#include "storage/smgr.h"
6869
#include "storage/spin.h"
6970
#include "storage/sync.h"
@@ -7844,6 +7845,30 @@ StartupXLOG(void)
78447845
}
78457846
}
78467847

7848+
/*
7849+
* Invalidate all sinval-managed caches before READ WRITE transactions
7850+
* begin. The xl_heap_inplace WAL record doesn't store sufficient data
7851+
* for invalidations. The commit record, if any, has the invalidations.
7852+
* However, the inplace update is permanent, whether or not we reach a
7853+
* commit record. Fortunately, read-only transactions tolerate caches not
7854+
* reflecting the latest inplace updates. Read-only transactions
7855+
* experience the notable inplace updates as follows:
7856+
*
7857+
* - relhasindex=true affects readers only after the CREATE INDEX
7858+
* transaction commit makes an index fully available to them.
7859+
*
7860+
* - datconnlimit=DATCONNLIMIT_INVALID_DB affects readers only at
7861+
* InitPostgres() time, and that read does not use a cache.
7862+
*
7863+
* - relfrozenxid, datfrozenxid, relminmxid, and datminmxid have no effect
7864+
* on readers.
7865+
*
7866+
* Hence, hot standby queries (all READ ONLY) function correctly without
7867+
* the missing invalidations. This avoided changing the WAL format in
7868+
* back branches.
7869+
*/
7870+
SIResetAll();
7871+
78477872
/*
78487873
* Preallocate additional log files, if wanted.
78497874
*/

src/backend/storage/ipc/sinvaladt.c

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -751,6 +751,47 @@ SICleanupQueue(bool callerHasWriteLock, int minFree)
751751
}
752752
}
753753

754+
/*
755+
* SIResetAll
756+
* Mark all active backends as "reset"
757+
*
758+
* Use this when we don't know what needs to be invalidated. It's a
759+
* cluster-wide InvalidateSystemCaches(). This was a back-branch-only remedy
760+
* to avoid a WAL format change.
761+
*
762+
* The implementation is like SICleanupQueue(false, MAXNUMMESSAGES + 1), with
763+
* one addition. SICleanupQueue() assumes minFree << MAXNUMMESSAGES, so it
764+
* assumes hasMessages==true for any backend it resets. We're resetting even
765+
* fully-caught-up backends, so we set hasMessages.
766+
*/
767+
void
768+
SIResetAll(void)
769+
{
770+
SISeg *segP = shmInvalBuffer;
771+
int i;
772+
773+
LWLockAcquire(SInvalWriteLock, LW_EXCLUSIVE);
774+
LWLockAcquire(SInvalReadLock, LW_EXCLUSIVE);
775+
776+
for (i = 0; i < segP->lastBackend; i++)
777+
{
778+
ProcState *stateP = &segP->procState[i];
779+
780+
if (stateP->procPid == 0 || stateP->sendOnly)
781+
continue;
782+
783+
/* Consuming the reset will update "nextMsgNum" and "signaled". */
784+
stateP->resetState = true;
785+
stateP->hasMessages = true;
786+
}
787+
788+
segP->minMsgNum = segP->maxMsgNum;
789+
segP->nextThreshold = CLEANUP_MIN;
790+
791+
LWLockRelease(SInvalReadLock);
792+
LWLockRelease(SInvalWriteLock);
793+
}
794+
754795

755796
/*
756797
* GetNextLocalTransactionId --- allocate a new LocalTransactionId

src/include/storage/sinvaladt.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ extern void BackendIdGetTransactionIds(int backendID, TransactionId *xid, Transa
3737
extern void SIInsertDataEntries(const SharedInvalidationMessage *data, int n);
3838
extern int SIGetDataEntries(SharedInvalidationMessage *data, int datasize);
3939
extern void SICleanupQueue(bool callerHasWriteLock, int minFree);
40+
extern void SIResetAll(void);
4041

4142
extern LocalTransactionId GetNextLocalTransactionId(void);
4243

0 commit comments

Comments
 (0)