Header And Logo

PostgreSQL
| The world's most advanced open source database.

Defines | Functions | Variables

standby.c File Reference

#include "postgres.h"
#include "access/transam.h"
#include "access/twophase.h"
#include "access/xact.h"
#include "access/xlog.h"
#include "miscadmin.h"
#include "storage/bufmgr.h"
#include "storage/lmgr.h"
#include "storage/proc.h"
#include "storage/procarray.h"
#include "storage/sinvaladt.h"
#include "storage/standby.h"
#include "utils/ps_status.h"
#include "utils/timeout.h"
#include "utils/timestamp.h"
Include dependency graph for standby.c:

Go to the source code of this file.

Defines

#define STANDBY_INITIAL_WAIT_US   1000

Functions

static void ResolveRecoveryConflictWithVirtualXIDs (VirtualTransactionId *waitlist, ProcSignalReason reason)
static void ResolveRecoveryConflictWithLock (Oid dbOid, Oid relOid)
static void SendRecoveryConflictWithBufferPin (ProcSignalReason reason)
static void LogCurrentRunningXacts (RunningTransactions CurrRunningXacts)
static void LogAccessExclusiveLocks (int nlocks, xl_standby_lock *locks)
void InitRecoveryTransactionEnvironment (void)
void ShutdownRecoveryTransactionEnvironment (void)
static TimestampTz GetStandbyLimitTime (void)
static bool WaitExceedsMaxStandbyDelay (void)
void ResolveRecoveryConflictWithSnapshot (TransactionId latestRemovedXid, RelFileNode node)
void ResolveRecoveryConflictWithTablespace (Oid tsid)
void ResolveRecoveryConflictWithDatabase (Oid dbid)
void ResolveRecoveryConflictWithBufferPin (void)
void CheckRecoveryConflictDeadlock (void)
void StandbyDeadLockHandler (void)
void StandbyTimeoutHandler (void)
void StandbyAcquireAccessExclusiveLock (TransactionId xid, Oid dbOid, Oid relOid)
static void StandbyReleaseLocks (TransactionId xid)
void StandbyReleaseLockTree (TransactionId xid, int nsubxids, TransactionId *subxids)
void StandbyReleaseAllLocks (void)
void StandbyReleaseOldLocks (int nxids, TransactionId *xids)
void standby_redo (XLogRecPtr lsn, XLogRecord *record)
void LogStandbySnapshot (void)
void LogAccessExclusiveLock (Oid dbOid, Oid relOid)
void LogAccessExclusiveLockPrepare (void)

Variables

int vacuum_defer_cleanup_age
int max_standby_archive_delay = 30 * 1000
int max_standby_streaming_delay = 30 * 1000
static ListRecoveryLockList
static int standbyWait_us = STANDBY_INITIAL_WAIT_US

Define Documentation

#define STANDBY_INITIAL_WAIT_US   1000

Definition at line 150 of file standby.c.


Function Documentation

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.")));
}

static TimestampTz GetStandbyLimitTime ( void   )  [static]

Definition at line 126 of file standby.c.

References GetXLogReceiptTime(), max_standby_archive_delay, max_standby_streaming_delay, and TimestampTzPlusMilliseconds.

Referenced by ResolveRecoveryConflictWithBufferPin(), and WaitExceedsMaxStandbyDelay().

{
    TimestampTz rtime;
    bool        fromStream;

    /*
     * The cutoff time is the last WAL data receipt time plus the appropriate
     * delay variable.  Delay of -1 means wait forever.
     */
    GetXLogReceiptTime(&rtime, &fromStream);
    if (fromStream)
    {
        if (max_standby_streaming_delay < 0)
            return 0;           /* wait forever */
        return TimestampTzPlusMilliseconds(rtime, max_standby_streaming_delay);
    }
    else
    {
        if (max_standby_archive_delay < 0)
            return 0;           /* wait forever */
        return TimestampTzPlusMilliseconds(rtime, max_standby_archive_delay);
    }
}

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;
}

void LogAccessExclusiveLock ( Oid  dbOid,
Oid  relOid 
)

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();
}

static void LogAccessExclusiveLocks ( int  nlocks,
xl_standby_lock locks 
) [static]

Definition at line 954 of file standby.c.

References XLogRecData::buffer, XLogRecData::data, XLogRecData::len, XLogRecData::next, xl_standby_locks::nlocks, offsetof, XLOG_STANDBY_LOCK, and XLogInsert().

Referenced by LogAccessExclusiveLock(), and LogStandbySnapshot().

{
    XLogRecData rdata[2];
    xl_standby_locks xlrec;

    xlrec.nlocks = nlocks;

    rdata[0].data = (char *) &xlrec;
    rdata[0].len = offsetof(xl_standby_locks, locks);
    rdata[0].buffer = InvalidBuffer;
    rdata[0].next = &rdata[1];

    rdata[1].data = (char *) locks;
    rdata[1].len = nlocks * sizeof(xl_standby_lock);
    rdata[1].buffer = InvalidBuffer;
    rdata[1].next = NULL;

    (void) XLogInsert(RM_STANDBY_ID, XLOG_STANDBY_LOCK, rdata);
}

