* Set the next-to-be-assigned MultiXactId and offset
*
* This is used when we can determine the correct next ID/offset exactly
- * from a checkpoint record. We need no locking since it is only called
- * during bootstrap and XLog replay.
+ * from a checkpoint record. Although this is only called during bootstrap
+ * and XLog replay, we take the lock in case any hot-standby backends are
+ * examining the values.
*/
void
MultiXactSetNextMXact(MultiXactId nextMulti,
{
debug_elog4(DEBUG2, "MultiXact: setting next multi to %u offset %u",
nextMulti, nextMultiOffset);
+ LWLockAcquire(MultiXactGenLock, LW_EXCLUSIVE);
MultiXactState->nextMXact = nextMulti;
MultiXactState->nextOffset = nextMultiOffset;
+ LWLockRelease(MultiXactGenLock);
}
/*
*
* This is used when we can determine minimum safe values from an XLog
* record (either an on-line checkpoint or an mxact creation log entry).
- * We need no locking since it is only called during XLog replay.
+ * Although this is only called during XLog replay, we take the lock in case
+ * any hot-standby backends are examining the values.
*/
void
MultiXactAdvanceNextMXact(MultiXactId minMulti,
MultiXactOffset minMultiOffset)
{
+ LWLockAcquire(MultiXactGenLock, LW_EXCLUSIVE);
if (MultiXactIdPrecedes(MultiXactState->nextMXact, minMulti))
{
debug_elog3(DEBUG2, "MultiXact: setting next multi to %u", minMulti);
minMultiOffset);
MultiXactState->nextOffset = minMultiOffset;
}
+ LWLockRelease(MultiXactGenLock);
}
/*
errcontext.previous = error_context_stack;
error_context_stack = &errcontext;
- /* nextXid must be beyond record's xid */
+ /*
+ * ShmemVariableCache->nextXid must be beyond record's xid.
+ *
+ * We don't expect anyone else to modify nextXid, hence we
+ * don't need to hold a lock while examining it. We still
+ * acquire the lock to modify it, though.
+ */
if (TransactionIdFollowsOrEquals(record->xl_xid,
ShmemVariableCache->nextXid))
{
+ LWLockAcquire(XidGenLock, LW_EXCLUSIVE);
ShmemVariableCache->nextXid = record->xl_xid;
TransactionIdAdvance(ShmemVariableCache->nextXid);
+ LWLockRelease(XidGenLock);
}
/*
TransactionIdIsValid(record->xl_xid))
RecordKnownAssignedTransactionIds(record->xl_xid);
+ /* Now apply the WAL record itself */
RmgrTable[record->xl_rmid].rm_redo(EndRecPtr, record);
/* Pop the error context stack */
XLogCtl->ckptXid = ControlFile->checkPointCopy.nextXid;
/* also initialize latestCompletedXid, to nextXid - 1 */
+ LWLockAcquire(ProcArrayLock, LW_EXCLUSIVE);
ShmemVariableCache->latestCompletedXid = ShmemVariableCache->nextXid;
TransactionIdRetreat(ShmemVariableCache->latestCompletedXid);
+ LWLockRelease(ProcArrayLock);
/*
* Start up the commit log and subtrans, if not already done for
{
Oid nextOid;
+ /*
+ * We used to try to take the maximum of ShmemVariableCache->nextOid
+ * and the recorded nextOid, but that fails if the OID counter wraps
+ * around. Since no OID allocation should be happening during replay
+ * anyway, better to just believe the record exactly. We still take
+ * OidGenLock while setting the variable, just in case.
+ */
memcpy(&nextOid, XLogRecGetData(record), sizeof(Oid));
- if (ShmemVariableCache->nextOid < nextOid)
- {
- ShmemVariableCache->nextOid = nextOid;
- ShmemVariableCache->oidCount = 0;
- }
+ LWLockAcquire(OidGenLock, LW_EXCLUSIVE);
+ ShmemVariableCache->nextOid = nextOid;
+ ShmemVariableCache->oidCount = 0;
+ LWLockRelease(OidGenLock);
}
else if (info == XLOG_CHECKPOINT_SHUTDOWN)
{
memcpy(&checkPoint, XLogRecGetData(record), sizeof(CheckPoint));
/* In a SHUTDOWN checkpoint, believe the counters exactly */
+ LWLockAcquire(XidGenLock, LW_EXCLUSIVE);
ShmemVariableCache->nextXid = checkPoint.nextXid;
+ LWLockRelease(XidGenLock);
+ LWLockAcquire(OidGenLock, LW_EXCLUSIVE);
ShmemVariableCache->nextOid = checkPoint.nextOid;
ShmemVariableCache->oidCount = 0;
+ LWLockRelease(OidGenLock);
MultiXactSetNextMXact(checkPoint.nextMulti,
checkPoint.nextMultiOffset);
SetTransactionIdLimit(checkPoint.oldestXid, checkPoint.oldestXidDB);
if (InArchiveRecovery &&
!XLogRecPtrIsInvalid(ControlFile->backupStartPoint) &&
XLogRecPtrIsInvalid(ControlFile->backupEndPoint))
- ereport(ERROR,
+ ereport(PANIC,
(errmsg("online backup was canceled, recovery cannot continue")));
/*
CheckPoint checkPoint;
memcpy(&checkPoint, XLogRecGetData(record), sizeof(CheckPoint));
- /* In an ONLINE checkpoint, treat the counters like NEXTOID */
+ /* In an ONLINE checkpoint, treat the XID counter as a minimum */
+ LWLockAcquire(XidGenLock, LW_EXCLUSIVE);
if (TransactionIdPrecedes(ShmemVariableCache->nextXid,
checkPoint.nextXid))
ShmemVariableCache->nextXid = checkPoint.nextXid;
- if (ShmemVariableCache->nextOid < checkPoint.nextOid)
- {
- ShmemVariableCache->nextOid = checkPoint.nextOid;
- ShmemVariableCache->oidCount = 0;
- }
+ LWLockRelease(XidGenLock);
+ /* ... but still treat OID counter as exact */
+ LWLockAcquire(OidGenLock, LW_EXCLUSIVE);
+ ShmemVariableCache->nextOid = checkPoint.nextOid;
+ ShmemVariableCache->oidCount = 0;
+ LWLockRelease(OidGenLock);
MultiXactAdvanceNextMXact(checkPoint.nextMulti,
checkPoint.nextMultiOffset);
if (TransactionIdPrecedes(ShmemVariableCache->oldestXid,
running->latestCompletedXid))
ShmemVariableCache->latestCompletedXid = running->latestCompletedXid;
- /* nextXid must be beyond any observed xid */
+ Assert(TransactionIdIsNormal(ShmemVariableCache->latestCompletedXid));
+
+ LWLockRelease(ProcArrayLock);
+
+ /*
+ * ShmemVariableCache->nextXid must be beyond any observed xid.
+ *
+ * We don't expect anyone else to modify nextXid, hence we don't need to
+ * hold a lock while examining it. We still acquire the lock to modify
+ * it, though.
+ */
nextXid = latestObservedXid;
TransactionIdAdvance(nextXid);
if (TransactionIdFollows(nextXid, ShmemVariableCache->nextXid))
+ {
+ LWLockAcquire(XidGenLock, LW_EXCLUSIVE);
ShmemVariableCache->nextXid = nextXid;
+ LWLockRelease(XidGenLock);
+ }
- Assert(TransactionIdIsNormal(ShmemVariableCache->latestCompletedXid));
Assert(TransactionIdIsValid(ShmemVariableCache->nextXid));
- LWLockRelease(ProcArrayLock);
-
KnownAssignedXidsDisplay(trace_recovery(DEBUG3));
if (standbyState == STANDBY_SNAPSHOT_READY)
elog(trace_recovery(DEBUG1), "recovery snapshots are now enabled");
LWLockAcquire(ProcArrayLock, LW_SHARED);
+ /*
+ * It's okay to read nextXid without acquiring XidGenLock because (1) we
+ * assume TransactionIds can be read atomically and (2) we don't care if
+ * we get a slightly stale value. It can't be very stale anyway, because
+ * the LWLockAcquire above will have done any necessary memory
+ * interlocking.
+ */
oldestRunningXid = ShmemVariableCache->nextXid;
/*
/* ShmemVariableCache->nextXid must be beyond any observed xid */
next_expected_xid = latestObservedXid;
TransactionIdAdvance(next_expected_xid);
+ LWLockAcquire(XidGenLock, LW_EXCLUSIVE);
ShmemVariableCache->nextXid = next_expected_xid;
+ LWLockRelease(XidGenLock);
}
}