Tom Lane wrote:
> Alvaro Herrera <[EMAIL PROTECTED]> writes:
> > I'm wondering if it's safe to do something like
> > MyProc->vacuumFlags |= PROC_FOR_XID_WRAPAROUND
> > without holding the ProcArrayLock.
> 
> This seems a bit itchy.
> 
> One thing I'd be worried about is processors that implement that by
> fetching the whole word containing the field, setting the bit, and
> storing back the whole word.  (I believe some RISC processors are likely
> to do it like that.)  This would mean that it'd work OK only as long as
> no other process was concurrently changing any field that happened to be
> in the same word, which is the kind of requirement that seems horribly
> fragile.

I did it that way (i.e. added locking) and then realized that it
shouldn't really be a problem, because the only one who can be setting
vacuum flags is the process itself.  Other processes can only read the
flags.

Maybe the locking is not a problem anyway, but there are two additional
flag sets in analyze and two more in the autovacuum code when it detects
that it's vacuuming a table for Xid wraparound.  (The idea is to use
these flags in the deadlock patch Simon posted, so that signals are
automatically sent or not according to the current activity of
autovacuum.  This way the signal handler can be kept stupid.)

Also, I forgot to mention it on the first email, but this patch adds
errcontext() lines when an autovacuum/analyze is being aborted.  It
works fine, but I'm not seeing code anywhere else that does something
like this.

-- 
Alvaro Herrera                                http://www.CommandPrompt.com/
PostgreSQL Replication, Consulting, Custom Development, 24x7 support
Index: src/backend/access/transam/twophase.c
===================================================================
RCS file: /home/alvherre/Code/cvs/pgsql/src/backend/access/transam/twophase.c,v
retrieving revision 1.36
diff -c -p -r1.36 twophase.c
*** src/backend/access/transam/twophase.c	21 Sep 2007 16:32:19 -0000	1.36
--- src/backend/access/transam/twophase.c	24 Oct 2007 14:59:47 -0000
*************** MarkAsPreparing(TransactionId xid, const
*** 283,290 ****
  	gxact->proc.databaseId = databaseid;
  	gxact->proc.roleId = owner;
  	gxact->proc.inCommit = false;
! 	gxact->proc.inVacuum = false;
! 	gxact->proc.isAutovacuum = false;
  	gxact->proc.lwWaiting = false;
  	gxact->proc.lwExclusive = false;
  	gxact->proc.lwWaitLink = NULL;
--- 283,289 ----
  	gxact->proc.databaseId = databaseid;
  	gxact->proc.roleId = owner;
  	gxact->proc.inCommit = false;
! 	gxact->proc.vacuumFlags = 0;
  	gxact->proc.lwWaiting = false;
  	gxact->proc.lwExclusive = false;
  	gxact->proc.lwWaitLink = NULL;
Index: src/backend/commands/analyze.c
===================================================================
RCS file: /home/alvherre/Code/cvs/pgsql/src/backend/commands/analyze.c,v
retrieving revision 1.109
diff -c -p -r1.109 analyze.c
*** src/backend/commands/analyze.c	24 Sep 2007 03:12:23 -0000	1.109
--- src/backend/commands/analyze.c	24 Oct 2007 14:59:47 -0000
***************
*** 31,36 ****
--- 31,37 ----
  #include "parser/parse_relation.h"
  #include "pgstat.h"
  #include "postmaster/autovacuum.h"
+ #include "storage/proc.h"
  #include "utils/acl.h"
  #include "utils/datum.h"
  #include "utils/lsyscache.h"
*************** analyze_rel(Oid relid, VacuumStmt *vacst
*** 201,206 ****
--- 202,212 ----
  		return;
  	}
  
+ 	/* let others know what I'm doing */
+ 	LWLockAcquire(ProcArrayLock, LW_EXCLUSIVE);
+ 	MyProc->vacuumFlags |= PROC_IN_ANALYZE;
+ 	LWLockRelease(ProcArrayLock);
+ 
  	/* measure elapsed time iff autovacuum logging requires it */
  	if (IsAutoVacuumWorkerProcess() && Log_autovacuum_min_duration >= 0)
  	{
*************** analyze_rel(Oid relid, VacuumStmt *vacst
*** 484,489 ****
--- 490,503 ----
  							RelationGetRelationName(onerel),
  							pg_rusage_show(&ru0))));
  	}
