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

Commit 6cc842a

Browse files
committed
Revise lock manager to support "session level" locks as well as "transaction
level" locks. A session lock is not released at transaction commit (but it is released on transaction abort, to ensure recovery after an elog(ERROR)). In VACUUM, use a session lock to protect the master table while vacuuming a TOAST table, so that the TOAST table can be done in an independent transaction. I also took this opportunity to do some cleanup and renaming in the lock code. The previously noted bug in ProcLockWakeup, that it couldn't wake up any waiters beyond the first non-wakeable waiter, is now fixed. Also found a previously unknown bug of the same kind (failure to scan all members of a lock queue in some cases) in DeadLockCheck. This might have led to failure to detect a deadlock condition, resulting in indefinite waits, but it's difficult to characterize the conditions required to trigger a failure.
1 parent b2145e9 commit 6cc842a

File tree

11 files changed

+1018
-960
lines changed

11 files changed

+1018
-960
lines changed

contrib/userlock/user_locks.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ user_lock(uint32 id1, uint32 id2, LOCKMODE lockmode)
3333
tag.objId.blkno = (BlockNumber) id2;
3434
tag.offnum = (OffsetNumber) (id1 & 0xffff);
3535

36-
return LockAcquire(USER_LOCKMETHOD, &tag, lockmode);
36+
return LockAcquire(USER_LOCKMETHOD, &tag, InvalidTransactionId, lockmode);
3737
}
3838

3939
int
@@ -47,7 +47,7 @@ user_unlock(uint32 id1, uint32 id2, LOCKMODE lockmode)
4747
tag.objId.blkno = (BlockNumber) id2;
4848
tag.offnum = (OffsetNumber) (id1 & 0xffff);
4949

50-
return LockRelease(USER_LOCKMETHOD, &tag, lockmode);
50+
return LockRelease(USER_LOCKMETHOD, &tag, InvalidTransactionId, lockmode);
5151
}
5252

5353
int
@@ -89,7 +89,7 @@ user_unlock_all()
8989
}
9090

9191
proc = (PROC *) MAKE_PTR(location);
92-
return LockReleaseAll(USER_LOCKMETHOD, &proc->lockQueue);
92+
return LockReleaseAll(USER_LOCKMETHOD, proc, false, InvalidTransactionId);
9393
}
9494

9595
/* end of file */

src/backend/access/transam/xact.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
*
99
*
1010
* IDENTIFICATION
11-
* $Header: /cvsroot/pgsql/src/backend/access/transam/xact.c,v 1.89 2000/12/18 00:44:45 tgl Exp $
11+
* $Header: /cvsroot/pgsql/src/backend/access/transam/xact.c,v 1.90 2000/12/22 00:51:53 tgl Exp $
1212
*
1313
* NOTES
1414
* Transaction aborts can now occur two ways:
@@ -741,7 +741,7 @@ AtCommit_Locks(void)
741741
* Then you're up a creek! -mer 5/24/92
742742
* ----------------
743743
*/
744-
ProcReleaseLocks();
744+
ProcReleaseLocks(true);
745745
}
746746

747747
/* --------------------------------
@@ -828,7 +828,7 @@ AtAbort_Locks(void)
828828
* Then you're up a creek without a paddle! -mer
829829
* ----------------
830830
*/
831-
ProcReleaseLocks();
831+
ProcReleaseLocks(false);
832832
}
833833

834834

src/backend/commands/vacuum.c

