]> granicus.if.org Git - postgresql/commitdiff
Don't ignore locktable-full failures in StandbyAcquireAccessExclusiveLock.
authorTom Lane <tgl@sss.pgh.pa.us>
Wed, 19 Sep 2018 16:43:51 +0000 (12:43 -0400)
committerTom Lane <tgl@sss.pgh.pa.us>
Wed, 19 Sep 2018 16:43:51 +0000 (12:43 -0400)
Commit 37c54863c removed the code in StandbyAcquireAccessExclusiveLock
that checked the return value of LockAcquireExtended.  That created a
bug, because it's still passing reportMemoryError = false to
LockAcquireExtended, meaning that LOCKACQUIRE_NOT_AVAIL will be returned
if we're out of shared memory for the lock table.

In such a situation, the startup process would believe it had acquired an
exclusive lock even though it hadn't, with potentially dire consequences.

To fix, just drop the use of reportMemoryError = false, which allows us
to simplify the call into a plain LockAcquire().  It's unclear that the
locktable-full situation arises often enough that it's worth having a
better recovery method than crash-and-restart.  (I strongly suspect that
the only reason the code path existed at all was that it was relatively
simple to do in the pre-37c54863c implementation.  But now it's not.)

LockAcquireExtended's reportMemoryError parameter is now dead code and
could be removed.  I refrained from doing so, however, because there
was some interest in resurrecting the behavior if we do get reports of
locktable-full failures in the field.  Also, it seems unwise to remove
the parameter concurrently with shipping commit f868a8143, which added a
parameter; if there are any third-party callers of LockAcquireExtended,
we want them to get a wrong-number-of-parameters compile error rather
than a possibly-silent misinterpretation of its last parameter.

Back-patch to 9.6 where the bug was introduced.

Discussion: https://postgr.es/m/6202.1536359835@sss.pgh.pa.us

src/backend/storage/ipc/standby.c
src/backend/storage/lmgr/lock.c

index f9c12e603b1c65b206bd295f3244b9a7c9f19ada..c9bb3e987d0b16e1f00e3631ff7c1e7cab22dfd3 100644 (file)
@@ -661,8 +661,7 @@ StandbyAcquireAccessExclusiveLock(TransactionId xid, Oid dbOid, Oid relOid)
 
        SET_LOCKTAG_RELATION(locktag, newlock->dbOid, newlock->relOid);
 
-       (void) LockAcquireExtended(&locktag, AccessExclusiveLock, true, false,
-                                                          false, NULL);
+       (void) LockAcquire(&locktag, AccessExclusiveLock, true, false);
 }
 
 static void
index 58d1f32cf36076bffeaf6efb5f859605eda0b484..831ae62525806d58119d22e7425b028b2c4eaf59 100644 (file)
@@ -693,11 +693,13 @@ LockAcquire(const LOCKTAG *locktag,
 /*
  * LockAcquireExtended - allows us to specify additional options
  *
- * reportMemoryError specifies whether a lock request that fills the
- * lock table should generate an ERROR or not. This allows a priority
- * caller to note that the lock table is full and then begin taking
- * extreme action to reduce the number of other lock holders before
- * retrying the action.
+ * reportMemoryError specifies whether a lock request that fills the lock
+ * table should generate an ERROR or not.  Passing "false" allows the caller
+ * to attempt to recover from lock-table-full situations, perhaps by forcibly
+ * cancelling other lock holders and then retrying.  Note, however, that the
+ * return code for that is LOCKACQUIRE_NOT_AVAIL, so that it's unsafe to use
+ * in combination with dontWait = true, as the cause of failure couldn't be
+ * distinguished.
  *
  * If locallockp isn't NULL, *locallockp receives a pointer to the LOCALLOCK
  * table entry if a lock is successfully acquired, or NULL if not.