+ 
+ 	/*
+ 	 * Reset my PGPROC flag.  Note: we need this here, and not in vacuum_rel,
+ 	 * because the vacuum flag is cleared by the end-of-xact code.
+ 	 */
+ 	LWLockAcquire(ProcArrayLock, LW_EXCLUSIVE);
+ 	MyProc->vacuumFlags &= ~PROC_IN_ANALYZE;
+ 	LWLockRelease(ProcArrayLock);
  }
  
  /*
Index: src/backend/commands/vacuum.c
===================================================================
RCS file: /home/alvherre/Code/cvs/pgsql/src/backend/commands/vacuum.c,v
retrieving revision 1.359
diff -c -p -r1.359 vacuum.c
*** src/backend/commands/vacuum.c	20 Sep 2007 17:56:31 -0000	1.359
--- src/backend/commands/vacuum.c	24 Oct 2007 14:59:47 -0000
*************** vacuum_set_xid_limits(int freeze_min_age
*** 660,668 ****
   *		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.
--- 660,668 ----
   *		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 PROC_IN_VACUUM 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
*** 987,995 ****
  		 * 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
--- 987,995 ----
  		 * 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 PROC_IN_VACUUM flag, which lets other
  		 * concurrent VACUUMs know that they can ignore this one while
! 		 * determining their OldestXmin.  (The reason we don't set it
  		 * 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
*************** vacuum_rel(Oid relid, VacuumStmt *vacstm
*** 997,1008 ****
  		 * 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;
  	}
  
  	/*
--- 997,1010 ----
  		 * depends on the contents of other tables is arguably broken, but we
  		 * won't break it here by violating transaction semantics.)
  		 *
! 		 * Note: this 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.
  		 */