static void LogCurrentRunningXacts ( RunningTransactions  CurrRunningXacts  )  [static]

Definition at line 898 of file standby.c.

References XLogRecData::buffer, XLogRecData::data, DEBUG2, elog, RunningTransactionsData::latestCompletedXid, xl_running_xacts::latestCompletedXid, XLogRecData::len, XLogRecData::next, RunningTransactionsData::nextXid, xl_running_xacts::nextXid, RunningTransactionsData::oldestRunningXid, xl_running_xacts::oldestRunningXid, RunningTransactionsData::subxcnt, xl_running_xacts::subxcnt, RunningTransactionsData::subxid_overflow, xl_running_xacts::subxid_overflow, trace_recovery(), RunningTransactionsData::xcnt, xl_running_xacts::xcnt, RunningTransactionsData::xids, XLOG_RUNNING_XACTS, and XLogInsert().

Referenced by LogStandbySnapshot().

{
    xl_running_xacts xlrec;
    XLogRecData rdata[2];
    int         lastrdata = 0;
    XLogRecPtr  recptr;

    xlrec.xcnt = CurrRunningXacts->xcnt;
    xlrec.subxcnt = CurrRunningXacts->subxcnt;
    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 + xlrec.subxcnt) * 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,
             (uint32) (recptr >> 32), (uint32) recptr,
             CurrRunningXacts->oldestRunningXid,
             CurrRunningXacts->latestCompletedXid,
             CurrRunningXacts->nextXid);
    else
        elog(trace_recovery(DEBUG2),
             "snapshot of %u+%u running transaction ids (lsn %X/%X oldest xid %u latest complete %u next xid %u)",
             CurrRunningXacts->xcnt, CurrRunningXacts->subxcnt,
             (uint32) (recptr >> 32), (uint32) recptr,
             CurrRunningXacts->oldestRunningXid,
             CurrRunningXacts->latestCompletedXid,
             CurrRunningXacts->nextXid);
}

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);
    }
}

static void ResolveRecoveryConflictWithLock ( Oid  dbOid,
Oid  relOid 
) [static]

Definition at line 340 of file standby.c.

References AccessExclusiveLock, GetConflictingVirtualXIDs(), GetLockConflicts(), InvalidOid, InvalidTransactionId, LOCKACQUIRE_NOT_AVAIL, LockAcquireExtended(), PROCSIG_RECOVERY_CONFLICT_LOCK, ResolveRecoveryConflictWithVirtualXIDs(), and SET_LOCKTAG_RELATION.

Referenced by StandbyAcquireAccessExclusiveLock().

{
    VirtualTransactionId *backends;
    bool        lock_acquired = false;
    int         num_attempts = 0;
    LOCKTAG     locktag;

    SET_LOCKTAG_RELATION(locktag, dbOid, relOid);

    /*
     * If blowing away everybody with conflicting locks doesn't work, after
     * the first two attempts then we just start blowing everybody away until
     * it does work. We do this because its likely that we either have too
     * many locks and we just can't get one at all, or that there are many
     * people crowding for the same table. Recovery must win; the end
     * justifies the means.
     */
    while (!lock_acquired)
    {
        if (++num_attempts < 3)
            backends = GetLockConflicts(&locktag, AccessExclusiveLock);
        else
            backends = GetConflictingVirtualXIDs(InvalidTransactionId,
                                                 InvalidOid);

        ResolveRecoveryConflictWithVirtualXIDs(backends,
                                             PROCSIG_RECOVERY_CONFLICT_LOCK);

        if (LockAcquireExtended(&locktag, AccessExclusiveLock, true, true, false)
            != LOCKACQUIRE_NOT_AVAIL)
            lock_acquired = true;
    }
}

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);
}

static void ResolveRecoveryConflictWithVirtualXIDs ( VirtualTransactionId waitlist,
ProcSignalReason  reason 
) [static]

Definition at line 191 of file standby.c.

References Assert, CancelVirtualTransaction(), get_ps_display(), GetCurrentTimestamp(), NULL, palloc(), pfree(), pg_usleep(), set_ps_display(), standbyWait_us, TimestampDifferenceExceeds(), update_process_title, VirtualTransactionIdIsValid, VirtualXactLock(), and WaitExceedsMaxStandbyDelay().

Referenced by ResolveRecoveryConflictWithLock(), ResolveRecoveryConflictWithSnapshot(), and ResolveRecoveryConflictWithTablespace().

