⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 autovacuum.c

📁 PostgreSQL 8.1.4的源码 适用于Linux下的开源数据库系统
💻 C
📖 第 1 页 / 共 2 页
字号:
/*------------------------------------------------------------------------- * * autovacuum.c * * PostgreSQL Integrated Autovacuum Daemon * * * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION *	  $PostgreSQL: pgsql/src/backend/postmaster/autovacuum.c,v 1.5.2.6 2006/05/19 15:15:38 alvherre Exp $ * *------------------------------------------------------------------------- */#include "postgres.h"#include <signal.h>#include <sys/types.h>#include <time.h>#include <unistd.h>#include "access/genam.h"#include "access/heapam.h"#include "access/xlog.h"#include "catalog/indexing.h"#include "catalog/namespace.h"#include "catalog/pg_autovacuum.h"#include "catalog/pg_database.h"#include "commands/vacuum.h"#include "libpq/hba.h"#include "libpq/pqsignal.h"#include "miscadmin.h"#include "pgstat.h"#include "postmaster/autovacuum.h"#include "postmaster/fork_process.h"#include "postmaster/postmaster.h"#include "storage/fd.h"#include "storage/ipc.h"#include "storage/proc.h"#include "storage/sinval.h"#include "tcop/tcopprot.h"#include "utils/flatfiles.h"#include "utils/fmgroids.h"#include "utils/memutils.h"#include "utils/ps_status.h"#include "utils/lsyscache.h"#include "utils/rel.h"#include "utils/relcache.h"/* * GUC parameters */bool		autovacuum_start_daemon = false;int			autovacuum_naptime;int			autovacuum_vac_thresh;double		autovacuum_vac_scale;int			autovacuum_anl_thresh;double		autovacuum_anl_scale;int			autovacuum_vac_cost_delay;int			autovacuum_vac_cost_limit;/* Flag to tell if we are in the autovacuum daemon process */static bool am_autovacuum = false;/* Last time autovac daemon started/stopped (only valid in postmaster) */static time_t last_autovac_start_time = 0;static time_t last_autovac_stop_time = 0;/* Memory context for long-lived data */static MemoryContext AutovacMemCxt;/* struct to keep list of candidate databases for vacuum */typedef struct autovac_dbase{	Oid			oid;	char	   *name;	TransactionId frozenxid;	TransactionId vacuumxid;	PgStat_StatDBEntry *entry;	int32		age;} autovac_dbase;/* struct to keep track of tables to vacuum and/or analyze */typedef struct autovac_table{	Oid			relid;	Oid			toastrelid;	bool		dovacuum;	bool		doanalyze;	int			vacuum_cost_delay;	int			vacuum_cost_limit;} autovac_table;#ifdef EXEC_BACKENDstatic pid_t autovac_forkexec(void);#endifNON_EXEC_STATIC void AutoVacMain(int argc, char *argv[]);static void process_whole_db(void);static void do_autovacuum(PgStat_StatDBEntry *dbentry);static List *autovac_get_database_list(void);static void test_rel_for_autovac(Oid relid, PgStat_StatTabEntry *tabentry,					 Form_pg_class classForm,					 Form_pg_autovacuum avForm,					 List **vacuum_tables,					 List **toast_table_ids);static void autovacuum_do_vac_analyze(List *relids, bool dovacuum,						  bool doanalyze, bool freeze);static void autovac_report_activity(VacuumStmt *vacstmt,						List *relids);/* * Main entry point for autovacuum controller process. * * This code is heavily based on pgarch.c, q.v. */intautovac_start(void){	time_t		curtime;	pid_t		AutoVacPID;	/* Do nothing if no autovacuum process needed */	if (!AutoVacuumingActive())		return 0;	/*	 * Do nothing if too soon since last autovacuum exit.  This limits how	 * often the daemon runs.  Since the time per iteration can be quite	 * variable, it seems more useful to measure/control the time since last	 * subprocess exit than since last subprocess launch.	 *	 * However, we *also* check the time since last subprocess launch; this	 * prevents thrashing under fork-failure conditions.	 *	 * Note that since we will be re-called from the postmaster main loop, we	 * will get another chance later if we do nothing now.	 *	 * XXX todo: implement sleep scale factor that existed in contrib code.	 */	curtime = time(NULL);	if ((unsigned int) (curtime - last_autovac_stop_time) <		(unsigned int) autovacuum_naptime)		return 0;	if ((unsigned int) (curtime - last_autovac_start_time) <		(unsigned int) autovacuum_naptime)		return 0;	last_autovac_start_time = curtime;#ifdef EXEC_BACKEND	switch ((AutoVacPID = autovac_forkexec()))#else	switch ((AutoVacPID = fork_process()))#endif	{		case -1:			ereport(LOG,					(errmsg("could not fork autovacuum process: %m")));			return 0;#ifndef EXEC_BACKEND		case 0:			/* in postmaster child ... */			/* Close the postmaster's sockets */			ClosePostmasterPorts(false);			AutoVacMain(0, NULL);			break;#endif		default:			return (int) AutoVacPID;	}	/* shouldn't get here */	return 0;}/* * autovac_stopped --- called by postmaster when subprocess exit is detected */voidautovac_stopped(void){	last_autovac_stop_time = time(NULL);}#ifdef EXEC_BACKEND/* * autovac_forkexec() * * Format up the arglist for the autovacuum process, then fork and exec. */static pid_tautovac_forkexec(void){	char	   *av[10];	int			ac = 0;	av[ac++] = "postgres";	av[ac++] = "-forkautovac";	av[ac++] = NULL;			/* filled in by postmaster_forkexec */	av[ac] = NULL;	Assert(ac < lengthof(av));	return postmaster_forkexec(ac, av);}#endif   /* EXEC_BACKEND *//* * AutoVacMain */NON_EXEC_STATIC voidAutoVacMain(int argc, char *argv[]){	ListCell   *cell;	List	   *dblist;	TransactionId nextXid;	autovac_dbase *db;	bool		whole_db;	sigjmp_buf	local_sigjmp_buf;	/* we are a postmaster subprocess now */	IsUnderPostmaster = true;	am_autovacuum = true;	/* reset MyProcPid */	MyProcPid = getpid();	/* Lose the postmaster's on-exit routines */	on_exit_reset();	/* Identify myself via ps */	init_ps_display("autovacuum process", "", "");	set_ps_display("");	SetProcessingMode(InitProcessing);	/*	 * Set up signal handlers.	We operate on databases much like a regular	 * backend, so we use the same signal handling.  See equivalent code in	 * tcop/postgres.c.	 *	 * Currently, we don't pay attention to postgresql.conf changes that	 * happen during a single daemon iteration, so we can ignore SIGHUP.	 */	pqsignal(SIGHUP, SIG_IGN);	/*	 * Presently, SIGINT will lead to autovacuum shutdown, because that's how	 * we handle ereport(ERROR).  It could be improved however.	 */	pqsignal(SIGINT, StatementCancelHandler);	pqsignal(SIGTERM, die);	pqsignal(SIGQUIT, quickdie);	pqsignal(SIGALRM, handle_sig_alarm);	pqsignal(SIGPIPE, SIG_IGN);	pqsignal(SIGUSR1, CatchupInterruptHandler);	/* We don't listen for async notifies */	pqsignal(SIGUSR2, SIG_IGN);	pqsignal(SIGFPE, FloatExceptionHandler);	pqsignal(SIGCHLD, SIG_DFL);	/* Early initialization */	BaseInit();	/*	 * If an exception is encountered, processing resumes here.	 *	 * See notes in postgres.c about the design of this coding.	 */	if (sigsetjmp(local_sigjmp_buf, 1) != 0)	{		/* Prevents interrupts while cleaning up */		HOLD_INTERRUPTS();		/* Report the error to the server log */		EmitErrorReport();		/*		 * We can now go away.	Note that because we'll call InitProcess, a		 * callback will be registered to do ProcKill, which will clean up		 * necessary state.		 */		proc_exit(0);	}	/* We can now handle ereport(ERROR) */	PG_exception_stack = &local_sigjmp_buf;	PG_SETMASK(&UnBlockSig);	/* Get a list of databases */	dblist = autovac_get_database_list();	/*	 * Get the next Xid that was current as of the last checkpoint. We need it	 * to determine whether databases are about to need database-wide vacuums.	 */	nextXid = GetRecentNextXid();	/*	 * Choose a database to connect to.  We pick the database that was least	 * recently auto-vacuumed, or one that needs database-wide vacuum (to	 * prevent Xid wraparound-related data loss).	 *	 * Note that a database with no stats entry is not considered, except for	 * Xid wraparound purposes.  The theory is that if no one has ever	 * connected to it since the stats were last initialized, it doesn't need	 * vacuuming.	 *	 * XXX This could be improved if we had more info about whether it needs	 * vacuuming before connecting to it.  Perhaps look through the pgstats	 * data for the database's tables?  One idea is to keep track of the	 * number of new and dead tuples per database in pgstats.  However it	 * isn't clear how to construct a metric that measures that and not cause	 * starvation for less busy databases.	 */	db = NULL;	whole_db = false;	foreach(cell, dblist)	{		autovac_dbase *tmp = lfirst(cell);		bool		this_whole_db;		int32		freeze_age,					vacuum_age;		/*		 * We look for the database that most urgently needs a database-wide		 * vacuum.	We decide that a database-wide vacuum is needed 100000		 * transactions sooner than vacuum.c's vac_truncate_clog() would		 * decide to start giving warnings.  If any such db is found, we		 * ignore all other dbs.		 *		 * Unlike vacuum.c, we also look at vacuumxid.	This is so that		 * pg_clog can be kept trimmed to a reasonable size.		 */		freeze_age = (int32) (nextXid - tmp->frozenxid);		vacuum_age = (int32) (nextXid - tmp->vacuumxid);		tmp->age = Max(freeze_age, vacuum_age);		this_whole_db = (tmp->age >						 (int32) ((MaxTransactionId >> 3) * 3 - 100000));		if (whole_db || this_whole_db)		{			if (!this_whole_db)				continue;			if (db == NULL || tmp->age > db->age)			{				db = tmp;				whole_db = true;			}			continue;		}		/*		 * Otherwise, skip a database with no pgstat entry; it means it hasn't		 * seen any activity.		 */		tmp->entry = pgstat_fetch_stat_dbentry(tmp->oid);		if (!tmp->entry)			continue;		/*		 * Don't try to access a database that was dropped.  This could only		 * happen if we read the pg_database flat file right before it was		 * modified, after the database was dropped from the pg_database		 * table.  (This is of course a not-very-bulletproof test, but it's		 * cheap to make.  If we do mistakenly choose a recently dropped		 * database, InitPostgres will fail and we'll drop out until the next		 * autovac run.)		 */		if (tmp->entry->destroy != 0)			continue;		/*		 * Else remember the db with oldest autovac time.		 */		if (db == NULL ||			tmp->entry->last_autovac_time < db->entry->last_autovac_time)			db = tmp;	}	if (db)	{		/*		 * Report autovac startup to the stats collector.  We deliberately do		 * this before InitPostgres, so that the last_autovac_time will get		 * updated even if the connection attempt fails.  This is to prevent		 * autovac from getting "stuck" repeatedly selecting an unopenable		 * database, rather than making any progress on stuff it can connect		 * to.		 */		pgstat_report_autovac(db->oid);		/*		 * Connect to the selected database		 */		InitPostgres(db->name, NULL);		SetProcessingMode(NormalProcessing);		set_ps_display(db->name);		ereport(LOG,				(errmsg("autovacuum: processing database \"%s\"", db->name)));		/* Create the memory context where cross-transaction state is stored */		AutovacMemCxt = AllocSetContextCreate(TopMemoryContext,											  "Autovacuum context",											  ALLOCSET_DEFAULT_MINSIZE,											  ALLOCSET_DEFAULT_INITSIZE,											  ALLOCSET_DEFAULT_MAXSIZE);		/*		 * And do an appropriate amount of work		 */		if (whole_db)			process_whole_db();		else			do_autovacuum(db->entry);	}	/* One iteration done, go away */	proc_exit(0);}/* * autovac_get_database_list * *		Return a list of all databases.  Note we cannot use pg_database, *		because we aren't connected yet; we use the flat database file. */static List *autovac_get_database_list(void){	char	   *filename;	List	   *dblist = NIL;	char		thisname[NAMEDATALEN];	FILE	   *db_file;	Oid			db_id;	Oid			db_tablespace;	TransactionId db_frozenxid;	TransactionId db_vacuumxid;	filename = database_getflatfilename();	db_file = AllocateFile(filename, "r");	if (db_file == NULL)		ereport(FATAL,				(errcode_for_file_access(),				 errmsg("could not open file \"%s\": %m", filename)));	while (read_pg_database_line(db_file, thisname, &db_id,								 &db_tablespace, &db_frozenxid,								 &db_vacuumxid))	{		autovac_dbase *db;		db = (autovac_dbase *) palloc(sizeof(autovac_dbase));		db->oid = db_id;		db->name = pstrdup(thisname);		db->frozenxid = db_frozenxid;		db->vacuumxid = db_vacuumxid;		/* these get set later: */		db->entry = NULL;		db->age = 0;		dblist = lappend(dblist, db);	}	FreeFile(db_file);	pfree(filename);	return dblist;}/* * Process a whole database.  If it's a template database or is disallowing * connection by means of datallowconn=false, then issue a VACUUM FREEZE. * Else use a plain VACUUM. */static voidprocess_whole_db(void){	Relation	dbRel;	ScanKeyData entry[1];	SysScanDesc scan;	HeapTuple	tup;	Form_pg_database dbForm;	bool		freeze;	/* Start a transaction so our commands have one to play into. */	StartTransactionCommand();	 /* functions in indexes may want a snapshot set */	ActiveSnapshot = CopySnapshot(GetTransactionSnapshot());	/*	 * Clean up any dead statistics collector entries for this DB.	 */	pgstat_vacuum_tabstat();	dbRel = heap_open(DatabaseRelationId, AccessShareLock);	/* Must use a table scan, since there's no syscache for pg_database */	ScanKeyInit(&entry[0],				ObjectIdAttributeNumber,				BTEqualStrategyNumber, F_OIDEQ,				ObjectIdGetDatum(MyDatabaseId));	scan = systable_beginscan(dbRel, DatabaseOidIndexId, true,							  SnapshotNow, 1, entry);	tup = systable_getnext(scan);

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -