aboutsummaryrefslogtreecommitdiff
path: root/src/backend/storage/ipc
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/storage/ipc')
-rw-r--r--src/backend/storage/ipc/procarray.c20
-rw-r--r--src/backend/storage/ipc/procsignal.c5
-rw-r--r--src/backend/storage/ipc/standby.c124
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
* -----------------------------------------------------