diff options
Diffstat (limited to 'src/backend/storage/ipc')
-rw-r--r-- | src/backend/storage/ipc/procarray.c | 20 | ||||
-rw-r--r-- | src/backend/storage/ipc/procsignal.c | 5 | ||||
-rw-r--r-- | src/backend/storage/ipc/standby.c | 124 |
3 files changed, 123 insertions, 26 deletions
diff --git a/src/backend/storage/ipc/procarray.c b/src/backend/storage/ipc/procarray.c index 1793783cab9..7cd57f31405 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 453d94aea5b..6c38d423f23 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 dcead94b27c..f079dba8dcf 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; } @@ -346,6 +339,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 * ----------------------------------------------------- |