! 		LWLockAcquire(ProcArrayLock, LW_EXCLUSIVE);
! 		MyProc->vacuumFlags |= PROC_IN_VACUUM;
! 		LWLockRelease(ProcArrayLock);
  	}
  
  	/*
Index: src/backend/postmaster/autovacuum.c
===================================================================
RCS file: /home/alvherre/Code/cvs/pgsql/src/backend/postmaster/autovacuum.c,v
retrieving revision 1.61
diff -c -p -r1.61 autovacuum.c
*** src/backend/postmaster/autovacuum.c	24 Sep 2007 04:12:01 -0000	1.61
--- src/backend/postmaster/autovacuum.c	24 Oct 2007 14:59:47 -0000
*************** typedef struct autovac_table
*** 172,177 ****
--- 172,178 ----
  	int			at_freeze_min_age;
  	int			at_vacuum_cost_delay;
  	int			at_vacuum_cost_limit;
+ 	bool		at_wraparound;
  } autovac_table;
  
  /*-------------
*************** typedef struct autovac_table
*** 182,188 ****
   * wi_links		entry into free list or running list
   * wi_dboid		OID of the database this worker is supposed to work on
   * wi_tableoid	OID of the table currently being vacuumed
!  * wi_workerpid	PID of the running worker, 0 if not yet started
   * wi_launchtime Time at which this worker was launched
   * wi_cost_*	Vacuum cost-based delay parameters current in this worker
   *
--- 183,189 ----
   * wi_links		entry into free list or running list
   * wi_dboid		OID of the database this worker is supposed to work on
   * wi_tableoid	OID of the table currently being vacuumed
!  * wi_proc		pointer to PGPROC of the running worker, NULL if not started
   * wi_launchtime Time at which this worker was launched
   * wi_cost_*	Vacuum cost-based delay parameters current in this worker
   *
*************** typedef struct WorkerInfoData
*** 196,202 ****
  	SHM_QUEUE	wi_links;
  	Oid			wi_dboid;
  	Oid			wi_tableoid;
! 	int			wi_workerpid;
  	TimestampTz	wi_launchtime;
  	int			wi_cost_delay;
  	int			wi_cost_limit;
--- 197,203 ----
  	SHM_QUEUE	wi_links;
  	Oid			wi_dboid;
  	Oid			wi_tableoid;
! 	PGPROC	   *wi_proc;
  	TimestampTz	wi_launchtime;
  	int			wi_cost_delay;
  	int			wi_cost_limit;
*************** static autovac_table *table_recheck_auto
*** 280,286 ****
  static void relation_needs_vacanalyze(Oid relid, Form_pg_autovacuum avForm,
  						  Form_pg_class classForm,
  						  PgStat_StatTabEntry *tabentry, bool *dovacuum,
! 						  bool *doanalyze);
  
  static void autovacuum_do_vac_analyze(Oid relid, bool dovacuum,
  						  bool doanalyze, int freeze_min_age,
--- 281,287 ----
  static void relation_needs_vacanalyze(Oid relid, Form_pg_autovacuum avForm,
  						  Form_pg_class classForm,
  						  PgStat_StatTabEntry *tabentry, bool *dovacuum,
! 						  bool *doanalyze, bool *wraparound);
  
  static void autovacuum_do_vac_analyze(Oid relid, bool dovacuum,
  						  bool doanalyze, int freeze_min_age,
*************** AutoVacLauncherMain(int argc, char *argv
*** 694,700 ****
  					worker = (WorkerInfo) MAKE_PTR(AutoVacuumShmem->av_startingWorker);
  					worker->wi_dboid = InvalidOid;
  					worker->wi_tableoid = InvalidOid;
! 					worker->wi_workerpid = 0;
  					worker->wi_launchtime = 0;
  					worker->wi_links.next = AutoVacuumShmem->av_freeWorkers;
  					AutoVacuumShmem->av_freeWorkers = MAKE_OFFSET(worker);
--- 695,701 ----
  					worker = (WorkerInfo) MAKE_PTR(AutoVacuumShmem->av_startingWorker);
  					worker->wi_dboid = InvalidOid;
  					worker->wi_tableoid = InvalidOid;
! 					worker->wi_proc = NULL;
  					worker->wi_launchtime = 0;
  					worker->wi_links.next = AutoVacuumShmem->av_freeWorkers;
  					AutoVacuumShmem->av_freeWorkers = MAKE_OFFSET(worker);
*************** do_start_worker(void)
*** 1198,1204 ****
  		AutoVacuumShmem->av_freeWorkers = worker->wi_links.next;
  
  		worker->wi_dboid = avdb->adw_datid;
! 		worker->wi_workerpid = 0;
  		worker->wi_launchtime = GetCurrentTimestamp();
  
  		AutoVacuumShmem->av_startingWorker = sworker;
--- 1199,1205 ----
  		AutoVacuumShmem->av_freeWorkers = worker->wi_links.next;
  
  		worker->wi_dboid = avdb->adw_datid;
! 		worker->wi_proc = NULL;
  		worker->wi_launchtime = GetCurrentTimestamp();
  
  		AutoVacuumShmem->av_startingWorker = sworker;
*************** AutoVacWorkerMain(int argc, char *argv[]
*** 1542,1548 ****
  	{
  		MyWorkerInfo = (WorkerInfo) MAKE_PTR(AutoVacuumShmem->av_startingWorker);
  		dbid = MyWorkerInfo->wi_dboid;
! 		MyWorkerInfo->wi_workerpid = MyProcPid;
  
  		/* insert into the running list */
  		SHMQueueInsertBefore(&AutoVacuumShmem->av_runningWorkers, 
--- 1543,1549 ----
  	{
  		MyWorkerInfo = (WorkerInfo) MAKE_PTR(AutoVacuumShmem->av_startingWorker);
  		dbid = MyWorkerInfo->wi_dboid;
! 		MyWorkerInfo->wi_proc = MyProc;
  
  		/* insert into the running list */
  		SHMQueueInsertBefore(&AutoVacuumShmem->av_runningWorkers, 
*************** FreeWorkerInfo(int code, Datum arg)
*** 1637,1643 ****
  		MyWorkerInfo->wi_links.next = AutoVacuumShmem->av_freeWorkers;
  		MyWorkerInfo->wi_dboid = InvalidOid;
  		MyWorkerInfo->wi_tableoid = InvalidOid;
! 		MyWorkerInfo->wi_workerpid = 0;
  		MyWorkerInfo->wi_launchtime = 0;
  		MyWorkerInfo->wi_cost_delay = 0;
  		MyWorkerInfo->wi_cost_limit = 0;
--- 1638,1644 ----
  		MyWorkerInfo->wi_links.next = AutoVacuumShmem->av_freeWorkers;
  		MyWorkerInfo->wi_dboid = InvalidOid;
  		MyWorkerInfo->wi_tableoid = InvalidOid;
! 		MyWorkerInfo->wi_proc = NULL;
  		MyWorkerInfo->wi_launchtime = 0;
  		MyWorkerInfo->wi_cost_delay = 0;
  		MyWorkerInfo->wi_cost_limit = 0;
*************** autovac_balance_cost(void)
*** 1701,1707 ****
  									   offsetof(WorkerInfoData, wi_links));
  	while (worker)
  	{
! 		if (worker->wi_workerpid != 0 &&
  			worker->wi_cost_limit_base > 0 && worker->wi_cost_delay > 0)
  			cost_total +=
  				(double) worker->wi_cost_limit_base / worker->wi_cost_delay;
--- 1702,1708 ----
  									   offsetof(WorkerInfoData, wi_links));
  	while (worker)
  	{
! 		if (worker->wi_proc != NULL &&
  			worker->wi_cost_limit_base > 0 && worker->wi_cost_delay > 0)
  			cost_total +=
  				(double) worker->wi_cost_limit_base / worker->wi_cost_delay;
*************** autovac_balance_cost(void)
*** 1724,1730 ****
  									   offsetof(WorkerInfoData, wi_links));
  	while (worker)
  	{
! 		if (worker->wi_workerpid != 0 &&
  			worker->wi_cost_limit_base > 0 && worker->wi_cost_delay > 0)
  		{
  			int     limit = (int)
--- 1725,1731 ----
  									   offsetof(WorkerInfoData, wi_links));
  	while (worker)
  	{
! 		if (worker->wi_proc != NULL &&
  			worker->wi_cost_limit_base > 0 && worker->wi_cost_delay > 0)
  		{
  			int     limit = (int)
*************** autovac_balance_cost(void)
*** 1737,1743 ****
  			worker->wi_cost_limit = Max(Min(limit, worker->wi_cost_limit_base), 1);
  
  			elog(DEBUG2, "autovac_balance_cost(pid=%u db=%u, rel=%u, cost_limit=%d, cost_delay=%d)",
! 				 worker->wi_workerpid, worker->wi_dboid,
  				 worker->wi_tableoid, worker->wi_cost_limit, worker->wi_cost_delay);
  		}
  
--- 1738,1744 ----
  			worker->wi_cost_limit = Max(Min(limit, worker->wi_cost_limit_base), 1);
  
  			elog(DEBUG2, "autovac_balance_cost(pid=%u db=%u, rel=%u, cost_limit=%d, cost_delay=%d)",
! 				 worker->wi_proc->pid, worker->wi_dboid,
  				 worker->wi_tableoid, worker->wi_cost_limit, worker->wi_cost_delay);
  		}
  
*************** next_worker:
*** 2062,2086 ****
  		VacuumCostDelay = tab->at_vacuum_cost_delay;
  		VacuumCostLimit = tab->at_vacuum_cost_limit;
  
! 		/*
! 		 * Advertise my cost delay parameters for the balancing algorithm, and
! 		 * do a balance
! 		 */
  		LWLockAcquire(AutovacuumLock, LW_EXCLUSIVE);
  		MyWorkerInfo->wi_cost_delay = tab->at_vacuum_cost_delay;
  		MyWorkerInfo->wi_cost_limit = tab->at_vacuum_cost_limit;
  		MyWorkerInfo->wi_cost_limit_base = tab->at_vacuum_cost_limit;
  		autovac_balance_cost();
  		LWLockRelease(AutovacuumLock);
  
  		/* clean up memory before each iteration */
  		MemoryContextResetAndDeleteChildren(PortalContext);
  
  		/*
! 		 * We will abort vacuuming the current table if we are interrupted, and
! 		 * continue with the next one in schedule; but if anything else
! 		 * happens, we will do our usual error handling which is to cause the
! 		 * worker process to exit.
  		 */
  		PG_TRY();
  		{
--- 2063,2097 ----
  		VacuumCostDelay = tab->at_vacuum_cost_delay;
  		VacuumCostLimit = tab->at_vacuum_cost_limit;
  
! 		/* Last fixups before actually starting to work */
  		LWLockAcquire(AutovacuumLock, LW_EXCLUSIVE);
+ 
+ 		/* advertise my cost delay parameters for the balancing algorithm */
  		MyWorkerInfo->wi_cost_delay = tab->at_vacuum_cost_delay;
  		MyWorkerInfo->wi_cost_limit = tab->at_vacuum_cost_limit;
  		MyWorkerInfo->wi_cost_limit_base = tab->at_vacuum_cost_limit;
+ 
+ 		/* do a balance */
  		autovac_balance_cost();
+ 
+ 		/* done */
  		LWLockRelease(AutovacuumLock);
  
  		/* clean up memory before each iteration */
  		MemoryContextResetAndDeleteChildren(PortalContext);
  
+ 		/* set the "vacuum for wraparound" flag in PGPROC */
+ 		if (tab->at_wraparound)
+ 		{
+ 			LWLockAcquire(ProcArrayLock, LW_EXCLUSIVE);
+ 			MyProc->vacuumFlags |= PROC_VACUUM_FOR_WRAPAROUND;
+ 			LWLockRelease(ProcArrayLock);
+ 		}
+ 
  		/*
! 		 * We will abort vacuuming the current table if something errors out,
! 		 * and continue with the next one in schedule; in particular, this
! 		 * happens if we are interrupted with SIGINT.
  		 */
  		PG_TRY();
  		{
*************** next_worker:
*** 2094,2132 ****
  		}
  		PG_CATCH();
  		{
- 			ErrorData	   *errdata;
- 
- 			MemoryContextSwitchTo(TopTransactionContext);
- 			errdata = CopyErrorData();
- 
  			/*
! 			 * If we errored out due to a cancel request, abort and restart the
! 			 * transaction and go to the next table.  Otherwise rethrow the
! 			 * error so that the outermost handler deals with it.
  			 */
! 			if (errdata->sqlerrcode == ERRCODE_QUERY_CANCELED)
! 			{
! 				HOLD_INTERRUPTS();
! 				elog(LOG, "cancelling autovacuum of table \"%s.%s.%s\"",
! 					 get_database_name(MyDatabaseId),
! 					 get_namespace_name(get_rel_namespace(tab->at_relid)),
! 					 get_rel_name(tab->at_relid));
! 
! 				AbortOutOfAnyTransaction();
! 				FlushErrorState();
! 				MemoryContextResetAndDeleteChildren(PortalContext);
! 
! 				/* restart our transaction for the following operations */
! 				StartTransactionCommand();
! 				RESUME_INTERRUPTS();
! 			}
  			else
! 				PG_RE_THROW();
  		}
  		PG_END_TRY();
  
  		/* be tidy */
  		pfree(tab);
  	}
  
  	/*
--- 2105,2153 ----
  		}
  		PG_CATCH();
  		{
  			/*
! 			 * Abort the transaction, start a new one, and proceed with the
! 			 * next table in our list.
  			 */
! 			HOLD_INTERRUPTS();
! 			if (tab->at_dovacuum)
! 				errcontext("automatic vacuum of table \"%s.%s.%s\"",
! 						   get_database_name(MyDatabaseId),
! 						   get_namespace_name(get_rel_namespace(tab->at_relid)),
! 						   get_rel_name(tab->at_relid));
  			else
! 				errcontext("automatic analyze of table \"%s.%s.%s\"",
! 						   get_database_name(MyDatabaseId),
! 						   get_namespace_name(get_rel_namespace(tab->at_relid)),
! 						   get_rel_name(tab->at_relid));
! 			EmitErrorReport();
! 
! 			/* this resets the PGPROC flags too */
! 			AbortOutOfAnyTransaction();
! 			FlushErrorState();
! 			MemoryContextResetAndDeleteChildren(PortalContext);
! 
! 			/* restart our transaction for the following operations */
! 			StartTransactionCommand();
! 			RESUME_INTERRUPTS();
  		}
  		PG_END_TRY();
  
+ 		/* reset my PGPROC flag */
+ 		if (tab->at_wraparound)
+ 		{
+ 			LWLockAcquire(ProcArrayLock, LW_EXCLUSIVE);
+ 			MyProc->vacuumFlags &= ~PROC_VACUUM_FOR_WRAPAROUND;
+ 			LWLockRelease(ProcArrayLock);
+ 		}
+ 
  		/* be tidy */
  		pfree(tab);
+ 
+ 		/* remove my info from shared memory */
+ 		LWLockAcquire(AutovacuumLock, LW_EXCLUSIVE);
+ 		MyWorkerInfo->wi_tableoid = InvalidOid;
+ 		LWLockRelease(AutovacuumLock);
  	}
  
  	/*
*************** relation_check_autovac(Oid relid, Form_p
*** 2214,2222 ****
  {
  	bool	dovacuum;
  	bool	doanalyze;
  
  	relation_needs_vacanalyze(relid, avForm, classForm, tabentry,
! 							  &dovacuum, &doanalyze);
  
  	if (classForm->relkind == RELKIND_TOASTVALUE)
  	{
--- 2235,2244 ----
  {
  	bool	dovacuum;
  	bool	doanalyze;
+ 	bool	dummy;
  
  	relation_needs_vacanalyze(relid, avForm, classForm, tabentry,
! 							  &dovacuum, &doanalyze, &dummy);
  
  	if (classForm->relkind == RELKIND_TOASTVALUE)
  	{
*************** table_recheck_autovac(Oid relid)
*** 2263,2268 ****
--- 2285,2292 ----
  	bool		doit = false;
  	PgStat_StatDBEntry *shared;
  	PgStat_StatDBEntry *dbentry;
+ 	bool		wraparound,
+ 				toast_wraparound = false;
  
  	/* use fresh stats */
  	autovac_refresh_stats();
*************** table_recheck_autovac(Oid relid)
*** 2289,2295 ****
  										 shared, dbentry);
  
  	relation_needs_vacanalyze(relid, avForm, classForm, tabentry,
! 							  &dovacuum, &doanalyze);
  
  	/* OK, it needs vacuum by itself */
  	if (dovacuum)