Lines changed: 49 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
*
99
*
1010
* IDENTIFICATION
11-
* $Header: /cvsroot/pgsql/src/backend/commands/vacuum.c,v 1.177 2000/12/08 06:43:44 inoue Exp $
11+
* $Header: /cvsroot/pgsql/src/backend/commands/vacuum.c,v 1.178 2000/12/22 00:51:53 tgl Exp $
1212
*
1313
*-------------------------------------------------------------------------
1414
*/
@@ -61,7 +61,7 @@ static void vacuum_init(void);
6161
static void vacuum_shutdown(void);
6262
static void vac_vacuum(NameData *VacRelP, bool analyze, List *anal_cols2);
6363
static VRelList getrels(NameData *VacRelP);
64-
static void vacuum_rel(Oid relid, bool is_toastrel);
64+
static void vacuum_rel(Oid relid);
6565
static void scan_heap(VRelStats *vacrelstats, Relation onerel, VacPageList vacuum_pages, VacPageList fraged_pages);
6666
static void repair_frag(VRelStats *vacrelstats, Relation onerel, VacPageList vacuum_pages, VacPageList fraged_pages, int nindices, Relation *Irel);
6767
static void vacuum_heap(VRelStats *vacrelstats, Relation onerel, VacPageList vacpagelist);
@@ -239,7 +239,7 @@ vac_vacuum(NameData *VacRelP, bool analyze, List *anal_cols2)
239239
/* vacuum each heap relation */
240240
for (cur = vrl; cur != (VRelList) NULL; cur = cur->vrl_next)
241241
{
242-
vacuum_rel(cur->vrl_relid, false);
242+
vacuum_rel(cur->vrl_relid);
243243
/* analyze separately so locking is minimized */
244244
if (analyze)
245245
analyze_rel(cur->vrl_relid, anal_cols2, MESSAGE_LEVEL);
@@ -308,7 +308,7 @@ getrels(NameData *VacRelP)
308308

309309
if (rkind != RELKIND_RELATION)
310310
{
311-
elog(NOTICE, "Vacuum: can not process indecies, views and certain system tables");
311+
elog(NOTICE, "Vacuum: can not process indices, views and certain system tables");
312312
continue;
313313
}
314314

@@ -342,32 +342,34 @@ getrels(NameData *VacRelP)
342342
* vacuum_rel() -- vacuum one heap relation
343343
*
344344
* This routine vacuums a single heap, cleans out its indices, and
345-
* updates its statistics num_pages and num_tuples statistics.
345+
* updates its num_pages and num_tuples statistics.
346346
*
347347
* Doing one heap at a time incurs extra overhead, since we need to
348348
* check that the heap exists again just before we vacuum it. The
349349
* reason that we do this is so that vacuuming can be spread across
350350
* many small transactions. Otherwise, two-phase locking would require
351351
* us to lock the entire database during one pass of the vacuum cleaner.
352+
*
353+
* At entry and exit, we are not inside a transaction.
352354
*/
353355
static void
354-
vacuum_rel(Oid relid, bool is_toastrel)
356+
vacuum_rel(Oid relid)
355357
{
356358
Relation onerel;
359+
LockRelId onerelid;
357360
VacPageListData vacuum_pages; /* List of pages to vacuum and/or clean
358-
* indices */
361+
* indices */
359362
VacPageListData fraged_pages; /* List of pages with space enough for
360-
* re-using */
361-
VacPage *vacpage;
363+
* re-using */
362364
Relation *Irel;
363365
int32 nindices,
364366
i;
365367
VRelStats *vacrelstats;
366368
bool reindex = false;
367369
Oid toast_relid;
368370

369-
if (!is_toastrel)
370-
StartTransactionCommand();
371+
/* Begin a transaction for vacuuming this relation */
372+
StartTransactionCommand();
371373

372374
/*
373375
* Check for user-requested abort. Note we want this to be inside a
@@ -384,8 +386,7 @@ vacuum_rel(Oid relid, bool is_toastrel)
384386
ObjectIdGetDatum(relid),
385387
0, 0, 0))
386388
{
387-
if (!is_toastrel)
388-
CommitTransactionCommand();
389+
CommitTransactionCommand();
389390
return;
390391
}
391392

@@ -403,13 +404,25 @@ vacuum_rel(Oid relid, bool is_toastrel)
403404
elog(NOTICE, "Skipping \"%s\" --- only table owner can VACUUM it",
404405
RelationGetRelationName(onerel));
405406
heap_close(onerel, AccessExclusiveLock);
406-
if (!is_toastrel)
407-
CommitTransactionCommand();
407+
CommitTransactionCommand();
408408
return;
409409
}
410410

411411
/*
412-
* Remember the relation'ss TOAST relation for later
412+
* Get a session-level exclusive lock too. This will protect our
413+
* exclusive access to the relation across multiple transactions,
414+
* so that we can vacuum the relation's TOAST table (if any) secure
415+
* in the knowledge that no one is diddling the parent relation.
416+
*
417+
* NOTE: this cannot block, even if someone else is waiting for access,
418+
* because the lock manager knows that both lock requests are from the
419+
* same process.
420+
*/
421+
onerelid = onerel->rd_lockInfo.lockRelId;
422+
LockRelationForSession(&onerelid, AccessExclusiveLock);
423+
424+
/*
425+
* Remember the relation's TOAST relation for later
413426
*/
414427
toast_relid = onerel->rd_rel->reltoastrelid;
415428

@@ -500,21 +513,6 @@ vacuum_rel(Oid relid, bool is_toastrel)
500513
if (reindex)
501514
activate_indexes_of_a_table(relid, true);
502515

503-
/*
504-
* ok - free vacuum_pages list of reaped pages
505-
*
506-
* Isn't this a waste of code? Upcoming commit should free memory, no?
507-
*/
508-
if (vacuum_pages.num_pages > 0)
509-
{
510-
vacpage = vacuum_pages.pagedesc;
511-
for (i = 0; i < vacuum_pages.num_pages; i++, vacpage++)
512-
pfree(*vacpage);
513-
pfree(vacuum_pages.pagedesc);
514-
if (fraged_pages.num_pages > 0)
515-
pfree(fraged_pages.pagedesc);
516-
}
517-
518516
/* all done with this class, but hold lock until commit */
519517
heap_close(onerel, NoLock);
520518

@@ -523,19 +521,25 @@ vacuum_rel(Oid relid, bool is_toastrel)
523521
vacrelstats->num_tuples, vacrelstats->hasindex,
524522
vacrelstats);
525523

524+
/*
525+
* Complete the transaction and free all temporary memory used.
526+
*/
527+
CommitTransactionCommand();
528+
526529
/*
527530
* If the relation has a secondary toast one, vacuum that too
528-
* while we still hold the lock on the master table. We don't
529-
* need to propagate "analyze" to it, because the toaster
531+
* while we still hold the session lock on the master table.
532+
* We don't need to propagate "analyze" to it, because the toaster
530533
* always uses hardcoded index access and statistics are
531534
* totally unimportant for toast relations
532535
*/
533536
if (toast_relid != InvalidOid)
534-
vacuum_rel(toast_relid, true);
537+
vacuum_rel(toast_relid);
535538

536-
/* next command frees attribute stats */
537-
if (!is_toastrel)
538-
CommitTransactionCommand();
539+
/*
540+
* Now release the session-level lock on the master table.
541+
*/
542+
UnlockRelationForSession(&onerelid, AccessExclusiveLock);
539543
}
540544