{
    TimestampTz waitStart;
    char       *new_status;

    /* Fast exit, to avoid a kernel call if there's no work to be done. */
    if (!VirtualTransactionIdIsValid(*waitlist))
        return;

    waitStart = GetCurrentTimestamp();
    new_status = NULL;          /* we haven't changed the ps display */

    while (VirtualTransactionIdIsValid(*waitlist))
    {
        /* reset standbyWait_us for each xact we wait for */
        standbyWait_us = STANDBY_INITIAL_WAIT_US;

        /* wait until the virtual xid is gone */
        while (!VirtualXactLock(*waitlist, false))
        {
            /*
             * Report via ps if we have been waiting for more than 500 msec
             * (should that be configurable?)
             */
            if (update_process_title && new_status == NULL &&
                TimestampDifferenceExceeds(waitStart, GetCurrentTimestamp(),
                                           500))
            {
                const char *old_status;
                int         len;

                old_status = get_ps_display(&len);
                new_status = (char *) palloc(len + 8 + 1);
                memcpy(new_status, old_status, len);
                strcpy(new_status + len, " waiting");
                set_ps_display(new_status, false);
                new_status[len] = '\0'; /* truncate off " waiting" */
            }

            /* Is it time to kill it? */
            if (WaitExceedsMaxStandbyDelay())
            {
                pid_t       pid;

                /*
                 * Now find out who to throw out of the balloon.
                 */
                Assert(VirtualTransactionIdIsValid(*waitlist));
                pid = CancelVirtualTransaction(*waitlist, reason);

                /*
                 * Wait a little bit for it to die so that we avoid flooding
                 * an unresponsive backend when system is heavily loaded.
                 */
                if (pid != 0)
                    pg_usleep(5000L);
            }
        }

        /* The virtual transaction is gone now, wait for the next one */
        waitlist++;
    }

    /* Reset ps display if we changed it */
    if (new_status)
    {
        set_ps_display(new_status, false);
        pfree(new_status);
    }
}

static void SendRecoveryConflictWithBufferPin ( ProcSignalReason  reason  )  [static]

Definition at line 455 of file standby.c.

References Assert, CancelDBBackends(), InvalidOid, PROCSIG_RECOVERY_CONFLICT_BUFFERPIN, and PROCSIG_RECOVERY_CONFLICT_STARTUP_DEADLOCK.

Referenced by ResolveRecoveryConflictWithBufferPin(), StandbyDeadLockHandler(), and StandbyTimeoutHandler().

{
    Assert(reason == PROCSIG_RECOVERY_CONFLICT_BUFFERPIN ||
           reason == PROCSIG_RECOVERY_CONFLICT_STARTUP_DEADLOCK);

    /*
     * We send signal to all backends to ask them if they are holding the
     * buffer pin which is delaying the Startup process. We must not set the
     * conflict flag yet, since most backends will be innocent. Let the
     * SIGUSR1 handling in each backend decide their own fate.
     */
    CancelDBBackends(InvalidOid, reason, false);
}

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_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   ) 
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);
    }
}

static void StandbyReleaseLocks ( TransactionId  xid  )  [static]

Definition at line 600 of file standby.c.

References AccessExclusiveLock, xl_standby_lock::dbOid, DEBUG4, elog, lfirst, list_delete_cell(), list_head(), lnext, LockRelease(), LOG, pfree(), xl_standby_lock::relOid, SET_LOCKTAG_RELATION, trace_recovery(), TransactionIdIsValid, and xl_standby_lock::xid.

Referenced by StandbyReleaseLockTree().

{
    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(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 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   ) 
static bool WaitExceedsMaxStandbyDelay ( void   )  [static]

Definition at line 159 of file standby.c.

References GetCurrentTimestamp(), GetStandbyLimitTime(), pg_usleep(), and standbyWait_us.

Referenced by ResolveRecoveryConflictWithVirtualXIDs().

{
    TimestampTz ltime;

    /* Are we past the limit time? */
    ltime = GetStandbyLimitTime();
    if (ltime && GetCurrentTimestamp() >= ltime)
        return true;

    /*
     * Sleep a bit (this is essential to avoid busy-waiting).
     */
    pg_usleep(standbyWait_us);

    /*
     * Progressively increase the sleep times, but not to more than 1s, since
     * pg_usleep isn't interruptable on some platforms.
     */
    standbyWait_us *= 2;
    if (standbyWait_us > 1000000)
        standbyWait_us = 1000000;

    return false;
}


Variable Documentation

int max_standby_archive_delay = 30 * 1000

Definition at line 36 of file standby.c.

Referenced by GetStandbyLimitTime().

int max_standby_streaming_delay = 30 * 1000

Definition at line 37 of file standby.c.

Referenced by GetStandbyLimitTime().

Definition at line 39 of file standby.c.

int standbyWait_us = STANDBY_INITIAL_WAIT_US [static]

Definition at line 35 of file standby.c.

Referenced by GetOldestXmin(), and GetSnapshotData().