@@ -41,7 +41,6 @@ static List *RecoveryLockList;
41
41
42
42
static void ResolveRecoveryConflictWithVirtualXIDs (VirtualTransactionId * waitlist ,
43
43
ProcSignalReason reason );
44
- static void ResolveRecoveryConflictWithLock (Oid dbOid , Oid relOid );
45
44
static void SendRecoveryConflictWithBufferPin (ProcSignalReason reason );
46
45
static XLogRecPtr LogCurrentRunningXacts (RunningTransactions CurrRunningXacts );
47
46
static void LogAccessExclusiveLocks (int nlocks , xl_standby_lock * locks );
@@ -339,39 +338,65 @@ ResolveRecoveryConflictWithDatabase(Oid dbid)
339
338
}
340
339
}
341
340
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 )
344
360
{
345
- VirtualTransactionId * backends ;
346
- bool lock_acquired = false;
347
- int num_attempts = 0 ;
348
- LOCKTAG locktag ;
361
+ TimestampTz ltime ;
349
362
350
- SET_LOCKTAG_RELATION ( locktag , dbOid , relOid );
363
+ Assert ( InHotStandby );
351
364
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 ();
367
366
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 );
368
374
ResolveRecoveryConflictWithVirtualXIDs (backends ,
369
375
PROCSIG_RECOVERY_CONFLICT_LOCK );
376
+ }
377
+ else
378
+ {
379
+ /*
380
+ * Wait (or wait again) until ltime
381
+ */
382
+ EnableTimeoutParams timeouts [1 ];
370
383
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 );
374
388
}
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);
375
400
}
376
401
377
402
/*
@@ -534,6 +559,14 @@ StandbyTimeoutHandler(void)
534
559
SendRecoveryConflictWithBufferPin (PROCSIG_RECOVERY_CONFLICT_BUFFERPIN );
535
560
}
536
561
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
+ }
537
570
538
571
/*
539
572
* -----------------------------------------------------
@@ -547,7 +580,7 @@ StandbyTimeoutHandler(void)
547
580
* process is the proxy by which the original locks are implemented.
548
581
*
549
582
* 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.
551
584
*
552
585
* We keep a single dynamically expandible list of locks in local memory,
553
586
* RelationLockList, so we can keep track of the various entries made by
@@ -589,14 +622,9 @@ StandbyAcquireAccessExclusiveLock(TransactionId xid, Oid dbOid, Oid relOid)
589
622
newlock -> relOid = relOid ;
590
623
RecoveryLockList = lappend (RecoveryLockList , newlock );
591
624
592
- /*
593
- * Attempt to acquire the lock as requested, if not resolve conflict
594
- */
595
625
SET_LOCKTAG_RELATION (locktag , newlock -> dbOid , newlock -> relOid );
596
626
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);
600
628
}
601
629
602
630
static void
0 commit comments