541545
/*
@@ -1786,9 +1790,13 @@ failed to add item with len = %lu to page %u (free space %lu, nusd %u, noff %u)"
17861790
if (num_moved > 0)
17871791
{
17881792
/*
1789-
* We have to commit our tuple' movings before we'll truncate
1790-
* relation, but we shouldn't lose our locks. And so - quick hack:
1791-
* record status of current transaction as committed, and continue.
1793+
* We have to commit our tuple movings before we truncate the
1794+
* relation. Ideally we should do Commit/StartTransactionCommand
1795+
* here, relying on the session-level table lock to protect our
1796+
* exclusive access to the relation. However, that would require
1797+
* a lot of extra code to close and re-open the relation, indices,
1798+
* etc. For now, a quick hack: record status of current transaction
1799+
* as committed, and continue.
17921800
*/
17931801
RecordTransactionCommit();
17941802
}
@@ -1852,7 +1860,7 @@ failed to add item with len = %lu to page %u (free space %lu, nusd %u, noff %u)"
18521860
/*
18531861
* Reflect the motion of system tuples to catalog cache here.
18541862
*/
1855-
CommandCounterIncrement();
1863+
CommandCounterIncrement();
18561864

18571865
if (Nvacpagelist.num_pages > 0)
18581866
{

src/backend/storage/ipc/ipci.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
*
99
*
1010
* IDENTIFICATION
11-
* $Header: /cvsroot/pgsql/src/backend/storage/ipc/ipci.c,v 1.37 2000/12/03 17:18:10 tgl Exp $
11+
* $Header: /cvsroot/pgsql/src/backend/storage/ipc/ipci.c,v 1.38 2000/12/22 00:51:54 tgl Exp $
1212
*
1313
*-------------------------------------------------------------------------
1414
*/
@@ -84,7 +84,7 @@ CreateSharedMemoryAndSemaphores(bool makePrivate, int maxBackends)
8484
* Set up lock manager
8585
*/
8686
InitLocks();
87-
if (InitLockTable() == INVALID_TABLEID)
87+
if (InitLockTable(maxBackends) == INVALID_TABLEID)
8888
elog(FATAL, "Couldn't create the lock table");
8989

9090
/*

src/backend/storage/lmgr/README

Lines changed: 56 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,15 @@
1-
$Header: /cvsroot/pgsql/src/backend/storage/lmgr/README,v 1.3 1998/07/06 18:16:07 momjian Exp $
1+
$Header: /cvsroot/pgsql/src/backend/storage/lmgr/README,v 1.4 2000/12/22 00:51:54 tgl Exp $
22

3-
There are two fundemental lock structures. Lock methods describe the
4-
locking behavior. We currently only support multi-level locking. Lock
5-
modes describe the mode of the lock(read/write or shared/exclusive).
3+
There are two fundamental lock structures: the per-lockable-object LOCK
4+
struct, and the per-lock-holder HOLDER struct. A LOCK object exists
5+
for each lockable object that currently has locks held or requested on it.
6+
A HOLDER struct exists for each transaction that is holding or requesting
7+
lock(s) on each LOCK object.
8+
9+
Lock methods describe the overall locking behavior. Currently there are
10+
two lock methods: DEFAULT and USER. (USER locks are non-blocking.)
11+
12+
Lock modes describe the type of the lock (read/write or shared/exclusive).
613
See src/tools/backend/index.html and src/include/storage/lock.h for more
714
details.
815

@@ -12,10 +19,10 @@ The lock manager's LOCK:
1219

1320
tag -
1421
The key fields that are used for hashing locks in the shared memory
15-
lock hash table. This is kept as a separate struct to ensure that we
16-
always zero out the correct number of bytes. This is a problem as
17-
part of the tag is an itempointer which is 6 bytes and causes 2
18-
additional bytes to be added as padding.
22+
lock hash table. This is declared as a separate struct to ensure that
23+
we always zero out the correct number of bytes. It is critical that
24+
any alignment-padding bytes the compiler might insert in the struct
25+
be zeroed out, else the hash computation will be random.
1926

2027
tag.relId -
2128
Uniquely identifies the relation that the lock corresponds to.
@@ -30,17 +37,17 @@ tag -
3037
tuple within the block. If we are setting a table level lock
3138
both the blockId and tupleId (in an item pointer this is called
3239
the position) are set to invalid, if it is a page level lock the
33-
blockId is valid, while the tuleId is still invalid. Finally if
40+
blockId is valid, while the tupleId is still invalid. Finally if
3441
this is a tuple level lock (we currently never do this) then both
3542
the blockId and tupleId are set to valid specifications. This is
3643
how we get the appearance of a multi-level lock table while using
3744
only a single table (see Gray's paper on 2 phase locking if
3845
you are puzzled about how multi-level lock tables work).
3946

4047
mask -
41-
This field indicates what types of locks are currently held in the
42-
given lock. It is used (against the lock table's conflict table)
43-
to determine if the new lock request will conflict with existing
48+
This field indicates what types of locks are currently held on the
49+
given lockable object. It is used (against the lock table's conflict
50+
table) to determine if the new lock request will conflict with existing
4451
lock types held. Conficts are determined by bitwise AND operations
4552
between the mask and the conflict table entry for the given lock type
4653
to be set. The current representation is that each bit (1 through 5)
@@ -73,7 +80,7 @@ holders -
7380

7481
nActive -
7582
Keeps a count of how many times this lock has been succesfully acquired.
76-
This count does not include attempts that were rejected due to conflicts,
83+
This count does not include attempts that are waiting due to conflicts,
7784
but can count the same backend twice (e.g. a read then a write -- since
7885
its the same transaction this won't cause a conflict)
7986

@@ -85,3 +92,39 @@ activeHolders -
8592

8693
---------------------------------------------------------------------------
8794

95+
The lock manager's HOLDER:
96+
97+
tag -
98+
The key fields that are used for hashing entries in the shared memory
99+
holder hash table. This is declared as a separate struct to ensure that
100+
we always zero out the correct number of bytes.
101+
102+
tag.lock
103+
SHMEM offset of the LOCK object this holder is for.
104+
105+
tag.pid
106+
PID of backend process that owns this holder.
107+
108+
tag.xid
109+
XID of transaction this holder is for, or InvalidTransactionId
110+
if the holder is for session-level locking.
111+
112+
Note that this structure will support multiple transactions running
113+
concurrently in one backend, which may be handy if we someday decide
114+
to support nested transactions. Currently, the XID field is only needed
115+
to distinguish per-transaction locks from session locks. User locks
116+
are always session locks, and we also use session locks for multi-
117+
transaction operations like VACUUM.
118+
119+
holders -
120+
The number of successfully acquired locks of each type for this holder.
121+
(CAUTION: the semantics are not the same as the LOCK's holder[], which
122+
counts both acquired and pending requests. Probably a different name
123+
should be used...)
124+
125+
nHolding -
126+
Sum of the holders[] array.
127+
128+
queue -
129+
List link for shared memory queue of all the HOLDER objects for the
130+
same backend.

0 commit comments

Comments
 (0)