On E, 2005-07-04 at 10:24 +0300, Hannu Krosing wrote: > On P, 2005-07-03 at 12:19 -0400, Tom Lane wrote: > > Hannu Krosing <[EMAIL PROTECTED]> writes: > > > Ok, this is a new version of the vacuum patch with the following changes > > > following some suggestions in this thread. > > > > The more I look at this, the uglier it looks ... and I still haven't > > seen any convincing demonstration that it *works*, ie doesn't have > > bad side-effects on the transaction-is-in-progress logic.
Ok, I changed GetOldestXmin() to use proc->inVacuum only when determining the oldest visible xid for vacuum and index (i.e. which tuples are safe to delete and which tuples there is no need to index). The third use on GetOldestXmin() in xlog.c is changed to use old GetOldestXmin() logic. My reasoning for why the patch should work is as follows: 1) the only transaction during which inVacuum is set is the 2nd transaction (of originally 3, now 4) of lazy VACUUM, which does simple heap scanning and old tuple removal (lazy_vacuum_rel()), and does no externally visible changes to the data. It only removes tuples which are already invisible to all running transactions. 2) That transaction never deletes, updates or inserts any tuples on it own. 3) As it can't add any tuples or change any existing tuples to have its xid as either xmin or xmax, it already does run logically "outside of transactions". 4) The only use made of of proc->inVacuum is when determining which tuples are safe to remove (in vacuum.c) or not worth indexing (in index.c) and thus can't affect anything else. I can easily demonstrate that it "works" in the sense that it allows several concurrent vacuums to clean out old tuples, and I have thus far been unable to construct the counterexample where it does anything bad. Could you tell me which part of my reasoning is wrong or what else do I overlook. -- Hannu Krosing <[EMAIL PROTECTED]>
Index: src/backend/access/transam/xact.c =================================================================== RCS file: /projects/cvsroot/pgsql/src/backend/access/transam/xact.c,v retrieving revision 1.209 diff -c -r1.209 xact.c *** src/backend/access/transam/xact.c 29 Jun 2005 22:51:53 -0000 1.209 --- src/backend/access/transam/xact.c 4 Jul 2005 10:57:06 -0000 *************** *** 1402,1407 **** --- 1402,1416 ---- AfterTriggerBeginXact(); /* + * mark the transaction as not VACUUM (vacuum_rel will set isVacuum + * to true directly after calling BeginTransactionCommand() ) + * + * this can probably be moved to be done only once when establishing + * connection as this is now quaranteedto be reset to false in vacuum.c + */ + MyProc->inVacuum = false; + + /* * done with start processing, set current transaction state to "in * progress" */ Index: src/backend/access/transam/xlog.c =================================================================== RCS file: /projects/cvsroot/pgsql/src/backend/access/transam/xlog.c,v retrieving revision 1.205 diff -c -r1.205 xlog.c *** src/backend/access/transam/xlog.c 30 Jun 2005 00:00:50 -0000 1.205 --- src/backend/access/transam/xlog.c 4 Jul 2005 10:57:07 -0000 *************** *** 5161,5167 **** * mustn't do this because StartupSUBTRANS hasn't been called yet. */ if (!InRecovery) ! TruncateSUBTRANS(GetOldestXmin(true)); if (!shutdown) ereport(DEBUG2, --- 5161,5167 ---- * mustn't do this because StartupSUBTRANS hasn't been called yet. */ if (!InRecovery) ! TruncateSUBTRANS(GetOldestXmin(true, false)); if (!shutdown) ereport(DEBUG2, Index: src/backend/catalog/index.c =================================================================== RCS file: /projects/cvsroot/pgsql/src/backend/catalog/index.c,v retrieving revision 1.258 diff -c -r1.258 index.c *** src/backend/catalog/index.c 25 Jun 2005 16:53:49 -0000 1.258 --- src/backend/catalog/index.c 4 Jul 2005 10:57:07 -0000 *************** *** 1420,1426 **** else { snapshot = SnapshotAny; ! OldestXmin = GetOldestXmin(heapRelation->rd_rel->relisshared); } scan = heap_beginscan(heapRelation, /* relation */ --- 1420,1426 ---- else { snapshot = SnapshotAny; ! OldestXmin = GetOldestXmin(heapRelation->rd_rel->relisshared, true); } scan = heap_beginscan(heapRelation, /* relation */ Index: src/backend/commands/vacuum.c =================================================================== RCS file: /projects/cvsroot/pgsql/src/backend/commands/vacuum.c,v retrieving revision 1.310 diff -c -r1.310 vacuum.c *** src/backend/commands/vacuum.c 14 Jun 2005 22:15:32 -0000 1.310 --- src/backend/commands/vacuum.c 4 Jul 2005 10:57:08 -0000 *************** *** 37,42 **** --- 37,43 ---- #include "miscadmin.h" #include "storage/freespace.h" #include "storage/procarray.h" + #include "storage/proc.h" #include "storage/smgr.h" #include "tcop/pquery.h" #include "utils/acl.h" *************** *** 571,577 **** { TransactionId limit; ! *oldestXmin = GetOldestXmin(sharedRel); Assert(TransactionIdIsNormal(*oldestXmin)); --- 572,578 ---- { TransactionId limit; ! *oldestXmin = GetOldestXmin(sharedRel, true); Assert(TransactionIdIsNormal(*oldestXmin)); *************** *** 903,908 **** --- 904,913 ---- Oid toast_relid; bool result; + BlockNumber stats_rel_pages=0; + double stats_rel_tuples=0; + bool stats_hasindex=false; + /* Begin a transaction for vacuuming this relation */ StartTransactionCommand(); /* functions in indexes may want a snapshot set */ *************** *** 1016,1039 **** */ toast_relid = onerel->rd_rel->reltoastrelid; ! /* ! * Do the actual work --- either FULL or "lazy" vacuum ! */ ! if (vacstmt->full) ! full_vacuum_rel(onerel, vacstmt); ! else ! lazy_vacuum_rel(onerel, vacstmt); ! result = true; /* did the vacuum */ ! /* all done with this class, but hold lock until commit */ ! relation_close(onerel, NoLock); ! /* ! * Complete the transaction and free all temporary memory used. ! */ ! StrategyHintVacuum(false); ! CommitTransactionCommand(); /* * If the relation has a secondary toast rel, vacuum that too while we --- 1021,1071 ---- */ toast_relid = onerel->rd_rel->reltoastrelid; ! PG_TRY(); ! { ! /* ! * Do the actual work --- either FULL or "lazy" vacuum ! */ ! if (vacstmt->full) ! full_vacuum_rel(onerel, vacstmt); ! else ! /* mark this transaction as being a lazy vacuum */ ! MyProc->inVacuum = true; ! lazy_vacuum_rel(onerel, vacstmt, &stats_rel_pages, &stats_rel_tuples, &stats_hasindex); ! result = true; /* did the vacuum */ ! /* all done with this class, but hold lock until commit */ ! relation_close(onerel, NoLock); ! ! /* ! * Complete the transaction and free all temporary memory used. ! */ ! StrategyHintVacuum(false); ! CommitTransactionCommand(); ! } ! PG_CATCH(); ! { ! /* make sure in-vacuum flag is cleared is cleared */ ! MyProc->inVacuum = false; ! PG_RE_THROW(); ! } ! PG_END_TRY(); ! MyProc->inVacuum = false; ! ! ! /* use yet another transaction for saving stats from lazy_vacuum_rel into pg_class */ ! if (stats_rel_pages > 0) { ! StartTransactionCommand(); ! ActiveSnapshot = CopySnapshot(GetTransactionSnapshot()); /* is this needed ? */ ! /* Update statistics in pg_class */ ! vac_update_relstats(RelationGetRelid(onerel), ! stats_rel_pages, ! stats_rel_tuples, ! stats_hasindex); ! CommitTransactionCommand(); ! } /* * If the relation has a secondary toast rel, vacuum that too while we Index: src/backend/commands/vacuumlazy.c =================================================================== RCS file: /projects/cvsroot/pgsql/src/backend/commands/vacuumlazy.c,v retrieving revision 1.54 diff -c -r1.54 vacuumlazy.c *** src/backend/commands/vacuumlazy.c 19 May 2005 21:35:46 -0000 1.54 --- src/backend/commands/vacuumlazy.c 4 Jul 2005 10:57:08 -0000 *************** *** 128,134 **** * and locked the relation. */ void ! lazy_vacuum_rel(Relation onerel, VacuumStmt *vacstmt) { LVRelStats *vacrelstats; Relation *Irel; --- 128,134 ---- * and locked the relation. */ void ! lazy_vacuum_rel(Relation onerel, VacuumStmt *vacstmt, BlockNumber *stats_rel_pages, double *stats_rel_tuples, bool *stats_hasindex) { LVRelStats *vacrelstats; Relation *Irel; *************** *** 175,184 **** --- 175,190 ---- lazy_update_fsm(onerel, vacrelstats); /* Update statistics in pg_class */ + /* vac_update_relstats(RelationGetRelid(onerel), vacrelstats->rel_pages, vacrelstats->rel_tuples, hasindex); + */ + /* return values for setting relstats in another transaction */ + *stats_rel_pages = vacrelstats->rel_pages; + *stats_rel_tuples = vacrelstats->rel_tuples; + *stats_hasindex = hasindex; } Index: src/backend/storage/ipc/procarray.c =================================================================== RCS file: /projects/cvsroot/pgsql/src/backend/storage/ipc/procarray.c,v retrieving revision 1.3 diff -c -r1.3 procarray.c *** src/backend/storage/ipc/procarray.c 17 Jun 2005 22:32:45 -0000 1.3 --- src/backend/storage/ipc/procarray.c 4 Jul 2005 10:57:08 -0000 *************** *** 399,405 **** * when it does set the snapshot it cannot set xmin less than what we compute. */ TransactionId ! GetOldestXmin(bool allDbs) { ProcArrayStruct *arrayP = procArray; TransactionId result; --- 399,405 ---- * when it does set the snapshot it cannot set xmin less than what we compute. */ TransactionId ! GetOldestXmin(bool allDbs, bool dataChangingTrxOnly) { ProcArrayStruct *arrayP = procArray; TransactionId result; *************** *** 423,429 **** { PGPROC *proc = arrayP->procs[index]; ! if (allDbs || proc->databaseId == MyDatabaseId) { /* Fetch xid just once - see GetNewTransactionId */ TransactionId xid = proc->xid; --- 423,432 ---- { PGPROC *proc = arrayP->procs[index]; ! /* if dataChangingTrxOnly==true then don't use xid of transactions ! * running lazy vacuum */ ! if ( (dataChangingTrxOnly && (proc->inVacuum == false)) ! && (allDbs || proc->databaseId == MyDatabaseId)) { /* Fetch xid just once - see GetNewTransactionId */ TransactionId xid = proc->xid; Index: src/include/commands/vacuum.h =================================================================== RCS file: /projects/cvsroot/pgsql/src/include/commands/vacuum.h,v retrieving revision 1.59 diff -c -r1.59 vacuum.h *** src/include/commands/vacuum.h 31 Dec 2004 22:03:28 -0000 1.59 --- src/include/commands/vacuum.h 4 Jul 2005 10:57:08 -0000 *************** *** 142,148 **** extern void vacuum_delay_point(void); /* in commands/vacuumlazy.c */ ! extern void lazy_vacuum_rel(Relation onerel, VacuumStmt *vacstmt); /* in commands/analyze.c */ extern void analyze_rel(Oid relid, VacuumStmt *vacstmt); --- 142,151 ---- extern void vacuum_delay_point(void); /* in commands/vacuumlazy.c */ ! extern void lazy_vacuum_rel(Relation onerel, VacuumStmt *vacstmt, ! BlockNumber *stats_rel_pages, ! double *stats_rel_tuples, ! bool *stats_hasindex); /* in commands/analyze.c */ extern void analyze_rel(Oid relid, VacuumStmt *vacstmt); Index: src/include/storage/proc.h =================================================================== RCS file: /projects/cvsroot/pgsql/src/include/storage/proc.h,v retrieving revision 1.79 diff -c -r1.79 proc.h *** src/include/storage/proc.h 17 Jun 2005 22:32:50 -0000 1.79 --- src/include/storage/proc.h 4 Jul 2005 10:57:08 -0000 *************** *** 69,74 **** --- 69,80 ---- * were starting our xact: vacuum must not * remove tuples deleted by xid >= xmin ! */ + bool inVacuum; /* true if current command is vacuum. + * used by other vacuum commands + * as xid or xmin of this cmd need not be used + * when finding global xmin for removing + * tuples */ + int pid; /* This backend's process id, or 0 */ Oid databaseId; /* OID of database this backend is using */ Index: src/include/storage/procarray.h =================================================================== RCS file: /projects/cvsroot/pgsql/src/include/storage/procarray.h,v retrieving revision 1.2 diff -c -r1.2 procarray.h *** src/include/storage/procarray.h 17 Jun 2005 22:32:50 -0000 1.2 --- src/include/storage/procarray.h 4 Jul 2005 10:57:08 -0000 *************** *** 24,30 **** extern bool TransactionIdIsInProgress(TransactionId xid); extern bool TransactionIdIsActive(TransactionId xid); ! extern TransactionId GetOldestXmin(bool allDbs); extern PGPROC *BackendPidGetProc(int pid); extern bool IsBackendPid(int pid); --- 24,30 ---- extern bool TransactionIdIsInProgress(TransactionId xid); extern bool TransactionIdIsActive(TransactionId xid); ! extern TransactionId GetOldestXmin(bool allDbs, bool dataChangingTrxOnly); extern PGPROC *BackendPidGetProc(int pid); extern bool IsBackendPid(int pid);
---------------------------(end of broadcast)--------------------------- TIP 6: Have you searched our list archives? http://archives.postgresql.org