--- 2313,2319 ----
  										 shared, dbentry);
  
  	relation_needs_vacanalyze(relid, avForm, classForm, tabentry,
! 							  &dovacuum, &doanalyze, &wraparound);
  
  	/* OK, it needs vacuum by itself */
  	if (dovacuum)
*************** table_recheck_autovac(Oid relid)
*** 2307,2312 ****
--- 2331,2337 ----
  		{
  			bool			toast_dovacuum;
  			bool			toast_doanalyze;
+ 			bool			toast_wraparound;
  			Form_pg_class	toastClassForm;
  			PgStat_StatTabEntry *toasttabentry;
  
*************** table_recheck_autovac(Oid relid)
*** 2316,2324 ****
  													  shared, dbentry);
  
  			/* note we use the pg_autovacuum entry for the main table */
! 			relation_needs_vacanalyze(toastrelid, avForm, toastClassForm,
! 									  toasttabentry, &toast_dovacuum,
! 									  &toast_doanalyze);
  			/* we only consider VACUUM for toast tables */
  			if (toast_dovacuum)
  			{
--- 2341,2350 ----
  													  shared, dbentry);
  
  			/* note we use the pg_autovacuum entry for the main table */
! 			relation_needs_vacanalyze(toastrelid, avForm,
! 									  toastClassForm, toasttabentry,
! 									  &toast_dovacuum, &toast_doanalyze,
! 									  &toast_wraparound);
  			/* we only consider VACUUM for toast tables */
  			if (toast_dovacuum)
  			{
*************** table_recheck_autovac(Oid relid)
*** 2380,2385 ****
--- 2406,2412 ----
  		tab->at_freeze_min_age = freeze_min_age;
  		tab->at_vacuum_cost_limit = vac_cost_limit;
  		tab->at_vacuum_cost_delay = vac_cost_delay;
+ 		tab->at_wraparound = wraparound | toast_wraparound;
  	}
  
  	heap_close(avRel, AccessShareLock);
