diff --git a/doc/src/sgml/backup.sgml b/doc/src/sgml/backup.sgml index da52f42af69bf6e243654072497d1959431d4db3..a2c1375431aeb7e35000f4b558a90146013ef42d 100644 --- a/doc/src/sgml/backup.sgml +++ b/doc/src/sgml/backup.sgml @@ -1,4 +1,4 @@ -<!-- $PostgreSQL: pgsql/doc/src/sgml/backup.sgml,v 2.136 2010/01/15 09:18:56 heikki Exp $ --> +<!-- $PostgreSQL: pgsql/doc/src/sgml/backup.sgml,v 2.137 2010/01/23 16:37:12 sriggs Exp $ --> <chapter id="backup"> <title>Backup and Restore</title> @@ -2399,7 +2399,7 @@ primary_conninfo = 'host=192.168.1.50 port=5432 user=foo password=foopass' </listitem> <listitem> <para> - Waiting to acquire buffer cleanup locks (for which there is no time out) + Waiting to acquire buffer cleanup locks </para> </listitem> <listitem> @@ -2536,11 +2536,7 @@ primary_conninfo = 'host=192.168.1.50 port=5432 user=foo password=foopass' Three-way deadlocks are possible between AccessExclusiveLocks arriving from the primary, cleanup WAL records that require buffer cleanup locks and user requests that are waiting behind replayed AccessExclusiveLocks. Deadlocks - are currently resolved by the cancellation of user processes that would - need to wait on a lock. This is heavy-handed and generates more query - cancellations than we need to, though does remove the possibility of deadlock. - This behaviour is expected to improve substantially for the main release - version of 8.5. + are resolved by time-out when we exceed <varname>max_standby_delay</>. </para> <para> @@ -2630,11 +2626,7 @@ LOG: database system is ready to accept read only connections <varname>max_standby_delay</> or even set it to zero, though that is a very aggressive setting. If the standby server is tasked as an additional server for decision support queries then it may be acceptable to set this - to a value of many hours (in seconds). It is also possible to set - <varname>max_standby_delay</> to -1 which means wait forever for queries - to complete, if there are conflicts; this will be useful when performing - an archive recovery from a backup. - </para> + to a value of many hours (in seconds). <para> Transaction status "hint bits" written on primary are not WAL-logged, diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index 03667754b859153da36e8cf0113317d4fdcd0aa6..4a681991954d21671624a55b7ab99c847033db5d 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -1,4 +1,4 @@ -<!-- $PostgreSQL: pgsql/doc/src/sgml/config.sgml,v 1.244 2010/01/15 09:18:58 heikki Exp $ --> +<!-- $PostgreSQL: pgsql/doc/src/sgml/config.sgml,v 1.245 2010/01/23 16:37:12 sriggs Exp $ --> <chapter Id="runtime-config"> <title>Server Configuration</title> @@ -1825,14 +1825,15 @@ archive_command = 'copy "%p" "C:\\server\\archivedir\\%f"' # Windows <listitem> <para> When server acts as a standby, this parameter specifies a wait policy - for queries that conflict with incoming data changes. Valid settings - are -1, meaning wait forever, or a wait time of 0 or more seconds. - If a conflict should occur the server will delay up to this - amount before it begins trying to resolve things less amicably, as + for queries that conflict with data changes being replayed by recovery. + If a conflict should occur the server will delay up to this number + of seconds before it begins trying to resolve things less amicably, as described in <xref linkend="hot-standby-conflict">. Typically, this parameter makes sense only during replication, so when - performing an archive recovery to recover from data loss a - parameter setting of 0 is recommended. The default is 30 seconds. + performing an archive recovery to recover from data loss a very high + parameter setting is recommended. The default is 30 seconds. + There is no wait-forever setting because of the potential for deadlock + which that setting would introduce. This parameter can only be set in the <filename>postgresql.conf</> file or on the server command line. </para> diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c index a7ff66a6b8614cd8ba5b5fdd069f8d3c39c4f5e7..004ea0a46bf779c40a6e10003dd256dac36db5d1 100644 --- a/src/backend/access/transam/xlog.c +++ b/src/backend/access/transam/xlog.c @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/backend/access/transam/xlog.c,v 1.359 2010/01/20 19:43:40 heikki Exp $ + * $PostgreSQL: pgsql/src/backend/access/transam/xlog.c,v 1.360 2010/01/23 16:37:12 sriggs Exp $ * *------------------------------------------------------------------------- */ @@ -8803,9 +8803,12 @@ StartupProcessMain(void) */ pqsignal(SIGHUP, StartupProcSigHupHandler); /* reload config file */ pqsignal(SIGINT, SIG_IGN); /* ignore query cancel */ - pqsignal(SIGTERM, StartupProcShutdownHandler); /* request shutdown */ - pqsignal(SIGQUIT, startupproc_quickdie); /* hard crash time */ - pqsignal(SIGALRM, SIG_IGN); + pqsignal(SIGTERM, StartupProcShutdownHandler); /* request shutdown */ + pqsignal(SIGQUIT, startupproc_quickdie); /* hard crash time */ + if (XLogRequestRecoveryConnections) + pqsignal(SIGALRM, handle_standby_sig_alarm); /* ignored unless InHotStandby */ + else + pqsignal(SIGALRM, SIG_IGN); pqsignal(SIGPIPE, SIG_IGN); pqsignal(SIGUSR1, SIG_IGN); pqsignal(SIGUSR2, SIG_IGN); diff --git a/src/backend/storage/buffer/bufmgr.c b/src/backend/storage/buffer/bufmgr.c index 77f35fd361d75627333af16c57112c2741af11a2..470800d5f47b9f9b9765087f4f93546c0ac1fb0e 100644 --- a/src/backend/storage/buffer/bufmgr.c +++ b/src/backend/storage/buffer/bufmgr.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/storage/buffer/bufmgr.c,v 1.254 2010/01/02 16:57:51 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/storage/buffer/bufmgr.c,v 1.255 2010/01/23 16:37:12 sriggs Exp $ * *------------------------------------------------------------------------- */ @@ -44,6 +44,7 @@ #include "storage/ipc.h" #include "storage/proc.h" #include "storage/smgr.h" +#include "storage/standby.h" #include "utils/rel.h" #include "utils/resowner.h" @@ -2417,13 +2418,48 @@ LockBufferForCleanup(Buffer buffer) PinCountWaitBuf = bufHdr; UnlockBufHdr(bufHdr); LockBuffer(buffer, BUFFER_LOCK_UNLOCK); + /* Wait to be signaled by UnpinBuffer() */ - ProcWaitForSignal(); + if (InHotStandby) + { + /* Share the bufid that Startup process waits on */ + SetStartupBufferPinWaitBufId(buffer - 1); + /* Set alarm and then wait to be signaled by UnpinBuffer() */ + ResolveRecoveryConflictWithBufferPin(); + SetStartupBufferPinWaitBufId(-1); + } + else + ProcWaitForSignal(); + PinCountWaitBuf = NULL; /* Loop back and try again */ } } +/* + * Check called from RecoveryConflictInterrupt handler when Startup + * process requests cancelation of all pin holders that are blocking it. + */ +bool +HoldingBufferPinThatDelaysRecovery(void) +{ + int bufid = GetStartupBufferPinWaitBufId(); + + /* + * If we get woken slowly then it's possible that the Startup process + * was already woken by other backends before we got here. Also possible + * that we get here by multiple interrupts or interrupts at inappropriate + * times, so make sure we do nothing if the bufid is not set. + */ + if (bufid < 0) + return false; + + if (PrivateRefCount[bufid] > 0) + return true; + + return false; +} + /* * ConditionalLockBufferForCleanup - as above, but don't wait to get the lock * diff --git a/src/backend/storage/ipc/procarray.c b/src/backend/storage/ipc/procarray.c index 1793783cab987b0247342653b1105d8e673ac727..7cd57f31405d1cc4747dd2602c1b41e0b494760e 100644 --- a/src/backend/storage/ipc/procarray.c +++ b/src/backend/storage/ipc/procarray.c @@ -37,7 +37,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/storage/ipc/procarray.c,v 1.58 2010/01/21 00:53:58 sriggs Exp $ + * $PostgreSQL: pgsql/src/backend/storage/ipc/procarray.c,v 1.59 2010/01/23 16:37:12 sriggs Exp $ * *------------------------------------------------------------------------- */ @@ -1680,15 +1680,13 @@ GetCurrentVirtualXIDs(TransactionId limitXmin, bool excludeXmin0, * latestCompletedXid since doing so would be a performance issue during * normal running, so we check it essentially for free on the standby. * - * If dbOid is valid we skip backends attached to other databases. Some - * callers choose to skipExistingConflicts. + * 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, - bool skipExistingConflicts) +GetConflictingVirtualXIDs(TransactionId limitXmin, Oid dbOid) { static VirtualTransactionId *vxids; ProcArrayStruct *arrayP = procArray; @@ -1727,9 +1725,6 @@ GetConflictingVirtualXIDs(TransactionId limitXmin, Oid dbOid, if (proc->pid == 0) continue; - if (skipExistingConflicts && proc->recoveryConflictPending) - continue; - if (!OidIsValid(dbOid) || proc->databaseId == dbOid) { @@ -1886,7 +1881,7 @@ CountDBBackends(Oid databaseid) * CancelDBBackends --- cancel backends that are using specified database */ void -CancelDBBackends(Oid databaseid) +CancelDBBackends(Oid databaseid, ProcSignalReason sigmode, bool conflictPending) { ProcArrayStruct *arrayP = procArray; int index; @@ -1899,13 +1894,13 @@ CancelDBBackends(Oid databaseid) { volatile PGPROC *proc = arrayP->procs[index]; - if (proc->databaseId == databaseid) + if (databaseid == InvalidOid || proc->databaseId == databaseid) { VirtualTransactionId procvxid; GET_VXID_FROM_PGPROC(procvxid, *proc); - proc->recoveryConflictPending = true; + proc->recoveryConflictPending = conflictPending; pid = proc->pid; if (pid != 0) { @@ -1913,8 +1908,7 @@ CancelDBBackends(Oid databaseid) * Kill the pid if it's still here. If not, that's what we wanted * so ignore any errors. */ - (void) SendProcSignal(pid, PROCSIG_RECOVERY_CONFLICT_DATABASE, - procvxid.backendId); + (void) SendProcSignal(pid, sigmode, procvxid.backendId); } } } diff --git a/src/backend/storage/ipc/procsignal.c b/src/backend/storage/ipc/procsignal.c index 453d94aea5bc4762ab0a0017ed14a27e2ec78577..6c38d423f230a7bccf7a2ce60461d453c3ece3fb 100644 --- a/src/backend/storage/ipc/procsignal.c +++ b/src/backend/storage/ipc/procsignal.c @@ -8,7 +8,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/storage/ipc/procsignal.c,v 1.3 2010/01/16 10:05:50 sriggs Exp $ + * $PostgreSQL: pgsql/src/backend/storage/ipc/procsignal.c,v 1.4 2010/01/23 16:37:12 sriggs Exp $ * *------------------------------------------------------------------------- */ @@ -272,5 +272,8 @@ procsignal_sigusr1_handler(SIGNAL_ARGS) if (CheckProcSignal(PROCSIG_RECOVERY_CONFLICT_SNAPSHOT)) RecoveryConflictInterrupt(PROCSIG_RECOVERY_CONFLICT_SNAPSHOT); + if (CheckProcSignal(PROCSIG_RECOVERY_CONFLICT_BUFFERPIN)) + RecoveryConflictInterrupt(PROCSIG_RECOVERY_CONFLICT_BUFFERPIN); + errno = save_errno; } diff --git a/src/backend/storage/ipc/standby.c b/src/backend/storage/ipc/standby.c index dcead94b27c8355cedea1d3579deff498cd9f16d..f079dba8dcf4fe885c1d9ac202bce0ba26ad7b3b 100644 --- a/src/backend/storage/ipc/standby.c +++ b/src/backend/storage/ipc/standby.c @@ -11,7 +11,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/storage/ipc/standby.c,v 1.6 2010/01/16 10:13:04 sriggs Exp $ + * $PostgreSQL: pgsql/src/backend/storage/ipc/standby.c,v 1.7 2010/01/23 16:37:12 sriggs Exp $ * *------------------------------------------------------------------------- */ @@ -126,10 +126,6 @@ WaitExceedsMaxStandbyDelay(void) long delay_secs; int delay_usecs; - /* max_standby_delay = -1 means wait forever, if necessary */ - if (MaxStandbyDelay < 0) - return false; - /* Are we past max_standby_delay? */ TimestampDifference(GetLatestXLogTime(), GetCurrentTimestamp(), &delay_secs, &delay_usecs); @@ -241,8 +237,7 @@ ResolveRecoveryConflictWithSnapshot(TransactionId latestRemovedXid) VirtualTransactionId *backends; backends = GetConflictingVirtualXIDs(latestRemovedXid, - InvalidOid, - true); + InvalidOid); ResolveRecoveryConflictWithVirtualXIDs(backends, PROCSIG_RECOVERY_CONFLICT_SNAPSHOT); @@ -273,8 +268,7 @@ ResolveRecoveryConflictWithTablespace(Oid tsid) * non-transactional. */ temp_file_users = GetConflictingVirtualXIDs(InvalidTransactionId, - InvalidOid, - false); + InvalidOid); ResolveRecoveryConflictWithVirtualXIDs(temp_file_users, PROCSIG_RECOVERY_CONFLICT_TABLESPACE); } @@ -295,7 +289,7 @@ ResolveRecoveryConflictWithDatabase(Oid dbid) */ while (CountDBBackends(dbid) > 0) { - CancelDBBackends(dbid); + CancelDBBackends(dbid, PROCSIG_RECOVERY_CONFLICT_TABLESPACE, true); /* * Wait awhile for them to die so that we avoid flooding an @@ -331,8 +325,7 @@ ResolveRecoveryConflictWithLock(Oid dbOid, Oid relOid) else { backends = GetConflictingVirtualXIDs(InvalidTransactionId, - InvalidOid, - true); + InvalidOid); report_memory_error = true; } @@ -345,6 +338,113 @@ ResolveRecoveryConflictWithLock(Oid dbOid, Oid relOid) } } +/* + * ResolveRecoveryConflictWithBufferPin is called from LockBufferForCleanup() + * to resolve conflicts with other backends holding buffer pins. + * + * We either resolve conflicts immediately or set a SIGALRM to wake us at + * the limit of our patience. The sleep in LockBufferForCleanup() is + * performed here, for code clarity. + * + * Resolve conflict by sending a SIGUSR1 reason to all backends to check if + * they hold one of the buffer pins that is blocking Startup process. If so, + * backends will take an appropriate error action, ERROR or FATAL. + * + * A secondary purpose of this is to avoid deadlocks that might occur between + * the Startup process and lock waiters. Deadlocks occur because if queries + * wait on a lock, that must be behind an AccessExclusiveLock, which can only + * be clared if the Startup process replays a transaction completion record. + * If Startup process is waiting then that is a deadlock. If we allowed a + * setting of max_standby_delay that meant "wait forever" we would then need + * special code to protect against deadlock. Such deadlocks are rare, so the + * code would be almost certainly buggy, so we avoid both long waits and + * deadlocks using the same mechanism. + */ +void +ResolveRecoveryConflictWithBufferPin(void) +{ + bool sig_alarm_enabled = false; + + Assert(InHotStandby); + + /* + * Signal immediately or set alarm for later. + */ + if (MaxStandbyDelay == 0) + SendRecoveryConflictWithBufferPin(); + else + { + TimestampTz now; + long standby_delay_secs; /* How far Startup process is lagging */ + int standby_delay_usecs; + + now = GetCurrentTimestamp(); + + /* Are we past max_standby_delay? */ + TimestampDifference(GetLatestXLogTime(), now, + &standby_delay_secs, &standby_delay_usecs); + + if (standby_delay_secs >= (long) MaxStandbyDelay) + SendRecoveryConflictWithBufferPin(); + else + { + TimestampTz fin_time; /* Expected wake-up time by timer */ + long timer_delay_secs; /* Amount of time we set timer for */ + int timer_delay_usecs = 0; + + /* + * How much longer we should wait? + */ + timer_delay_secs = MaxStandbyDelay - standby_delay_secs; + if (standby_delay_usecs > 0) + { + timer_delay_secs -= 1; + timer_delay_usecs = 1000000 - standby_delay_usecs; + } + + /* + * It's possible that the difference is less than a microsecond; + * ensure we don't cancel, rather than set, the interrupt. + */ + if (timer_delay_secs == 0 && timer_delay_usecs == 0) + timer_delay_usecs = 1; + + /* + * When is the finish time? We recheck this if we are woken early. + */ + fin_time = TimestampTzPlusMilliseconds(now, + (timer_delay_secs * 1000) + + (timer_delay_usecs / 1000)); + + if (enable_standby_sig_alarm(timer_delay_secs, timer_delay_usecs, fin_time)) + sig_alarm_enabled = true; + else + elog(FATAL, "could not set timer for process wakeup"); + } + } + + /* Wait to be signaled by UnpinBuffer() */ + ProcWaitForSignal(); + + if (sig_alarm_enabled) + { + if (!disable_standby_sig_alarm()) + elog(FATAL, "could not disable timer for process wakeup"); + } +} + +void +SendRecoveryConflictWithBufferPin(void) +{ + /* + * 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, PROCSIG_RECOVERY_CONFLICT_BUFFERPIN, false); +} + /* * ----------------------------------------------------- * Locking in Recovery Mode diff --git a/src/backend/storage/lmgr/lock.c b/src/backend/storage/lmgr/lock.c index 74fbcbd18eca435f2508a478d6432818897c0138..ea781a8b29cfa979e142d3985e112cfd02ae5287 100644 --- a/src/backend/storage/lmgr/lock.c +++ b/src/backend/storage/lmgr/lock.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/storage/lmgr/lock.c,v 1.190 2010/01/02 16:57:52 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/storage/lmgr/lock.c,v 1.191 2010/01/23 16:37:12 sriggs Exp $ * * NOTES * A lock table is a shared memory hash table. When @@ -814,25 +814,6 @@ LockAcquireExtended(const LOCKTAG *locktag, return LOCKACQUIRE_NOT_AVAIL; } - /* - * In Hot Standby we abort the lock wait if Startup process is waiting - * since this would result in a deadlock. The deadlock occurs because - * if we are waiting it must be behind an AccessExclusiveLock, which - * can only clear when a transaction completion record is replayed. - * If Startup process is waiting we never will clear that lock, so to - * wait for it just causes a deadlock. - */ - if (RecoveryInProgress() && !InRecovery && - locktag->locktag_type == LOCKTAG_RELATION) - { - LWLockRelease(partitionLock); - ereport(ERROR, - (errcode(ERRCODE_T_R_DEADLOCK_DETECTED), - errmsg("possible deadlock detected"), - errdetail("process conflicts with recovery - please resubmit query later"), - errdetail_log("process conflicts with recovery"))); - } - /* * Set bitmask of locks this process already holds on this object. */ diff --git a/src/backend/storage/lmgr/proc.c b/src/backend/storage/lmgr/proc.c index ac891827ec1c4b0665ba06f67ff520d17ad0c8cd..da64e1953a3259ffdb2769b9282938b4ec422db5 100644 --- a/src/backend/storage/lmgr/proc.c +++ b/src/backend/storage/lmgr/proc.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/storage/lmgr/proc.c,v 1.213 2010/01/16 10:05:50 sriggs Exp $ + * $PostgreSQL: pgsql/src/backend/storage/lmgr/proc.c,v 1.214 2010/01/23 16:37:12 sriggs Exp $ * *------------------------------------------------------------------------- */ @@ -73,6 +73,7 @@ NON_EXEC_STATIC PGPROC *AuxiliaryProcs = NULL; static LOCALLOCK *lockAwaited = NULL; /* Mark these volatile because they can be changed by signal handler */ +static volatile bool standby_timeout_active = false; static volatile bool statement_timeout_active = false; static volatile bool deadlock_timeout_active = false; static volatile DeadLockState deadlock_state = DS_NOT_YET_CHECKED; @@ -89,6 +90,7 @@ static void RemoveProcFromArray(int code, Datum arg); static void ProcKill(int code, Datum arg); static void AuxiliaryProcKill(int code, Datum arg); static bool CheckStatementTimeout(void); +static bool CheckStandbyTimeout(void); /* @@ -107,6 +109,8 @@ ProcGlobalShmemSize(void) size = add_size(size, mul_size(MaxBackends, sizeof(PGPROC))); /* ProcStructLock */ size = add_size(size, sizeof(slock_t)); + /* startupBufferPinWaitBufId */ + size = add_size(size, sizeof(NBuffers)); return size; } @@ -487,10 +491,43 @@ PublishStartupProcessInformation(void) procglobal->startupProc = MyProc; procglobal->startupProcPid = MyProcPid; + procglobal->startupBufferPinWaitBufId = 0; SpinLockRelease(ProcStructLock); } +/* + * Used from bufgr to share the value of the buffer that Startup waits on, + * or to reset the value to "not waiting" (-1). This allows processing + * of recovery conflicts for buffer pins. Set is made before backends look + * at this value, so locking not required, especially since the set is + * an atomic integer set operation. + */ +void +SetStartupBufferPinWaitBufId(int bufid) +{ + /* use volatile pointer to prevent code rearrangement */ + volatile PROC_HDR *procglobal = ProcGlobal; + + procglobal->startupBufferPinWaitBufId = bufid; +} + +/* + * Used by backends when they receive a request to check for buffer pin waits. + */ +int +GetStartupBufferPinWaitBufId(void) +{ + int bufid; + + /* use volatile pointer to prevent code rearrangement */ + volatile PROC_HDR *procglobal = ProcGlobal; + + bufid = procglobal->startupBufferPinWaitBufId; + + return bufid; +} + /* * Check whether there are at least N free PGPROC objects. * @@ -1542,7 +1579,7 @@ CheckStatementTimeout(void) /* - * Signal handler for SIGALRM + * Signal handler for SIGALRM for normal user backends * * Process deadlock check and/or statement timeout check, as needed. * To avoid various edge cases, we must be careful to do nothing @@ -1565,3 +1602,112 @@ handle_sig_alarm(SIGNAL_ARGS) errno = save_errno; } + +/* + * Signal handler for SIGALRM in Startup process + * + * To avoid various edge cases, we must be careful to do nothing + * when there is nothing to be done. We also need to be able to + * reschedule the timer interrupt if called before end of statement. + */ +bool +enable_standby_sig_alarm(long delay_s, int delay_us, TimestampTz fin_time) +{ + struct itimerval timeval; + + Assert(delay_s >= 0 && delay_us >= 0); + + statement_fin_time = fin_time; + + standby_timeout_active = true; + + MemSet(&timeval, 0, sizeof(struct itimerval)); + timeval.it_value.tv_sec = delay_s; + timeval.it_value.tv_usec = delay_us; + if (setitimer(ITIMER_REAL, &timeval, NULL)) + return false; + return true; +} + +bool +disable_standby_sig_alarm(void) +{ + /* + * Always disable the interrupt if it is active; this avoids being + * interrupted by the signal handler and thereby possibly getting + * confused. + * + * We will re-enable the interrupt if necessary in CheckStandbyTimeout. + */ + if (standby_timeout_active) + { + struct itimerval timeval; + + MemSet(&timeval, 0, sizeof(struct itimerval)); + if (setitimer(ITIMER_REAL, &timeval, NULL)) + { + standby_timeout_active = false; + return false; + } + } + + standby_timeout_active = false; + + return true; +} + +/* + * CheckStandbyTimeout() runs unconditionally in the Startup process + * SIGALRM handler. Timers will only be set when InHotStandby. + * We simply ignore any signals unless the timer has been set. + */ +static bool +CheckStandbyTimeout(void) +{ + TimestampTz now; + + standby_timeout_active = false; + + now = GetCurrentTimestamp(); + + if (now >= statement_fin_time) + SendRecoveryConflictWithBufferPin(); + else + { + /* Not time yet, so (re)schedule the interrupt */ + long secs; + int usecs; + struct itimerval timeval; + + TimestampDifference(now, statement_fin_time, + &secs, &usecs); + + /* + * It's possible that the difference is less than a microsecond; + * ensure we don't cancel, rather than set, the interrupt. + */ + if (secs == 0 && usecs == 0) + usecs = 1; + + standby_timeout_active = true; + + MemSet(&timeval, 0, sizeof(struct itimerval)); + timeval.it_value.tv_sec = secs; + timeval.it_value.tv_usec = usecs; + if (setitimer(ITIMER_REAL, &timeval, NULL)) + return false; + } + + return true; +} + +void +handle_standby_sig_alarm(SIGNAL_ARGS) +{ + int save_errno = errno; + + if (standby_timeout_active) + (void) CheckStandbyTimeout(); + + errno = save_errno; +} diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c index e034a4e6501ffd0df71592f55c2037ff6dbca749..86d9e3c9847741d91839bf66bc6cc8d60e46e9b4 100644 --- a/src/backend/tcop/postgres.c +++ b/src/backend/tcop/postgres.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/tcop/postgres.c,v 1.585 2010/01/21 09:30:36 sriggs Exp $ + * $PostgreSQL: pgsql/src/backend/tcop/postgres.c,v 1.586 2010/01/23 16:37:12 sriggs Exp $ * * NOTES * this is the "main" module of the postgres backend and @@ -2718,6 +2718,18 @@ RecoveryConflictInterrupt(ProcSignalReason reason) { switch (reason) { + case PROCSIG_RECOVERY_CONFLICT_BUFFERPIN: + /* + * If we aren't blocking the Startup process there is + * nothing more to do. + */ + if (!HoldingBufferPinThatDelaysRecovery()) + return; + + MyProc->recoveryConflictPending = true; + + /* Intentional drop through to error handling */ + case PROCSIG_RECOVERY_CONFLICT_LOCK: case PROCSIG_RECOVERY_CONFLICT_TABLESPACE: case PROCSIG_RECOVERY_CONFLICT_SNAPSHOT: diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c index 274030195fb417f74ed2e4311602956a70af3fb1..6846d13a9c7fae4f92d5560829fc806324d17b72 100644 --- a/src/backend/utils/misc/guc.c +++ b/src/backend/utils/misc/guc.c @@ -10,7 +10,7 @@ * Written by Peter Eisentraut <peter_e@gmx.net>. * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/utils/misc/guc.c,v 1.533 2010/01/15 09:19:04 heikki Exp $ + * $PostgreSQL: pgsql/src/backend/utils/misc/guc.c,v 1.534 2010/01/23 16:37:12 sriggs Exp $ * *-------------------------------------------------------------------- */ @@ -1383,7 +1383,7 @@ static struct config_int ConfigureNamesInt[] = NULL }, &MaxStandbyDelay, - 30, -1, INT_MAX, NULL, NULL + 30, 0, INT_MAX, NULL, NULL }, { diff --git a/src/include/storage/bufmgr.h b/src/include/storage/bufmgr.h index e8e2abae1ea98d089d33fccb02d97f0d1c32fde0..dc4376ee9a5885e825795d8b3c5c28209a0b104b 100644 --- a/src/include/storage/bufmgr.h +++ b/src/include/storage/bufmgr.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/storage/bufmgr.h,v 1.123 2010/01/02 16:58:08 momjian Exp $ + * $PostgreSQL: pgsql/src/include/storage/bufmgr.h,v 1.124 2010/01/23 16:37:12 sriggs Exp $ * *------------------------------------------------------------------------- */ @@ -198,6 +198,7 @@ extern void LockBuffer(Buffer buffer, int mode); extern bool ConditionalLockBuffer(Buffer buffer); extern void LockBufferForCleanup(Buffer buffer); extern bool ConditionalLockBufferForCleanup(Buffer buffer); +extern bool HoldingBufferPinThatDelaysRecovery(void); extern void AbortBufferIO(void); diff --git a/src/include/storage/proc.h b/src/include/storage/proc.h index 5050090324583710def8810b4247ffbc1825cf3d..5a4421bfa5ca9e640946999df0eed755c7fda8a8 100644 --- a/src/include/storage/proc.h +++ b/src/include/storage/proc.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/storage/proc.h,v 1.118 2010/01/16 10:05:50 sriggs Exp $ + * $PostgreSQL: pgsql/src/include/storage/proc.h,v 1.119 2010/01/23 16:37:12 sriggs Exp $ * *------------------------------------------------------------------------- */ @@ -16,7 +16,7 @@ #include "storage/lock.h" #include "storage/pg_sema.h" - +#include "utils/timestamp.h" /* * Each backend advertises up to PGPROC_MAX_CACHED_SUBXIDS TransactionIds @@ -145,6 +145,8 @@ typedef struct PROC_HDR /* The proc of the Startup process, since not in ProcArray */ PGPROC *startupProc; int startupProcPid; + /* Buffer id of the buffer that Startup process waits for pin on */ + int startupBufferPinWaitBufId; } PROC_HDR; /* @@ -177,6 +179,8 @@ extern void InitProcessPhase2(void); extern void InitAuxiliaryProcess(void); extern void PublishStartupProcessInformation(void); +extern void SetStartupBufferPinWaitBufId(int bufid); +extern int GetStartupBufferPinWaitBufId(void); extern bool HaveNFreeProcs(int n); extern void ProcReleaseLocks(bool isCommit); @@ -194,4 +198,8 @@ extern bool enable_sig_alarm(int delayms, bool is_statement_timeout); extern bool disable_sig_alarm(bool is_statement_timeout); extern void handle_sig_alarm(SIGNAL_ARGS); +extern bool enable_standby_sig_alarm(long delay_s, int delay_us, TimestampTz fin_time); +extern bool disable_standby_sig_alarm(void); +extern void handle_standby_sig_alarm(SIGNAL_ARGS); + #endif /* PROC_H */ diff --git a/src/include/storage/procarray.h b/src/include/storage/procarray.h index 42953b0843cf48438434d3f5063a1e2d4bbebcec..ed68be6f27198b9f1e119ce7d6d2854ccc39e005 100644 --- a/src/include/storage/procarray.h +++ b/src/include/storage/procarray.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/storage/procarray.h,v 1.30 2010/01/16 10:05:56 sriggs Exp $ + * $PostgreSQL: pgsql/src/include/storage/procarray.h,v 1.31 2010/01/23 16:37:12 sriggs Exp $ * *------------------------------------------------------------------------- */ @@ -57,13 +57,12 @@ extern bool IsBackendPid(int pid); extern VirtualTransactionId *GetCurrentVirtualXIDs(TransactionId limitXmin, bool excludeXmin0, bool allDbs, int excludeVacuum, int *nvxids); -extern VirtualTransactionId *GetConflictingVirtualXIDs(TransactionId limitXmin, - Oid dbOid, bool skipExistingConflicts); +extern VirtualTransactionId *GetConflictingVirtualXIDs(TransactionId limitXmin, Oid dbOid); extern pid_t CancelVirtualTransaction(VirtualTransactionId vxid, ProcSignalReason sigmode); extern int CountActiveBackends(void); extern int CountDBBackends(Oid databaseid); -extern void CancelDBBackends(Oid databaseid); +extern void CancelDBBackends(Oid databaseid, ProcSignalReason sigmode, bool conflictPending); extern int CountUserBackends(Oid roleid); extern bool CountOtherDBBackends(Oid databaseId, int *nbackends, int *nprepared); diff --git a/src/include/storage/procsignal.h b/src/include/storage/procsignal.h index cbe0e24c6725b3ee0ff6f6102c217ea31747574a..2a67a62a9228b9cc9102d816d1bb4bfc76b79da7 100644 --- a/src/include/storage/procsignal.h +++ b/src/include/storage/procsignal.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/storage/procsignal.h,v 1.3 2010/01/16 10:05:57 sriggs Exp $ + * $PostgreSQL: pgsql/src/include/storage/procsignal.h,v 1.4 2010/01/23 16:37:12 sriggs Exp $ * *------------------------------------------------------------------------- */ @@ -37,6 +37,7 @@ typedef enum PROCSIG_RECOVERY_CONFLICT_TABLESPACE, PROCSIG_RECOVERY_CONFLICT_LOCK, PROCSIG_RECOVERY_CONFLICT_SNAPSHOT, + PROCSIG_RECOVERY_CONFLICT_BUFFERPIN, NUM_PROCSIGNALS /* Must be last! */ } ProcSignalReason; diff --git a/src/include/storage/standby.h b/src/include/storage/standby.h index 8c982cffb633d08303b6ac0057c2297098d475dd..e12798045ae0047b3f1a836c89b69536097d687c 100644 --- a/src/include/storage/standby.h +++ b/src/include/storage/standby.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/storage/standby.h,v 1.4 2010/01/16 10:05:57 sriggs Exp $ + * $PostgreSQL: pgsql/src/include/storage/standby.h,v 1.5 2010/01/23 16:37:12 sriggs Exp $ * *------------------------------------------------------------------------- */ @@ -19,12 +19,15 @@ extern int vacuum_defer_cleanup_age; +extern void InitRecoveryTransactionEnvironment(void); +extern void ShutdownRecoveryTransactionEnvironment(void); + extern void ResolveRecoveryConflictWithSnapshot(TransactionId latestRemovedXid); extern void ResolveRecoveryConflictWithTablespace(Oid tsid); extern void ResolveRecoveryConflictWithDatabase(Oid dbid); -extern void InitRecoveryTransactionEnvironment(void); -extern void ShutdownRecoveryTransactionEnvironment(void); +extern void ResolveRecoveryConflictWithBufferPin(void); +extern void SendRecoveryConflictWithBufferPin(void); /* * Standby Rmgr (RM_STANDBY_ID)