/* * ProcessCommittedInvalidationMessages is executed by xact_redo_commit() * to process invalidation messages added to commit records. * * Relcache init file invalidation requires processing both * before and after we send the SI messages. See AtEOXact_Inval() */ void ProcessCommittedInvalidationMessages(SharedInvalidationMessage *msgs, int nmsgs, bool RelcacheInitFileInval, Oid dbid, Oid tsid) { if (nmsgs <= 0) return; elog(trace_recovery(DEBUG4), "replaying commit with %d messages%s", nmsgs, (RelcacheInitFileInval ? " and relcache file invalidation" : "")); if (RelcacheInitFileInval) { /* * RelationCacheInitFilePreInvalidate requires DatabasePath to be set, * but we should not use SetDatabasePath during recovery, since it is * intended to be used only once by normal backends. Hence, a quick * hack: set DatabasePath directly then unset after use. */ DatabasePath = GetDatabasePath(dbid, tsid); elog(trace_recovery(DEBUG4), "removing relcache init file in \"%s\"", DatabasePath); RelationCacheInitFilePreInvalidate(); pfree(DatabasePath); DatabasePath = NULL; } SendSharedInvalidMessages(msgs, nmsgs); if (RelcacheInitFileInval) RelationCacheInitFilePostInvalidate(); }
/* * Record an enhanced snapshot of running transactions into WAL. * * The definitions of RunningTransactionsData and xl_xact_running_xacts * are similar. We keep them separate because xl_xact_running_xacts * is a contiguous chunk of memory and never exists fully until it is * assembled in WAL. */ static void LogCurrentRunningXacts(RunningTransactions CurrRunningXacts) { xl_running_xacts xlrec; XLogRecData rdata[2]; int lastrdata = 0; XLogRecPtr recptr; xlrec.xcnt = CurrRunningXacts->xcnt; xlrec.subxid_overflow = CurrRunningXacts->subxid_overflow; xlrec.nextXid = CurrRunningXacts->nextXid; xlrec.oldestRunningXid = CurrRunningXacts->oldestRunningXid; xlrec.latestCompletedXid = CurrRunningXacts->latestCompletedXid; /* Header */ rdata[0].data = (char *) (&xlrec); rdata[0].len = MinSizeOfXactRunningXacts; rdata[0].buffer = InvalidBuffer; /* array of TransactionIds */ if (xlrec.xcnt > 0) { rdata[0].next = &(rdata[1]); rdata[1].data = (char *) CurrRunningXacts->xids; rdata[1].len = xlrec.xcnt * sizeof(TransactionId); rdata[1].buffer = InvalidBuffer; lastrdata = 1; } rdata[lastrdata].next = NULL; recptr = XLogInsert(RM_STANDBY_ID, XLOG_RUNNING_XACTS, rdata); if (CurrRunningXacts->subxid_overflow) elog(trace_recovery(DEBUG2), "snapshot of %u running transactions overflowed (lsn %X/%X oldest xid %u latest complete %u next xid %u)", CurrRunningXacts->xcnt, recptr.xlogid, recptr.xrecoff, CurrRunningXacts->oldestRunningXid, CurrRunningXacts->latestCompletedXid, CurrRunningXacts->nextXid); else elog(trace_recovery(DEBUG2), "snapshot of %u running transaction ids (lsn %X/%X oldest xid %u latest complete %u next xid %u)", CurrRunningXacts->xcnt, recptr.xlogid, recptr.xrecoff, CurrRunningXacts->oldestRunningXid, CurrRunningXacts->latestCompletedXid, CurrRunningXacts->nextXid); }
void StandbyAcquireAccessExclusiveLock(TransactionId xid, Oid dbOid, Oid relOid) { xl_standby_lock *newlock; LOCKTAG locktag; /* Already processed? */ if (TransactionIdDidCommit(xid) || TransactionIdDidAbort(xid)) return; elog(trace_recovery(DEBUG4), "adding recovery lock: db %u rel %u", dbOid, relOid); /* dbOid is InvalidOid when we are locking a shared relation. */ Assert(OidIsValid(relOid)); newlock = palloc(sizeof(xl_standby_lock)); newlock->xid = xid; newlock->dbOid = dbOid; newlock->relOid = relOid; RecoveryLockList = lappend(RecoveryLockList, newlock); /* * Attempt to acquire the lock as requested, if not resolve conflict */ SET_LOCKTAG_RELATION(locktag, newlock->dbOid, newlock->relOid); if (LockAcquireExtended(&locktag, AccessExclusiveLock, true, true, false) == LOCKACQUIRE_NOT_AVAIL) ResolveRecoveryConflictWithLock(newlock->dbOid, newlock->relOid); }
/* * StandbyReleaseLocksMany * Release standby locks held by XIDs < removeXid * * If keepPreparedXacts is true, keep prepared transactions even if * they're older than removeXid */ static void StandbyReleaseLocksMany(TransactionId removeXid, bool keepPreparedXacts) { ListCell *cell, *prev, *next; LOCKTAG locktag; /* * Release all matching locks. */ prev = NULL; for (cell = list_head(RecoveryLockList); cell; cell = next) { xl_standby_lock *lock = (xl_standby_lock *) lfirst(cell); next = lnext(cell); if (!TransactionIdIsValid(removeXid) || TransactionIdPrecedes(lock->xid, removeXid)) { if (keepPreparedXacts && StandbyTransactionIdIsPrepared(lock->xid)) continue; elog(trace_recovery(DEBUG4), "releasing recovery lock: xid %u db %u rel %u", lock->xid, lock->dbOid, lock->relOid); SET_LOCKTAG_RELATION(locktag, lock->dbOid, lock->relOid); if (!LockRelease(&locktag, AccessExclusiveLock, true)) elog(trace_recovery(LOG), "RecoveryLockList contains entry for lock no longer recorded by lock manager: xid %u database %u relation %u", lock->xid, lock->dbOid, lock->relOid); RecoveryLockList = list_delete_cell(RecoveryLockList, cell, prev); pfree(lock); } else prev = cell; } }
static void StandbyReleaseLocks(TransactionId xid) { ListCell *cell, *prev, *next; /* * Release all matching locks and remove them from list */ prev = NULL; for (cell = list_head(RecoveryLockList); cell; cell = next) { xl_standby_lock *lock = (xl_standby_lock *) lfirst(cell); next = lnext(cell); if (!TransactionIdIsValid(xid) || lock->xid == xid) { LOCKTAG locktag; elog(trace_recovery(DEBUG4), "releasing recovery lock: xid %u db %u rel %u", lock->xid, lock->dbOid, lock->relOid); SET_LOCKTAG_RELATION(locktag, lock->dbOid, lock->relOid); if (!LockRelease(&locktag, AccessExclusiveLock, true)) elog(trace_recovery(LOG), "RecoveryLockList contains entry for lock no longer recorded by lock manager: xid %u database %u relation %u", lock->xid, lock->dbOid, lock->relOid); RecoveryLockList = list_delete_cell(RecoveryLockList, cell, prev); pfree(lock); } else prev = cell; } }
/* * ProcessCommittedInvalidationMessages is executed by xact_redo_commit() * to process invalidation messages added to commit records. * * Relcache init file invalidation requires processing both * before and after we send the SI messages. See AtEOXact_Inval() * * We deliberately avoid SetDatabasePath() since it is intended to be used * only once by normal backends, so we set DatabasePath directly then * pfree after use. See RecoveryRelationCacheInitFileInvalidate() macro. */ void ProcessCommittedInvalidationMessages(SharedInvalidationMessage *msgs, int nmsgs, bool RelcacheInitFileInval, Oid dbid, Oid tsid) { if (nmsgs <= 0) return; elog(trace_recovery(DEBUG4), "replaying commit with %d messages%s", nmsgs, (RelcacheInitFileInval ? " and relcache file invalidation" : "")); if (RelcacheInitFileInval) RecoveryRelationCacheInitFileInvalidate(dbid, tsid, true); SendSharedInvalidMessages(msgs, nmsgs); if (RelcacheInitFileInval) RecoveryRelationCacheInitFileInvalidate(dbid, tsid, false); }
/* * Called at end of recovery and when we see a shutdown checkpoint. */ void StandbyReleaseAllLocks(void) { elog(trace_recovery(DEBUG2), "release all standby locks"); StandbyReleaseLocksMany(InvalidTransactionId, false); }