On Sat, 2007-04-21 at 17:56 -0400, Neil Conway wrote: > Right, I'm envisioning doing a conditional LockAcquire and then > heap_open() / heap_getnext() by hand. That will be relatively slow, but > code that emits a deadlock error message is almost by definition not > performance critical.
... although it turns out you'd need to conditionally lock a *lot* of system catalogs to guarantee that you're not going to block on a lock at some point. Needless to say, that approach would be pretty ugly and fragile. > BTW, another alternative would be to set a global variable instructing > LockAcquire() to not block waiting for a lock; instead, it would > longjmp(), a la elog(ERROR). You could even construct something similar > to PG_TRY() Attached is a very quick hack of a patch to do this. -Neil
Index: src/backend/storage/lmgr/deadlock.c =================================================================== RCS file: /home/neilc/postgres/cvs_root/pgsql/src/backend/storage/lmgr/deadlock.c,v retrieving revision 1.47 diff -c -p -r1.47 deadlock.c *** src/backend/storage/lmgr/deadlock.c 20 Apr 2007 20:15:52 -0000 1.47 --- src/backend/storage/lmgr/deadlock.c 21 Apr 2007 23:43:13 -0000 *************** *** 25,34 **** --- 25,39 ---- */ #include "postgres.h" + #include "access/htup.h" + #include "commands/dbcommands.h" #include "lib/stringinfo.h" #include "miscadmin.h" #include "storage/proc.h" + #include "utils/builtins.h" + #include "utils/lsyscache.h" #include "utils/memutils.h" + #include "utils/syscache.h" /* One edge in the waits-for graph */ *************** static bool FindLockCycleRecurse(PGPROC *** 73,78 **** --- 78,85 ---- static bool ExpandConstraints(EDGE *constraints, int nConstraints); static bool TopoSort(LOCK *lock, EDGE *constraints, int nConstraints, PGPROC **ordering); + static char *format_relation(Oid reloid); + static const char *format_database(Oid dboid); #ifdef DEBUG_DEADLOCK static void PrintLockQueue(LOCK *lock, const char *info); *************** DescribeLockTag(StringInfo buf, const LO *** 846,875 **** { case LOCKTAG_RELATION: appendStringInfo(buf, ! _("relation %u of database %u"), ! lock->locktag_field2, ! lock->locktag_field1); break; case LOCKTAG_RELATION_EXTEND: appendStringInfo(buf, ! _("extension of relation %u of database %u"), ! lock->locktag_field2, ! lock->locktag_field1); break; case LOCKTAG_PAGE: appendStringInfo(buf, ! _("page %u of relation %u of database %u"), lock->locktag_field3, ! lock->locktag_field2, ! lock->locktag_field1); break; case LOCKTAG_TUPLE: appendStringInfo(buf, ! _("tuple (%u,%u) of relation %u of database %u"), lock->locktag_field3, lock->locktag_field4, ! lock->locktag_field2, ! lock->locktag_field1); break; case LOCKTAG_TRANSACTION: appendStringInfo(buf, --- 853,882 ---- { case LOCKTAG_RELATION: appendStringInfo(buf, ! _("relation %s of database %s"), ! format_relation(lock->locktag_field2), ! format_database(lock->locktag_field1)); break; case LOCKTAG_RELATION_EXTEND: appendStringInfo(buf, ! _("extension of relation %s of database %s"), ! format_relation(lock->locktag_field2), ! format_database(lock->locktag_field1)); break; case LOCKTAG_PAGE: appendStringInfo(buf, ! _("page %u of relation %s of database %s"), lock->locktag_field3, ! format_relation(lock->locktag_field2), ! format_database(lock->locktag_field1)); break; case LOCKTAG_TUPLE: appendStringInfo(buf, ! _("tuple (%u,%u) of relation %s of database %s"), lock->locktag_field3, lock->locktag_field4, ! format_relation(lock->locktag_field2), ! format_database(lock->locktag_field1)); break; case LOCKTAG_TRANSACTION: appendStringInfo(buf, *************** DescribeLockTag(StringInfo buf, const LO *** 878,887 **** break; case LOCKTAG_OBJECT: appendStringInfo(buf, ! _("object %u of class %u of database %u"), lock->locktag_field3, lock->locktag_field2, ! lock->locktag_field1); break; case LOCKTAG_USERLOCK: /* reserved for old contrib code, now on pgfoundry */ --- 885,894 ---- break; case LOCKTAG_OBJECT: appendStringInfo(buf, ! _("object %u of class %u of database %s"), lock->locktag_field3, lock->locktag_field2, ! format_database(lock->locktag_field1)); break; case LOCKTAG_USERLOCK: /* reserved for old contrib code, now on pgfoundry */ *************** DescribeLockTag(StringInfo buf, const LO *** 907,912 **** --- 914,977 ---- } } + static char * + format_relation(Oid reloid) + { + char *result; + + PG_LOCK_NOWAIT(); + { + HeapTuple class_tup; + + class_tup = SearchSysCache(RELOID, + ObjectIdGetDatum(reloid), + 0, 0, 0); + + if (HeapTupleIsValid(class_tup)) + { + Form_pg_class classform = (Form_pg_class) GETSTRUCT(class_tup); + char *relname; + char *nspname; + + relname = NameStr(classform->relname); + nspname = get_namespace_name(classform->relnamespace); + result = quote_qualified_identifier(nspname, relname); + + ReleaseSysCache(class_tup); + return result; + } + } + PG_LOCK_FAILED(); + { + ; + } + PG_END_LOCK_NOWAIT(); + + /* + * Either we failed to acquire a necessary lmgr lock, or else the + * rel isn't in our database, so just return the OID + */ + result = (char *) palloc(NAMEDATALEN); + snprintf(result, NAMEDATALEN, "%u", reloid); + return result; + } + + static const char * + format_database(Oid dboid) + { + PG_LOCK_NOWAIT(); + { + return quote_identifier(get_database_name(dboid)); + } + PG_LOCK_FAILED(); + { + char *result = (char *) palloc(NAMEDATALEN); + snprintf(result, NAMEDATALEN, "%u", dboid); + return result; + } + PG_END_LOCK_NOWAIT(); + } + /* * Report a detected DS_HARD_DEADLOCK, with available details. */ Index: src/backend/storage/lmgr/lock.c =================================================================== RCS file: /home/neilc/postgres/cvs_root/pgsql/src/backend/storage/lmgr/lock.c,v retrieving revision 1.176 diff -c -p -r1.176 lock.c *** src/backend/storage/lmgr/lock.c 1 Feb 2007 19:10:28 -0000 1.176 --- src/backend/storage/lmgr/lock.c 21 Apr 2007 23:38:32 -0000 *************** *** 46,51 **** --- 46,54 ---- /* This configuration variable is used to set the lock table size */ int max_locks_per_xact; /* set by guc.c */ + int lock_no_wait_count = 0; + sigjmp_buf *lock_no_wait_stack = NULL; + #define NLOCKENTS() \ mul_size(max_locks_per_xact, add_size(MaxBackends, max_prepared_xacts)) *************** LockAcquire(const LOCKTAG *locktag, *** 752,758 **** * blocking, remove useless table entries and return NOT_AVAIL without * waiting. */ ! if (dontWait) { if (proclock->holdMask == 0) { --- 755,761 ---- * blocking, remove useless table entries and return NOT_AVAIL without * waiting. */ ! if (dontWait || lock_no_wait_count > 0) { if (proclock->holdMask == 0) { *************** LockAcquire(const LOCKTAG *locktag, *** 775,781 **** LWLockRelease(partitionLock); if (locallock->nLocks == 0) RemoveLocalLock(locallock); ! return LOCKACQUIRE_NOT_AVAIL; } /* --- 778,794 ---- LWLockRelease(partitionLock); if (locallock->nLocks == 0) RemoveLocalLock(locallock); ! ! if (dontWait) ! return LOCKACQUIRE_NOT_AVAIL; ! ! /* ! * If we're inside a PG_LOCK_NOWAIT() block, longjmp ! * instead of waiting. ! */ ! if (lock_no_wait_stack == NULL) ! elog(FATAL, "lmgr nowait lock stack corrupted"); ! siglongjmp(*lock_no_wait_stack, 1); } /* Index: src/include/storage/lock.h =================================================================== RCS file: /home/neilc/postgres/cvs_root/pgsql/src/include/storage/lock.h,v retrieving revision 1.104 diff -c -p -r1.104 lock.h *** src/include/storage/lock.h 3 Mar 2007 18:46:40 -0000 1.104 --- src/include/storage/lock.h 21 Apr 2007 23:34:43 -0000 *************** extern void DumpLocks(PGPROC *proc); *** 462,465 **** --- 462,491 ---- extern void DumpAllLocks(void); #endif + #define PG_LOCK_NOWAIT() \ + do { \ + sigjmp_buf *save_lock_stack = lock_no_wait_stack; \ + int save_lock_count = lock_no_wait_count; \ + sigjmp_buf local_sigjmp_buf; \ + if (sigsetjmp(local_sigjmp_buf, 0) == 0) \ + { \ + lock_no_wait_stack = &local_sigjmp_buf; \ + lock_no_wait_count++ + + #define PG_LOCK_FAILED() \ + } \ + else \ + { \ + lock_no_wait_stack = save_lock_stack; \ + lock_no_wait_count = save_lock_count; + + #define PG_END_LOCK_NOWAIT() \ + } \ + lock_no_wait_stack = save_lock_stack; \ + lock_no_wait_count = save_lock_count; \ + } while (0) + + extern int lock_no_wait_count; + extern sigjmp_buf *lock_no_wait_stack; + #endif /* LOCK_H */
---------------------------(end of broadcast)--------------------------- TIP 3: Have you checked our extensive FAQ? http://www.postgresql.org/docs/faq