#include "access/xlog.h"
#include "storage/lock.h"
#include "storage/procsignal.h"
#include "storage/relfilenode.h"
Go to the source code of this file.
#define MinSizeOfXactRunningXacts offsetof(xl_running_xacts, xids) |
#define XLOG_RUNNING_XACTS 0x10 |
Definition at line 57 of file standby.h.
Referenced by LogCurrentRunningXacts(), standby_desc(), and standby_redo().
#define XLOG_STANDBY_LOCK 0x00 |
Definition at line 56 of file standby.h.
Referenced by LogAccessExclusiveLocks(), standby_desc(), and standby_redo().
typedef struct RunningTransactionsData RunningTransactionsData |
typedef struct xl_running_xacts xl_running_xacts |
typedef struct xl_standby_locks xl_standby_locks |
void CheckRecoveryConflictDeadlock | ( | void | ) |
Definition at line 483 of file standby.c.
References Assert, ereport, errcode(), errdetail(), errmsg(), ERROR, HoldingBufferPinThatDelaysRecovery(), and InRecovery.
Referenced by ProcSleep().
{ Assert(!InRecovery); /* do not call in Startup process */ if (!HoldingBufferPinThatDelaysRecovery()) return; /* * Error message should match ProcessInterrupts() but we avoid calling * that because we aren't handling an interrupt at this point. Note that * we only cancel the current transaction here, so if we are in a * subtransaction and the pin is held by a parent, then the Startup * process will continue to wait even though we have avoided deadlock. */ ereport(ERROR, (errcode(ERRCODE_T_R_DEADLOCK_DETECTED), errmsg("canceling statement due to conflict with recovery"), errdetail("User transaction caused buffer deadlock with recovery."))); }
void InitRecoveryTransactionEnvironment | ( | void | ) |
Definition at line 62 of file standby.c.
References VirtualTransactionId::backendId, GetNextLocalTransactionId(), VirtualTransactionId::localTransactionId, MyBackendId, SharedInvalBackendInit(), standbyState, and VirtualXactLockTableInsert().
Referenced by StartupXLOG().
{ VirtualTransactionId vxid; /* * Initialize shared invalidation management for Startup process, being * careful to register ourselves as a sendOnly process so we don't need to * read messages, nor will we get signalled when the queue starts filling * up. */ SharedInvalBackendInit(true); /* * Lock a virtual transaction id for Startup process. * * We need to do GetNextLocalTransactionId() because * SharedInvalBackendInit() leaves localTransactionid invalid and the lock * manager doesn't like that at all. * * Note that we don't need to run XactLockTableInsert() because nobody * needs to wait on xids. That sounds a little strange, but table locks * are held by vxids and row level locks are held by xids. All queries * hold AccessShareLocks so never block while we write or lock new rows. */ vxid.backendId = MyBackendId; vxid.localTransactionId = GetNextLocalTransactionId(); VirtualXactLockTableInsert(vxid); standbyState = STANDBY_INITIALIZED; }
Definition at line 978 of file standby.c.
References xl_standby_lock::dbOid, GetTopTransactionId(), LogAccessExclusiveLocks(), xl_standby_lock::relOid, and xl_standby_lock::xid.
Referenced by LockAcquireExtended().
{ xl_standby_lock xlrec; xlrec.xid = GetTopTransactionId(); /* * Decode the locktag back to the original values, to avoid sending lots * of empty bytes with every message. See lock.h to check how a locktag * is defined for LOCKTAG_RELATION */ xlrec.dbOid = dbOid; xlrec.relOid = relOid; LogAccessExclusiveLocks(1, &xlrec); }
void LogAccessExclusiveLockPrepare | ( | void | ) |
Definition at line 999 of file standby.c.
References GetTopTransactionId().
Referenced by LockAcquireExtended().
{ /* * Ensure that a TransactionId has been assigned to this transaction, for * two reasons, both related to lock release on the standby. First, we * must assign an xid so that RecordTransactionCommit() and * RecordTransactionAbort() do not optimise away the transaction * completion record which recovery relies upon to release locks. It's a * hack, but for a corner case not worth adding code for into the main * commit path. Second, we must assign an xid before the lock is recorded * in shared memory, otherwise a concurrently executing * GetRunningTransactionLocks() might see a lock associated with an * InvalidTransactionId which we later assert cannot happen. */ (void) GetTopTransactionId(); }
void LogStandbySnapshot | ( | void | ) |
Definition at line 858 of file standby.c.
References Assert, GetRunningTransactionData(), GetRunningTransactionLocks(), LogAccessExclusiveLocks(), LogCurrentRunningXacts(), LWLockRelease(), XidGenLock, and XLogStandbyInfoActive.
Referenced by CreateCheckPoint().
{ RunningTransactions running; xl_standby_lock *locks; int nlocks; Assert(XLogStandbyInfoActive()); /* * Get details of any AccessExclusiveLocks being held at the moment. * * XXX GetRunningTransactionLocks() currently holds a lock on all * partitions though it is possible to further optimise the locking. By * reference counting locks and storing the value on the ProcArray entry * for each backend we can easily tell if any locks need recording without * trying to acquire the partition locks and scanning the lock table. */ locks = GetRunningTransactionLocks(&nlocks); if (nlocks > 0) LogAccessExclusiveLocks(nlocks, locks); /* * Log details of all in-progress transactions. This should be the last * record we write, because standby will open up when it sees this. */ running = GetRunningTransactionData(); LogCurrentRunningXacts(running); /* GetRunningTransactionData() acquired XidGenLock, we must release it */ LWLockRelease(XidGenLock); }
void ResolveRecoveryConflictWithBufferPin | ( | void | ) |
Definition at line 402 of file standby.c.
References Assert, DeadlockTimeout, EnableTimeoutParams::delay_ms, disable_all_timeouts(), enable_timeout_after(), enable_timeouts(), EnableTimeoutParams::fin_time, GetCurrentTimestamp(), GetStandbyLimitTime(), EnableTimeoutParams::id, InHotStandby, PROCSIG_RECOVERY_CONFLICT_BUFFERPIN, ProcWaitForSignal(), SendRecoveryConflictWithBufferPin(), STANDBY_DEADLOCK_TIMEOUT, and EnableTimeoutParams::type.
Referenced by LockBufferForCleanup().
{ TimestampTz ltime; Assert(InHotStandby); ltime = GetStandbyLimitTime(); if (ltime == 0) { /* * We're willing to wait forever for conflicts, so set timeout for * deadlock check only */ enable_timeout_after(STANDBY_DEADLOCK_TIMEOUT, DeadlockTimeout); } else if (GetCurrentTimestamp() >= ltime) { /* * We're already behind, so clear a path as quickly as possible. */ SendRecoveryConflictWithBufferPin(PROCSIG_RECOVERY_CONFLICT_BUFFERPIN); } else { /* * Wake up at ltime, and check for deadlocks as well if we will be * waiting longer than deadlock_timeout */ EnableTimeoutParams timeouts[2]; timeouts[0].id = STANDBY_TIMEOUT; timeouts[0].type = TMPARAM_AT; timeouts[0].fin_time = ltime; timeouts[1].id = STANDBY_DEADLOCK_TIMEOUT; timeouts[1].type = TMPARAM_AFTER; timeouts[1].delay_ms = DeadlockTimeout; enable_timeouts(timeouts, 2); } /* Wait to be signaled by UnpinBuffer() */ ProcWaitForSignal(); /* * Clear any timeout requests established above. We assume here that * the Startup process doesn't have any other timeouts than what this * function uses. If that stops being true, we could cancel the * timeouts individually, but that'd be slower. */ disable_all_timeouts(false); }
void ResolveRecoveryConflictWithDatabase | ( | Oid | dbid | ) |
Definition at line 314 of file standby.c.
References CancelDBBackends(), CountDBBackends(), pg_usleep(), and PROCSIG_RECOVERY_CONFLICT_DATABASE.
Referenced by dbase_redo().
{ /* * We don't do ResolveRecoveryConflictWithVirtualXIDs() here since that * only waits for transactions and completely idle sessions would block * us. This is rare enough that we do this as simply as possible: no wait, * just force them off immediately. * * No locking is required here because we already acquired * AccessExclusiveLock. Anybody trying to connect while we do this will * block during InitPostgres() and then disconnect when they see the * database has been removed. */ while (CountDBBackends(dbid) > 0) { CancelDBBackends(dbid, PROCSIG_RECOVERY_CONFLICT_DATABASE, true); /* * Wait awhile for them to die so that we avoid flooding an * unresponsive backend when system is heavily loaded. */ pg_usleep(10000); } }
void ResolveRecoveryConflictWithSnapshot | ( | TransactionId | latestRemovedXid, | |
RelFileNode | node | |||
) |
Definition at line 264 of file standby.c.
References RelFileNode::dbNode, GetConflictingVirtualXIDs(), PROCSIG_RECOVERY_CONFLICT_SNAPSHOT, ResolveRecoveryConflictWithVirtualXIDs(), and TransactionIdIsValid.
Referenced by btree_xlog_delete(), btree_xlog_reuse_page(), heap_xlog_clean(), heap_xlog_cleanup_info(), heap_xlog_freeze(), heap_xlog_visible(), and spgRedoVacuumRedirect().
{ VirtualTransactionId *backends; /* * If we get passed InvalidTransactionId then we are a little surprised, * but it is theoretically possible in normal running. It also happens * when replaying already applied WAL records after a standby crash or * restart. If latestRemovedXid is invalid then there is no conflict. That * rule applies across all record types that suffer from this conflict. */ if (!TransactionIdIsValid(latestRemovedXid)) return; backends = GetConflictingVirtualXIDs(latestRemovedXid, node.dbNode); ResolveRecoveryConflictWithVirtualXIDs(backends, PROCSIG_RECOVERY_CONFLICT_SNAPSHOT); }
void ResolveRecoveryConflictWithTablespace | ( | Oid | tsid | ) |
Definition at line 286 of file standby.c.
References GetConflictingVirtualXIDs(), InvalidOid, InvalidTransactionId, PROCSIG_RECOVERY_CONFLICT_TABLESPACE, and ResolveRecoveryConflictWithVirtualXIDs().
Referenced by tblspc_redo().
{ VirtualTransactionId *temp_file_users; /* * Standby users may be currently using this tablespace for their * temporary files. We only care about current users because * temp_tablespace parameter will just ignore tablespaces that no longer * exist. * * Ask everybody to cancel their queries immediately so we can ensure no * temp files remain and we can remove the tablespace. Nuke the entire * site from orbit, it's the only way to be sure. * * XXX: We could work out the pids of active backends using this * tablespace by examining the temp filenames in the directory. We would * then convert the pids into VirtualXIDs before attempting to cancel * them. * * We don't wait for commit because drop tablespace is non-transactional. */ temp_file_users = GetConflictingVirtualXIDs(InvalidTransactionId, InvalidOid); ResolveRecoveryConflictWithVirtualXIDs(temp_file_users, PROCSIG_RECOVERY_CONFLICT_TABLESPACE); }
void ShutdownRecoveryTransactionEnvironment | ( | void | ) |
Definition at line 101 of file standby.c.
References ExpireAllKnownAssignedTransactionIds(), StandbyReleaseAllLocks(), and VirtualXactLockTableCleanup().
Referenced by StartupXLOG().
{ /* Mark all tracked in-progress transactions as finished. */ ExpireAllKnownAssignedTransactionIds(); /* Release all locks the tracked transactions were holding */ StandbyReleaseAllLocks(); /* Cleanup our VirtualTransaction */ VirtualXactLockTableCleanup(); }
void standby_desc | ( | StringInfo | buf, | |
uint8 | xl_info, | |||
char * | rec | |||
) |
Definition at line 40 of file standbydesc.c.
References appendStringInfo(), xl_standby_lock::dbOid, i, xl_standby_locks::locks, xl_standby_locks::nlocks, xl_standby_lock::relOid, standby_desc_running_xacts(), xl_standby_lock::xid, XLOG_RUNNING_XACTS, and XLOG_STANDBY_LOCK.
{ uint8 info = xl_info & ~XLR_INFO_MASK; if (info == XLOG_STANDBY_LOCK) { xl_standby_locks *xlrec = (xl_standby_locks *) rec; int i; appendStringInfo(buf, "AccessExclusive locks:"); for (i = 0; i < xlrec->nlocks; i++) appendStringInfo(buf, " xid %u db %u rel %u", xlrec->locks[i].xid, xlrec->locks[i].dbOid, xlrec->locks[i].relOid); } else if (info == XLOG_RUNNING_XACTS) { xl_running_xacts *xlrec = (xl_running_xacts *) rec; appendStringInfo(buf, "running xacts:"); standby_desc_running_xacts(buf, xlrec); } else appendStringInfo(buf, "UNKNOWN"); }
void standby_redo | ( | XLogRecPtr | lsn, | |
XLogRecord * | record | |||
) |
Definition at line 761 of file standby.c.
References Assert, xl_standby_lock::dbOid, elog, i, xl_running_xacts::latestCompletedXid, RunningTransactionsData::latestCompletedXid, xl_standby_locks::locks, xl_running_xacts::nextXid, RunningTransactionsData::nextXid, xl_standby_locks::nlocks, xl_running_xacts::oldestRunningXid, RunningTransactionsData::oldestRunningXid, PANIC, ProcArrayApplyRecoveryInfo(), xl_standby_lock::relOid, STANDBY_DISABLED, StandbyAcquireAccessExclusiveLock(), standbyState, xl_running_xacts::subxcnt, RunningTransactionsData::subxcnt, xl_running_xacts::subxid_overflow, RunningTransactionsData::subxid_overflow, xl_running_xacts::xcnt, RunningTransactionsData::xcnt, xl_standby_lock::xid, xl_running_xacts::xids, RunningTransactionsData::xids, XLogRecord::xl_info, XLOG_RUNNING_XACTS, XLOG_STANDBY_LOCK, XLogRecGetData, and XLR_BKP_BLOCK_MASK.
{ uint8 info = record->xl_info & ~XLR_INFO_MASK; /* Backup blocks are not used in standby records */ Assert(!(record->xl_info & XLR_BKP_BLOCK_MASK)); /* Do nothing if we're not in hot standby mode */ if (standbyState == STANDBY_DISABLED) return; if (info == XLOG_STANDBY_LOCK) { xl_standby_locks *xlrec = (xl_standby_locks *) XLogRecGetData(record); int i; for (i = 0; i < xlrec->nlocks; i++) StandbyAcquireAccessExclusiveLock(xlrec->locks[i].xid, xlrec->locks[i].dbOid, xlrec->locks[i].relOid); } else if (info == XLOG_RUNNING_XACTS) { xl_running_xacts *xlrec = (xl_running_xacts *) XLogRecGetData(record); RunningTransactionsData running; running.xcnt = xlrec->xcnt; running.subxcnt = xlrec->subxcnt; running.subxid_overflow = xlrec->subxid_overflow; running.nextXid = xlrec->nextXid; running.latestCompletedXid = xlrec->latestCompletedXid; running.oldestRunningXid = xlrec->oldestRunningXid; running.xids = xlrec->xids; ProcArrayApplyRecoveryInfo(&running); } else elog(PANIC, "standby_redo: unknown op code %u", info); }
void StandbyAcquireAccessExclusiveLock | ( | TransactionId | xid, | |
Oid | dbOid, | |||
Oid | relOid | |||
) |
Definition at line 566 of file standby.c.
References AccessExclusiveLock, Assert, xl_standby_lock::dbOid, DEBUG4, elog, lappend(), LOCKACQUIRE_NOT_AVAIL, LockAcquireExtended(), OidIsValid, palloc(), xl_standby_lock::relOid, ResolveRecoveryConflictWithLock(), SET_LOCKTAG_RELATION, trace_recovery(), TransactionIdDidAbort(), TransactionIdDidCommit(), TransactionIdIsValid, and xl_standby_lock::xid.
Referenced by lock_twophase_standby_recover(), and standby_redo().
{ xl_standby_lock *newlock; LOCKTAG locktag; /* Already processed? */ if (!TransactionIdIsValid(xid) || 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); }
void StandbyDeadLockHandler | ( | void | ) |
Definition at line 515 of file standby.c.
References PROCSIG_RECOVERY_CONFLICT_STARTUP_DEADLOCK, and SendRecoveryConflictWithBufferPin().
Referenced by StartupProcessMain().
void StandbyReleaseAllLocks | ( | void | ) |
Definition at line 659 of file standby.c.
References AccessExclusiveLock, xl_standby_lock::dbOid, DEBUG2, DEBUG4, elog, lfirst, list_delete_cell(), list_head(), lnext, LockRelease(), LOG, pfree(), xl_standby_lock::relOid, SET_LOCKTAG_RELATION, trace_recovery(), and xl_standby_lock::xid.
Referenced by ShutdownRecoveryTransactionEnvironment().
{ ListCell *cell, *prev, *next; LOCKTAG locktag; elog(trace_recovery(DEBUG2), "release all standby locks"); prev = NULL; for (cell = list_head(RecoveryLockList); cell; cell = next) { xl_standby_lock *lock = (xl_standby_lock *) lfirst(cell); next = lnext(cell); 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(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); } }
void StandbyReleaseLockTree | ( | TransactionId | xid, | |
int | nsubxids, | |||
TransactionId * | subxids | |||
) |
Definition at line 645 of file standby.c.
References i, and StandbyReleaseLocks().
Referenced by RecoverPreparedTransactions(), xact_redo_abort(), and xact_redo_commit_internal().
{ int i; StandbyReleaseLocks(xid); for (i = 0; i < nsubxids; i++) StandbyReleaseLocks(subxids[i]); }
void StandbyReleaseOldLocks | ( | int | nxids, | |
TransactionId * | xids | |||
) |
Definition at line 694 of file standby.c.
References AccessExclusiveLock, Assert, xl_standby_lock::dbOid, DEBUG4, elog, i, lfirst, list_delete_cell(), list_head(), lnext, LockRelease(), LOG, pfree(), xl_standby_lock::relOid, SET_LOCKTAG_RELATION, StandbyTransactionIdIsPrepared(), trace_recovery(), TransactionIdIsValid, and xl_standby_lock::xid.
Referenced by ProcArrayApplyRecoveryInfo().
{ ListCell *cell, *prev, *next; LOCKTAG locktag; prev = NULL; for (cell = list_head(RecoveryLockList); cell; cell = next) { xl_standby_lock *lock = (xl_standby_lock *) lfirst(cell); bool remove = false; next = lnext(cell); Assert(TransactionIdIsValid(lock->xid)); if (StandbyTransactionIdIsPrepared(lock->xid)) remove = false; else { int i; bool found = false; for (i = 0; i < nxids; i++) { if (lock->xid == xids[i]) { found = true; break; } } /* * If its not a running transaction, remove it. */ if (!found) remove = true; } if (remove) { 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(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; } }
void StandbyTimeoutHandler | ( | void | ) |
Definition at line 526 of file standby.c.
References disable_timeout(), PROCSIG_RECOVERY_CONFLICT_BUFFERPIN, SendRecoveryConflictWithBufferPin(), and STANDBY_DEADLOCK_TIMEOUT.
Referenced by StartupProcessMain().
{ /* forget any pending STANDBY_DEADLOCK_TIMEOUT request */ disable_timeout(STANDBY_DEADLOCK_TIMEOUT, false); SendRecoveryConflictWithBufferPin(PROCSIG_RECOVERY_CONFLICT_BUFFERPIN); }
Definition at line 36 of file standby.c.
Referenced by GetStandbyLimitTime().
Definition at line 37 of file standby.c.
Referenced by GetStandbyLimitTime().
Definition at line 35 of file standby.c.
Referenced by GetOldestXmin(), and GetSnapshotData().