diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/backend/storage/ipc/procarray.c | 9 | ||||
-rw-r--r-- | src/backend/storage/ipc/standby.c | 143 | ||||
-rw-r--r-- | src/backend/storage/lmgr/proc.c | 3 | ||||
-rw-r--r-- | src/backend/tcop/postgres.c | 16 | ||||
-rw-r--r-- | src/include/storage/procarray.h | 2 |
5 files changed, 141 insertions, 32 deletions
diff --git a/src/backend/storage/ipc/procarray.c b/src/backend/storage/ipc/procarray.c index 18a0f62ba67..fc5bb6dc263 100644 --- a/src/backend/storage/ipc/procarray.c +++ b/src/backend/storage/ipc/procarray.c @@ -2655,6 +2655,13 @@ GetConflictingVirtualXIDs(TransactionId limitXmin, Oid dbOid) pid_t CancelVirtualTransaction(VirtualTransactionId vxid, ProcSignalReason sigmode) { + return SignalVirtualTransaction(vxid, sigmode, true); +} + +pid_t +SignalVirtualTransaction(VirtualTransactionId vxid, ProcSignalReason sigmode, + bool conflictPending) +{ ProcArrayStruct *arrayP = procArray; int index; pid_t pid = 0; @@ -2672,7 +2679,7 @@ CancelVirtualTransaction(VirtualTransactionId vxid, ProcSignalReason sigmode) if (procvxid.backendId == vxid.backendId && procvxid.localTransactionId == vxid.localTransactionId) { - proc->recoveryConflictPending = true; + proc->recoveryConflictPending = conflictPending; pid = proc->pid; if (pid != 0) { diff --git a/src/backend/storage/ipc/standby.c b/src/backend/storage/ipc/standby.c index 5fb0eb78111..1bef7b666d4 100644 --- a/src/backend/storage/ipc/standby.c +++ b/src/backend/storage/ipc/standby.c @@ -42,6 +42,10 @@ int max_standby_streaming_delay = 30 * 1000; static HTAB *RecoveryLockLists; +/* Flags set by timeout handlers */ +static volatile sig_atomic_t got_standby_deadlock_timeout = false; +static volatile sig_atomic_t got_standby_lock_timeout = false; + static void ResolveRecoveryConflictWithVirtualXIDs(VirtualTransactionId *waitlist, ProcSignalReason reason, bool report_waiting); static void SendRecoveryConflictWithBufferPin(ProcSignalReason reason); @@ -391,8 +395,10 @@ ResolveRecoveryConflictWithDatabase(Oid dbid) * lock. As we are already queued to be granted the lock, no new lock * requests conflicting with ours will be granted in the meantime. * - * Deadlocks involving the Startup process and an ordinary backend process - * will be detected by the deadlock detector within the ordinary backend. + * We also must check for deadlocks involving the Startup process and + * hot-standby backend processes. If deadlock_timeout is reached in + * this function, all the backends holding the conflicting locks are + * requested to check themselves for deadlocks. */ void ResolveRecoveryConflictWithLock(LOCKTAG locktag) @@ -403,7 +409,7 @@ ResolveRecoveryConflictWithLock(LOCKTAG locktag) ltime = GetStandbyLimitTime(); - if (GetCurrentTimestamp() >= ltime) + if (GetCurrentTimestamp() >= ltime && ltime != 0) { /* * We're already behind, so clear a path as quickly as possible. @@ -424,26 +430,85 @@ ResolveRecoveryConflictWithLock(LOCKTAG locktag) else { /* - * Wait (or wait again) until ltime + * Wait (or wait again) until ltime, and check for deadlocks as well + * if we will be waiting longer than deadlock_timeout */ - EnableTimeoutParams timeouts[1]; + EnableTimeoutParams timeouts[2]; + int cnt = 0; + + if (ltime != 0) + { + got_standby_lock_timeout = false; + timeouts[cnt].id = STANDBY_LOCK_TIMEOUT; + timeouts[cnt].type = TMPARAM_AT; + timeouts[cnt].fin_time = ltime; + cnt++; + } - timeouts[0].id = STANDBY_LOCK_TIMEOUT; - timeouts[0].type = TMPARAM_AT; - timeouts[0].fin_time = ltime; - enable_timeouts(timeouts, 1); + got_standby_deadlock_timeout = false; + timeouts[cnt].id = STANDBY_DEADLOCK_TIMEOUT; + timeouts[cnt].type = TMPARAM_AFTER; + timeouts[cnt].delay_ms = DeadlockTimeout; + cnt++; + + enable_timeouts(timeouts, cnt); } /* Wait to be signaled by the release of the Relation Lock */ ProcWaitForSignal(PG_WAIT_LOCK | locktag.locktag_type); /* + * Exit if ltime is reached. Then all the backends holding conflicting + * locks will be canceled in the next ResolveRecoveryConflictWithLock() + * call. + */ + if (got_standby_lock_timeout) + goto cleanup; + + if (got_standby_deadlock_timeout) + { + VirtualTransactionId *backends; + + backends = GetLockConflicts(&locktag, AccessExclusiveLock, NULL); + + /* Quick exit if there's no work to be done */ + if (!VirtualTransactionIdIsValid(*backends)) + goto cleanup; + + /* + * Send signals to all the backends holding the conflicting locks, to + * ask them to check themselves for deadlocks. + */ + while (VirtualTransactionIdIsValid(*backends)) + { + SignalVirtualTransaction(*backends, + PROCSIG_RECOVERY_CONFLICT_STARTUP_DEADLOCK, + false); + backends++; + } + + /* + * Wait again here to be signaled by the release of the Relation Lock, + * to prevent the subsequent RecoveryConflictWithLock() from causing + * deadlock_timeout and sending a request for deadlocks check again. + * Otherwise the request continues to be sent every deadlock_timeout + * until the relation locks are released or ltime is reached. + */ + got_standby_deadlock_timeout = false; + ProcWaitForSignal(PG_WAIT_LOCK | locktag.locktag_type); + } + +cleanup: + + /* * Clear any timeout requests established above. We assume here that the * Startup process doesn't have any other outstanding timeouts than those * used by this function. If that stops being true, we could cancel the * timeouts individually, but that'd be slower. */ disable_all_timeouts(false); + got_standby_lock_timeout = false; + got_standby_deadlock_timeout = false; } /* @@ -482,15 +547,7 @@ ResolveRecoveryConflictWithBufferPin(void) 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) + if (GetCurrentTimestamp() >= ltime && ltime != 0) { /* * We're already behind, so clear a path as quickly as possible. @@ -504,19 +561,47 @@ ResolveRecoveryConflictWithBufferPin(void) * waiting longer than deadlock_timeout */ EnableTimeoutParams timeouts[2]; + int cnt = 0; - 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); + if (ltime != 0) + { + timeouts[cnt].id = STANDBY_TIMEOUT; + timeouts[cnt].type = TMPARAM_AT; + timeouts[cnt].fin_time = ltime; + cnt++; + } + + got_standby_deadlock_timeout = false; + timeouts[cnt].id = STANDBY_DEADLOCK_TIMEOUT; + timeouts[cnt].type = TMPARAM_AFTER; + timeouts[cnt].delay_ms = DeadlockTimeout; + cnt++; + + enable_timeouts(timeouts, cnt); } /* Wait to be signaled by UnpinBuffer() */ ProcWaitForSignal(PG_WAIT_BUFFER_PIN); + if (got_standby_deadlock_timeout) + { + /* + * Send out a request for hot-standby backends to check themselves for + * deadlocks. + * + * XXX The subsequent ResolveRecoveryConflictWithBufferPin() will wait + * to be signaled by UnpinBuffer() again and send a request for + * deadlocks check if deadlock_timeout happens. This causes the + * request to continue to be sent every deadlock_timeout until the + * buffer is unpinned or ltime is reached. This would increase the + * workload in the startup process and backends. In practice it may + * not be so harmful because the period that the buffer is kept pinned + * is basically no so long. But we should fix this? + */ + SendRecoveryConflictWithBufferPin( + PROCSIG_RECOVERY_CONFLICT_STARTUP_DEADLOCK); + } + /* * Clear any timeout requests established above. We assume here that the * Startup process doesn't have any other timeouts than what this function @@ -524,6 +609,7 @@ ResolveRecoveryConflictWithBufferPin(void) * individually, but that'd be slower. */ disable_all_timeouts(false); + got_standby_deadlock_timeout = false; } static void @@ -583,13 +669,12 @@ CheckRecoveryConflictDeadlock(void) /* * StandbyDeadLockHandler() will be called if STANDBY_DEADLOCK_TIMEOUT - * occurs before STANDBY_TIMEOUT. Send out a request for hot-standby - * backends to check themselves for deadlocks. + * occurs before STANDBY_TIMEOUT. */ void StandbyDeadLockHandler(void) { - SendRecoveryConflictWithBufferPin(PROCSIG_RECOVERY_CONFLICT_STARTUP_DEADLOCK); + got_standby_deadlock_timeout = true; } /* @@ -608,11 +693,11 @@ StandbyTimeoutHandler(void) /* * StandbyLockTimeoutHandler() will be called if STANDBY_LOCK_TIMEOUT is exceeded. - * This doesn't need to do anything, simply waking up is enough. */ void StandbyLockTimeoutHandler(void) { + got_standby_lock_timeout = true; } /* diff --git a/src/backend/storage/lmgr/proc.c b/src/backend/storage/lmgr/proc.c index b15e576e449..4850df2e14e 100644 --- a/src/backend/storage/lmgr/proc.c +++ b/src/backend/storage/lmgr/proc.c @@ -1783,6 +1783,9 @@ CheckDeadLockAlert(void) * Have to set the latch again, even if handle_sig_alarm already did. Back * then got_deadlock_timeout wasn't yet set... It's unlikely that this * ever would be a problem, but setting a set latch again is cheap. + * + * Note that, when this function runs inside procsignal_sigusr1_handler(), + * the handler function sets the latch again after the latch is set here. */ SetLatch(MyLatch); errno = save_errno; diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c index ee1b4f68570..5501f2aaec6 100644 --- a/src/backend/tcop/postgres.c +++ b/src/backend/tcop/postgres.c @@ -2852,11 +2852,23 @@ RecoveryConflictInterrupt(ProcSignalReason reason) case PROCSIG_RECOVERY_CONFLICT_BUFFERPIN: /* - * If we aren't blocking the Startup process there is nothing - * more to do. + * If PROCSIG_RECOVERY_CONFLICT_BUFFERPIN is requested but we + * aren't blocking the Startup process there is nothing more + * to do. + * + * When PROCSIG_RECOVERY_CONFLICT_STARTUP_DEADLOCK is + * requested, if we're waiting for locks and the startup + * process is not waiting for buffer pin (i.e., also waiting + * for locks), we set the flag so that ProcSleep() will check + * for deadlocks. */ if (!HoldingBufferPinThatDelaysRecovery()) + { + if (reason == PROCSIG_RECOVERY_CONFLICT_STARTUP_DEADLOCK && + GetStartupBufferPinWaitBufId() < 0) + CheckDeadLockAlert(); return; + } MyProc->recoveryConflictPending = true; diff --git a/src/include/storage/procarray.h b/src/include/storage/procarray.h index da8b672096f..d1dc0ffe28b 100644 --- a/src/include/storage/procarray.h +++ b/src/include/storage/procarray.h @@ -105,6 +105,8 @@ extern VirtualTransactionId *GetCurrentVirtualXIDs(TransactionId limitXmin, int *nvxids); extern VirtualTransactionId *GetConflictingVirtualXIDs(TransactionId limitXmin, Oid dbOid); extern pid_t CancelVirtualTransaction(VirtualTransactionId vxid, ProcSignalReason sigmode); +extern pid_t SignalVirtualTransaction(VirtualTransactionId vxid, ProcSignalReason sigmode, + bool conflictPending); extern bool MinimumActiveBackends(int min); extern int CountDBBackends(Oid databaseid); |