@@ -41,7 +41,6 @@ static List *RecoveryLockList;
4141
4242static void ResolveRecoveryConflictWithVirtualXIDs (VirtualTransactionId * waitlist ,
4343 ProcSignalReason reason );
44- static void ResolveRecoveryConflictWithLock (Oid dbOid , Oid relOid );
4544static void SendRecoveryConflictWithBufferPin (ProcSignalReason reason );
4645static XLogRecPtr LogCurrentRunningXacts (RunningTransactions CurrRunningXacts );
4746static void LogAccessExclusiveLocks (int nlocks , xl_standby_lock * locks );
@@ -339,39 +338,65 @@ ResolveRecoveryConflictWithDatabase(Oid dbid)
339338 }
340339}
341340
342- static void
343- ResolveRecoveryConflictWithLock (Oid dbOid , Oid relOid )
341+ /*
342+ * ResolveRecoveryConflictWithLock is called from ProcSleep()
343+ * to resolve conflicts with other backends holding relation locks.
344+ *
345+ * The WaitLatch sleep normally done in ProcSleep()
346+ * (when not InHotStandby) is performed here, for code clarity.
347+ *
348+ * We either resolve conflicts immediately or set a timeout to wake us at
349+ * the limit of our patience.
350+ *
351+ * Resolve conflicts by cancelling to all backends holding a conflicting
352+ * lock. As we are already queued to be granted the lock, no new lock
353+ * requests conflicting with ours will be granted in the meantime.
354+ *
355+ * Deadlocks involving the Startup process and an ordinary backend process
356+ * will be detected by the deadlock detector within the ordinary backend.
357+ */
358+ void
359+ ResolveRecoveryConflictWithLock (LOCKTAG locktag )
344360{
345- VirtualTransactionId * backends ;
346- bool lock_acquired = false;
347- int num_attempts = 0 ;
348- LOCKTAG locktag ;
361+ TimestampTz ltime ;
349362
350- SET_LOCKTAG_RELATION ( locktag , dbOid , relOid );
363+ Assert ( InHotStandby );
351364
352- /*
353- * If blowing away everybody with conflicting locks doesn't work, after
354- * the first two attempts then we just start blowing everybody away until
355- * it does work. We do this because its likely that we either have too
356- * many locks and we just can't get one at all, or that there are many
357- * people crowding for the same table. Recovery must win; the end
358- * justifies the means.
359- */
360- while (!lock_acquired )
361- {
362- if (++ num_attempts < 3 )
363- backends = GetLockConflicts (& locktag , AccessExclusiveLock );
364- else
365- backends = GetConflictingVirtualXIDs (InvalidTransactionId ,
366- InvalidOid );
365+ ltime = GetStandbyLimitTime ();
367366
367+ if (GetCurrentTimestamp () >= ltime )
368+ {
369+ /*
370+ * We're already behind, so clear a path as quickly as possible.
371+ */
372+ VirtualTransactionId * backends ;
373+ backends = GetLockConflicts (& locktag , AccessExclusiveLock );
368374 ResolveRecoveryConflictWithVirtualXIDs (backends ,
369375 PROCSIG_RECOVERY_CONFLICT_LOCK );
376+ }
377+ else
378+ {
379+ /*
380+ * Wait (or wait again) until ltime
381+ */
382+ EnableTimeoutParams timeouts [1 ];
370383
371- if (LockAcquireExtended (& locktag , AccessExclusiveLock , true, true, false)
372- != LOCKACQUIRE_NOT_AVAIL )
373- lock_acquired = true;
384+ timeouts [0 ].id = STANDBY_LOCK_TIMEOUT ;
385+ timeouts [0 ].type = TMPARAM_AT ;
386+ timeouts [0 ].fin_time = ltime ;
387+ enable_timeouts (timeouts , 1 );
374388 }
389+
390+ /* Wait to be signaled by the release of the Relation Lock */
391+ ProcWaitForSignal ();
392+
393+ /*
394+ * Clear any timeout requests established above. We assume here that the
395+ * Startup process doesn't have any other outstanding timeouts than those
396+ * used by this function. If that stops being true, we could cancel the
397+ * timeouts individually, but that'd be slower.
398+ */
399+ disable_all_timeouts (false);
375400}
376401
377402/*
@@ -534,6 +559,14 @@ StandbyTimeoutHandler(void)
534559 SendRecoveryConflictWithBufferPin (PROCSIG_RECOVERY_CONFLICT_BUFFERPIN );
535560}
536561
562+ /*
563+ * StandbyLockTimeoutHandler() will be called if STANDBY_LOCK_TIMEOUT is exceeded.
564+ * This doesn't need to do anything, simply waking up is enough.
565+ */
566+ void
567+ StandbyLockTimeoutHandler (void )
568+ {
569+ }
537570
538571/*
539572 * -----------------------------------------------------
@@ -547,7 +580,7 @@ StandbyTimeoutHandler(void)
547580 * process is the proxy by which the original locks are implemented.
548581 *
549582 * We only keep track of AccessExclusiveLocks, which are only ever held by
550- * one transaction on one relation, and don't worry about lock queuing .
583+ * one transaction on one relation.
551584 *
552585 * We keep a single dynamically expandible list of locks in local memory,
553586 * RelationLockList, so we can keep track of the various entries made by
@@ -589,14 +622,9 @@ StandbyAcquireAccessExclusiveLock(TransactionId xid, Oid dbOid, Oid relOid)
589622 newlock -> relOid = relOid ;
590623 RecoveryLockList = lappend (RecoveryLockList , newlock );
591624
592- /*
593- * Attempt to acquire the lock as requested, if not resolve conflict
594- */
595625 SET_LOCKTAG_RELATION (locktag , newlock -> dbOid , newlock -> relOid );
596626
597- if (LockAcquireExtended (& locktag , AccessExclusiveLock , true, true, false)
598- == LOCKACQUIRE_NOT_AVAIL )
599- ResolveRecoveryConflictWithLock (newlock -> dbOid , newlock -> relOid );
627+ LockAcquireExtended (& locktag , AccessExclusiveLock , true, false, false);
600628}
601629
602630static void
0 commit comments