*************** table_recheck_autovac(Oid relid)
*** 2394,2400 ****
   * relation_needs_vacanalyze
   *
   * Check whether a relation needs to be vacuumed or analyzed; return each into
!  * "dovacuum" and "doanalyze", respectively.  avForm and tabentry can be NULL,
   * classForm shouldn't.
   *
   * A table needs to be vacuumed if the number of dead tuples exceeds a
--- 2421,2428 ----
   * relation_needs_vacanalyze
   *
   * Check whether a relation needs to be vacuumed or analyzed; return each into
!  * "dovacuum" and "doanalyze", respectively.  Also return whether the vacuum is
!  * being forced because of Xid wraparound.  avForm and tabentry can be NULL,
   * classForm shouldn't.
   *
   * A table needs to be vacuumed if the number of dead tuples exceeds a
*************** relation_needs_vacanalyze(Oid relid,
*** 2428,2434 ****
  						  PgStat_StatTabEntry *tabentry,
  						  /* output params below */
  						  bool *dovacuum,
! 						  bool *doanalyze)
  {
  	bool		force_vacuum;
  	float4		reltuples;		/* pg_class.reltuples */
--- 2456,2463 ----
  						  PgStat_StatTabEntry *tabentry,
  						  /* output params below */
  						  bool *dovacuum,
! 						  bool *doanalyze,
! 						  bool *wraparound)
  {
  	bool		force_vacuum;
  	float4		reltuples;		/* pg_class.reltuples */
*************** relation_needs_vacanalyze(Oid relid,
*** 2490,2495 ****
--- 2519,2525 ----
  	force_vacuum = (TransactionIdIsNormal(classForm->relfrozenxid) &&
  					TransactionIdPrecedes(classForm->relfrozenxid,
  										  xidForceLimit));
+ 	*wraparound = force_vacuum;
  
  	/* User disabled it in pg_autovacuum?  (But ignore if at risk) */
  	if (avForm && !avForm->enabled && !force_vacuum)
Index: src/backend/storage/ipc/procarray.c
===================================================================
RCS file: /home/alvherre/Code/cvs/pgsql/src/backend/storage/ipc/procarray.c,v
retrieving revision 1.35
diff -c -p -r1.35 procarray.c
*** src/backend/storage/ipc/procarray.c	23 Sep 2007 18:50:38 -0000	1.35
--- src/backend/storage/ipc/procarray.c	24 Oct 2007 14:59:47 -0000
*************** ProcArrayEndTransaction(PGPROC *proc, Tr
*** 242,248 ****
  		proc->xid = InvalidTransactionId;
  		proc->lxid = InvalidLocalTransactionId;
  		proc->xmin = InvalidTransactionId;
! 		proc->inVacuum = false;			/* must be cleared with xid/xmin */
  		proc->inCommit = false;			/* be sure this is cleared in abort */
  
  		/* Clear the subtransaction-XID cache too while holding the lock */
--- 242,249 ----
  		proc->xid = InvalidTransactionId;
  		proc->lxid = InvalidLocalTransactionId;
  		proc->xmin = InvalidTransactionId;
! 		/* must be cleared with xid/xmin: */
! 		proc->vacuumFlags &= ~PROC_VACUUM_STATE_MASK;
  		proc->inCommit = false;			/* be sure this is cleared in abort */
  
  		/* Clear the subtransaction-XID cache too while holding the lock */
*************** ProcArrayEndTransaction(PGPROC *proc, Tr
*** 267,273 ****
  
  		proc->lxid = InvalidLocalTransactionId;
  		proc->xmin = InvalidTransactionId;
! 		proc->inVacuum = false;			/* must be cleared with xid/xmin */
  		proc->inCommit = false;			/* be sure this is cleared in abort */
  
  		Assert(proc->subxids.nxids == 0);
--- 268,275 ----
  
  		proc->lxid = InvalidLocalTransactionId;
  		proc->xmin = InvalidTransactionId;
! 		/* must be cleared with xid/xmin: */
! 		proc->vacuumFlags &= ~PROC_VACUUM_STATE_MASK;
  		proc->inCommit = false;			/* be sure this is cleared in abort */
  
  		Assert(proc->subxids.nxids == 0);
*************** ProcArrayClearTransaction(PGPROC *proc)
*** 296,303 ****
  	proc->xid = InvalidTransactionId;
  	proc->lxid = InvalidLocalTransactionId;
  	proc->xmin = InvalidTransactionId;
! 	proc->inVacuum = false;			/* redundant, but just in case */
! 	proc->inCommit = false;			/* ditto */
  
  	/* Clear the subtransaction-XID cache too */
  	proc->subxids.nxids = 0;
--- 298,307 ----
  	proc->xid = InvalidTransactionId;
  	proc->lxid = InvalidLocalTransactionId;
  	proc->xmin = InvalidTransactionId;
! 
! 	/* redundant, but just in case */
! 	proc->vacuumFlags &= ~PROC_VACUUM_STATE_MASK;
! 	proc->inCommit = false;
  
  	/* Clear the subtransaction-XID cache too */
  	proc->subxids.nxids = 0;
*************** TransactionIdIsActive(TransactionId xid)
*** 546,552 ****
   * 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 =
--- 550,557 ----
   * 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 the PROC_IN_VACUUM flag 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 =
*************** GetOldestXmin(bool allDbs, bool ignoreVa
*** 586,592 ****
  	{
  		volatile PGPROC	   *proc = arrayP->procs[index];
  
! 		if (ignoreVacuum && proc->inVacuum)
  			continue;
  
  		if (allDbs || proc->databaseId == MyDatabaseId)
--- 591,597 ----
  	{
  		volatile PGPROC	   *proc = arrayP->procs[index];
  
! 		if (ignoreVacuum && (proc->vacuumFlags & PROC_IN_VACUUM))
  			continue;
  
  		if (allDbs || proc->databaseId == MyDatabaseId)
*************** GetSnapshotData(Snapshot snapshot, bool 
*** 723,729 ****
  		TransactionId xid;
  
  		/* Ignore procs running LAZY VACUUM */
! 		if (proc->inVacuum)
  			continue;
  
  		/* Update globalxmin to be the smallest valid xmin */
--- 728,734 ----
  		TransactionId xid;
  
  		/* Ignore procs running LAZY VACUUM */
! 		if (proc->vacuumFlags & PROC_IN_VACUUM)
  			continue;
  
  		/* Update globalxmin to be the smallest valid xmin */
*************** CheckOtherDBBackends(Oid databaseId)
*** 1193,1199 ****
  
  			found = true;
  
! 			if (proc->isAutovacuum)
  			{
  				/* an autovacuum --- send it SIGTERM before sleeping */
  				int		autopid = proc->pid;
--- 1198,1204 ----
  
  			found = true;
  
! 			if (proc->vacuumFlags & PROC_IS_AUTOVACUUM)
  			{
  				/* an autovacuum --- send it SIGTERM before sleeping */
  				int		autopid = proc->pid;
Index: src/backend/storage/lmgr/proc.c
===================================================================
RCS file: /home/alvherre/Code/cvs/pgsql/src/backend/storage/lmgr/proc.c,v
retrieving revision 1.194
diff -c -p -r1.194 proc.c
*** src/backend/storage/lmgr/proc.c	8 Sep 2007 20:31:15 -0000	1.194
--- src/backend/storage/lmgr/proc.c	24 Oct 2007 14:59:47 -0000
*************** InitProcess(void)
*** 291,298 ****
  	MyProc->databaseId = InvalidOid;
  	MyProc->roleId = InvalidOid;
  	MyProc->inCommit = false;
! 	MyProc->inVacuum = false;
! 	MyProc->isAutovacuum = IsAutoVacuumWorkerProcess();
  	MyProc->lwWaiting = false;
  	MyProc->lwExclusive = false;
  	MyProc->lwWaitLink = NULL;
--- 291,298 ----
  	MyProc->databaseId = InvalidOid;
  	MyProc->roleId = InvalidOid;
  	MyProc->inCommit = false;
! 	if (IsAutoVacuumWorkerProcess())
! 		MyProc->vacuumFlags |= PROC_IS_AUTOVACUUM;
  	MyProc->lwWaiting = false;
  	MyProc->lwExclusive = false;
  	MyProc->lwWaitLink = NULL;
*************** InitAuxiliaryProcess(void)
*** 429,436 ****
  	MyProc->databaseId = InvalidOid;
  	MyProc->roleId = InvalidOid;
  	MyProc->inCommit = false;
! 	MyProc->inVacuum = false;
! 	MyProc->isAutovacuum = IsAutoVacuumLauncherProcess(); /* is this needed? */
  	MyProc->lwWaiting = false;
  	MyProc->lwExclusive = false;
  	MyProc->lwWaitLink = NULL;
--- 429,436 ----
  	MyProc->databaseId = InvalidOid;
  	MyProc->roleId = InvalidOid;
  	MyProc->inCommit = false;
! 	/* we don't set the "is autovacuum" flag in the launcher */
! 	MyProc->vacuumFlags = 0;
  	MyProc->lwWaiting = false;
  	MyProc->lwExclusive = false;
  	MyProc->lwWaitLink = NULL;
Index: src/include/storage/proc.h
===================================================================
RCS file: /home/alvherre/Code/cvs/pgsql/src/include/storage/proc.h,v
retrieving revision 1.100
diff -c -p -r1.100 proc.h
*** src/include/storage/proc.h	5 Sep 2007 18:10:48 -0000	1.100
--- src/include/storage/proc.h	24 Oct 2007 14:59:47 -0000
*************** struct XidCache
*** 38,43 ****
--- 38,52 ----
  	TransactionId xids[PGPROC_MAX_CACHED_SUBXIDS];
  };
  
+ /* Flags for PGPROC->vacuumFlags */
+ #define		PROC_IS_AUTOVACUUM	0x01	/* is it an autovac worker? */
+ #define		PROC_IN_VACUUM		0x02	/* currently running lazy vacuum */
+ #define		PROC_IN_ANALYZE		0x04	/* currently running analyze */
+ #define		PROC_VACUUM_FOR_WRAPAROUND 0x08 /* set by autovac only */
+ 
+ /* flags reset at EOXact */
+ #define		PROC_VACUUM_STATE_MASK (0x0E)
+ 
  /*
   * Each backend has a PGPROC struct in shared memory.  There is also a list of
   * currently-unused PGPROC structs that will be reallocated to new backends.
*************** struct PGPROC
*** 82,93 ****
  
  	bool		inCommit;		/* true if within commit critical section */
  
- 	bool		inVacuum;		/* true if current xact is a LAZY VACUUM */
- 	bool		isAutovacuum;	/* true if it's autovacuum */
- 
  	/* 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 */
  	struct PGPROC *lwWaitLink;	/* next waiter for same LW lock */
  
  	/* Info about lock the process is currently waiting for, if any. */
--- 91,101 ----
  
  	bool		inCommit;		/* true if within commit critical section */
  
  	/* 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 */
+ 
+ 	uint8		vacuumFlags;	/* vacuum-related flags, see above */
  	struct PGPROC *lwWaitLink;	/* next waiter for same LW lock */
  
  	/* Info about lock the process is currently waiting for, if any. */
---------------------------(end of broadcast)---------------------------
TIP 2: Don't 'kill -9' the postmaster

Reply via email to