diff --git a/src/backend/commands/vacuumlazy.c b/src/backend/commands/vacuumlazy.c index 231e92d..56b2f1a 100644 --- a/src/backend/commands/vacuumlazy.c +++ b/src/backend/commands/vacuumlazy.c @@ -1700,7 +1700,6 @@ lazy_truncate_heap(Relation onerel, LVRelStats *vacrelstats) BlockNumber old_rel_pages = vacrelstats->rel_pages; BlockNumber new_rel_pages; PGRUsage ru0; - int lock_retry; pg_rusage_init(&ru0); @@ -1721,33 +1720,19 @@ lazy_truncate_heap(Relation onerel, LVRelStats *vacrelstats) * lock). */ vacrelstats->lock_waiter_detected = false; - lock_retry = 0; - while (true) + if (!PatientLockRelation(onerel, AccessExclusiveLock, + VACUUM_TRUNCATE_LOCK_TIMEOUT, + VACUUM_TRUNCATE_LOCK_WAIT_INTERVAL)) { - if (ConditionalLockRelation(onerel, AccessExclusiveLock)) - break; - /* - * Check for interrupts while trying to (re-)acquire the exclusive - * lock. + * We failed to establish the lock in the specified number of + * retries. This means we give up truncating. */ - CHECK_FOR_INTERRUPTS(); - - if (++lock_retry > (VACUUM_TRUNCATE_LOCK_TIMEOUT / - VACUUM_TRUNCATE_LOCK_WAIT_INTERVAL)) - { - /* - * We failed to establish the lock in the specified number of - * retries. This means we give up truncating. - */ - vacrelstats->lock_waiter_detected = true; - ereport(elevel, - (errmsg("\"%s\": stopping truncate due to conflicting lock request", - RelationGetRelationName(onerel)))); - return; - } - - pg_usleep(VACUUM_TRUNCATE_LOCK_WAIT_INTERVAL); + vacrelstats->lock_waiter_detected = true; + ereport(elevel, + (errmsg("\"%s\": stopping truncate due to conflicting lock request", + RelationGetRelationName(onerel)))); + return; } /* diff --git a/src/backend/storage/lmgr/lmgr.c b/src/backend/storage/lmgr/lmgr.c index cbee20e..63caf36 100644 --- a/src/backend/storage/lmgr/lmgr.c +++ b/src/backend/storage/lmgr/lmgr.c @@ -248,6 +248,62 @@ ConditionalLockRelation(Relation relation, LOCKMODE lockmode) } /* + * PatientLockRelation + * + * This is a convenience routine for acquiring a lock while allowing other + * locks to pass by. "Patient" might also be taken to mean "low priority". + */ +bool +PatientLockRelation(Relation relation, LOCKMODE lockmode, + long lock_timeout, long lock_wait_interval) +{ + LOCKTAG tag; + LockAcquireResult res; + int lock_retry = 0; + int num_attempts; + + if (lock_wait_interval <= 0) + elog(ERROR, "invalid lock wait interval"); + + if (lock_timeout < 0) + elog(ERROR, "invalid lock timeout"); + + num_attempts = (int) lock_timeout / lock_wait_interval; + if (num_attempts < 1) + num_attempts = 1; + + SET_LOCKTAG_RELATION(tag, + relation->rd_lockInfo.lockRelId.dbId, + relation->rd_lockInfo.lockRelId.relId); + + while (lock_retry++ < num_attempts) + { + res = LockAcquire(&tag, lockmode, false, true); + + if (res != LOCKACQUIRE_NOT_AVAIL) + { + /* + * Now that we have the lock, check for invalidation messages; see notes + * in LockRelationOid. + */ + if (res != LOCKACQUIRE_ALREADY_HELD) + AcceptInvalidationMessages(); + + return true; + } + + /* + * Check for interrupts while trying to (re-)acquire the lock. + */ + CHECK_FOR_INTERRUPTS(); + + pg_usleep(lock_wait_interval); + } + + return false; +} + +/* * UnlockRelation * * This is a convenience routine for unlocking a relation without also diff --git a/src/include/storage/lmgr.h b/src/include/storage/lmgr.h index 8288e7d..4b66c10 100644 --- a/src/include/storage/lmgr.h +++ b/src/include/storage/lmgr.h @@ -44,6 +44,8 @@ extern void UnlockRelationOid(Oid relid, LOCKMODE lockmode); extern void LockRelation(Relation relation, LOCKMODE lockmode); extern bool ConditionalLockRelation(Relation relation, LOCKMODE lockmode); +extern bool PatientLockRelation(Relation relation, LOCKMODE lockmode, + long lock_timeout, long lock_wait_interval); extern void UnlockRelation(Relation relation, LOCKMODE lockmode); extern bool LockHasWaitersRelation(Relation relation, LOCKMODE lockmode);