diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index e55700f35b8..ecd5e36ffbc 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -7678,6 +7678,25 @@ log_line_prefix = '%m [%p] %q%u@%d/%a ' + + log_lock_failure (boolean) + + log_lock_failure configuration parameter + + + + + Controls whether a log message is produced when a lock acquisition + fails. This is useful for analyzing the causes of lock failures. + Currently, only lock failures due to SELECT NOWAIT + is supported. The default is off. Only superusers + and users with the appropriate SET privilege can change + this setting. + + + + + log_recovery_conflict_waits (boolean) diff --git a/src/backend/access/heap/heapam.c b/src/backend/access/heap/heapam.c index fa7935a0ed3..e640e55a285 100644 --- a/src/backend/access/heap/heapam.c +++ b/src/backend/access/heap/heapam.c @@ -98,7 +98,8 @@ static void MultiXactIdWait(MultiXactId multi, MultiXactStatus status, uint16 in Relation rel, ItemPointer ctid, XLTW_Oper oper, int *remaining); static bool ConditionalMultiXactIdWait(MultiXactId multi, MultiXactStatus status, - uint16 infomask, Relation rel, int *remaining); + uint16 infomask, Relation rel, int *remaining, + LockHoldersAndWaiters *lockHoldersAndWaiters); static void index_delete_sort(TM_IndexDeleteOp *delstate); static int bottomup_sort_and_shrink(TM_IndexDeleteOp *delstate); static XLogRecPtr log_heap_new_cid(Relation relation, HeapTuple tup); @@ -162,8 +163,8 @@ static const struct LockTuple((rel), (tup), tupleLockExtraInfo[mode].hwlock) #define UnlockTupleTuplock(rel, tup, mode) \ UnlockTuple((rel), (tup), tupleLockExtraInfo[mode].hwlock) -#define ConditionalLockTupleTuplock(rel, tup, mode) \ - ConditionalLockTuple((rel), (tup), tupleLockExtraInfo[mode].hwlock) +#define ConditionalLockTupleTuplock(rel, tup, mode, buf) \ + ConditionalLockTuple((rel), (tup), tupleLockExtraInfo[mode].hwlock, (buf)) #ifdef USE_PREFETCH /* @@ -4894,7 +4895,7 @@ l3: case LockWaitSkip: if (!ConditionalMultiXactIdWait((MultiXactId) xwait, status, infomask, relation, - NULL)) + NULL, NULL)) { result = TM_WouldBlock; /* recovery code expects to have buffer lock held */ @@ -4903,13 +4904,25 @@ l3: } break; case LockWaitError: + LockHoldersAndWaiters *lockHoldersAndWaiters = CreateLockHoldersAndWaiters(); if (!ConditionalMultiXactIdWait((MultiXactId) xwait, status, infomask, relation, - NULL)) + NULL, lockHoldersAndWaiters)) ereport(ERROR, - (errcode(ERRCODE_LOCK_NOT_AVAILABLE), - errmsg("could not obtain lock on row in relation \"%s\"", - RelationGetRelationName(relation)))); + (errcode(ERRCODE_LOCK_NOT_AVAILABLE), + errmsg("could not obtain lock on row in relation \"%s\"", + RelationGetRelationName(relation)), + errdetail_log_plural( + "process %d could not obtain %s on %s, " + "Process holding the lock: %s, Wait queue: %s.", + "process %d could not obtain %s on %s, " + "Processes holding the lock: %s, Wait queue: %s.", + *lockHoldersAndWaiters->lockHoldersNum, + MyProcPid, + lockHoldersAndWaiters->modename, + lockHoldersAndWaiters->describe_locktag->data, + lockHoldersAndWaiters->lock_holders_sbuf->data, + lockHoldersAndWaiters->lock_waiters_sbuf->data))); break; } @@ -4934,7 +4947,7 @@ l3: XLTW_Lock); break; case LockWaitSkip: - if (!ConditionalXactLockTableWait(xwait)) + if (!ConditionalXactLockTableWait(xwait, NULL)) { result = TM_WouldBlock; /* recovery code expects to have buffer lock held */ @@ -4943,11 +4956,22 @@ l3: } break; case LockWaitError: - if (!ConditionalXactLockTableWait(xwait)) + LockHoldersAndWaiters *lockHoldersAndWaiters = CreateLockHoldersAndWaiters(); + if (!ConditionalXactLockTableWait(xwait, lockHoldersAndWaiters)) ereport(ERROR, - (errcode(ERRCODE_LOCK_NOT_AVAILABLE), - errmsg("could not obtain lock on row in relation \"%s\"", - RelationGetRelationName(relation)))); + (errcode(ERRCODE_LOCK_NOT_AVAILABLE), + errmsg("could not obtain lock on row in relation \"%s\"", + RelationGetRelationName(relation)), + errdetail_log_plural("process %d could not obtain %s on %s, " + "Process holding the lock: %s, Wait queue: %s.", + "process %d could not obtain %s on %s, " + "Processes holding the lock: %s, Wait queue: %s.", + *lockHoldersAndWaiters->lockHoldersNum, + MyProcPid, + lockHoldersAndWaiters->modename, + lockHoldersAndWaiters->describe_locktag->data, + lockHoldersAndWaiters->lock_holders_sbuf->data, + lockHoldersAndWaiters->lock_waiters_sbuf->data))); break; } } @@ -5203,16 +5227,27 @@ heap_acquire_tuplock(Relation relation, ItemPointer tid, LockTupleMode mode, break; case LockWaitSkip: - if (!ConditionalLockTupleTuplock(relation, tid, mode)) + if (!ConditionalLockTupleTuplock(relation, tid, mode, NULL)) return false; break; case LockWaitError: - if (!ConditionalLockTupleTuplock(relation, tid, mode)) + LockHoldersAndWaiters *lockHoldersAndWaiters = CreateLockHoldersAndWaiters(); + if (!ConditionalLockTupleTuplock(relation, tid, mode, lockHoldersAndWaiters)) ereport(ERROR, - (errcode(ERRCODE_LOCK_NOT_AVAILABLE), - errmsg("could not obtain lock on row in relation \"%s\"", - RelationGetRelationName(relation)))); + (errcode(ERRCODE_LOCK_NOT_AVAILABLE), + errmsg("could not obtain lock on row in relation \"%s\"", + RelationGetRelationName(relation)), + errdetail_log_plural("process %d could not obtain %s on %s, " + "Process holding the lock: %s, Wait queue: %s.", + "process %d could not obtain %s on %s, " + "Processes holding the lock: %s, Wait queue: %s.", + *lockHoldersAndWaiters->lockHoldersNum, + MyProcPid, + lockHoldersAndWaiters->modename, + lockHoldersAndWaiters->describe_locktag->data, + lockHoldersAndWaiters->lock_holders_sbuf->data, + lockHoldersAndWaiters->lock_waiters_sbuf->data))); break; } *have_tuple_lock = true; @@ -7602,7 +7637,7 @@ static bool Do_MultiXactIdWait(MultiXactId multi, MultiXactStatus status, uint16 infomask, bool nowait, Relation rel, ItemPointer ctid, XLTW_Oper oper, - int *remaining) + int *remaining, LockHoldersAndWaiters *buf) { bool result = true; MultiXactMember *members; @@ -7649,7 +7684,7 @@ Do_MultiXactIdWait(MultiXactId multi, MultiXactStatus status, */ if (nowait) { - result = ConditionalXactLockTableWait(memxid); + result = ConditionalXactLockTableWait(memxid, buf); if (!result) break; } @@ -7682,7 +7717,7 @@ MultiXactIdWait(MultiXactId multi, MultiXactStatus status, uint16 infomask, int *remaining) { (void) Do_MultiXactIdWait(multi, status, infomask, false, - rel, ctid, oper, remaining); + rel, ctid, oper, remaining, NULL); } /* @@ -7700,10 +7735,11 @@ MultiXactIdWait(MultiXactId multi, MultiXactStatus status, uint16 infomask, */ static bool ConditionalMultiXactIdWait(MultiXactId multi, MultiXactStatus status, - uint16 infomask, Relation rel, int *remaining) + uint16 infomask, Relation rel, int *remaining, + LockHoldersAndWaiters *lockHoldersAndWaiters) { return Do_MultiXactIdWait(multi, status, infomask, true, - rel, NULL, XLTW_None, remaining); + rel, NULL, XLTW_None, remaining, lockHoldersAndWaiters); } /* diff --git a/src/backend/access/heap/heapam_handler.c b/src/backend/access/heap/heapam_handler.c index e78682c3cef..2ad65931f50 100644 --- a/src/backend/access/heap/heapam_handler.c +++ b/src/backend/access/heap/heapam_handler.c @@ -455,16 +455,28 @@ tuple_lock_retry: XLTW_FetchUpdated); break; case LockWaitSkip: - if (!ConditionalXactLockTableWait(SnapshotDirty.xmax)) + if (!ConditionalXactLockTableWait(SnapshotDirty.xmax, NULL)) /* skip instead of waiting */ return TM_WouldBlock; break; case LockWaitError: - if (!ConditionalXactLockTableWait(SnapshotDirty.xmax)) + LockHoldersAndWaiters *lockHoldersAndWaiters = CreateLockHoldersAndWaiters(); + if (!ConditionalXactLockTableWait(SnapshotDirty.xmax, lockHoldersAndWaiters)) ereport(ERROR, - (errcode(ERRCODE_LOCK_NOT_AVAILABLE), - errmsg("could not obtain lock on row in relation \"%s\"", - RelationGetRelationName(relation)))); + (errcode(ERRCODE_LOCK_NOT_AVAILABLE), + errmsg("could not obtain lock on row in relation \"%s\"", + RelationGetRelationName(relation)), + errdetail_log_plural( + "process %d could not obtain %s on %s, " + "Process holding the lock: %s, Wait queue: %s.", + "process %d could not obtain %s on %s, " + "Processes holding the lock: %s, Wait queue: %s.", + *lockHoldersAndWaiters->lockHoldersNum, + MyProcPid, + lockHoldersAndWaiters->modename, + lockHoldersAndWaiters->describe_locktag->data, + lockHoldersAndWaiters->lock_holders_sbuf->data, + lockHoldersAndWaiters->lock_waiters_sbuf->data))); break; } continue; /* loop back to repeat heap_fetch */ diff --git a/src/backend/storage/lmgr/lmgr.c b/src/backend/storage/lmgr/lmgr.c index 68271cb6408..46f039ad389 100644 --- a/src/backend/storage/lmgr/lmgr.c +++ b/src/backend/storage/lmgr/lmgr.c @@ -22,6 +22,7 @@ #include "miscadmin.h" #include "pgstat.h" #include "storage/lmgr.h" +#include "storage/lock.h" #include "storage/proc.h" #include "storage/procarray.h" #include "utils/inval.h" @@ -112,7 +113,8 @@ LockRelationOid(Oid relid, LOCKMODE lockmode) SetLocktagRelationOid(&tag, relid); - res = LockAcquireExtended(&tag, lockmode, false, false, true, &locallock); + res = LockAcquireExtended(&tag, lockmode, false, false, true, + &locallock, NULL); /* * Now that we have the lock, check for invalidation messages, so that we @@ -155,7 +157,8 @@ ConditionalLockRelationOid(Oid relid, LOCKMODE lockmode) SetLocktagRelationOid(&tag, relid); - res = LockAcquireExtended(&tag, lockmode, false, true, true, &locallock); + res = LockAcquireExtended(&tag, lockmode, false, true, true, + &locallock, NULL); if (res == LOCKACQUIRE_NOT_AVAIL) return false; @@ -188,7 +191,8 @@ LockRelationId(LockRelId *relid, LOCKMODE lockmode) SET_LOCKTAG_RELATION(tag, relid->dbId, relid->relId); - res = LockAcquireExtended(&tag, lockmode, false, false, true, &locallock); + res = LockAcquireExtended(&tag, lockmode, false, false, true, + &locallock, NULL); /* * Now that we have the lock, check for invalidation messages; see notes @@ -250,7 +254,8 @@ LockRelation(Relation relation, LOCKMODE lockmode) relation->rd_lockInfo.lockRelId.dbId, relation->rd_lockInfo.lockRelId.relId); - res = LockAcquireExtended(&tag, lockmode, false, false, true, &locallock); + res = LockAcquireExtended(&tag, lockmode, false, false, true, + &locallock, NULL); /* * Now that we have the lock, check for invalidation messages; see notes @@ -281,7 +286,8 @@ ConditionalLockRelation(Relation relation, LOCKMODE lockmode) relation->rd_lockInfo.lockRelId.dbId, relation->rd_lockInfo.lockRelId.relId); - res = LockAcquireExtended(&tag, lockmode, false, true, true, &locallock); + res = LockAcquireExtended(&tag, lockmode, false, true, true, + &locallock, NULL); if (res == LOCKACQUIRE_NOT_AVAIL) return false; @@ -574,7 +580,8 @@ LockTuple(Relation relation, ItemPointer tid, LOCKMODE lockmode) * Returns true iff the lock was acquired. */ bool -ConditionalLockTuple(Relation relation, ItemPointer tid, LOCKMODE lockmode) +ConditionalLockTuple(Relation relation, ItemPointer tid, + LOCKMODE lockmode, LockHoldersAndWaiters *lockHoldersAndWaiters) { LOCKTAG tag; @@ -584,7 +591,8 @@ ConditionalLockTuple(Relation relation, ItemPointer tid, LOCKMODE lockmode) ItemPointerGetBlockNumber(tid), ItemPointerGetOffsetNumber(tid)); - return (LockAcquire(&tag, lockmode, false, true) != LOCKACQUIRE_NOT_AVAIL); + return (LockAcquireExtended(&tag, lockmode, false, true, true, + NULL, lockHoldersAndWaiters) != LOCKACQUIRE_NOT_AVAIL); } /* @@ -726,7 +734,8 @@ XactLockTableWait(TransactionId xid, Relation rel, ItemPointer ctid, * Returns true if the lock was acquired. */ bool -ConditionalXactLockTableWait(TransactionId xid) +ConditionalXactLockTableWait(TransactionId xid, + LockHoldersAndWaiters *lockHoldersAndWaiters) { LOCKTAG tag; bool first = true; @@ -738,7 +747,9 @@ ConditionalXactLockTableWait(TransactionId xid) SET_LOCKTAG_TRANSACTION(tag, xid); - if (LockAcquire(&tag, ShareLock, false, true) == LOCKACQUIRE_NOT_AVAIL) + if (LockAcquireExtended(&tag, ShareLock, false, true, true, + NULL, lockHoldersAndWaiters) + == LOCKACQUIRE_NOT_AVAIL) return false; LockRelease(&tag, ShareLock, false); @@ -1027,7 +1038,8 @@ ConditionalLockDatabaseObject(Oid classid, Oid objid, uint16 objsubid, objid, objsubid); - res = LockAcquireExtended(&tag, lockmode, false, true, true, &locallock); + res = LockAcquireExtended(&tag, lockmode, false, true, true, + &locallock, NULL); if (res == LOCKACQUIRE_NOT_AVAIL) return false; @@ -1106,7 +1118,8 @@ ConditionalLockSharedObject(Oid classid, Oid objid, uint16 objsubid, objid, objsubid); - res = LockAcquireExtended(&tag, lockmode, false, true, true, &locallock); + res = LockAcquireExtended(&tag, lockmode, false, true, true, + &locallock, NULL); if (res == LOCKACQUIRE_NOT_AVAIL) return false; diff --git a/src/backend/storage/lmgr/lock.c b/src/backend/storage/lmgr/lock.c index 11b4d1085bb..666cbce5acf 100644 --- a/src/backend/storage/lmgr/lock.c +++ b/src/backend/storage/lmgr/lock.c @@ -39,6 +39,7 @@ #include "access/xlogutils.h" #include "miscadmin.h" #include "pg_trace.h" +#include "storage/lmgr.h" #include "storage/proc.h" #include "storage/procarray.h" #include "storage/spin.h" @@ -48,8 +49,9 @@ #include "utils/resowner.h" -/* This configuration variable is used to set the lock table size */ -int max_locks_per_xact; /* set by guc.c */ +/* GUC variables. */ +int max_locks_per_xact; /* This configuration variable is used to set the lock table size */ +bool log_lock_failure = false; #define NLOCKENTS() \ mul_size(max_locks_per_xact, add_size(MaxBackends, max_prepared_xacts)) @@ -806,7 +808,7 @@ LockAcquire(const LOCKTAG *locktag, bool dontWait) { return LockAcquireExtended(locktag, lockmode, sessionLock, dontWait, - true, NULL); + true, NULL, NULL); } /* @@ -829,7 +831,8 @@ LockAcquireExtended(const LOCKTAG *locktag, bool sessionLock, bool dontWait, bool reportMemoryError, - LOCALLOCK **locallockp) + LOCALLOCK **locallockp, + LockHoldersAndWaiters *lockHoldersAndWaiters) { LOCKMETHODID lockmethodid = locktag->locktag_lockmethodid; LockMethod lockMethodTable; @@ -1145,6 +1148,24 @@ LockAcquireExtended(const LOCKTAG *locktag, if (dontWait) { + if (log_lock_failure && lockHoldersAndWaiters) + { + DescribeLockTag(lockHoldersAndWaiters->describe_locktag, + &locallock->tag.lock); + lockHoldersAndWaiters->modename = + GetLockmodeName(locallock->tag.lock.locktag_lockmethodid, + lockmode); + + /* Collect lock holders and waiters */ + LWLockAcquire(partitionLock, LW_SHARED); + + CollectLockHoldersAndWaiters(locallock, + lockHoldersAndWaiters->lock_holders_sbuf, + lockHoldersAndWaiters->lock_waiters_sbuf, + lockHoldersAndWaiters->lockHoldersNum); + + LWLockRelease(partitionLock); + } if (locallockp) *locallockp = NULL; return LOCKACQUIRE_NOT_AVAIL; @@ -4788,3 +4809,24 @@ LockWaiterCount(const LOCKTAG *locktag) return waiters; } +/* + * CreateLockHoldersAndWaiters + * + * initializing the LockHoldersAndWaiters + */ +LockHoldersAndWaiters * CreateLockHoldersAndWaiters(void) +{ + LockHoldersAndWaiters *buf = + (LockHoldersAndWaiters *) palloc(sizeof(LockHoldersAndWaiters)); + buf->describe_locktag = palloc(sizeof(StringInfoData)); + buf->lock_waiters_sbuf = palloc(sizeof(StringInfoData)); + buf->lock_holders_sbuf = palloc(sizeof(StringInfoData)); + buf->lockHoldersNum = palloc(sizeof(int)); + + initStringInfo(buf->describe_locktag); + initStringInfo(buf->lock_holders_sbuf); + initStringInfo(buf->lock_waiters_sbuf); + *(buf->lockHoldersNum) = 0; + + return buf; +} \ No newline at end of file diff --git a/src/backend/storage/lmgr/proc.c b/src/backend/storage/lmgr/proc.c index 49204f91a20..34d6cbbb896 100644 --- a/src/backend/storage/lmgr/proc.c +++ b/src/backend/storage/lmgr/proc.c @@ -1514,10 +1514,6 @@ ProcSleep(LOCALLOCK *locallock) long secs; int usecs; long msecs; - dlist_iter proc_iter; - PROCLOCK *curproclock; - bool first_holder = true, - first_waiter = true; int lockHoldersNum = 0; initStringInfo(&buf); @@ -1533,53 +1529,11 @@ ProcSleep(LOCALLOCK *locallock) msecs = secs * 1000 + usecs / 1000; usecs = usecs % 1000; - /* - * we loop over the lock's procLocks to gather a list of all - * holders and waiters. Thus we will be able to provide more - * detailed information for lock debugging purposes. - * - * lock->procLocks contains all processes which hold or wait for - * this lock. - */ - LWLockAcquire(partitionLock, LW_SHARED); - dlist_foreach(proc_iter, &lock->procLocks) - { - curproclock = - dlist_container(PROCLOCK, lockLink, proc_iter.cur); - - /* - * we are a waiter if myProc->waitProcLock == curproclock; we - * are a holder if it is NULL or something different - */ - if (curproclock->tag.myProc->waitProcLock == curproclock) - { - if (first_waiter) - { - appendStringInfo(&lock_waiters_sbuf, "%d", - curproclock->tag.myProc->pid); - first_waiter = false; - } - else - appendStringInfo(&lock_waiters_sbuf, ", %d", - curproclock->tag.myProc->pid); - } - else - { - if (first_holder) - { - appendStringInfo(&lock_holders_sbuf, "%d", - curproclock->tag.myProc->pid); - first_holder = false; - } - else - appendStringInfo(&lock_holders_sbuf, ", %d", - curproclock->tag.myProc->pid); - - lockHoldersNum++; - } - } + /* Collect lock holders and waiters */ + CollectLockHoldersAndWaiters(locallock, &lock_holders_sbuf, + &lock_waiters_sbuf, &lockHoldersNum); LWLockRelease(partitionLock); @@ -1985,3 +1939,76 @@ BecomeLockGroupMember(PGPROC *leader, int pid) return ok; } + +/* + * CollectLockHoldersAndWaiters - collect lock holders and waiters for a lock + * + * The lock table's partition lock must be held on entry and remains held on exit. + * Fill lock_holders_sbuf and lock_waiters_sbuf with the PIDs of processes holding + * and waiting for the lock, and set lockHoldersNum to the number of lock holders. + */ +void +CollectLockHoldersAndWaiters(LOCALLOCK *locallock, StringInfo lock_holders_sbuf, + StringInfo lock_waiters_sbuf, int *lockHoldersNum) +{ + bool first_holder = true; + bool first_waiter = true; + dlist_iter proc_iter; + PROCLOCK *curproclock; + LOCK *lock = locallock->lock; +#ifdef USE_ASSERT_CHECKING + { + uint32 hashcode = locallock->hashcode; + LWLock *partitionLock = LockHashPartitionLock(hashcode); + + Assert(LWLockHeldByMe(partitionLock)); + } +#endif + + *lockHoldersNum = 0; + + /* + * we loop over the lock's procLocks to gather a list of all + * holders and waiters. Thus we will be able to provide more + * detailed information for lock debugging purposes. + * + * lock->procLocks contains all processes which hold or wait for + * this lock. + */ + dlist_foreach(proc_iter, &lock->procLocks) + { + curproclock = + dlist_container(PROCLOCK, lockLink, proc_iter.cur); + + /* + * we are a waiter if myProc->waitProcLock == curproclock; we + * are a holder if it is NULL or something different + */ + if (curproclock->tag.myProc->waitProcLock == curproclock) + { + if (first_waiter) + { + appendStringInfo(lock_waiters_sbuf, "%d", + curproclock->tag.myProc->pid); + first_waiter = false; + } + else + appendStringInfo(lock_waiters_sbuf, ", %d", + curproclock->tag.myProc->pid); + } + else + { + if (first_holder) + { + appendStringInfo(lock_holders_sbuf, "%d", + curproclock->tag.myProc->pid); + first_holder = false; + } + else + appendStringInfo(lock_holders_sbuf, ", %d", + curproclock->tag.myProc->pid); + + (*lockHoldersNum)++; + } + } +} \ No newline at end of file diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c index ad25cbb39c5..d7b3ed43920 100644 --- a/src/backend/utils/misc/guc_tables.c +++ b/src/backend/utils/misc/guc_tables.c @@ -75,6 +75,7 @@ #include "storage/bufmgr.h" #include "storage/bufpage.h" #include "storage/large_object.h" +#include "storage/lmgr.h" #include "storage/pg_shmem.h" #include "storage/predicate.h" #include "storage/procnumber.h" @@ -1594,6 +1595,15 @@ struct config_bool ConfigureNamesBool[] = false, NULL, NULL, NULL }, + { + {"log_lock_failure", PGC_SUSET, LOGGING_WHAT, + gettext_noop("Logs lock failed."), + NULL + }, + &log_lock_failure, + false, + NULL, NULL, NULL + }, { {"log_recovery_conflict_waits", PGC_SIGHUP, LOGGING_WHAT, gettext_noop("Logs standby recovery conflict waits."), diff --git a/src/backend/utils/misc/postgresql.conf.sample b/src/backend/utils/misc/postgresql.conf.sample index 5362ff80519..0b570a81d76 100644 --- a/src/backend/utils/misc/postgresql.conf.sample +++ b/src/backend/utils/misc/postgresql.conf.sample @@ -608,6 +608,7 @@ # %% = '%' # e.g. '<%u%%%d> ' #log_lock_waits = off # log lock waits >= deadlock_timeout +#log_lock_failure = off # log lock failed #log_recovery_conflict_waits = off # log standby recovery conflict waits # >= deadlock_timeout #log_parameter_max_length = -1 # when logging statements, limit logged diff --git a/src/include/storage/lmgr.h b/src/include/storage/lmgr.h index 728260c1cc0..d0306229642 100644 --- a/src/include/storage/lmgr.h +++ b/src/include/storage/lmgr.h @@ -72,8 +72,8 @@ extern void UnlockPage(Relation relation, BlockNumber blkno, LOCKMODE lockmode); /* Lock a tuple (see heap_lock_tuple before assuming you understand this) */ extern void LockTuple(Relation relation, ItemPointer tid, LOCKMODE lockmode); -extern bool ConditionalLockTuple(Relation relation, ItemPointer tid, - LOCKMODE lockmode); +extern bool ConditionalLockTuple(Relation relation, ItemPointer tid, LOCKMODE lockmode, + LockHoldersAndWaiters *lockHoldersAndWaiters); extern void UnlockTuple(Relation relation, ItemPointer tid, LOCKMODE lockmode); /* Lock an XID (used to wait for a transaction to finish) */ @@ -81,7 +81,8 @@ extern void XactLockTableInsert(TransactionId xid); extern void XactLockTableDelete(TransactionId xid); extern void XactLockTableWait(TransactionId xid, Relation rel, ItemPointer ctid, XLTW_Oper oper); -extern bool ConditionalXactLockTableWait(TransactionId xid); +extern bool ConditionalXactLockTableWait(TransactionId xid, + LockHoldersAndWaiters *lockHoldersAndWaiters); /* Lock VXIDs, specified by conflicting locktags */ extern void WaitForLockers(LOCKTAG heaplocktag, LOCKMODE lockmode, bool progress); diff --git a/src/include/storage/lock.h b/src/include/storage/lock.h index 1076995518f..2ba8a0f736d 100644 --- a/src/include/storage/lock.h +++ b/src/include/storage/lock.h @@ -30,6 +30,7 @@ typedef struct PGPROC PGPROC; /* GUC variables */ extern PGDLLIMPORT int max_locks_per_xact; +extern PGDLLIMPORT bool log_lock_failure; #ifdef LOCK_DEBUG extern PGDLLIMPORT int Trace_lock_oidmin; @@ -515,6 +516,15 @@ typedef enum * worker */ } DeadLockState; +typedef struct LockHoldersAndWaiters +{ + StringInfoData *describe_locktag, + *lock_waiters_sbuf, + *lock_holders_sbuf; + const char *modename; + int *lockHoldersNum; +} LockHoldersAndWaiters; + /* * The lockmgr's shared hash tables are partitioned to reduce contention. * To determine which partition a given locktag belongs to, compute the tag's @@ -560,7 +570,8 @@ extern LockAcquireResult LockAcquireExtended(const LOCKTAG *locktag, bool sessionLock, bool dontWait, bool reportMemoryError, - LOCALLOCK **locallockp); + LOCALLOCK **locallockp, + LockHoldersAndWaiters *lockHoldersAndWaiters); extern void AbortStrongLockAcquire(void); extern void MarkLockClear(LOCALLOCK *locallock); extern bool LockRelease(const LOCKTAG *locktag, @@ -613,6 +624,7 @@ extern void RememberSimpleDeadLock(PGPROC *proc1, extern void InitDeadLockChecking(void); extern int LockWaiterCount(const LOCKTAG *locktag); +extern LockHoldersAndWaiters * CreateLockHoldersAndWaiters(void); #ifdef LOCK_DEBUG extern void DumpLocks(PGPROC *proc); diff --git a/src/include/storage/proc.h b/src/include/storage/proc.h index 20777f7d5ae..05b5b0f3337 100644 --- a/src/include/storage/proc.h +++ b/src/include/storage/proc.h @@ -497,4 +497,7 @@ extern PGPROC *AuxiliaryPidGetProc(int pid); extern void BecomeLockGroupLeader(void); extern bool BecomeLockGroupMember(PGPROC *leader, int pid); +extern void CollectLockHoldersAndWaiters(LOCALLOCK *locallock, StringInfo lock_holders_sbuf, + StringInfo lock_waiters_sbuf, int *lockHoldersNum); + #endif /* _PROC_H_ */ diff --git a/src/test/regress/pg_regress.c b/src/test/regress/pg_regress.c index 5d85dcc62f0..fe568b52b5d 100644 --- a/src/test/regress/pg_regress.c +++ b/src/test/regress/pg_regress.c @@ -2404,6 +2404,7 @@ regression_main(int argc, char *argv[], fputs("log_checkpoints = on\n", pg_conf); fputs("log_line_prefix = '%m %b[%p] %q%a '\n", pg_conf); fputs("log_lock_waits = on\n", pg_conf); + fputs("log_lock_failure = on\n", pg_conf); fputs("log_temp_files = 128kB\n", pg_conf); fputs("max_prepared_transactions = 2\n", pg_conf);