Index: src/backend/access/transam/twophase.c =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/backend/access/transam/twophase.c,v retrieving revision 1.21 diff -c -p -r1.21 twophase.c *** src/backend/access/transam/twophase.c 14 Jul 2006 14:52:17 -0000 1.21 --- src/backend/access/transam/twophase.c 28 Jul 2006 21:59:42 -0000 *************** MarkAsPreparing(TransactionId xid, const *** 279,284 **** --- 279,285 ---- gxact->proc.pid = 0; gxact->proc.databaseId = databaseid; gxact->proc.roleId = owner; + gxact->proc.inVacuum = false; gxact->proc.lwWaiting = false; gxact->proc.lwExclusive = false; gxact->proc.lwWaitLink = NULL; Index: src/backend/access/transam/xact.c =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/backend/access/transam/xact.c,v retrieving revision 1.224 diff -c -p -r1.224 xact.c *** src/backend/access/transam/xact.c 24 Jul 2006 16:32:44 -0000 1.224 --- src/backend/access/transam/xact.c 28 Jul 2006 21:59:42 -0000 *************** CommitTransaction(void) *** 1529,1534 **** --- 1529,1535 ---- LWLockAcquire(ProcArrayLock, LW_EXCLUSIVE); MyProc->xid = InvalidTransactionId; MyProc->xmin = InvalidTransactionId; + MyProc->inVacuum = false; /* must be cleared with xid/xmin */ /* Clear the subtransaction-XID cache too while holding the lock */ MyProc->subxids.nxids = 0; *************** PrepareTransaction(void) *** 1764,1769 **** --- 1765,1771 ---- LWLockAcquire(ProcArrayLock, LW_EXCLUSIVE); MyProc->xid = InvalidTransactionId; MyProc->xmin = InvalidTransactionId; + MyProc->inVacuum = false; /* must be cleared with xid/xmin */ /* Clear the subtransaction-XID cache too while holding the lock */ MyProc->subxids.nxids = 0; *************** AbortTransaction(void) *** 1927,1932 **** --- 1929,1935 ---- LWLockAcquire(ProcArrayLock, LW_EXCLUSIVE); MyProc->xid = InvalidTransactionId; MyProc->xmin = InvalidTransactionId; + MyProc->inVacuum = false; /* must be cleared with xid/xmin */ /* Clear the subtransaction-XID cache too while holding the lock */ MyProc->subxids.nxids = 0; Index: src/backend/access/transam/xlog.c =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/backend/access/transam/xlog.c,v retrieving revision 1.244 diff -c -p -r1.244 xlog.c *** src/backend/access/transam/xlog.c 14 Jul 2006 14:52:17 -0000 1.244 --- src/backend/access/transam/xlog.c 28 Jul 2006 21:59:42 -0000 *************** CreateCheckPoint(bool shutdown, bool for *** 5413,5419 **** * StartupSUBTRANS hasn't been called yet. */ if (!InRecovery) ! TruncateSUBTRANS(GetOldestXmin(true)); if (!shutdown) ereport(DEBUG2, --- 5413,5419 ---- * StartupSUBTRANS hasn't been called yet. */ if (!InRecovery) ! TruncateSUBTRANS(GetOldestXmin(true, false)); if (!shutdown) ereport(DEBUG2, Index: src/backend/catalog/index.c =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/backend/catalog/index.c,v retrieving revision 1.269 diff -c -p -r1.269 index.c *** src/backend/catalog/index.c 13 Jul 2006 16:49:13 -0000 1.269 --- src/backend/catalog/index.c 28 Jul 2006 21:59:42 -0000 *************** IndexBuildHeapScan(Relation heapRelation *** 1367,1373 **** else { snapshot = SnapshotAny; ! OldestXmin = GetOldestXmin(heapRelation->rd_rel->relisshared); } scan = heap_beginscan(heapRelation, /* relation */ --- 1367,1374 ---- else { snapshot = SnapshotAny; ! /* okay to ignore lazy VACUUMs here */ ! OldestXmin = GetOldestXmin(heapRelation->rd_rel->relisshared, true); } scan = heap_beginscan(heapRelation, /* relation */ Index: src/backend/commands/vacuum.c =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/backend/commands/vacuum.c,v retrieving revision 1.335 diff -c -p -r1.335 vacuum.c *** src/backend/commands/vacuum.c 14 Jul 2006 14:52:18 -0000 1.335 --- src/backend/commands/vacuum.c 28 Jul 2006 21:59:42 -0000 *************** *** 37,42 **** --- 37,43 ---- #include "postmaster/autovacuum.h" #include "storage/freespace.h" #include "storage/pmsignal.h" + #include "storage/proc.h" #include "storage/procarray.h" #include "utils/acl.h" #include "utils/builtins.h" *************** vacuum_set_xid_limits(VacuumStmt *vacstm *** 589,595 **** { TransactionId limit; ! *oldestXmin = GetOldestXmin(sharedRel); Assert(TransactionIdIsNormal(*oldestXmin)); --- 590,605 ---- { TransactionId limit; ! /* ! * We can always ignore processes running lazy vacuum. This is because we ! * use these values only for deciding which tuples we must keep in the ! * tables. Since lazy vacuum doesn't write its xid to the table, it's ! * safe to ignore it. In theory it could be problematic to ignore lazy ! * vacuums on a full vacuum, but keep in mind that only one vacuum process ! * can be working on a particular table at any time, and that each vacuum ! * is always an independent transaction. ! */ ! *oldestXmin = GetOldestXmin(sharedRel, true); Assert(TransactionIdIsNormal(*oldestXmin)); *************** vacuum_set_xid_limits(VacuumStmt *vacstm *** 645,650 **** --- 655,665 ---- * pg_class would've been obsoleted. Of course, this only works for * fixed-size never-null columns, but these are. * + * Another reason for doing it this way is that when we are in a lazy + * VACUUM and have inVacuum set, we mustn't do any updates --- somebody + * vacuuming pg_class might think they could delete a tuple marked with + * xmin = our xid. + * * This routine is shared by full VACUUM, lazy VACUUM, and stand-alone * ANALYZE. */ *************** vacuum_rel(Oid relid, VacuumStmt *vacstm *** 996,1003 **** /* Begin a transaction for vacuuming this relation */ StartTransactionCommand(); ! /* functions in indexes may want a snapshot set */ ! ActiveSnapshot = CopySnapshot(GetTransactionSnapshot()); /* * Tell the cache replacement strategy that vacuum is causing all --- 1011,1045 ---- /* Begin a transaction for vacuuming this relation */ StartTransactionCommand(); ! ! if (vacstmt->full) ! { ! /* functions in indexes may want a snapshot set */ ! ActiveSnapshot = CopySnapshot(GetTransactionSnapshot()); ! } ! else ! { ! /* ! * During a lazy VACUUM we do not run any user-supplied functions, ! * and so it should be safe to not create a transaction snapshot. ! * ! * We can furthermore set the inVacuum flag, which lets other ! * concurrent VACUUMs know that they can ignore this one while ! * determining their OldestXmin. (The reason we don't set inVacuum ! * during a full VACUUM is exactly that we may have to run user- ! * defined functions for functional indexes, and we want to make ! * sure that if they use the snapshot set above, any tuples it ! * requires can't get removed from other tables. An index function ! * that depends on the contents of other tables is arguably broken, ! * but we won't break it here by violating transaction semantics.) ! * ! * Note: the inVacuum flag remains set until CommitTransaction or ! * AbortTransaction. We don't want to clear it until we reset ! * MyProc->xid/xmin, else OldestXmin might appear to go backwards, ! * which is probably Not Good. ! */ ! MyProc->inVacuum = true; ! } /* * Tell the cache replacement strategy that vacuum is causing all Index: src/backend/storage/ipc/procarray.c =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/backend/storage/ipc/procarray.c,v retrieving revision 1.14 diff -c -p -r1.14 procarray.c *** src/backend/storage/ipc/procarray.c 14 Jul 2006 14:52:22 -0000 1.14 --- src/backend/storage/ipc/procarray.c 28 Jul 2006 21:59:42 -0000 *************** TransactionIdIsActive(TransactionId xid) *** 388,407 **** * If allDbs is TRUE then all backends are considered; if allDbs is FALSE * then only backends running in my own database are considered. * * This is used by VACUUM to decide which deleted tuples must be preserved * in a table. allDbs = TRUE is needed for shared relations, but allDbs = * FALSE is sufficient for non-shared relations, since only backends in my ! * own database could ever see the tuples in them. * * This is also used to determine where to truncate pg_subtrans. allDbs ! * must be TRUE for that case. * * Note: we include the currently running xids in the set of considered xids. * This ensures that if a just-started xact has not yet set its snapshot, * when it does set the snapshot it cannot set xmin less than what we compute. */ TransactionId ! GetOldestXmin(bool allDbs) { ProcArrayStruct *arrayP = procArray; TransactionId result; --- 388,411 ---- * If allDbs is TRUE then all backends are considered; if allDbs is FALSE * then only backends running in my own database are considered. * + * If ignoreVacuum is TRUE then backends with inVacuum set are ignored. + * * This is used by VACUUM to decide which deleted tuples must be preserved * in a table. allDbs = TRUE is needed for shared relations, but allDbs = * FALSE is sufficient for non-shared relations, since only backends in my ! * own database could ever see the tuples in them. Also, we can ignore ! * concurrently running lazy VACUUMs because (a) they must be working on other ! * tables, and (b) they don't need to do snapshot-based lookups. * * This is also used to determine where to truncate pg_subtrans. allDbs ! * must be TRUE for that case, and ignoreVacuum FALSE. * * Note: we include the currently running xids in the set of considered xids. * This ensures that if a just-started xact has not yet set its snapshot, * when it does set the snapshot it cannot set xmin less than what we compute. */ TransactionId ! GetOldestXmin(bool allDbs, bool ignoreVacuum) { ProcArrayStruct *arrayP = procArray; TransactionId result; *************** GetOldestXmin(bool allDbs) *** 425,430 **** --- 429,437 ---- { PGPROC *proc = arrayP->procs[index]; + if (ignoreVacuum && proc->inVacuum) + continue; + if (allDbs || proc->databaseId == MyDatabaseId) { /* Fetch xid just once - see GetNewTransactionId */ *************** GetOldestXmin(bool allDbs) *** 432,439 **** --- 439,456 ---- if (TransactionIdIsNormal(xid)) { + /* First consider the transaction own's Xid */ if (TransactionIdPrecedes(xid, result)) result = xid; + + /* + * Also consider the transaction's Xmin, if set. + * + * Note that this Xmin may seem to be guaranteed to be always + * lower than the transaction's Xid, but this is not so because + * there is a time window on which the Xid is already assigned + * but the Xmin has not being calculated yet. + */ xid = proc->xmin; if (TransactionIdIsNormal(xid)) if (TransactionIdPrecedes(xid, result)) *************** GetOldestXmin(bool allDbs) *** 471,478 **** * RecentXmin: the xmin computed for the most recent snapshot. XIDs * older than this are known not running any more. * RecentGlobalXmin: the global xmin (oldest TransactionXmin across all ! * running transactions). This is the same computation done by ! * GetOldestXmin(TRUE). *---------- */ Snapshot --- 488,495 ---- * RecentXmin: the xmin computed for the most recent snapshot. XIDs * older than this are known not running any more. * RecentGlobalXmin: the global xmin (oldest TransactionXmin across all ! * running transactions, except those running LAZY VACUUM). This is ! * the same computation done by GetOldestXmin(true, false). *---------- */ Snapshot *************** GetSnapshotData(Snapshot snapshot, bool *** 561,575 **** /* * Ignore my own proc (dealt with my xid above), procs not running a ! * transaction, and xacts started since we read the next transaction ! * ID. There's no need to store XIDs above what we got from ! * ReadNewTransactionId, since we'll treat them as running anyway. We ! * also assume that such xacts can't compute an xmin older than ours, ! * so they needn't be considered in computing globalxmin. */ if (proc == MyProc || !TransactionIdIsNormal(xid) || ! TransactionIdFollowsOrEquals(xid, xmax)) continue; if (TransactionIdPrecedes(xid, xmin)) --- 578,594 ---- /* * Ignore my own proc (dealt with my xid above), procs not running a ! * transaction, xacts started since we read the next transaction ! * ID, and xacts executing LAZY VACUUM. There's no need to store XIDs ! * above what we got from ReadNewTransactionId, since we'll treat them ! * as running anyway. We also assume that such xacts can't compute an ! * xmin older than ours, so they needn't be considered in computing ! * globalxmin. */ if (proc == MyProc || !TransactionIdIsNormal(xid) || ! TransactionIdFollowsOrEquals(xid, xmax) || ! proc->inVacuum) continue; if (TransactionIdPrecedes(xid, xmin)) Index: src/backend/storage/lmgr/proc.c =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/backend/storage/lmgr/proc.c,v retrieving revision 1.178 diff -c -p -r1.178 proc.c *** src/backend/storage/lmgr/proc.c 23 Jul 2006 23:08:46 -0000 1.178 --- src/backend/storage/lmgr/proc.c 28 Jul 2006 21:59:42 -0000 *************** InitProcess(void) *** 257,262 **** --- 257,263 ---- /* databaseId and roleId will be filled in later */ MyProc->databaseId = InvalidOid; MyProc->roleId = InvalidOid; + MyProc->inVacuum = false; MyProc->lwWaiting = false; MyProc->lwExclusive = false; MyProc->lwWaitLink = NULL; *************** InitDummyProcess(void) *** 388,393 **** --- 389,395 ---- MyProc->xmin = InvalidTransactionId; MyProc->databaseId = InvalidOid; MyProc->roleId = InvalidOid; + MyProc->inVacuum = false; MyProc->lwWaiting = false; MyProc->lwExclusive = false; MyProc->lwWaitLink = NULL; Index: src/include/storage/proc.h =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/include/storage/proc.h,v retrieving revision 1.89 diff -c -p -r1.89 proc.h *** src/include/storage/proc.h 13 Jul 2006 16:49:20 -0000 1.89 --- src/include/storage/proc.h 28 Jul 2006 21:59:42 -0000 *************** struct PGPROC *** 66,78 **** * this proc */ TransactionId xmin; /* minimal running XID as it was when we were ! * starting our xact: vacuum must not remove ! * tuples deleted by xid >= xmin ! */ int pid; /* This backend's process id, or 0 */ Oid databaseId; /* OID of database this backend is using */ Oid roleId; /* OID of role using this backend */ /* Info about LWLock the process is currently waiting for, if any. */ bool lwWaiting; /* true if waiting for an LW lock */ bool lwExclusive; /* true if waiting for exclusive access */ --- 66,81 ---- * this proc */ TransactionId xmin; /* minimal running XID as it was when we were ! * starting our xact, excluding LAZY VACUUM: ! * vacuum must not remove tuples deleted by ! * xid >= xmin ! */ int pid; /* This backend's process id, or 0 */ Oid databaseId; /* OID of database this backend is using */ Oid roleId; /* OID of role using this backend */ + bool inVacuum; /* true if current xact is a LAZY VACUUM */ + /* Info about LWLock the process is currently waiting for, if any. */ bool lwWaiting; /* true if waiting for an LW lock */ bool lwExclusive; /* true if waiting for exclusive access */ Index: src/include/storage/procarray.h =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/include/storage/procarray.h,v retrieving revision 1.9 diff -c -p -r1.9 procarray.h *** src/include/storage/procarray.h 19 Jun 2006 01:51:22 -0000 1.9 --- src/include/storage/procarray.h 28 Jul 2006 21:59:42 -0000 *************** extern void ProcArrayRemove(PGPROC *proc *** 24,30 **** extern bool TransactionIdIsInProgress(TransactionId xid); extern bool TransactionIdIsActive(TransactionId xid); ! extern TransactionId GetOldestXmin(bool allDbs); extern PGPROC *BackendPidGetProc(int pid); extern int BackendXidGetPid(TransactionId xid); --- 24,30 ---- extern bool TransactionIdIsInProgress(TransactionId xid); extern bool TransactionIdIsActive(TransactionId xid); ! extern TransactionId GetOldestXmin(bool allDbs, bool ignoreVacuum); extern PGPROC *BackendPidGetProc(int pid); extern int BackendXidGetPid(TransactionId xid);