Jaime Casanova írta: > 2010/1/13 Boszormenyi Zoltan <z...@cybertec.at>: > >>> Your smaller patch is attached, with the above strangeness. :-) >>> >>> > > you still had to add this parameter to the postgresql.conf.sample in > the section about lock management >
Attached with the required change. Thanks, Zoltán Böszörményi -- Bible has answers for everything. Proof: "But let your communication be, Yea, yea; Nay, nay: for whatsoever is more than these cometh of evil." (Matthew 5:37) - basics of digital technology. "May your kingdom come" - superficial description of plate tectonics ---------------------------------- Zoltán Böszörményi Cybertec Schönig & Schönig GmbH http://www.postgresql.at/
diff -dcrpN pgsql.orig/doc/src/sgml/config.sgml pgsql.5/doc/src/sgml/config.sgml *** pgsql.orig/doc/src/sgml/config.sgml 2010-01-06 08:43:22.000000000 +0100 --- pgsql.5/doc/src/sgml/config.sgml 2010-01-13 18:59:19.000000000 +0100 *************** COPY postgres_log FROM '/full/path/to/lo *** 4191,4196 **** --- 4191,4220 ---- </listitem> </varlistentry> + <varlistentry id="guc-lock-timeout" xreflabel="lock_timeout"> + <term><varname>lock_timeout</varname> (<type>integer</type>)</term> + <indexterm> + <primary><varname>lock_timeout</> configuration parameter</primary> + </indexterm> + <listitem> + <para> + Abort any statement that tries to acquire a heavy-weight lock (e.g. rows, + pages, tables, indices or other objects) and the lock has to wait more + than the specified number of milliseconds, starting from the time the + command arrives at the server from the client. + If <varname>log_min_error_statement</> is set to <literal>ERROR</> or lower, + the statement that timed out will also be logged. A value of zero + (the default) turns off the limitation. + </para> + + <para> + Setting <varname>lock_timeout</> in + <filename>postgresql.conf</> is not recommended because it + affects all sessions. + </para> + </listitem> + </varlistentry> + <varlistentry id="guc-vacuum-freeze-table-age" xreflabel="vacuum_freeze_table_age"> <term><varname>vacuum_freeze_table_age</varname> (<type>integer</type>)</term> <indexterm> diff -dcrpN pgsql.orig/doc/src/sgml/ref/lock.sgml pgsql.5/doc/src/sgml/ref/lock.sgml *** pgsql.orig/doc/src/sgml/ref/lock.sgml 2009-09-18 08:26:40.000000000 +0200 --- pgsql.5/doc/src/sgml/ref/lock.sgml 2010-01-13 18:59:19.000000000 +0100 *************** LOCK [ TABLE ] [ ONLY ] <replaceable cla *** 39,46 **** <literal>NOWAIT</literal> is specified, <command>LOCK TABLE</command> does not wait to acquire the desired lock: if it cannot be acquired immediately, the command is aborted and an ! error is emitted. Once obtained, the lock is held for the ! remainder of the current transaction. (There is no <command>UNLOCK TABLE</command> command; locks are always released at transaction end.) </para> --- 39,49 ---- <literal>NOWAIT</literal> is specified, <command>LOCK TABLE</command> does not wait to acquire the desired lock: if it cannot be acquired immediately, the command is aborted and an ! error is emitted. If <varname>lock_timeout</varname> is set to a value ! higher than 0, and the lock cannot be acquired under the specified ! timeout value in milliseconds, the command is aborted and an error ! is emitted. Once obtained, the lock is held for the remainder of ! the current transaction. (There is no <command>UNLOCK TABLE</command> command; locks are always released at transaction end.) </para> diff -dcrpN pgsql.orig/doc/src/sgml/ref/select.sgml pgsql.5/doc/src/sgml/ref/select.sgml *** pgsql.orig/doc/src/sgml/ref/select.sgml 2009-10-29 15:23:52.000000000 +0100 --- pgsql.5/doc/src/sgml/ref/select.sgml 2010-01-13 18:59:19.000000000 +0100 *************** FOR SHARE [ OF <replaceable class="param *** 1121,1126 **** --- 1121,1134 ---- </para> <para> + If <literal>NOWAIT</> option is not specified and <varname>lock_timeout</varname> + is set to a value higher than 0, and the lock needs to wait more than + the specified value in milliseconds, the command reports an error after + timing out, rather than waiting indefinitely. The note in the previous + paragraph applies to the <varname>lock_timeout</varname>, too. + </para> + + <para> If specific tables are named in <literal>FOR UPDATE</literal> or <literal>FOR SHARE</literal>, then only rows coming from those tables are locked; any other diff -dcrpN pgsql.orig/src/backend/access/heap/heapam.c pgsql.5/src/backend/access/heap/heapam.c *** pgsql.orig/src/backend/access/heap/heapam.c 2010-01-10 15:49:30.000000000 +0100 --- pgsql.5/src/backend/access/heap/heapam.c 2010-01-13 22:08:23.000000000 +0100 *************** l1: *** 2119,2125 **** if (infomask & HEAP_XMAX_IS_MULTI) { /* wait for multixact */ ! MultiXactIdWait((MultiXactId) xwait); LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE); /* --- 2119,2125 ---- if (infomask & HEAP_XMAX_IS_MULTI) { /* wait for multixact */ ! MultiXactIdWait((MultiXactId) xwait, RelationGetRelationName(relation)); LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE); /* *************** l1: *** 2145,2151 **** else { /* wait for regular transaction to end */ ! XactLockTableWait(xwait); LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE); /* --- 2145,2151 ---- else { /* wait for regular transaction to end */ ! XactLockTableWait(xwait, RelationGetRelationName(relation)); LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE); /* *************** l2: *** 2471,2477 **** if (infomask & HEAP_XMAX_IS_MULTI) { /* wait for multixact */ ! MultiXactIdWait((MultiXactId) xwait); LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE); /* --- 2471,2477 ---- if (infomask & HEAP_XMAX_IS_MULTI) { /* wait for multixact */ ! MultiXactIdWait((MultiXactId) xwait, RelationGetRelationName(relation)); LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE); /* *************** l2: *** 2497,2503 **** else { /* wait for regular transaction to end */ ! XactLockTableWait(xwait); LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE); /* --- 2497,2503 ---- else { /* wait for regular transaction to end */ ! XactLockTableWait(xwait, RelationGetRelationName(relation)); LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE); /* *************** l3: *** 3183,3189 **** RelationGetRelationName(relation)))); } else ! MultiXactIdWait((MultiXactId) xwait); LockBuffer(*buffer, BUFFER_LOCK_EXCLUSIVE); --- 3183,3189 ---- RelationGetRelationName(relation)))); } else ! MultiXactIdWait((MultiXactId) xwait, RelationGetRelationName(relation)); LockBuffer(*buffer, BUFFER_LOCK_EXCLUSIVE); *************** l3: *** 3218,3224 **** RelationGetRelationName(relation)))); } else ! XactLockTableWait(xwait); LockBuffer(*buffer, BUFFER_LOCK_EXCLUSIVE); --- 3218,3224 ---- RelationGetRelationName(relation)))); } else ! XactLockTableWait(xwait, RelationGetRelationName(relation)); LockBuffer(*buffer, BUFFER_LOCK_EXCLUSIVE); diff -dcrpN pgsql.orig/src/backend/access/nbtree/nbtinsert.c pgsql.5/src/backend/access/nbtree/nbtinsert.c *** pgsql.orig/src/backend/access/nbtree/nbtinsert.c 2010-01-03 12:54:02.000000000 +0100 --- pgsql.5/src/backend/access/nbtree/nbtinsert.c 2010-01-13 22:11:18.000000000 +0100 *************** top: *** 165,171 **** { /* Have to wait for the other guy ... */ _bt_relbuf(rel, buf); ! XactLockTableWait(xwait); /* start over... */ _bt_freestack(stack); goto top; --- 165,171 ---- { /* Have to wait for the other guy ... */ _bt_relbuf(rel, buf); ! XactLockTableWait(xwait, RelationGetRelationName(rel)); /* start over... */ _bt_freestack(stack); goto top; diff -dcrpN pgsql.orig/src/backend/access/transam/multixact.c pgsql.5/src/backend/access/transam/multixact.c *** pgsql.orig/src/backend/access/transam/multixact.c 2010-01-03 12:54:03.000000000 +0100 --- pgsql.5/src/backend/access/transam/multixact.c 2010-01-13 22:12:13.000000000 +0100 *************** *** 58,63 **** --- 58,64 ---- #include "pg_trace.h" #include "storage/backendid.h" #include "storage/lmgr.h" + #include "storage/proc.h" #include "storage/procarray.h" #include "utils/builtins.h" #include "utils/memutils.h" *************** MultiXactIdSetOldestVisible(void) *** 584,590 **** * of the containing tuple, so the caller needs to iterate on us somehow. */ void ! MultiXactIdWait(MultiXactId multi) { TransactionId *members; int nmembers; --- 585,591 ---- * of the containing tuple, so the caller needs to iterate on us somehow. */ void ! MultiXactIdWait(MultiXactId multi, const char *relname) { TransactionId *members; int nmembers; *************** MultiXactIdWait(MultiXactId multi) *** 602,608 **** debug_elog4(DEBUG2, "MultiXactIdWait: waiting for %d (%u)", i, member); if (!TransactionIdIsCurrentTransactionId(member)) ! XactLockTableWait(member); } pfree(members); --- 603,609 ---- debug_elog4(DEBUG2, "MultiXactIdWait: waiting for %d (%u)", i, member); if (!TransactionIdIsCurrentTransactionId(member)) ! XactLockTableWait(member, relname); } pfree(members); diff -dcrpN pgsql.orig/src/backend/catalog/index.c pgsql.5/src/backend/catalog/index.c *** pgsql.orig/src/backend/catalog/index.c 2010-01-06 08:43:22.000000000 +0100 --- pgsql.5/src/backend/catalog/index.c 2010-01-13 22:11:31.000000000 +0100 *************** IndexBuildHeapScan(Relation heapRelation *** 1820,1826 **** TransactionId xwait = HeapTupleHeaderGetXmin(heapTuple->t_data); LockBuffer(scan->rs_cbuf, BUFFER_LOCK_UNLOCK); ! XactLockTableWait(xwait); goto recheck; } } --- 1820,1826 ---- TransactionId xwait = HeapTupleHeaderGetXmin(heapTuple->t_data); LockBuffer(scan->rs_cbuf, BUFFER_LOCK_UNLOCK); ! XactLockTableWait(xwait, RelationGetRelationName(heapRelation)); goto recheck; } } *************** IndexBuildHeapScan(Relation heapRelation *** 1860,1866 **** TransactionId xwait = HeapTupleHeaderGetXmax(heapTuple->t_data); LockBuffer(scan->rs_cbuf, BUFFER_LOCK_UNLOCK); ! XactLockTableWait(xwait); goto recheck; } } --- 1860,1866 ---- TransactionId xwait = HeapTupleHeaderGetXmax(heapTuple->t_data); LockBuffer(scan->rs_cbuf, BUFFER_LOCK_UNLOCK); ! XactLockTableWait(xwait, RelationGetRelationName(heapRelation)); goto recheck; } } diff -dcrpN pgsql.orig/src/backend/executor/execMain.c pgsql.5/src/backend/executor/execMain.c *** pgsql.orig/src/backend/executor/execMain.c 2010-01-08 14:37:07.000000000 +0100 --- pgsql.5/src/backend/executor/execMain.c 2010-01-13 22:11:41.000000000 +0100 *************** EvalPlanQualFetch(EState *estate, Relati *** 1493,1499 **** if (TransactionIdIsValid(SnapshotDirty.xmax)) { ReleaseBuffer(buffer); ! XactLockTableWait(SnapshotDirty.xmax); continue; /* loop back to repeat heap_fetch */ } --- 1493,1499 ---- if (TransactionIdIsValid(SnapshotDirty.xmax)) { ReleaseBuffer(buffer); ! XactLockTableWait(SnapshotDirty.xmax, RelationGetRelationName(relation)); continue; /* loop back to repeat heap_fetch */ } diff -dcrpN pgsql.orig/src/backend/executor/execUtils.c pgsql.5/src/backend/executor/execUtils.c *** pgsql.orig/src/backend/executor/execUtils.c 2010-01-03 12:54:11.000000000 +0100 --- pgsql.5/src/backend/executor/execUtils.c 2010-01-13 22:17:43.000000000 +0100 *************** retry: *** 1284,1290 **** if (TransactionIdIsValid(xwait)) { index_endscan(index_scan); ! XactLockTableWait(xwait); goto retry; } --- 1284,1290 ---- if (TransactionIdIsValid(xwait)) { index_endscan(index_scan); ! XactLockTableWait(xwait, RelationGetRelationName(heap)); goto retry; } diff -dcrpN pgsql.orig/src/backend/port/posix_sema.c pgsql.5/src/backend/port/posix_sema.c *** pgsql.orig/src/backend/port/posix_sema.c 2010-01-03 12:54:22.000000000 +0100 --- pgsql.5/src/backend/port/posix_sema.c 2010-01-13 18:59:19.000000000 +0100 *************** *** 24,29 **** --- 24,30 ---- #include "miscadmin.h" #include "storage/ipc.h" #include "storage/pg_sema.h" + #include "storage/proc.h" /* for LockTimeout */ #ifdef USE_NAMED_POSIX_SEMAPHORES *************** PGSemaphoreTryLock(PGSemaphore sema) *** 313,315 **** --- 314,358 ---- return true; } + + /* + * PGSemaphoreTimedLock + * + * Lock a semaphore only if able to do so under the lock_timeout + */ + bool + PGSemaphoreTimedLock(PGSemaphore sema, bool interruptOK) + { + int errStatus; + struct timespec timeout; + + /* + * See notes in sysv_sema.c's implementation of PGSemaphoreLock. Just as + * that code does for semop(), we handle both the case where sem_wait() + * returns errno == EINTR after a signal, and the case where it just keeps + * waiting. + */ + do + { + ImmediateInterruptOK = interruptOK; + CHECK_FOR_INTERRUPTS(); + if (LockTimeout) + { + timeout.tv_sec = LockTimeout / 1000; + timeout.tv_nsec = (LockTimeout % 1000) * 1000000; + errStatus = sem_timedwait(PG_SEM_REF(sema), &timeout); + } + else + errStatus = sem_wait(PG_SEM_REF(sema)); + ImmediateInterruptOK = false; + } while (errStatus < 0 && errno == EINTR); + + if (errStatus < 0) + { + if (errno == ETIMEDOUT) + return false; /* failed to lock it */ + /* Otherwise we got trouble */ + elog(FATAL, "sem_wait failed: %m"); + } + return true; + } diff -dcrpN pgsql.orig/src/backend/port/sysv_sema.c pgsql.5/src/backend/port/sysv_sema.c *** pgsql.orig/src/backend/port/sysv_sema.c 2010-01-03 12:54:22.000000000 +0100 --- pgsql.5/src/backend/port/sysv_sema.c 2010-01-13 18:59:19.000000000 +0100 *************** *** 30,35 **** --- 30,36 ---- #include "miscadmin.h" #include "storage/ipc.h" #include "storage/pg_sema.h" + #include "storage/proc.h" /* for LockTimeout */ #ifndef HAVE_UNION_SEMUN *************** PGSemaphoreTryLock(PGSemaphore sema) *** 497,499 **** --- 498,589 ---- return true; } + + /* + * PGSemaphoreTimedLock + * + * Lock a semaphore only if able to do so under the lock_timeout + */ + bool + PGSemaphoreTimedLock(PGSemaphore sema, bool interruptOK) + { + int errStatus; + struct sembuf sops; + struct timespec timeout; + + sops.sem_op = -1; /* decrement */ + sops.sem_flg = 0; + sops.sem_num = sema->semNum; + + /* + * Note: if errStatus is -1 and errno == EINTR then it means we returned + * from the operation prematurely because we were sent a signal. So we + * try and lock the semaphore again. + * + * Each time around the loop, we check for a cancel/die interrupt. On + * some platforms, if such an interrupt comes in while we are waiting, it + * will cause the semop() call to exit with errno == EINTR, allowing us to + * service the interrupt (if not in a critical section already) during the + * next loop iteration. + * + * Once we acquire the lock, we do NOT check for an interrupt before + * returning. The caller needs to be able to record ownership of the lock + * before any interrupt can be accepted. + * + * There is a window of a few instructions between CHECK_FOR_INTERRUPTS + * and entering the semop() call. If a cancel/die interrupt occurs in + * that window, we would fail to notice it until after we acquire the lock + * (or get another interrupt to escape the semop()). We can avoid this + * problem by temporarily setting ImmediateInterruptOK to true before we + * do CHECK_FOR_INTERRUPTS; then, a die() interrupt in this interval will + * execute directly. However, there is a huge pitfall: there is another + * window of a few instructions after the semop() before we are able to + * reset ImmediateInterruptOK. If an interrupt occurs then, we'll lose + * control, which means that the lock has been acquired but our caller did + * not get a chance to record the fact. Therefore, we only set + * ImmediateInterruptOK if the caller tells us it's OK to do so, ie, the + * caller does not need to record acquiring the lock. (This is currently + * true for lockmanager locks, since the process that granted us the lock + * did all the necessary state updates. It's not true for SysV semaphores + * used to implement LW locks or emulate spinlocks --- but the wait time + * for such locks should not be very long, anyway.) + * + * On some platforms, signals marked SA_RESTART (which is most, for us) + * will not interrupt the semop(); it will just keep waiting. Therefore + * it's necessary for cancel/die interrupts to be serviced directly by the + * signal handler. On these platforms the behavior is really the same + * whether the signal arrives just before the semop() begins, or while it + * is waiting. The loop on EINTR is thus important only for other types + * of interrupts. + */ + do + { + ImmediateInterruptOK = interruptOK; + CHECK_FOR_INTERRUPTS(); + if (LockTimeout) + { + timeout.tv_sec = LockTimeout / 1000; + timeout.tv_nsec = (LockTimeout % 1000) * 1000000; + errStatus = semtimedop(sema->semId, &sops, 1, &timeout); + } + else + errStatus = semop(sema->semId, &sops, 1); + ImmediateInterruptOK = false; + } while (errStatus < 0 && errno == EINTR); + + if (errStatus < 0) + { + /* Expect EAGAIN or EWOULDBLOCK (platform-dependent) */ + #ifdef EAGAIN + if (errno == EAGAIN) + return false; /* failed to lock it */ + #endif + #if defined(EWOULDBLOCK) && (!defined(EAGAIN) || (EWOULDBLOCK != EAGAIN)) + if (errno == EWOULDBLOCK) + return false; /* failed to lock it */ + #endif + /* Otherwise we got trouble */ + elog(FATAL, "semop(id=%d) failed: %m", sema->semId); + } + return true; + } diff -dcrpN pgsql.orig/src/backend/port/win32_sema.c pgsql.5/src/backend/port/win32_sema.c *** pgsql.orig/src/backend/port/win32_sema.c 2010-01-03 12:54:22.000000000 +0100 --- pgsql.5/src/backend/port/win32_sema.c 2010-01-13 18:59:19.000000000 +0100 *************** *** 16,21 **** --- 16,22 ---- #include "miscadmin.h" #include "storage/ipc.h" #include "storage/pg_sema.h" + #include "storage/proc.h" /* for LockTimeout */ static HANDLE *mySemSet; /* IDs of sema sets acquired so far */ static int numSems; /* number of sema sets acquired so far */ *************** PGSemaphoreTryLock(PGSemaphore sema) *** 205,207 **** --- 206,266 ---- /* keep compiler quiet */ return false; } + + /* + * PGSemaphoreTimedLock + * + * Lock a semaphore only if able to do so under the lock_timeout + * Serve the interrupt if interruptOK is true. + */ + bool + PGSemaphoreTimedLock(PGSemaphore sema, bool interruptOK) + { + DWORD ret; + HANDLE wh[2]; + + wh[0] = *sema; + wh[1] = pgwin32_signal_event; + + /* + * As in other implementations of PGSemaphoreLock, we need to check for + * cancel/die interrupts each time through the loop. But here, there is + * no hidden magic about whether the syscall will internally service a + * signal --- we do that ourselves. + */ + do + { + ImmediateInterruptOK = interruptOK; + CHECK_FOR_INTERRUPTS(); + + errno = 0; + ret = WaitForMultipleObjectsEx(2, wh, FALSE, LockTimeout ? LockTimeout : INFINITE, TRUE); + + if (ret == WAIT_OBJECT_0) + { + /* We got it! */ + return true; + } + else if (ret == WAIT_TIMEOUT) + { + /* Can't get it */ + errno = EAGAIN; + return false; + } + else if (ret == WAIT_OBJECT_0 + 1) + { + /* Signal event is set - we have a signal to deliver */ + pgwin32_dispatch_queued_signals(); + errno = EINTR; + } + else + /* Otherwise we are in trouble */ + errno = EIDRM; + + ImmediateInterruptOK = false; + } while (errno == EINTR); + + if (errno != 0) + ereport(FATAL, + (errmsg("could not lock semaphore: error code %d", (int) GetLastError()))); + } diff -dcrpN pgsql.orig/src/backend/storage/lmgr/lmgr.c pgsql.5/src/backend/storage/lmgr/lmgr.c *** pgsql.orig/src/backend/storage/lmgr/lmgr.c 2010-01-03 12:54:25.000000000 +0100 --- pgsql.5/src/backend/storage/lmgr/lmgr.c 2010-01-13 22:20:34.000000000 +0100 *************** *** 19,26 **** --- 19,30 ---- #include "access/transam.h" #include "access/xact.h" #include "catalog/catalog.h" + #include "catalog/pg_database.h" + #include "commands/dbcommands.h" #include "miscadmin.h" #include "storage/lmgr.h" + #include "utils/lsyscache.h" + #include "storage/proc.h" #include "storage/procarray.h" #include "utils/inval.h" *************** LockRelationOid(Oid relid, LOCKMODE lock *** 78,83 **** --- 82,102 ---- res = LockAcquire(&tag, lockmode, false, false); + if (res == LOCKACQUIRE_NOT_AVAIL) + { + char *relname = get_rel_name(relid); + if (relname) + ereport(ERROR, + (errcode(ERRCODE_LOCK_NOT_AVAILABLE), + errmsg("could not obtain lock on relation \"%s\"", + relname))); + else + ereport(ERROR, + (errcode(ERRCODE_LOCK_NOT_AVAILABLE), + errmsg("could not obtain lock on relation with OID %u", + relid))); + } + /* * Now that we have the lock, check for invalidation messages, so that we * will update or flush any stale relcache entry before we try to use it. *************** LockRelation(Relation relation, LOCKMODE *** 173,178 **** --- 192,203 ---- res = LockAcquire(&tag, lockmode, false, false); + if (res == LOCKACQUIRE_NOT_AVAIL) + ereport(ERROR, + (errcode(ERRCODE_LOCK_NOT_AVAILABLE), + errmsg("could not obtain lock on relation \"%s\"", + RelationGetRelationName(relation)))); + /* * Now that we have the lock, check for invalidation messages; see notes * in LockRelationOid. *************** LockRelationIdForSession(LockRelId *reli *** 250,256 **** SET_LOCKTAG_RELATION(tag, relid->dbId, relid->relId); ! (void) LockAcquire(&tag, lockmode, true, false); } /* --- 275,285 ---- SET_LOCKTAG_RELATION(tag, relid->dbId, relid->relId); ! if (LockAcquire(&tag, lockmode, true, false) == LOCKACQUIRE_NOT_AVAIL) ! ereport(ERROR, ! (errcode(ERRCODE_LOCK_NOT_AVAILABLE), ! errmsg("could not obtain lock on relation \"%s\"", ! get_rel_name(relid->relId)))); } /* *************** LockRelationForExtension(Relation relati *** 285,291 **** relation->rd_lockInfo.lockRelId.dbId, relation->rd_lockInfo.lockRelId.relId); ! (void) LockAcquire(&tag, lockmode, false, false); } /* --- 314,324 ---- relation->rd_lockInfo.lockRelId.dbId, relation->rd_lockInfo.lockRelId.relId); ! if (LockAcquire(&tag, lockmode, false, false) == LOCKACQUIRE_NOT_AVAIL) ! ereport(ERROR, ! (errcode(ERRCODE_LOCK_NOT_AVAILABLE), ! errmsg("could not obtain lock on index \"%s\"", ! RelationGetRelationName(relation)))); } /* *************** LockPage(Relation relation, BlockNumber *** 319,325 **** relation->rd_lockInfo.lockRelId.relId, blkno); ! (void) LockAcquire(&tag, lockmode, false, false); } /* --- 352,362 ---- relation->rd_lockInfo.lockRelId.relId, blkno); ! if (LockAcquire(&tag, lockmode, false, false) == LOCKACQUIRE_NOT_AVAIL) ! ereport(ERROR, ! (errcode(ERRCODE_LOCK_NOT_AVAILABLE), ! errmsg("could not obtain lock on page %u of relation \"%s\"", ! blkno, RelationGetRelationName(relation)))); } /* *************** LockTuple(Relation relation, ItemPointer *** 375,381 **** ItemPointerGetBlockNumber(tid), ItemPointerGetOffsetNumber(tid)); ! (void) LockAcquire(&tag, lockmode, false, false); } /* --- 412,422 ---- ItemPointerGetBlockNumber(tid), ItemPointerGetOffsetNumber(tid)); ! if (LockAcquire(&tag, lockmode, false, false) == LOCKACQUIRE_NOT_AVAIL) ! ereport(ERROR, ! (errcode(ERRCODE_LOCK_NOT_AVAILABLE), ! errmsg("could not obtain lock on row in relation \"%s\"", ! RelationGetRelationName(relation)))); } /* *************** XactLockTableInsert(TransactionId xid) *** 429,435 **** SET_LOCKTAG_TRANSACTION(tag, xid); ! (void) LockAcquire(&tag, ExclusiveLock, false, false); } /* --- 470,479 ---- SET_LOCKTAG_TRANSACTION(tag, xid); ! if (LockAcquire(&tag, ExclusiveLock, false, false) == LOCKACQUIRE_NOT_AVAIL) ! ereport(ERROR, ! (errcode(ERRCODE_LOCK_NOT_AVAILABLE), ! errmsg("could not obtain lock on transation with ID %u", xid))); } /* *************** XactLockTableDelete(TransactionId xid) *** 462,468 **** * and if so wait for its parent. */ void ! XactLockTableWait(TransactionId xid) { LOCKTAG tag; --- 506,512 ---- * and if so wait for its parent. */ void ! XactLockTableWait(TransactionId xid, const char *relname) { LOCKTAG tag; *************** XactLockTableWait(TransactionId xid) *** 473,479 **** SET_LOCKTAG_TRANSACTION(tag, xid); ! (void) LockAcquire(&tag, ShareLock, false, false); LockRelease(&tag, ShareLock, false); --- 517,527 ---- SET_LOCKTAG_TRANSACTION(tag, xid); ! if (LockAcquire(&tag, ShareLock, false, false) == LOCKACQUIRE_NOT_AVAIL) ! ereport(ERROR, ! (errcode(ERRCODE_LOCK_NOT_AVAILABLE), ! errmsg("could not obtain lock on row in relation \"%s\"", ! relname))); LockRelease(&tag, ShareLock, false); *************** VirtualXactLockTableInsert(VirtualTransa *** 531,537 **** SET_LOCKTAG_VIRTUALTRANSACTION(tag, vxid); ! (void) LockAcquire(&tag, ExclusiveLock, false, false); } /* --- 579,589 ---- SET_LOCKTAG_VIRTUALTRANSACTION(tag, vxid); ! if (LockAcquire(&tag, ExclusiveLock, false, false) == LOCKACQUIRE_NOT_AVAIL) ! ereport(ERROR, ! (errcode(ERRCODE_LOCK_NOT_AVAILABLE), ! errmsg("could not obtain lock on virtual transaction with ID %u", ! vxid.localTransactionId))); } /* *************** VirtualXactLockTableWait(VirtualTransact *** 549,555 **** SET_LOCKTAG_VIRTUALTRANSACTION(tag, vxid); ! (void) LockAcquire(&tag, ShareLock, false, false); LockRelease(&tag, ShareLock, false); } --- 601,611 ---- SET_LOCKTAG_VIRTUALTRANSACTION(tag, vxid); ! if (LockAcquire(&tag, ShareLock, false, false) == LOCKACQUIRE_NOT_AVAIL) ! ereport(ERROR, ! (errcode(ERRCODE_LOCK_NOT_AVAILABLE), ! errmsg("could not obtain lock on virtual transaction with ID %u", ! vxid.localTransactionId))); LockRelease(&tag, ShareLock, false); } *************** LockDatabaseObject(Oid classid, Oid obji *** 598,604 **** objid, objsubid); ! (void) LockAcquire(&tag, lockmode, false, false); } /* --- 654,664 ---- objid, objsubid); ! if (LockAcquire(&tag, lockmode, false, false) == LOCKACQUIRE_NOT_AVAIL) ! ereport(ERROR, ! (errcode(ERRCODE_LOCK_NOT_AVAILABLE), ! errmsg("could not obtain lock on class:object: %u:%u", ! classid, objid))); } /* *************** LockSharedObject(Oid classid, Oid objid, *** 636,642 **** objid, objsubid); ! (void) LockAcquire(&tag, lockmode, false, false); /* Make sure syscaches are up-to-date with any changes we waited for */ AcceptInvalidationMessages(); --- 696,706 ---- objid, objsubid); ! if (LockAcquire(&tag, lockmode, false, false) == LOCKACQUIRE_NOT_AVAIL) ! ereport(ERROR, ! (errcode(ERRCODE_LOCK_NOT_AVAILABLE), ! errmsg("could not obtain lock on class:object: %u:%u", ! classid, objid))); /* Make sure syscaches are up-to-date with any changes we waited for */ AcceptInvalidationMessages(); *************** LockSharedObjectForSession(Oid classid, *** 678,684 **** objid, objsubid); ! (void) LockAcquire(&tag, lockmode, true, false); } /* --- 742,763 ---- objid, objsubid); ! if (LockAcquire(&tag, lockmode, true, false) == LOCKACQUIRE_NOT_AVAIL) ! switch(classid) ! { ! case DatabaseRelationId: ! ereport(ERROR, ! (errcode(ERRCODE_LOCK_NOT_AVAILABLE), ! errmsg("could not obtain lock on database with ID \"%s\"", ! get_database_name(objid)))); ! break; ! default: ! ereport(ERROR, ! (errcode(ERRCODE_LOCK_NOT_AVAILABLE), ! errmsg("could not obtain lock on class:object: %u:%u", ! classid, objid))); ! break; ! } } /* diff -dcrpN pgsql.orig/src/backend/storage/lmgr/lock.c pgsql.5/src/backend/storage/lmgr/lock.c *** pgsql.orig/src/backend/storage/lmgr/lock.c 2010-01-03 12:54:25.000000000 +0100 --- pgsql.5/src/backend/storage/lmgr/lock.c 2010-01-13 18:59:19.000000000 +0100 *************** PROCLOCK_PRINT(const char *where, const *** 255,261 **** static uint32 proclock_hash(const void *key, Size keysize); static void RemoveLocalLock(LOCALLOCK *locallock); static void GrantLockLocal(LOCALLOCK *locallock, ResourceOwner owner); ! static void WaitOnLock(LOCALLOCK *locallock, ResourceOwner owner); static bool UnGrantLock(LOCK *lock, LOCKMODE lockmode, PROCLOCK *proclock, LockMethod lockMethodTable); static void CleanUpLock(LOCK *lock, PROCLOCK *proclock, --- 255,261 ---- static uint32 proclock_hash(const void *key, Size keysize); static void RemoveLocalLock(LOCALLOCK *locallock); static void GrantLockLocal(LOCALLOCK *locallock, ResourceOwner owner); ! static int WaitOnLock(LOCALLOCK *locallock, ResourceOwner owner); static bool UnGrantLock(LOCK *lock, LOCKMODE lockmode, PROCLOCK *proclock, LockMethod lockMethodTable); static void CleanUpLock(LOCK *lock, PROCLOCK *proclock, *************** ProcLockHashCode(const PROCLOCKTAG *proc *** 451,457 **** * dontWait: if true, don't wait to acquire lock * * Returns one of: ! * LOCKACQUIRE_NOT_AVAIL lock not available, and dontWait=true * LOCKACQUIRE_OK lock successfully acquired * LOCKACQUIRE_ALREADY_HELD incremented count for lock already held * --- 451,457 ---- * dontWait: if true, don't wait to acquire lock * * Returns one of: ! * LOCKACQUIRE_NOT_AVAIL lock not available, either dontWait=true or timeout * LOCKACQUIRE_OK lock successfully acquired * LOCKACQUIRE_ALREADY_HELD incremented count for lock already held * *************** LockAcquireExtended(const LOCKTAG *lockt *** 849,855 **** locktag->locktag_type, lockmode); ! WaitOnLock(locallock, owner); TRACE_POSTGRESQL_LOCK_WAIT_DONE(locktag->locktag_field1, locktag->locktag_field2, --- 849,855 ---- locktag->locktag_type, lockmode); ! status = WaitOnLock(locallock, owner); TRACE_POSTGRESQL_LOCK_WAIT_DONE(locktag->locktag_field1, locktag->locktag_field2, *************** LockAcquireExtended(const LOCKTAG *lockt *** 864,883 **** * done when the lock was granted to us --- see notes in WaitOnLock. */ ! /* ! * Check the proclock entry status, in case something in the ipc ! * communication doesn't work correctly. ! */ ! if (!(proclock->holdMask & LOCKBIT_ON(lockmode))) { ! PROCLOCK_PRINT("LockAcquire: INCONSISTENT", proclock); ! LOCK_PRINT("LockAcquire: INCONSISTENT", lock, lockmode); ! /* Should we retry ? */ ! LWLockRelease(partitionLock); ! elog(ERROR, "LockAcquire failed"); } - PROCLOCK_PRINT("LockAcquire: granted", proclock); - LOCK_PRINT("LockAcquire: granted", lock, lockmode); } LWLockRelease(partitionLock); --- 864,895 ---- * done when the lock was granted to us --- see notes in WaitOnLock. */ ! switch (status) { ! case STATUS_OK: ! /* ! * Check the proclock entry status, in case something in the ipc ! * communication doesn't work correctly. ! */ ! if (!(proclock->holdMask & LOCKBIT_ON(lockmode))) ! { ! PROCLOCK_PRINT("LockAcquire: INCONSISTENT", proclock); ! LOCK_PRINT("LockAcquire: INCONSISTENT", lock, lockmode); ! /* Should we retry ? */ ! LWLockRelease(partitionLock); ! elog(ERROR, "LockAcquire failed"); ! } ! PROCLOCK_PRINT("LockAcquire: granted", proclock); ! LOCK_PRINT("LockAcquire: granted", lock, lockmode); ! break; ! case STATUS_WAITING: ! PROCLOCK_PRINT("LockAcquire: timed out", proclock); ! LOCK_PRINT("LockAcquire: timed out", lock, lockmode); ! break; ! default: ! elog(ERROR, "LockAcquire invalid status"); ! break; } } LWLockRelease(partitionLock); *************** LockAcquireExtended(const LOCKTAG *lockt *** 903,909 **** locktag->locktag_field2); } ! return LOCKACQUIRE_OK; } /* --- 915,921 ---- locktag->locktag_field2); } ! return (status == STATUS_OK ? LOCKACQUIRE_OK : LOCKACQUIRE_NOT_AVAIL); } /* *************** GrantAwaitedLock(void) *** 1181,1194 **** * Caller must have set MyProc->heldLocks to reflect locks already held * on the lockable object by this process. * * The appropriate partition lock must be held at entry. */ ! static void WaitOnLock(LOCALLOCK *locallock, ResourceOwner owner) { LOCKMETHODID lockmethodid = LOCALLOCK_LOCKMETHOD(*locallock); LockMethod lockMethodTable = LockMethods[lockmethodid]; char *volatile new_status = NULL; LOCK_PRINT("WaitOnLock: sleeping on lock", locallock->lock, locallock->tag.mode); --- 1193,1212 ---- * Caller must have set MyProc->heldLocks to reflect locks already held * on the lockable object by this process. * + * Result: returns value of ProcSleep() + * STATUS_OK if we acquired the lock + * STATUS_ERROR if not (deadlock) + * STATUS_WAITING if not (timeout) + * * The appropriate partition lock must be held at entry. */ ! static int WaitOnLock(LOCALLOCK *locallock, ResourceOwner owner) { LOCKMETHODID lockmethodid = LOCALLOCK_LOCKMETHOD(*locallock); LockMethod lockMethodTable = LockMethods[lockmethodid]; char *volatile new_status = NULL; + int wait_status; LOCK_PRINT("WaitOnLock: sleeping on lock", locallock->lock, locallock->tag.mode); *************** WaitOnLock(LOCALLOCK *locallock, Resourc *** 1230,1237 **** */ PG_TRY(); { ! if (ProcSleep(locallock, lockMethodTable) != STATUS_OK) { /* * We failed as a result of a deadlock, see CheckDeadLock(). Quit * now. --- 1248,1260 ---- */ PG_TRY(); { ! wait_status = ProcSleep(locallock, lockMethodTable); ! switch (wait_status) { + case STATUS_OK: + case STATUS_WAITING: + break; + default: /* * We failed as a result of a deadlock, see CheckDeadLock(). Quit * now. *************** WaitOnLock(LOCALLOCK *locallock, Resourc *** 1276,1283 **** pfree(new_status); } ! LOCK_PRINT("WaitOnLock: wakeup on lock", locallock->lock, locallock->tag.mode); } /* --- 1299,1312 ---- pfree(new_status); } ! if (wait_status == STATUS_OK) ! LOCK_PRINT("WaitOnLock: wakeup on lock", ! locallock->lock, locallock->tag.mode); ! else if (wait_status == STATUS_WAITING) ! LOCK_PRINT("WaitOnLock: timeout on lock", locallock->lock, locallock->tag.mode); + + return wait_status; } /* diff -dcrpN pgsql.orig/src/backend/storage/lmgr/proc.c pgsql.5/src/backend/storage/lmgr/proc.c *** pgsql.orig/src/backend/storage/lmgr/proc.c 2010-01-03 12:54:25.000000000 +0100 --- pgsql.5/src/backend/storage/lmgr/proc.c 2010-01-13 18:59:19.000000000 +0100 *************** *** 46,55 **** #include "storage/procarray.h" #include "storage/spin.h" - /* GUC variables */ int DeadlockTimeout = 1000; int StatementTimeout = 0; bool log_lock_waits = false; /* Pointer to this process's PGPROC struct, if any */ --- 46,55 ---- #include "storage/procarray.h" #include "storage/spin.h" /* GUC variables */ int DeadlockTimeout = 1000; int StatementTimeout = 0; + int LockTimeout = 0; bool log_lock_waits = false; /* Pointer to this process's PGPROC struct, if any */ *************** ProcQueueInit(PROC_QUEUE *queue) *** 743,749 **** * The lock table's partition lock must be held at entry, and will be held * at exit. * ! * Result: STATUS_OK if we acquired the lock, STATUS_ERROR if not (deadlock). * * ASSUME: that no one will fiddle with the queue until after * we release the partition lock. --- 743,752 ---- * The lock table's partition lock must be held at entry, and will be held * at exit. * ! * Result: ! * STATUS_OK if we acquired the lock ! * STATUS_ERROR if not (deadlock) ! * STATUS_WAITING if not (timeout) * * ASSUME: that no one will fiddle with the queue until after * we release the partition lock. *************** ProcSleep(LOCALLOCK *locallock, LockMeth *** 765,770 **** --- 768,774 ---- LOCKMASK myHeldLocks = MyProc->heldLocks; bool early_deadlock = false; bool allow_autovacuum_cancel = true; + bool timeout_detected = false; int myWaitStatus; PGPROC *proc; int i; *************** ProcSleep(LOCALLOCK *locallock, LockMeth *** 897,903 **** elog(FATAL, "could not set timer for process wakeup"); /* ! * If someone wakes us between LWLockRelease and PGSemaphoreLock, * PGSemaphoreLock will not block. The wakeup is "saved" by the semaphore * implementation. While this is normally good, there are cases where a * saved wakeup might be leftover from a previous operation (for example, --- 901,907 ---- elog(FATAL, "could not set timer for process wakeup"); /* ! * If someone wakes us between LWLockRelease and PGSemaphoreTimedLock, * PGSemaphoreLock will not block. The wakeup is "saved" by the semaphore * implementation. While this is normally good, there are cases where a * saved wakeup might be leftover from a previous operation (for example, *************** ProcSleep(LOCALLOCK *locallock, LockMeth *** 915,921 **** */ do { ! PGSemaphoreLock(&MyProc->sem, true); /* * waitStatus could change from STATUS_WAITING to something else --- 919,929 ---- */ do { ! if (!PGSemaphoreTimedLock(&MyProc->sem, true)) ! { ! timeout_detected = true; ! break; ! } /* * waitStatus could change from STATUS_WAITING to something else *************** ProcSleep(LOCALLOCK *locallock, LockMeth *** 1055,1060 **** --- 1063,1076 ---- LWLockAcquire(partitionLock, LW_EXCLUSIVE); /* + * If we're in timeout, so we're not waiting anymore and + * we're not the one that the lock will be granted to. + * So remove ourselves from the wait queue. + */ + if (timeout_detected) + RemoveFromWaitQueue(MyProc, hashcode); + + /* * We no longer want LockWaitCancel to do anything. */ lockAwaited = NULL; *************** ProcSleep(LOCALLOCK *locallock, LockMeth *** 1068,1075 **** /* * We don't have to do anything else, because the awaker did all the * necessary update of the lock table and MyProc. */ ! return MyProc->waitStatus; } --- 1084,1093 ---- /* * We don't have to do anything else, because the awaker did all the * necessary update of the lock table and MyProc. + * RemoveFromWaitQueue() have set MyProc->waitStatus = STATUS_ERROR, + * we need to distinguish this case. */ ! return (timeout_detected ? STATUS_WAITING : MyProc->waitStatus); } diff -dcrpN pgsql.orig/src/backend/utils/misc/guc.c pgsql.5/src/backend/utils/misc/guc.c *** pgsql.orig/src/backend/utils/misc/guc.c 2010-01-08 14:37:12.000000000 +0100 --- pgsql.5/src/backend/utils/misc/guc.c 2010-01-13 18:59:19.000000000 +0100 *************** static struct config_int ConfigureNamesI *** 1584,1589 **** --- 1584,1599 ---- }, { + {"lock_timeout", PGC_USERSET, CLIENT_CONN_STATEMENT, + gettext_noop("Sets the maximum allowed timeout for any lock taken by a statement."), + gettext_noop("A value of 0 turns off the timeout."), + GUC_UNIT_MS + }, + &LockTimeout, + 0, 0, INT_MAX, NULL, NULL + }, + + { {"vacuum_freeze_min_age", PGC_USERSET, CLIENT_CONN_STATEMENT, gettext_noop("Minimum age at which VACUUM should freeze a table row."), NULL diff -dcrpN pgsql.orig/src/backend/utils/misc/postgresql.conf.sample pgsql.5/src/backend/utils/misc/postgresql.conf.sample *** pgsql.orig/src/backend/utils/misc/postgresql.conf.sample 2009-12-19 16:33:56.000000000 +0100 --- pgsql.5/src/backend/utils/misc/postgresql.conf.sample 2010-01-15 08:48:33.000000000 +0100 *************** *** 475,480 **** --- 475,483 ---- #------------------------------------------------------------------------------ #deadlock_timeout = 1s + #lock_timeout = 0 # timeout value for heavy-weight locks + # taken by statements. 0 disables timeout + # unit in milliseconds, default is 0 #max_locks_per_transaction = 64 # min 10 # (change requires restart) # Note: Each lock table slot uses ~270 bytes of shared memory, and there are diff -dcrpN pgsql.orig/src/include/access/multixact.h pgsql.5/src/include/access/multixact.h *** pgsql.orig/src/include/access/multixact.h 2010-01-03 12:54:36.000000000 +0100 --- pgsql.5/src/include/access/multixact.h 2010-01-13 21:52:45.000000000 +0100 *************** extern MultiXactId MultiXactIdCreate(Tra *** 46,52 **** extern MultiXactId MultiXactIdExpand(MultiXactId multi, TransactionId xid); extern bool MultiXactIdIsRunning(MultiXactId multi); extern bool MultiXactIdIsCurrent(MultiXactId multi); ! extern void MultiXactIdWait(MultiXactId multi); extern bool ConditionalMultiXactIdWait(MultiXactId multi); extern void MultiXactIdSetOldestMember(void); extern int GetMultiXactIdMembers(MultiXactId multi, TransactionId **xids); --- 46,52 ---- extern MultiXactId MultiXactIdExpand(MultiXactId multi, TransactionId xid); extern bool MultiXactIdIsRunning(MultiXactId multi); extern bool MultiXactIdIsCurrent(MultiXactId multi); ! extern void MultiXactIdWait(MultiXactId multi, const char *relname); extern bool ConditionalMultiXactIdWait(MultiXactId multi); extern void MultiXactIdSetOldestMember(void); extern int GetMultiXactIdMembers(MultiXactId multi, TransactionId **xids); diff -dcrpN pgsql.orig/src/include/storage/lmgr.h pgsql.5/src/include/storage/lmgr.h *** pgsql.orig/src/include/storage/lmgr.h 2010-01-03 12:54:39.000000000 +0100 --- pgsql.5/src/include/storage/lmgr.h 2010-01-13 22:07:35.000000000 +0100 *************** extern void UnlockTuple(Relation relatio *** 53,59 **** /* Lock an XID (used to wait for a transaction to finish) */ extern void XactLockTableInsert(TransactionId xid); extern void XactLockTableDelete(TransactionId xid); ! extern void XactLockTableWait(TransactionId xid); extern bool ConditionalXactLockTableWait(TransactionId xid); /* Lock a VXID (used to wait for a transaction to finish) */ --- 53,59 ---- /* Lock an XID (used to wait for a transaction to finish) */ extern void XactLockTableInsert(TransactionId xid); extern void XactLockTableDelete(TransactionId xid); ! extern void XactLockTableWait(TransactionId xid, const char *relname); extern bool ConditionalXactLockTableWait(TransactionId xid); /* Lock a VXID (used to wait for a transaction to finish) */ diff -dcrpN pgsql.orig/src/include/storage/pg_sema.h pgsql.5/src/include/storage/pg_sema.h *** pgsql.orig/src/include/storage/pg_sema.h 2010-01-03 12:54:39.000000000 +0100 --- pgsql.5/src/include/storage/pg_sema.h 2010-01-13 18:59:19.000000000 +0100 *************** extern void PGSemaphoreUnlock(PGSemaphor *** 80,83 **** --- 80,86 ---- /* Lock a semaphore only if able to do so without blocking */ extern bool PGSemaphoreTryLock(PGSemaphore sema); + /* Lock a semaphore only if able to do so under the lock_timeout */ + extern bool PGSemaphoreTimedLock(PGSemaphore sema, bool interruptOK); + #endif /* PG_SEMA_H */ diff -dcrpN pgsql.orig/src/include/storage/proc.h pgsql.5/src/include/storage/proc.h *** pgsql.orig/src/include/storage/proc.h 2010-01-03 12:54:39.000000000 +0100 --- pgsql.5/src/include/storage/proc.h 2010-01-13 18:59:19.000000000 +0100 *************** typedef struct PROC_HDR *** 161,166 **** --- 161,167 ---- /* configurable options */ extern int DeadlockTimeout; extern int StatementTimeout; + extern int LockTimeout; extern bool log_lock_waits; extern volatile bool cancel_from_timeout;
-- Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers