/* Our shared memory area */ typedefstruct ProcArrayStruct
{ int numProcs; /* number of valid procs entries */ int maxProcs; /* allocated size of procs array */
/* *KnownassignedXIDshandling
*/ int maxKnownAssignedXids; /* allocated size of array */ int numKnownAssignedXids; /* current # of valid entries */ int tailKnownAssignedXids; /* index of oldest valid element */ int headKnownAssignedXids; /* index of newest element, + 1 */
/* *ReasoncodesforKnownAssignedXidsCompress().
*/ typedefenum KAXCompressReason
{
KAX_NO_SPACE, /* need to free up space at array end */
KAX_PRUNE, /* we just pruned old entries */
KAX_TRANSACTION_END, /* we just committed/removed some XIDs */
KAX_STARTUP_PROCESS_IDLE, /* startup process is about to sleep */
} KAXCompressReason;
if (TransactionIdIsValid(latestXid))
{
Assert(TransactionIdIsValid(ProcGlobal->xids[myoff]));
/* Advance global latestCompletedXid while holding the lock */
MaintainLatestCompletedXid(latestXid);
/* Same with xactCompletionCount */
TransamVariables->xactCompletionCount++;
ProcGlobal->xids[myoff] = InvalidTransactionId;
ProcGlobal->subxidStates[myoff].overflowed = false;
ProcGlobal->subxidStates[myoff].count = 0;
} else
{ /* Shouldn't be trying to remove a live transaction here */
Assert(!TransactionIdIsValid(ProcGlobal->xids[myoff]));
}
arrayP->pgprocnos[arrayP->numProcs - 1] = -1; /* for debugging */
arrayP->numProcs--;
/* *AdjustpgxactoffoffollowingprocsforremovedPGPROC(notethat *numProcsalreadyhasbeendecremented).
*/ for (int index = myoff; index < arrayP->numProcs; index++)
{ int procno = arrayP->pgprocnos[index];
/* We should definitely have an XID to clear. */
Assert(TransactionIdIsValid(proc->xid));
/* Add ourselves to the list of processes needing a group XID clear. */
proc->procArrayGroupMember = true;
proc->procArrayGroupMemberXid = latestXid;
nextidx = pg_atomic_read_u32(&procglobal->procArrayGroupFirst); while (true)
{
pg_atomic_write_u32(&proc->procArrayGroupNext, nextidx);
if (pg_atomic_compare_exchange_u32(&procglobal->procArrayGroupFirst,
&nextidx,
(uint32) pgprocno)) break;
}
/* *Ifthelistwasnotempty,theleaderwillclearourXID.Itis *impossibletohavefollowerswithoutaleaderbecausethefirstprocess *thathasaddeditselftothelistwillalwayshavenextidxas *INVALID_PROC_NUMBER.
*/ if (nextidx != INVALID_PROC_NUMBER)
{ int extraWaits = 0;
/* Sleep until the leader clears our XID. */
pgstat_report_wait_start(WAIT_EVENT_PROCARRAY_GROUP_UPDATE); for (;;)
{ /* acts as a read barrier */
PGSemaphoreLock(proc->sem); if (!proc->procArrayGroupMember) break;
extraWaits++;
}
pgstat_report_wait_end();
KnownAssignedXidsDisplay(DEBUG3); if (standbyState == STANDBY_SNAPSHOT_READY)
elog(DEBUG1, "recovery snapshots are now enabled"); else
elog(DEBUG1, "recovery snapshot waiting for non-overflowed snapshot or " "until oldest active xid on standby is at least %u (now %u)",
standbySnapshotPendingXmin,
running->oldestRunningXid);
}
/* *ProcArrayApplyXidAssignment *ProcessanXLOG_XACT_ASSIGNMENTWALrecord
*/ void
ProcArrayApplyXidAssignment(TransactionId topxid, int nsubxids, TransactionId *subxids)
{
TransactionId max_xid; int i;
/* *Step3:inhotstandbymode,checktheknown-assigned-xidslist.XIDs *inthelistmustbetreatedasrunning.
*/ if (RecoveryInProgress())
{ /* none of the PGPROC entries should have XIDs in hot standby mode */
Assert(nxids == 0);
if (KnownAssignedXidExists(xid))
{
LWLockRelease(ProcArrayLock);
xc_by_known_assigned_inc(); returntrue;
}
/* shared tables need to take backends in all databases into account */
h->shared_oldest_nonremovable =
TransactionIdOlder(h->shared_oldest_nonremovable, xmin);
switch (GlobalVisHorizonKindForRel(rel))
{ case VISHORIZON_SHARED: return horizons.shared_oldest_nonremovable; case VISHORIZON_CATALOG: return horizons.catalog_oldest_nonremovable; case VISHORIZON_DATA: return horizons.data_oldest_nonremovable; case VISHORIZON_TEMP: return horizons.temp_oldest_nonremovable;
}
/* just to prevent compiler warnings */ return InvalidTransactionId;
}
/* initialize xmin calculation with xmax */
xmin = xmax;
/* take own xid into account, saves a check inside the loop */ if (TransactionIdIsNormal(myxid) && NormalTransactionIdPrecedes(myxid, xmin))
xmin = myxid;
/* We are only interested in the specific virtual transaction. */ if (proc->vxid.procNumber != sourcevxid->procNumber) continue; if (proc->vxid.lxid != sourcevxid->localTransactionId) continue;
for (index = 0; index < arrayP->numProcs; index++)
{ int pgprocno = arrayP->pgprocnos[index];
PGPROC *proc = &allProcs[pgprocno]; int nsubxids;
/* *SavesubtransactionXIDs.Otherbackendscan'taddorremove *entrieswhilewe'reholdingXidGenLock.
*/
nsubxids = other_subxidstates[index].count; if (nsubxids > 0)
{ /* barrier not really required, as XidGenLock is held, but ... */
pg_read_barrier(); /* pairs with GetNewTransactionId */
/* *SpinoverprocArraycollectingallxidsandsubxids.
*/
LWLockAcquire(ProcArrayLock, LW_SHARED); for (index = 0; index < arrayP->numProcs; index++)
{
TransactionId xid;
/* Fetch xid just once - see GetNewTransactionId */
xid = UINT32_ACCESS_ONCE(other_xids[index]);
if (!TransactionIdIsNormal(xid)) continue;
if (TransactionIdPrecedes(xid, oldestRunningXid))
oldestRunningXid = xid;
/*
* Top-level XID of a transaction is always less than any of its
* subxids, so we don't need to check if any of the subxids are
* smaller than oldestRunningXid
*/
}
LWLockRelease(ProcArrayLock);
return oldestRunningXid;
}
/*
* GetOldestSafeDecodingTransactionId -- lowest xid not affected by vacuum
*
* Returns the oldest xid that we can guarantee not to have been affected by
* vacuum, i.e. no rows >= that xid have been vacuumed away unless the
* transaction aborted. Note that the value can (and most of the time will) be
* much more conservative than what really has been affected by vacuum, but we
* currently don't have better data available.
*
* This is useful to initialize the cutoff xid after which a new changeset
* extraction replication slot can start decoding changes.
*
* Must be called with ProcArrayLock held either shared or exclusively,
* although most callers will want to use exclusive mode since it is expected
* that the caller will immediately use the xid to peg the xmin horizon.
*/
TransactionId
GetOldestSafeDecodingTransactionId(bool catalogOnly)
{
ProcArrayStruct *arrayP = procArray;
TransactionId oldestSafeXid;
int index;
bool recovery_in_progress = RecoveryInProgress();
Assert(LWLockHeldByMe(ProcArrayLock));
/*
* Acquire XidGenLock, so no transactions can acquire an xid while we're
* running. If no transaction with xid were running concurrently a new xid
* could influence the RecentXmin et al.
*
* We initialize the computation to nextXid since that's guaranteed to be
* a safe, albeit pessimal, value.
*/
LWLockAcquire(XidGenLock, LW_SHARED);
oldestSafeXid = XidFromFullTransactionId(TransamVariables->nextXid);
/*
* If there's already a slot pegging the xmin horizon, we can start with
* that value, it's guaranteed to be safe since it's computed by this
* routine initially and has been enforced since. We can always use the
* slot's general xmin horizon, but the catalog horizon is only usable
* when only catalog data is going to be looked at.
*/
if (TransactionIdIsValid(procArray->replication_slot_xmin) &&
TransactionIdPrecedes(procArray->replication_slot_xmin,
oldestSafeXid))
oldestSafeXid = procArray->replication_slot_xmin;
if (catalogOnly &&
TransactionIdIsValid(procArray->replication_slot_catalog_xmin) &&
TransactionIdPrecedes(procArray->replication_slot_catalog_xmin,
oldestSafeXid))
oldestSafeXid = procArray->replication_slot_catalog_xmin;
/*
* If we're not in recovery, we walk over the procarray and collect the
* lowest xid. Since we're called with ProcArrayLock held and have
* acquired XidGenLock, no entries can vanish concurrently, since
* ProcGlobal->xids[i] is only set with XidGenLock held and only cleared
* with ProcArrayLock held.
*
* In recovery we can't lower the safe value besides what we've computed
* above, so we'll have to wait a bit longer there. We unfortunately can
* *not* use KnownAssignedXidsGetOldestXmin() since the KnownAssignedXids
* machinery can miss values and return an older value than is safe.
*/
if (!recovery_in_progress)
{
TransactionId *other_xids = ProcGlobal->xids;
/*
* Spin over procArray collecting min(ProcGlobal->xids[i])
*/
for (index = 0; index < arrayP->numProcs; index++)
{
TransactionId xid;
/* Fetch xid just once - see GetNewTransactionId */
xid = UINT32_ACCESS_ONCE(other_xids[index]);
if (!TransactionIdIsNormal(xid))
continue;
if (TransactionIdPrecedes(xid, oldestSafeXid))
oldestSafeXid = xid;
}
}
LWLockRelease(XidGenLock);
return oldestSafeXid;
}
/*
* GetVirtualXIDsDelayingChkpt -- Get the VXIDs of transactions that are
* delaying checkpoint because they have critical actions in progress.
*
* Constructs an array of VXIDs of transactions that are currently in commit
* critical sections, as shown by having specified delayChkptFlags bits set
* in their PGPROC.
*
* Returns a palloc'd array that should be freed by the caller.
* *nvxids is the number of valid entries.
*
* Note that because backends set or clear delayChkptFlags without holding any
* lock, the result is somewhat indeterminate, but we don't really care. Even
* in a multiprocessor with delayed writes to shared memory, it should be
* certain that setting of delayChkptFlags will propagate to shared memory
* when the backend takes a lock, so we cannot fail to see a virtual xact as
* delayChkptFlags if it's already inserted its commit record. Whether it
* takes a little while for clearing of delayChkptFlags to propagate is
* unimportant for correctness.
*/
VirtualTransactionId *
GetVirtualXIDsDelayingChkpt(int *nvxids, int type)
{
VirtualTransactionId *vxids;
ProcArrayStruct *arrayP = procArray;
int count = 0;
int index;
Assert(type != 0);
/* allocate what's certainly enough result space */
vxids = (VirtualTransactionId *)
palloc(sizeof(VirtualTransactionId) * arrayP->maxProcs);
LWLockAcquire(ProcArrayLock, LW_SHARED);
for (index = 0; index < arrayP->numProcs; index++)
{
int pgprocno = arrayP->pgprocnos[index];
PGPROC *proc = &allProcs[pgprocno];
if ((proc->delayChkptFlags & type) != 0)
{
VirtualTransactionId vxid;
GET_VXID_FROM_PGPROC(vxid, *proc);
if (VirtualTransactionIdIsValid(vxid))
vxids[count++] = vxid;
}
}
LWLockRelease(ProcArrayLock);
*nvxids = count;
return vxids;
}
/*
* HaveVirtualXIDsDelayingChkpt -- Are any of the specified VXIDs delaying?
*
* This is used with the results of GetVirtualXIDsDelayingChkpt to see if any
* of the specified VXIDs are still in critical sections of code.
*
* Note: this is O(N^2) in the number of vxacts that are/were delaying, but
* those numbers should be small enough for it not to be a problem.
*/
bool
HaveVirtualXIDsDelayingChkpt(VirtualTransactionId *vxids, int nvxids, int type)
{
bool result = false;
ProcArrayStruct *arrayP = procArray;
int index;
Assert(type != 0);
LWLockAcquire(ProcArrayLock, LW_SHARED);
for (index = 0; index < arrayP->numProcs; index++)
{
int pgprocno = arrayP->pgprocnos[index];
PGPROC *proc = &allProcs[pgprocno];
VirtualTransactionId vxid;
GET_VXID_FROM_PGPROC(vxid, *proc);
if ((proc->delayChkptFlags & type) != 0 &&
VirtualTransactionIdIsValid(vxid))
{
int i;
for (i = 0; i < nvxids; i++)
{
if (VirtualTransactionIdEquals(vxid, vxids[i]))
{
result = true;
break;
}
}
if (result)
break;
}
}
LWLockRelease(ProcArrayLock);
return result;
}
/*
* ProcNumberGetProc -- get a backend's PGPROC given its proc number
*
* The result may be out of date arbitrarily quickly, so the caller
* must be careful about how this information is used. NULL is
* returned if the backend is not active.
*/
PGPROC *
ProcNumberGetProc(ProcNumber procNumber)
{
PGPROC *result;
if (procNumber < 0 || procNumber >= ProcGlobal->allProcCount)
return NULL;
result = GetPGProcByNumber(procNumber);
if (result->pid == 0)
return NULL;
return result;
}
/*
* ProcNumberGetTransactionIds -- get a backend's transaction status
*
* Get the xid, xmin, nsubxid and overflow status of the backend. The
* result may be out of date arbitrarily quickly, so the caller must be
* careful about how this information is used.
*/
void
ProcNumberGetTransactionIds(ProcNumber procNumber, TransactionId *xid,
TransactionId *xmin, int *nsubxid, bool *overflowed)
{
PGPROC *proc;
/*
* BackendPidGetProc -- get a backend's PGPROC given its PID
*
* Returns NULL if not found. Note that it is up to the caller to be
* sure that the question remains meaningful for long enough for the
* answer to be used ...
*/
PGPROC *
BackendPidGetProc(int pid)
{
PGPROC *result;
if (pid == 0) /* never match dummy PGPROCs */
return NULL;
LWLockAcquire(ProcArrayLock, LW_SHARED);
result = BackendPidGetProcWithLock(pid);
LWLockRelease(ProcArrayLock);
return result;
}
/*
* BackendPidGetProcWithLock -- get a backend's PGPROC given its PID
*
* Same as above, except caller must be holding ProcArrayLock. The found
* entry, if any, can be assumed to be valid as long as the lock remains held.
*/
PGPROC *
BackendPidGetProcWithLock(int pid)
{
PGPROC *result = NULL;
ProcArrayStruct *arrayP = procArray;
int index;
if (pid == 0) /* never match dummy PGPROCs */
return NULL;
for (index = 0; index < arrayP->numProcs; index++)
{
PGPROC *proc = &allProcs[arrayP->pgprocnos[index]];
if (proc->pid == pid)
{
result = proc;
break;
}
}
return result;
}
/*
* BackendXidGetPid -- get a backend's pid given its XID
*
* Returns 0 if not found or it's a prepared transaction. Note that
* it is up to the caller to be sure that the question remains
* meaningful for long enough for the answer to be used ...
*
* Only main transaction Ids are considered. This function is mainly
* useful for determining what backend owns a lock.
*
* Beware that not every xact has an XID assigned. However, as long as you
* only call this using an XID found on disk, you're safe.
*/
int
BackendXidGetPid(TransactionId xid)
{
int result = 0;
ProcArrayStruct *arrayP = procArray;
TransactionId *other_xids = ProcGlobal->xids;
int index;
if (xid == InvalidTransactionId) /* never match invalid xid */
return 0;
LWLockAcquire(ProcArrayLock, LW_SHARED);
for (index = 0; index < arrayP->numProcs; index++)
{
if (other_xids[index] == xid)
{
int pgprocno = arrayP->pgprocnos[index];
PGPROC *proc = &allProcs[pgprocno];
result = proc->pid;
break;
}
}
LWLockRelease(ProcArrayLock);
return result;
}
/*
* IsBackendPid -- is a given pid a running backend
*
* This is not called by the backend, but is called by external modules.
*/
bool
IsBackendPid(int pid)
{
return (BackendPidGetProc(pid) != NULL);
}
/*
* GetCurrentVirtualXIDs -- returns an array of currently active VXIDs.
*
* The array is palloc'd. The number of valid entries is returned into *nvxids.
*
* The arguments allow filtering the set of VXIDs returned. Our own process
* is always skipped. In addition:
* If limitXmin is not InvalidTransactionId, skip processes with
* xmin > limitXmin.
* If excludeXmin0 is true, skip processes with xmin = 0.
* If allDbs is false, skip processes attached to other databases.
* If excludeVacuum isn't zero, skip processes for which
* (statusFlags & excludeVacuum) is not zero.
*
* Note: the purpose of the limitXmin and excludeXmin0 parameters is to
* allow skipping backends whose oldest live snapshot is no older than
* some snapshot we have. Since we examine the procarray with only shared
* lock, there are race conditions: a backend could set its xmin just after
* we look. Indeed, on multiprocessors with weak memory ordering, the
* other backend could have set its xmin *before* we look. We know however
* that such a backend must have held shared ProcArrayLock overlapping our
* own hold of ProcArrayLock, else we would see its xmin update. Therefore,
* any snapshot the other backend is taking concurrently with our scan cannot
* consider any transactions as still running that we think are committed
* (since backends must hold ProcArrayLock exclusive to commit).
*/
VirtualTransactionId *
GetCurrentVirtualXIDs(TransactionId limitXmin, bool excludeXmin0,
bool allDbs, int excludeVacuum,
int *nvxids)
{
VirtualTransactionId *vxids;
ProcArrayStruct *arrayP = procArray;
int count = 0;
int index;
/* allocate what's certainly enough result space */
vxids = (VirtualTransactionId *)
palloc(sizeof(VirtualTransactionId) * arrayP->maxProcs);
LWLockAcquire(ProcArrayLock, LW_SHARED);
for (index = 0; index < arrayP->numProcs; index++)
{
int pgprocno = arrayP->pgprocnos[index];
PGPROC *proc = &allProcs[pgprocno];
uint8 statusFlags = ProcGlobal->statusFlags[index];
if (proc == MyProc)
continue;
if (excludeVacuum & statusFlags)
continue;
if (allDbs || proc->databaseId == MyDatabaseId)
{
/* Fetch xmin just once - might change on us */
TransactionId pxmin = UINT32_ACCESS_ONCE(proc->xmin);
if (excludeXmin0 && !TransactionIdIsValid(pxmin))
continue;
/*
* InvalidTransactionId precedes all other XIDs, so a proc that
* hasn't set xmin yet will not be rejected by this test.
*/
if (!TransactionIdIsValid(limitXmin) ||
TransactionIdPrecedesOrEquals(pxmin, limitXmin))
{
VirtualTransactionId vxid;
GET_VXID_FROM_PGPROC(vxid, *proc);
if (VirtualTransactionIdIsValid(vxid))
vxids[count++] = vxid;
}
}
}
LWLockRelease(ProcArrayLock);
*nvxids = count;
return vxids;
}
/*
* GetConflictingVirtualXIDs -- returns an array of currently active VXIDs.
*
* Usage is limited to conflict resolution during recovery on standby servers.
* limitXmin is supplied as either a cutoff with snapshotConflictHorizon
* semantics, or InvalidTransactionId in cases where caller cannot accurately
* determine a safe snapshotConflictHorizon value.
*
* If limitXmin is InvalidTransactionId then we want to kill everybody,
* so we're not worried if they have a snapshot or not, nor does it really
* matter what type of lock we hold. Caller must avoid calling here with
* snapshotConflictHorizon style cutoffs that were set to InvalidTransactionId
* during original execution, since that actually indicates that there is
* definitely no need for a recovery conflict (the snapshotConflictHorizon
* convention for InvalidTransactionId values is the opposite of our own!).
*
* All callers that are checking xmins always now supply a valid and useful
* value for limitXmin. The limitXmin is always lower than the lowest
* numbered KnownAssignedXid that is not already a FATAL error. This is
* because we only care about cleanup records that are cleaning up tuple
* versions from committed transactions. In that case they will only occur
* at the point where the record is less than the lowest running xid. That
* allows us to say that if any backend takes a snapshot concurrently with
* us then the conflict assessment made here would never include the snapshot
* that is being derived. So we take LW_SHARED on the ProcArray and allow
* concurrent snapshots when limitXmin is valid. We might think about adding
* Assert(limitXmin < lowest(KnownAssignedXids))
* but that would not be true in the case of FATAL errors lagging in array,
* but we already know those are bogus anyway, so we skip that test.
*
* If dbOid is valid we skip backends attached to other databases.
*
* Be careful to *not* pfree the result from this function. We reuse
* this array sufficiently often that we use malloc for the result.
*/
VirtualTransactionId *
GetConflictingVirtualXIDs(TransactionId limitXmin, Oid dbOid)
{
static VirtualTransactionId *vxids;
ProcArrayStruct *arrayP = procArray;
int count = 0;
int index;
/*
* If first time through, get workspace to remember main XIDs in. We
* malloc it permanently to avoid repeated palloc/pfree overhead. Allow
* result space, remembering room for a terminator.
*/
if (vxids == NULL)
{
vxids = (VirtualTransactionId *)
malloc(sizeof(VirtualTransactionId) * (arrayP->maxProcs + 1));
if (vxids == NULL)
ereport(ERROR,
(errcode(ERRCODE_OUT_OF_MEMORY),
errmsg("out of memory")));
}
LWLockAcquire(ProcArrayLock, LW_SHARED);
for (index = 0; index < arrayP->numProcs; index++)
{
int pgprocno = arrayP->pgprocnos[index];
PGPROC *proc = &allProcs[pgprocno];
/* Exclude prepared transactions */
if (proc->pid == 0)
continue;
if (!OidIsValid(dbOid) ||
proc->databaseId == dbOid)
{
/* Fetch xmin just once - can't change on us, but good coding */
TransactionId pxmin = UINT32_ACCESS_ONCE(proc->xmin);
/*
* We ignore an invalid pxmin because this means that backend has
* no snapshot currently. We hold a Share lock to avoid contention
* with users taking snapshots. That is not a problem because the
* current xmin is always at least one higher than the latest
* removed xid, so any new snapshot would never conflict with the
* test here.
*/
if (!TransactionIdIsValid(limitXmin) ||
(TransactionIdIsValid(pxmin) && !TransactionIdFollows(pxmin, limitXmin)))
{
VirtualTransactionId vxid;
GET_VXID_FROM_PGPROC(vxid, *proc);
if (VirtualTransactionIdIsValid(vxid))
vxids[count++] = vxid;
}
}
}
/*
* CancelVirtualTransaction - used in recovery conflict processing
*
* Returns pid of the process signaled, or 0 if not found.
*/
pid_t
CancelVirtualTransaction(VirtualTransactionId vxid, ProcSignalReason sigmode)
{
return SignalVirtualTransaction(vxid, sigmode, true);
}
for (index = 0; index < arrayP->numProcs; index++)
{
int pgprocno = arrayP->pgprocnos[index];
PGPROC *proc = &allProcs[pgprocno];
VirtualTransactionId procvxid;
GET_VXID_FROM_PGPROC(procvxid, *proc);
if (procvxid.procNumber == vxid.procNumber &&
procvxid.localTransactionId == vxid.localTransactionId)
{
proc->recoveryConflictPending = conflictPending;
pid = proc->pid;
if (pid != 0)
{
/*
* Kill the pid if it's still here. If not, that's what we
* wanted so ignore any errors.
*/
(void) SendProcSignal(pid, sigmode, vxid.procNumber);
}
break;
}
}
LWLockRelease(ProcArrayLock);
return pid;
}
/*
* MinimumActiveBackends --- count backends (other than myself) that are
* in active transactions. Return true if the count exceeds the
* minimum threshold passed. This is used as a heuristic to decide if
* a pre-XLOG-flush delay is worthwhile during commit.
*
* Do not count backends that are blocked waiting for locks, since they are
* not going to get to run until someone else commits.
*/
bool
MinimumActiveBackends(int min)
{
ProcArrayStruct *arrayP = procArray;
int count = 0;
int index;
/* Quick short-circuit if no minimum is specified */
if (min == 0)
return true;
/*
* Note: for speed, we don't acquire ProcArrayLock. This is a little bit
* bogus, but since we are only testing fields for zero or nonzero, it
* should be OK. The result is only used for heuristic purposes anyway...
*/
for (index = 0; index < arrayP->numProcs; index++)
{
int pgprocno = arrayP->pgprocnos[index];
PGPROC *proc = &allProcs[pgprocno];
/*
* Since we're not holding a lock, need to be prepared to deal with
* garbage, as someone could have incremented numProcs but not yet
* filled the structure.
*
* If someone just decremented numProcs, 'proc' could also point to a
* PGPROC entry that's no longer in the array. It still points to a
* PGPROC struct, though, because freed PGPROC entries just go to the
* free list and are recycled. Its contents are nonsense in that case,
* but that's acceptable for this function.
*/
if (pgprocno == -1)
continue; /* do not count deleted entries */
if (proc == MyProc)
continue; /* do not count myself */
if (proc->xid == InvalidTransactionId)
continue; /* do not count if no XID assigned */
if (proc->pid == 0)
continue; /* do not count prepared xacts */
if (proc->waitLock != NULL)
continue; /* do not count if blocked on a lock */
count++;
if (count >= min)
break;
}
return count >= min;
}
/*
* CountDBBackends --- count backends that are using specified database
*/
int
CountDBBackends(Oid databaseid)
{
ProcArrayStruct *arrayP = procArray;
int count = 0;
int index;
LWLockAcquire(ProcArrayLock, LW_SHARED);
for (index = 0; index < arrayP->numProcs; index++)
{
int pgprocno = arrayP->pgprocnos[index];
PGPROC *proc = &allProcs[pgprocno];
if (proc->pid == 0)
continue; /* do not count prepared xacts */
if (!OidIsValid(databaseid) ||
proc->databaseId == databaseid)
count++;
}
LWLockRelease(ProcArrayLock);
return count;
}
/*
* CountDBConnections --- counts database backends (only regular backends)
*/
int
CountDBConnections(Oid databaseid)
{
ProcArrayStruct *arrayP = procArray;
int count = 0;
int index;
LWLockAcquire(ProcArrayLock, LW_SHARED);
for (index = 0; index < arrayP->numProcs; index++)
{
int pgprocno = arrayP->pgprocnos[index];
PGPROC *proc = &allProcs[pgprocno];
if (proc->pid == 0)
continue; /* do not count prepared xacts */
if (!proc->isRegularBackend)
continue; /* count only regular backend processes */
if (!OidIsValid(databaseid) ||
proc->databaseId == databaseid)
count++;
}
LWLockRelease(ProcArrayLock);
return count;
}
/*
* CancelDBBackends --- cancel backends that are using specified database
*/
void
CancelDBBackends(Oid databaseid, ProcSignalReason sigmode, bool conflictPending)
{
ProcArrayStruct *arrayP = procArray;
int index;
/* tell all backends to die */
LWLockAcquire(ProcArrayLock, LW_EXCLUSIVE);
for (index = 0; index < arrayP->numProcs; index++)
{
int pgprocno = arrayP->pgprocnos[index];
PGPROC *proc = &allProcs[pgprocno];
proc->recoveryConflictPending = conflictPending;
pid = proc->pid;
if (pid != 0)
{
/*
* Kill the pid if it's still here. If not, that's what we
* wanted so ignore any errors.
*/
(void) SendProcSignal(pid, sigmode, procvxid.procNumber);
}
}
}
LWLockRelease(ProcArrayLock);
}
/*
* CountUserBackends --- count backends that are used by specified user
* (only regular backends, not any type of background worker)
*/
int
CountUserBackends(Oid roleid)
{
ProcArrayStruct *arrayP = procArray;
int count = 0;
int index;
LWLockAcquire(ProcArrayLock, LW_SHARED);
for (index = 0; index < arrayP->numProcs; index++)
{
int pgprocno = arrayP->pgprocnos[index];
PGPROC *proc = &allProcs[pgprocno];
if (proc->pid == 0)
continue; /* do not count prepared xacts */
if (!proc->isRegularBackend)
continue; /* count only regular backend processes */
if (proc->roleId == roleid)
count++;
}
LWLockRelease(ProcArrayLock);
return count;
}
/*
* CountOtherDBBackends -- check for other backends running in the given DB
*
* If there are other backends in the DB, we will wait a maximum of 5 seconds
* for them to exit. Autovacuum backends are encouraged to exit early by
* sending them SIGTERM, but normal user backends are just waited for.
*
* The current backend is always ignored; it is caller's responsibility to
* check whether the current backend uses the given DB, if it's important.
*
* Returns true if there are (still) other backends in the DB, false if not.
* Also, *nbackends and *nprepared are set to the number of other backends
* and prepared transactions in the DB, respectively.
*
* This function is used to interlock DROP DATABASE and related commands
* against there being any active backends in the target DB --- dropping the
* DB while active backends remain would be a Bad Thing. Note that we cannot
* detect here the possibility of a newly-started backend that is trying to
* connect to the doomed database, so additional interlocking is needed during
* backend startup. The caller should normally hold an exclusive lock on the
* target DB before calling this, which is one reason we mustn't wait
* indefinitely.
*/
bool
CountOtherDBBackends(Oid databaseId, int *nbackends, int *nprepared)
{
ProcArrayStruct *arrayP = procArray;
#define MAXAUTOVACPIDS 10 /* max autovacs to SIGTERM per iteration */
int autovac_pids[MAXAUTOVACPIDS];
int tries;
/* 50 tries with 100ms sleep between tries makes 5 sec total wait */
for (tries = 0; tries < 50; tries++)
{
int nautovacs = 0;
bool found = false;
int index;
CHECK_FOR_INTERRUPTS();
*nbackends = *nprepared = 0;
LWLockAcquire(ProcArrayLock, LW_SHARED);
for (index = 0; index < arrayP->numProcs; index++)
{
int pgprocno = arrayP->pgprocnos[index];
PGPROC *proc = &allProcs[pgprocno];
uint8 statusFlags = ProcGlobal->statusFlags[index];
if (proc->databaseId != databaseId)
continue;
if (proc == MyProc)
continue;
if (!found)
return false; /* no conflicting backends, so done */
/*
* Send SIGTERM to any conflicting autovacuums before sleeping. We
* postpone this step until after the loop because we don't want to
* hold ProcArrayLock while issuing kill(). We have no idea what might
* block kill() inside the kernel...
*/
for (index = 0; index < nautovacs; index++)
(void) kill(autovac_pids[index], SIGTERM); /* ignore any error */
/* sleep, then try again */
pg_usleep(100 * 1000L); /* 100ms */
}
return true; /* timed out, still conflicts */
}
/*
* Terminate existing connections to the specified database. This routine
* is used by the DROP DATABASE command when user has asked to forcefully
* drop the database.
*
* The current backend is always ignored; it is caller's responsibility to
* check whether the current backend uses the given DB, if it's important.
*
* If the target database has a prepared transaction or permissions checks
* fail for a connection, this fails without terminating anything.
*/
void
TerminateOtherDBBackends(Oid databaseId)
{
ProcArrayStruct *arrayP = procArray;
List *pids = NIL;
int nprepared = 0;
int i;
LWLockAcquire(ProcArrayLock, LW_SHARED);
for (i = 0; i < procArray->numProcs; i++)
{
int pgprocno = arrayP->pgprocnos[i];
PGPROC *proc = &allProcs[pgprocno];
if (proc->databaseId != databaseId)
continue;
if (proc == MyProc)
continue;
if (nprepared > 0)
ereport(ERROR,
(errcode(ERRCODE_OBJECT_IN_USE),
errmsg("database \"%s\" is being used by prepared transactions",
get_database_name(databaseId)),
errdetail_plural("There is %d prepared transaction using the database.",
"There are %d prepared transactions using the database.",
nprepared,
nprepared)));
if (pids)
{
ListCell *lc;
/*
* Permissions checks relax the pg_terminate_backend checks in two
* ways, both by omitting the !OidIsValid(proc->roleId) check:
*
* - Accept terminating autovacuum workers, since DROP DATABASE
* without FORCE terminates them.
*
* - Accept terminating bgworkers. For bgworker authors, it's
* convenient to be able to recommend FORCE if a worker is blocking
* DROP DATABASE unexpectedly.
*
* Unlike pg_terminate_backend, we don't raise some warnings - like
* "PID %d is not a PostgreSQL server process", because for us already
* finished session is not a problem.
*/
foreach(lc, pids)
{
int pid = lfirst_int(lc);
PGPROC *proc = BackendPidGetProc(pid);
if (proc != NULL)
{
if (superuser_arg(proc->roleId) && !superuser())
ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
errmsg("permission denied to terminate process"),
errdetail("Only roles with the %s attribute may terminate processes of roles with the %s attribute.",
"SUPERUSER", "SUPERUSER")));
if (!has_privs_of_role(GetUserId(), proc->roleId) &&
!has_privs_of_role(GetUserId(), ROLE_PG_SIGNAL_BACKEND))
ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
errmsg("permission denied to terminate process"),
errdetail("Only roles with privileges of the role whose process is being terminated or with privileges of the \"%s\" role may terminate this process.",
"pg_signal_backend")));
}
}
/*
* There's a race condition here: once we release the ProcArrayLock,
* it's possible for the session to exit before we issue kill. That
* race condition possibility seems too unlikely to worry about. See
* pg_signal_backend.
*/
foreach(lc, pids)
{
int pid = lfirst_int(lc);
PGPROC *proc = BackendPidGetProc(pid);
if (proc != NULL)
{
/*
* If we have setsid(), signal the backend's whole process
* group
*/
#ifdef HAVE_SETSID
(void) kill(-pid, SIGTERM);
#else
(void) kill(pid, SIGTERM);
#endif
}
}
}
}
/*
* ProcArraySetReplicationSlotXmin
*
* Install limits to future computations of the xmin horizon to prevent vacuum
* and HOT pruning from removing affected rows still needed by clients with
* replication slots.
*/
void
ProcArraySetReplicationSlotXmin(TransactionId xmin, TransactionId catalog_xmin,
bool already_locked)
{
Assert(!already_locked || LWLockHeldByMe(ProcArrayLock));
if (!already_locked)
LWLockAcquire(ProcArrayLock, LW_EXCLUSIVE);
if (!already_locked)
LWLockRelease(ProcArrayLock);
elog(DEBUG1, "xmin required by slots: data %u, catalog %u",
xmin, catalog_xmin);
}
/*
* ProcArrayGetReplicationSlotXmin
*
* Return the current slot xmin limits. That's useful to be able to remove
* data that's older than those limits.
*/
void
ProcArrayGetReplicationSlotXmin(TransactionId *xmin,
TransactionId *catalog_xmin)
{
LWLockAcquire(ProcArrayLock, LW_SHARED);
if (xmin != NULL)
*xmin = procArray->replication_slot_xmin;
if (catalog_xmin != NULL)
*catalog_xmin = procArray->replication_slot_catalog_xmin;
LWLockRelease(ProcArrayLock);
}
/*
* XidCacheRemoveRunningXids
*
* Remove a bunch of TransactionIds from the list of known-running
* subtransactions for my backend. Both the specified xid and those in
* the xids[] array (of length nxids) are removed from the subxids cache.
* latestXid must be the latest XID among the group.
*/
void
XidCacheRemoveRunningXids(TransactionId xid,
int nxids, const TransactionId *xids,
TransactionId latestXid)
{
int i,
j;
XidCacheStatus *mysubxidstat;
Assert(TransactionIdIsValid(xid));
/*
* We must hold ProcArrayLock exclusively in order to remove transactions
* from the PGPROC array. (See src/backend/access/transam/README.) It's
* possible this could be relaxed since we know this routine is only used
* to abort subtransactions, but pending closer analysis we'd best be
* conservative.
*
* Note that we do not have to be careful about memory ordering of our own
* reads wrt. GetNewTransactionId() here - only this process can modify
* relevant fields of MyProc/ProcGlobal->xids[]. But we do have to be
* careful about our own writes being well ordered.
*/
LWLockAcquire(ProcArrayLock, LW_EXCLUSIVE);
/*
* Under normal circumstances xid and xids[] will be in increasing order,
* as will be the entries in subxids. Scan backwards to avoid O(N^2)
* behavior when removing a lot of xids.
*/
for (i = nxids - 1; i >= 0; i--)
{
TransactionId anxid = xids[i];
/*
* Ordinarily we should have found it, unless the cache has
* overflowed. However it's also possible for this routine to be
* invoked multiple times for the same subtransaction, in case of an
* error during AbortSubTransaction. So instead of Assert, emit a
* debug warning.
*/
if (j < 0 && !MyProc->subxidStatus.overflowed)
elog(WARNING, "did not find subXID %u in MyProc", anxid);
}
for (j = MyProc->subxidStatus.count - 1; j >= 0; j--)
{
if (TransactionIdEquals(MyProc->subxids.xids[j], xid))
{
MyProc->subxids.xids[j] = MyProc->subxids.xids[MyProc->subxidStatus.count - 1];
pg_write_barrier();
mysubxidstat->count--;
MyProc->subxidStatus.count--;
break;
}
}
/* Ordinarily we should have found it, unless the cache has overflowed */
if (j < 0 && !MyProc->subxidStatus.overflowed)
elog(WARNING, "did not find subXID %u in MyProc", xid);
/* Also advance global latestCompletedXid while holding the lock */
MaintainLatestCompletedXid(latestXid);
/* ... and xactCompletionCount */
TransamVariables->xactCompletionCount++;
/*
* If rel != NULL, return test state appropriate for relation, otherwise
* return state usable for all relations. The latter may consider XIDs as
* not-yet-visible-to-everyone that a state for a specific relation would
* already consider visible-to-everyone.
*
* This needs to be called while a snapshot is active or registered, otherwise
* there are wraparound and other dangers.
*
* See comment for GlobalVisState for details.
*/
GlobalVisState *
GlobalVisTestFor(Relation rel)
{
GlobalVisState *state = NULL;
/* XXX: we should assert that a snapshot is pushed or registered */
Assert(RecentXmin);
switch (GlobalVisHorizonKindForRel(rel))
{
case VISHORIZON_SHARED:
state = &GlobalVisSharedRels;
break;
case VISHORIZON_CATALOG:
state = &GlobalVisCatalogRels;
break;
case VISHORIZON_DATA:
state = &GlobalVisDataRels;
break;
case VISHORIZON_TEMP:
state = &GlobalVisTempRels;
break;
}
/*
* Return true if it's worth updating the accurate maybe_needed boundary.
*
* As it is somewhat expensive to determine xmin horizons, we don't want to
* repeatedly do so when there is a low likelihood of it being beneficial.
*
* The current heuristic is that we update only if RecentXmin has changed
* since the last update. If the oldest currently running transaction has not
* finished, it is unlikely that recomputing the horizon would be useful.
*/
static bool
GlobalVisTestShouldUpdate(GlobalVisState *state)
{
/* hasn't been updated yet */
if (!TransactionIdIsValid(ComputeXidHorizonsResultLastXmin))
return true;
/*
* If the maybe_needed/definitely_needed boundaries are the same, it's
* unlikely to be beneficial to refresh boundaries.
*/
if (FullTransactionIdFollowsOrEquals(state->maybe_needed,
state->definitely_needed))
return false;
/* does the last snapshot built have a different xmin? */
return RecentXmin != ComputeXidHorizonsResultLastXmin;
}
/*
* In longer running transactions it's possible that transactions we
* previously needed to treat as running aren't around anymore. So update
* definitely_needed to not be earlier than maybe_needed.
*/
GlobalVisSharedRels.definitely_needed =
FullTransactionIdNewer(GlobalVisSharedRels.maybe_needed,
GlobalVisSharedRels.definitely_needed);
GlobalVisCatalogRels.definitely_needed =
FullTransactionIdNewer(GlobalVisCatalogRels.maybe_needed,
GlobalVisCatalogRels.definitely_needed);
GlobalVisDataRels.definitely_needed =
FullTransactionIdNewer(GlobalVisDataRels.maybe_needed,
GlobalVisDataRels.definitely_needed);
GlobalVisTempRels.definitely_needed = GlobalVisTempRels.maybe_needed;
ComputeXidHorizonsResultLastXmin = RecentXmin;
}
/*
* Update boundaries in GlobalVis{Shared,Catalog, Data}Rels
* using ComputeXidHorizons().
*/
static void
GlobalVisUpdate(void)
{
ComputeXidHorizonsResult horizons;
/* updates the horizons as a side-effect */
ComputeXidHorizons(&horizons);
}
/*
* Return true if no snapshot still considers fxid to be running.
*
* The state passed needs to have been initialized for the relation fxid is
* from (NULL is also OK), otherwise the result may not be correct.
*
* See comment for GlobalVisState for details.
*/
bool
GlobalVisTestIsRemovableFullXid(GlobalVisState *state,
FullTransactionId fxid)
{
/*
* If fxid is older than maybe_needed bound, it definitely is visible to
* everyone.
*/
if (FullTransactionIdPrecedes(fxid, state->maybe_needed))
return true;
/*
* If fxid is >= definitely_needed bound, it is very likely to still be
* considered running.
*/
if (FullTransactionIdFollowsOrEquals(fxid, state->definitely_needed))
return false;
/*
* fxid is between maybe_needed and definitely_needed, i.e. there might or
* might not exist a snapshot considering fxid running. If it makes sense,
* update boundaries and recheck.
*/
if (GlobalVisTestShouldUpdate(state))
{
GlobalVisUpdate();
/*
* Wrapper around GlobalVisTestIsRemovableFullXid() for 32bit xids.
*
* It is crucial that this only gets called for xids from a source that
* protects against xid wraparounds (e.g. from a table and thus protected by
* relfrozenxid).
*/
bool
GlobalVisTestIsRemovableXid(GlobalVisState *state, TransactionId xid)
{
FullTransactionId fxid;
/*
* Convert 32 bit argument to FullTransactionId. We can do so safely
* because we know the xid has to, at the very least, be between
* [oldestXid, nextXid), i.e. within 2 billion of xid. To avoid taking a
* lock to determine either, we can just compare with
* state->definitely_needed, which was based on those value at the time
* the current snapshot was built.
*/
fxid = FullXidRelativeTo(state->definitely_needed, xid);
/*
* Convenience wrapper around GlobalVisTestFor() and
* GlobalVisTestIsRemovableXid(), see their comments.
*/
bool
GlobalVisCheckRemovableXid(Relation rel, TransactionId xid)
{
GlobalVisState *state;
state = GlobalVisTestFor(rel);
return GlobalVisTestIsRemovableXid(state, xid);
}
/*
* Convert a 32 bit transaction id into 64 bit transaction id, by assuming it
* is within MaxTransactionId / 2 of XidFromFullTransactionId(rel).
*
* Be very careful about when to use this function. It can only safely be used
* when there is a guarantee that xid is within MaxTransactionId / 2 xids of
* rel. That e.g. can be guaranteed if the caller assures a snapshot is
* held by the backend and xid is from a table (where vacuum/freezing ensures
* the xid has to be within that range), or if xid is from the procarray and
* prevents xid wraparound that way.
*/
static inline FullTransactionId
FullXidRelativeTo(FullTransactionId rel, TransactionId xid)
{
TransactionId rel_xid = XidFromFullTransactionId(rel);
/*
* In Hot Standby mode, we maintain a list of transactions that are (or were)
* running on the primary at the current point in WAL. These XIDs must be
* treated as running by standby transactions, even though they are not in
* the standby server's PGPROC array.
*
* We record all XIDs that we know have been assigned. That includes all the
* XIDs seen in WAL records, plus all unobserved XIDs that we can deduce have
* been assigned. We can deduce the existence of unobserved XIDs because we
* know XIDs are assigned in sequence, with no gaps. The KnownAssignedXids
* list expands as new XIDs are observed or inferred, and contracts when
* transaction completion records arrive.
*
* During hot standby we do not fret too much about the distinction between
* top-level XIDs and subtransaction XIDs. We store both together in the
* KnownAssignedXids list. In backends, this is copied into snapshots in
* GetSnapshotData(), taking advantage of the fact that XidInMVCCSnapshot()
* doesn't care about the distinction either. Subtransaction XIDs are
* effectively treated as top-level XIDs and in the typical case pg_subtrans
* links are *not* maintained (which does not affect visibility).
*
* We have room in KnownAssignedXids and in snapshots to hold maxProcs *
* (1 + PGPROC_MAX_CACHED_SUBXIDS) XIDs, so every primary transaction must
* report its subtransaction XIDs in a WAL XLOG_XACT_ASSIGNMENT record at
* least every PGPROC_MAX_CACHED_SUBXIDS. When we receive one of these
* records, we mark the subXIDs as children of the top XID in pg_subtrans,
* and then remove them from KnownAssignedXids. This prevents overflow of
* KnownAssignedXids and snapshots, at the cost that status checks for these
* subXIDs will take a slower path through TransactionIdIsInProgress().
* This means that KnownAssignedXids is not necessarily complete for subXIDs,
* though it should be complete for top-level XIDs; this is the same situation
* that holds with respect to the PGPROC entries in normal running.
*
* When we throw away subXIDs from KnownAssignedXids, we need to keep track of
* that, similarly to tracking overflow of a PGPROC's subxids array. We do
* that by remembering the lastOverflowedXid, ie the last thrown-away subXID.
* As long as that is within the range of interesting XIDs, we have to assume
* that subXIDs are missing from snapshots. (Note that subXID overflow occurs
* on primary when 65th subXID arrives, whereas on standby it occurs when 64th
* subXID arrives - that is not an error.)
*
* Should a backend on primary somehow disappear before it can write an abort
* record, then we just leave those XIDs in KnownAssignedXids. They actually
* aborted but we think they were running; the distinction is irrelevant
* because either way any changes done by the transaction are not visible to
* backends in the standby. We prune KnownAssignedXids when
* XLOG_RUNNING_XACTS arrives, to forestall possible overflow of the
* array due to such dead XIDs.
*/
head = pArray->headKnownAssignedXids; /* note: we no longer care about the tail pointer */
/* *Ifitstillwon'tfitthenwe'reoutofmemory
*/ if (head + nxids > pArray->maxKnownAssignedXids)
elog(ERROR, "too many KnownAssignedXids");
}
/* Now we can insert the xids into the space starting at head */
next_xid = from_xid; for (i = 0; i < nxids; i++)
{
KnownAssignedXids[head] = next_xid;
KnownAssignedXidsValid[head] = true;
TransactionIdAdvance(next_xid);
head++;
}
/* Adjust count of number of valid entries */
pArray->numKnownAssignedXids += nxids;
/* *Nowupdatetheheadpointer.Weuseawritebarriertoensurethat *otherprocessorsseetheabovearrayupdatesbeforetheyseethehead *pointerchange.Thebarrierisn'trequiredifwe'reholding *ProcArrayLockexclusively.
*/ if (!exclusive_lock)
pg_write_barrier();
pArray->headKnownAssignedXids = head;
}
/* *KnownAssignedXidsSearch * *SearchesKnownAssignedXidsforaspecificxidandoptionallyremovesit. *Returnstrueifitwasfound,falseifnot. * *CallermustholdProcArrayLockinsharedorexclusivemode. *Exclusivelockmustbeheldforremove=true.
*/ static bool
KnownAssignedXidsSearch(TransactionId xid, bool remove)
{
ProcArrayStruct *pArray = procArray; int first,
last; int head; int tail; int result_index = -1;
tail = pArray->tailKnownAssignedXids;
head = pArray->headKnownAssignedXids;
/* *Onlythestartupprocessremovesentries,sowedon'tneedtheread *barrierinthatcase.
*/ if (!remove)
pg_read_barrier(); /* pairs with KnownAssignedXidsAdd */
/* *Standardbinarysearch.NotewecanignoretheKnownAssignedXidsValid *arrayhere,sinceeveninvalidentrieswillcontainsortedXIDs.
*/
first = tail;
last = head - 1; while (first <= last)
{ int mid_index;
TransactionId mid_xid;
¤ Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.0.470Bemerkung:
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.