Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit e06fda0

Browse files
committed
Add a function GetLockConflicts() to lock.c to report xacts holding
locks that would conflict with a specified lock request, without actually trying to get that lock. Use this instead of the former ad hoc method of doing the first wait step in CREATE INDEX CONCURRENTLY. Fixes problem with undetected deadlock and in many cases will allow the index creation to proceed sooner than it otherwise could've. Per discussion with Greg Stark.
1 parent ca1fd0e commit e06fda0

File tree

3 files changed

+129
-32
lines changed

3 files changed

+129
-32
lines changed

src/backend/commands/indexcmds.c

+19-23
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
*
99
*
1010
* IDENTIFICATION
11-
* $PostgreSQL: pgsql/src/backend/commands/indexcmds.c,v 1.147 2006/08/25 04:06:48 tgl Exp $
11+
* $PostgreSQL: pgsql/src/backend/commands/indexcmds.c,v 1.148 2006/08/27 19:14:34 tgl Exp $
1212
*
1313
*-------------------------------------------------------------------------
1414
*/
@@ -119,8 +119,11 @@ DefineIndex(RangeVar *heapRelation,
119119
Datum reloptions;
120120
IndexInfo *indexInfo;
121121
int numberOfAttributes;
122+
List *old_xact_list;
123+
ListCell *lc;
122124
uint32 ixcnt;
123125
LockRelId heaprelid;
126+
LOCKTAG heaplocktag;
124127
Snapshot snapshot;
125128
Relation pg_index;
126129
HeapTuple indexTuple;
@@ -466,33 +469,26 @@ DefineIndex(RangeVar *heapRelation,
466469
CommitTransactionCommand();
467470
StartTransactionCommand();
468471

469-
/* Establish transaction snapshot ... else GetLatestSnapshot complains */
470-
(void) GetTransactionSnapshot();
471-
472472
/*
473473
* Now we must wait until no running transaction could have the table open
474-
* with the old list of indexes. If we can take an exclusive lock then
475-
* there are none now and anybody who opens it later will get the new
476-
* index in their relcache entry. Alternatively, if our Xmin reaches our
477-
* own (new) transaction then we know no transactions that started before
478-
* the index was visible are left anyway.
474+
* with the old list of indexes. To do this, inquire which xacts currently
475+
* would conflict with ShareLock on the table -- ie, which ones have
476+
* a lock that permits writing the table. Then wait for each of these
477+
* xacts to commit or abort. Note we do not need to worry about xacts
478+
* that open the table for writing after this point; they will see the
479+
* new index when they open it.
480+
*
481+
* Note: GetLockConflicts() never reports our own xid,
482+
* hence we need not check for that.
479483
*/
480-
for (;;)
481-
{
482-
CHECK_FOR_INTERRUPTS();
484+
SET_LOCKTAG_RELATION(heaplocktag, heaprelid.dbId, heaprelid.relId);
485+
old_xact_list = GetLockConflicts(&heaplocktag, ShareLock);
483486

484-
if (ConditionalLockRelationOid(relationId, ExclusiveLock))
485-
{
486-
/* Release the lock right away to avoid blocking anyone */
487-
UnlockRelationOid(relationId, ExclusiveLock);
488-
break;
489-
}
490-
491-
if (TransactionIdEquals(GetLatestSnapshot()->xmin,
492-
GetTopTransactionId()))
493-
break;
487+
foreach(lc, old_xact_list)
488+
{
489+
TransactionId xid = lfirst_xid(lc);
494490

495-
pg_usleep(1000000L); /* 1 sec */
491+
XactLockTableWait(xid);
496492
}
497493

498494
/*

src/backend/storage/lmgr/lock.c

+99-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
*
99
*
1010
* IDENTIFICATION
11-
* $PostgreSQL: pgsql/src/backend/storage/lmgr/lock.c,v 1.171 2006/08/19 01:36:28 tgl Exp $
11+
* $PostgreSQL: pgsql/src/backend/storage/lmgr/lock.c,v 1.172 2006/08/27 19:14:34 tgl Exp $
1212
*
1313
* NOTES
1414
* A lock table is a shared memory hash table. When
@@ -1685,6 +1685,104 @@ LockReassignCurrentOwner(void)
16851685
}
16861686

16871687

1688+
/*
1689+
* GetLockConflicts
1690+
* Get a list of TransactionIds of xacts currently holding locks
1691+
* that would conflict with the specified lock/lockmode.
1692+
* xacts merely awaiting such a lock are NOT reported.
1693+
*
1694+
* Of course, the result could be out of date by the time it's returned,
1695+
* so use of this function has to be thought about carefully.
1696+
*
1697+
* Only top-level XIDs are reported. Note we never include the current xact
1698+
* in the result list, since an xact never blocks itself.
1699+
*/
1700+
List *
1701+
GetLockConflicts(const LOCKTAG *locktag, LOCKMODE lockmode)
1702+
{
1703+
List *result = NIL;
1704+
LOCKMETHODID lockmethodid = locktag->locktag_lockmethodid;
1705+
LockMethod lockMethodTable;
1706+
LOCK *lock;
1707+
LOCKMASK conflictMask;
1708+
SHM_QUEUE *procLocks;
1709+
PROCLOCK *proclock;
1710+
uint32 hashcode;
1711+
LWLockId partitionLock;
1712+
1713+
if (lockmethodid <= 0 || lockmethodid >= lengthof(LockMethods))
1714+
elog(ERROR, "unrecognized lock method: %d", lockmethodid);
1715+
lockMethodTable = LockMethods[lockmethodid];
1716+
if (lockmode <= 0 || lockmode > lockMethodTable->numLockModes)
1717+
elog(ERROR, "unrecognized lock mode: %d", lockmode);
1718+
1719+
/*
1720+
* Look up the lock object matching the tag.
1721+
*/
1722+
hashcode = LockTagHashCode(locktag);
1723+
partitionLock = LockHashPartitionLock(hashcode);
1724+
1725+
LWLockAcquire(partitionLock, LW_SHARED);
1726+
1727+
lock = (LOCK *) hash_search_with_hash_value(LockMethodLockHash,
1728+
(void *) locktag,
1729+
hashcode,
1730+
HASH_FIND,
1731+
NULL);
1732+
if (!lock)
1733+
{
1734+
/*
1735+
* If the lock object doesn't exist, there is nothing holding a
1736+
* lock on this lockable object.
1737+
*/
1738+
LWLockRelease(partitionLock);
1739+
return NIL;
1740+
}
1741+
1742+
/*
1743+
* Examine each existing holder (or awaiter) of the lock.
1744+
*/
1745+
conflictMask = lockMethodTable->conflictTab[lockmode];
1746+
1747+
procLocks = &(lock->procLocks);
1748+
1749+
proclock = (PROCLOCK *) SHMQueueNext(procLocks, procLocks,
1750+
offsetof(PROCLOCK, lockLink));
1751+
1752+
while (proclock)
1753+
{
1754+
if (conflictMask & proclock->holdMask)
1755+
{
1756+
PGPROC *proc = proclock->tag.myProc;
1757+
1758+
/* A backend never blocks itself */
1759+
if (proc != MyProc)
1760+
{
1761+
/* Fetch xid just once - see GetNewTransactionId */
1762+
TransactionId xid = proc->xid;
1763+
1764+
/*
1765+
* Race condition: during xact commit/abort we zero out
1766+
* PGPROC's xid before we mark its locks released. If we see
1767+
* zero in the xid field, assume the xact is in process of
1768+
* shutting down and act as though the lock is already
1769+
* released.
1770+
*/
1771+
if (TransactionIdIsValid(xid))
1772+
result = lappend_xid(result, xid);
1773+
}
1774+
}
1775+
1776+
proclock = (PROCLOCK *) SHMQueueNext(procLocks, &proclock->lockLink,
1777+
offsetof(PROCLOCK, lockLink));
1778+
}
1779+
1780+
LWLockRelease(partitionLock);
1781+
1782+
return result;
1783+
}
1784+
1785+
16881786
/*
16891787
* AtPrepare_Locks
16901788
* Do the preparatory work for a PREPARE: make 2PC state file records

src/include/storage/lock.h

+11-8
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,14 @@
77
* Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
88
* Portions Copyright (c) 1994, Regents of the University of California
99
*
10-
* $PostgreSQL: pgsql/src/include/storage/lock.h,v 1.97 2006/07/31 20:09:05 tgl Exp $
10+
* $PostgreSQL: pgsql/src/include/storage/lock.h,v 1.98 2006/08/27 19:14:34 tgl Exp $
1111
*
1212
*-------------------------------------------------------------------------
1313
*/
1414
#ifndef LOCK_H_
1515
#define LOCK_H_
1616

17+
#include "nodes/pg_list.h"
1718
#include "storage/itemptr.h"
1819
#include "storage/lwlock.h"
1920
#include "storage/shmem.h"
@@ -412,6 +413,7 @@ extern bool LockRelease(const LOCKTAG *locktag,
412413
extern void LockReleaseAll(LOCKMETHODID lockmethodid, bool allLocks);
413414
extern void LockReleaseCurrentOwner(void);
414415
extern void LockReassignCurrentOwner(void);
416+
extern List *GetLockConflicts(const LOCKTAG *locktag, LOCKMODE lockmode);
415417
extern void AtPrepare_Locks(void);
416418
extern void PostPrepare_Locks(TransactionId xid);
417419
extern int LockCheckConflicts(LockMethod lockMethodTable,
@@ -421,13 +423,6 @@ extern void GrantLock(LOCK *lock, PROCLOCK *proclock, LOCKMODE lockmode);
421423
extern void GrantAwaitedLock(void);
422424
extern void RemoveFromWaitQueue(PGPROC *proc, uint32 hashcode);
423425
extern Size LockShmemSize(void);
424-
extern bool DeadLockCheck(PGPROC *proc);
425-
extern void DeadLockReport(void);
426-
extern void RememberSimpleDeadLock(PGPROC *proc1,
427-
LOCKMODE lockmode,
428-
LOCK *lock,
429-
PGPROC *proc2);
430-
extern void InitDeadLockChecking(void);
431426
extern LockData *GetLockStatusData(void);
432427
extern const char *GetLockmodeName(LOCKMETHODID lockmethodid, LOCKMODE mode);
433428

@@ -438,6 +433,14 @@ extern void lock_twophase_postcommit(TransactionId xid, uint16 info,
438433
extern void lock_twophase_postabort(TransactionId xid, uint16 info,
439434
void *recdata, uint32 len);
440435

436+
extern bool DeadLockCheck(PGPROC *proc);
437+
extern void DeadLockReport(void);
438+
extern void RememberSimpleDeadLock(PGPROC *proc1,
439+
LOCKMODE lockmode,
440+
LOCK *lock,
441+
PGPROC *proc2);
442+
extern void InitDeadLockChecking(void);
443+
441444
#ifdef LOCK_DEBUG
442445
extern void DumpLocks(PGPROC *proc);
443446
extern void DumpAllLocks(void);

0 commit comments

Comments
 (0)