Index: src/backend/access/transam/xact.c =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/backend/access/transam/xact.c,v retrieving revision 1.208 diff -c -r1.208 xact.c *** src/backend/access/transam/xact.c 28 Jun 2005 05:08:51 -0000 1.208 --- src/backend/access/transam/xact.c 29 Jun 2005 20:13:32 -0000 *************** *** 1499,1505 **** /* * set the current transaction state information appropriately during ! * the abort processing */ s->state = TRANS_COMMIT; --- 1499,1505 ---- /* * set the current transaction state information appropriately during ! * the commit processing */ s->state = TRANS_COMMIT; Index: src/backend/catalog/Makefile =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/backend/catalog/Makefile,v retrieving revision 1.54 diff -c -r1.54 Makefile *** src/backend/catalog/Makefile 28 Jun 2005 05:08:52 -0000 1.54 --- src/backend/catalog/Makefile 29 Jun 2005 20:15:18 -0000 *************** *** 32,38 **** pg_language.h pg_largeobject.h pg_aggregate.h pg_statistic.h \ pg_rewrite.h pg_trigger.h pg_listener.h pg_description.h pg_cast.h \ pg_namespace.h pg_conversion.h pg_database.h \ ! pg_authid.h pg_auth_members.h pg_tablespace.h pg_depend.h \ indexing.h \ ) --- 32,38 ---- pg_language.h pg_largeobject.h pg_aggregate.h pg_statistic.h \ pg_rewrite.h pg_trigger.h pg_listener.h pg_description.h pg_cast.h \ pg_namespace.h pg_conversion.h pg_database.h \ ! pg_authid.h pg_auth_members.h pg_autovacuum.h pg_tablespace.h pg_depend.h \ indexing.h \ ) Index: src/backend/catalog/heap.c =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/backend/catalog/heap.c,v retrieving revision 1.285 diff -c -r1.285 heap.c *** src/backend/catalog/heap.c 5 Jun 2005 00:38:07 -0000 1.285 --- src/backend/catalog/heap.c 29 Jun 2005 22:26:51 -0000 *************** *** 37,42 **** --- 37,43 ---- #include "catalog/index.h" #include "catalog/indexing.h" #include "catalog/pg_attrdef.h" + #include "catalog/pg_autovacuum.h" #include "catalog/pg_constraint.h" #include "catalog/pg_inherits.h" #include "catalog/pg_namespace.h" *************** *** 75,80 **** --- 76,83 ---- static void StoreRelCheck(Relation rel, char *ccname, char *ccbin); static void StoreConstraints(Relation rel, TupleDesc tupdesc); static void SetRelationNumChecks(Relation rel, int numchecks); + static void DeleteAutovacuumTuple(Oid relid); + static void AddNewAutovacuumTuple(Oid new_rel_oid); /* ---------------------------------------------------------------- *************** *** 296,302 **** /* ---------------------------------------------------------------- * heap_create_with_catalog - Create a cataloged relation * ! * this is done in 6 steps: * * 1) CheckAttributeNamesTypes() is used to make certain the tuple * descriptor contains a valid set of attribute names and types --- 299,305 ---- /* ---------------------------------------------------------------- * heap_create_with_catalog - Create a cataloged relation * ! * this is done in 9 steps: * * 1) CheckAttributeNamesTypes() is used to make certain the tuple * descriptor contains a valid set of attribute names and types *************** *** 310,324 **** * 4) AddNewRelationTuple() is called to register the * relation in pg_class. * ! * 5) TypeCreate() is called to define a new type corresponding * to the new relation. * ! * 6) AddNewAttributeTuples() is called to register the * new relation's schema in pg_attribute. * ! * 7) StoreConstraints is called () - vadim 08/22/97 * ! * 8) the relations are closed and the new relation's oid * is returned. * * ---------------------------------------------------------------- --- 313,330 ---- * 4) AddNewRelationTuple() is called to register the * relation in pg_class. * ! * 5) AddNewAutovacuumTuple() is called to register the relation ! * in pg_autovacuum. ! * ! * 6) TypeCreate() is called to define a new type corresponding * to the new relation. * ! * 7) AddNewAttributeTuples() is called to register the * new relation's schema in pg_attribute. * ! * 8) StoreConstraints is called () - vadim 08/22/97 * ! * 9) the relations are closed and the new relation's oid * is returned. * * ---------------------------------------------------------------- *************** *** 548,553 **** --- 554,593 ---- heap_close(rel, RowExclusiveLock); } + static void + AddNewAutovacuumTuple(Oid new_rel_oid) + { + HeapTuple tup; + Relation rel; + Datum values[Natts_pg_autovacuum]; + bool nulls[Natts_pg_autovacuum]; + + rel = heap_open(AutovacuumRelationId, RowExclusiveLock); + + MemSet(values, 0, sizeof(values)); + MemSet(nulls, 0, sizeof(nulls)); + + /* Default initial values */ + values[Anum_pg_autovacuum_vacrelid - 1] = ObjectIdGetDatum(new_rel_oid); + values[Anum_pg_autovacuum_enabled - 1] = BoolGetDatum(true); + values[Anum_pg_autovacuum_vac_base_thresh - 1] = Int32GetDatum(-1); + values[Anum_pg_autovacuum_vac_scale_factor - 1] = Float4GetDatum(-1.0); + values[Anum_pg_autovacuum_vac_last_cnt - 1] = Float4GetDatum(0.0); + values[Anum_pg_autovacuum_anl_base_thresh - 1] = Int32GetDatum(-1); + values[Anum_pg_autovacuum_anl_scale_factor - 1] = Float4GetDatum(-1.0); + values[Anum_pg_autovacuum_anl_last_cnt - 1] = Float4GetDatum(0.0); + + tup = heap_form_tuple(RelationGetDescr(rel), values, nulls); + + simple_heap_insert(rel, tup); + + /* Keep indexes current */ + CatalogUpdateIndexes(rel, tup); + + heap_freetuple(tup); + heap_close(rel, RowExclusiveLock); + } + /* -------------------------------- * AddNewRelationTuple * *************** *** 752,757 **** --- 792,805 ---- oidislocal, oidinhcount); /* + * Add an autovacuum entry for the relation, if it's a regular table. Skip + * this in bootstrap mode, since the pg_autovacuum relation may not exist + * yet. + */ + if (!IsBootstrapProcessingMode() && relkind == RELKIND_RELATION) + AddNewAutovacuumTuple(new_rel_oid); + + /* * make a dependency link to force the relation to be deleted if its * namespace is. Skip this in bootstrap mode, since we don't make * dependencies while bootstrapping. *************** *** 862,867 **** --- 910,937 ---- heap_close(pg_class_desc, RowExclusiveLock); } + static void + DeleteAutovacuumTuple(Oid relid) + { + Relation avRel; + HeapTuple tup; + + /* Grab an appropiate lock on pg_autovacuum */ + avRel = heap_open(AutovacuumRelationId, RowExclusiveLock); + + tup = SearchSysCache(AUTOVACRELID, + ObjectIdGetDatum(relid), + 0, 0, 0); + if (!HeapTupleIsValid(tup)) + elog(ERROR, "cache lookup failed for relation %u", relid); + + /* delete the relation tuple from pg_autovacuum, and finish up */ + simple_heap_delete(avRel, &tup->t_self); + + ReleaseSysCache(tup); + heap_close(avRel, RowExclusiveLock); + } + /* * DeleteAttributeTuples * *************** *** 1134,1139 **** --- 1204,1210 ---- heap_drop_with_catalog(Oid relid) { Relation rel; + bool drop_autovac; /* * Open and lock the relation. *************** *** 1150,1155 **** --- 1221,1228 ---- smgrscheduleunlink(rel->rd_smgr, rel->rd_istemp); } + drop_autovac = rel->rd_rel->relkind == RELKIND_RELATION; + /* * Close relcache entry, but *keep* AccessExclusiveLock on the * relation until transaction commit. This ensures no one else will *************** *** 1187,1192 **** --- 1260,1271 ---- DeleteAttributeTuples(relid); /* + * delete autovacuum tuple if needed + */ + if (drop_autovac) + DeleteAutovacuumTuple(relid); + + /* * delete relation tuple */ DeleteRelationTuple(relid); Index: src/backend/commands/vacuum.c =================================================================== RCS file: /home/alvherre/cvs/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 29 Jun 2005 04:21:55 -0000 *************** *** 35,40 **** --- 35,42 ---- #include "commands/vacuum.h" #include "executor/executor.h" #include "miscadmin.h" + #include "pgstat.h" + #include "postmaster/autovacuum.h" #include "storage/freespace.h" #include "storage/procarray.h" #include "storage/smgr.h" *************** *** 47,53 **** #include "utils/memutils.h" #include "utils/relcache.h" #include "utils/syscache.h" - #include "pgstat.h" typedef struct VacPageData --- 49,54 ---- *************** *** 252,260 **** * after finishing the vacuum. This is mainly so that we can let go * the AccessExclusiveLock that we may be holding.) * ! * ANALYZE (without VACUUM) can run either way. */ ! if (vacstmt->vacuum) { PreventTransactionChain((void *) vacstmt, stmttype); in_outer_xact = false; --- 253,262 ---- * after finishing the vacuum. This is mainly so that we can let go * the AccessExclusiveLock that we may be holding.) * ! * ANALYZE (without VACUUM) can run either way. Also, assume ! * the Autovacuum process knows what it's doing. */ ! if (vacstmt->vacuum && !IsAutoVacuumProcess()) { PreventTransactionChain((void *) vacstmt, stmttype); in_outer_xact = false; Index: src/backend/postmaster/Makefile =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/backend/postmaster/Makefile,v retrieving revision 1.20 diff -c -r1.20 Makefile *** src/backend/postmaster/Makefile 10 Mar 2005 07:14:03 -0000 1.20 --- src/backend/postmaster/Makefile 29 Jun 2005 18:41:49 -0000 *************** *** 12,18 **** top_builddir = ../../.. include $(top_builddir)/src/Makefile.global ! OBJS = bgwriter.o fork_process.o pgarch.o pgstat.o postmaster.o syslogger.o all: SUBSYS.o --- 12,18 ---- top_builddir = ../../.. include $(top_builddir)/src/Makefile.global ! OBJS = bgwriter.o fork_process.o pgarch.o pgstat.o postmaster.o syslogger.o autovacuum.o all: SUBSYS.o Index: src/backend/postmaster/autovacuum.c =================================================================== RCS file: src/backend/postmaster/autovacuum.c diff -N src/backend/postmaster/autovacuum.c *** /dev/null 1 Jan 1970 00:00:00 -0000 --- src/backend/postmaster/autovacuum.c 29 Jun 2005 22:30:29 -0000 *************** *** 0 **** --- 1,803 ---- + /*------------- + * 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$ + *------------- + */ + #include "postgres.h" + + #include + #include + #include + #include + + #include "access/heapam.h" + #include "access/skey.h" + #include "access/xact.h" + #include "catalog/indexing.h" + #include "catalog/pg_autovacuum.h" + #include "commands/dbcommands.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/ipc.h" + #include "storage/fd.h" + #include "storage/lwlock.h" + #include "storage/pmsignal.h" + #include "utils/flatfiles.h" + #include "utils/guc.h" + #include "utils/lsyscache.h" + #include "utils/memutils.h" + #include "utils/ps_status.h" + #include "utils/relcache.h" + #include "utils/syscache.h" + + + bool am_autovacuum = false; + MemoryContext AutovacMemCxt; + + /* + * GUC parameters + * + * FIXME -- Verify these default values + */ + bool autovacuum_start_daemon = false; + int autovacuum_naptime = 60; + int autovacuum_vac_thresh = 1000; + double autovacuum_vac_scale = 0.4; + int autovacuum_anl_thresh = 500; + double autovacuum_anl_scale = 0.2; + + /* + * Flags set by interrupt handlers for later service in the main loop. + */ + static volatile sig_atomic_t shutdown_requested = false; + static volatile sig_atomic_t got_SIGHUP = false; + static time_t last_autovac_start_time = 0; + + /* struct to keep list of candidate databases for vacuum */ + typedef struct autovac_dbase + { + Oid oid; + char *name; + PgStat_StatDBEntry *entry; + } autovac_dbase; + + /* + * Operation to perform on a table. UpdateCounts means only + * update the pg_autovacuum vac_last_cnt and/or anl_last_cnt + * fields for the table, do nothing to the table proper. + */ + typedef enum AutovacOp + { + AVOperNone, + AVOperUpdateCounts, + AVOperAnalyze, + AVOperVacuumAnalyze + } AutovacOp; + + /* struct to keep track of tables to process */ + typedef struct autovac_table + { + Oid oid; + AutovacOp operation; + float4 anl_last_cnt; + float4 vac_last_cnt; + bool update_anltuples; + bool update_vactuples; + } autovac_table; + + #ifdef EXEC_BACKEND + static pid_t autovac_forkexec(void); + #endif + NON_EXEC_STATIC void AutoVacMain(int argc, char *argv[]); + static void do_autovacuum(PgStat_StatDBEntry *dbentry); + static List *autovac_get_database_list(void); + static void test_rel_for_autovac(PgStat_StatDBEntry *dbentry, + HeapTuple tuple, List **vacuum_tables, List **analyze_tables, + List **update_tables); + static void autovacuum_do_vac_analyze(autovac_table *tab); + static void update_autovacuum_tuple(autovac_table *tab); + static void check_shutdown_flag(void); + static void autovac_sigquit_handler(SIGNAL_ARGS); + static void autovac_sigusr2_handler(SIGNAL_ARGS); + static void autovac_sighup_handler(SIGNAL_ARGS); + + + /* + * Main entry point for autovacuum controller process. + * + * This code is heavily based on pgarch.c, q.v. + */ + int + autovac_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 start. This is a safety + * valve to protect against continuous respawn attempts if the autovacuum + * is dying immediately at launch. Note that since we will be re-called + * from the postmaster main loop, we will get another chance later. + */ + curtime = time(NULL); + 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; + } + + #ifdef EXEC_BACKEND + /* + * autovac_forkexec() + * + * Format up the arglist for the autovacuum process, then fork and exec. + */ + static pid_t + autovac_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 void + AutoVacMain(int argc, char *argv[]) + { + ListCell *cell; + List *dblist; + autovac_dbase *db = NULL; + 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(); + + /* + * Ignore all signals usually bound to some action in the postmaster, + * except for SIGHUP, SIGUSR2 and SIGQUIT. + */ + pqsignal(SIGHUP, autovac_sighup_handler); + pqsignal(SIGINT, SIG_IGN); + pqsignal(SIGTERM, SIG_IGN); + pqsignal(SIGQUIT, autovac_sigquit_handler); + pqsignal(SIGALRM, SIG_IGN); + pqsignal(SIGPIPE, SIG_IGN); + pqsignal(SIGUSR1, SIG_IGN); + pqsignal(SIGUSR2, autovac_sigusr2_handler); + pqsignal(SIGCHLD, SIG_DFL); + pqsignal(SIGTTIN, SIG_DFL); + pqsignal(SIGTTOU, SIG_DFL); + pqsignal(SIGCONT, SIG_DFL); + pqsignal(SIGWINCH, SIG_DFL); + PG_SETMASK(&UnBlockSig); + + /* Identify myself via ps */ + init_ps_display("autovacuum process", "", ""); + set_ps_display(""); + + /* Init XLOG file paths */ + XLOGPathInit(); + 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 was registered to do ProcKill, which will clean + * up necessary state. + */ + proc_exit(0); + } + + /* Get a list of databases */ + dblist = autovac_get_database_list(); + + /* + * Choose a database to connect to. We pick the database that was least + * recently auto-vacuumed. + * + * XXX This could be improved if we had more data about whether it needs + * vacuuming before connecting to it. (We could look at the pgstat data + * for each of its tables.) + */ + foreach(cell, dblist) + { + autovac_dbase *tmp = lfirst(cell); + + 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. + */ + if (tmp->entry->destroy != 0) + continue; + + if (!db || + tmp->entry->last_autovac_time < db->entry->last_autovac_time) + db = tmp; + } + + if (db) + { + /* do some work on the database */ + InitPostgres(db->name, NULL); + SetProcessingMode(NormalProcessing); + pgstat_report_autovac(time(NULL)); + + ereport(LOG, + (errmsg("autovacuum processing database %s", db->name))); + set_ps_display(db->name); + do_autovacuum(db->entry); + } + + /* One iteration done, go away */ + proc_exit(0); + } + + /* + * autovac_get_database_list + * + * Return a list of all databases with datautovacuum set. Note we + * cannot use pg_database, because we aren't connected yet. + */ + static List * + autovac_get_database_list(void) + { + char *filename; + List *dblist = NIL; + char thisname[NAMEDATALEN]; + FILE *db_file; + Oid db_id; + Oid db_tablespace; + + 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)) + { + autovac_dbase *db; + + db = (autovac_dbase *) palloc(sizeof(autovac_dbase)); + + db->oid = db_id; + db->name = pstrdup(thisname); + /* this gets set later */ + db->entry = NULL; + + dblist = lappend(dblist, db); + } + + FreeFile(db_file); + pfree(filename); + + return dblist; + } + + /* + * Process a database. + * + * Note that test_rel_for_autovac generates three separate lists, one for + * each AutovacOp. This is to facilitate processing all UpdateCounts entries + * on our very first transaction, then all analyzes, and all vacuums last. + * + * Note that check_shutdown_flag is supposed to be used in certain spots in + * order not to lose too much work. Note also that we update a table's + * pg_autovacuum entry only after we have actually processed it, so that + * we won't report work as done if we are told to shut down in the middle of + * processing. + */ + static void + do_autovacuum(PgStat_StatDBEntry *dbentry) + { + Relation avRel; + HeapTuple tuple; + HeapScanDesc scan; + List *vacuum_tables = NIL, + *analyze_tables = NIL, + *update_tables = NIL; + ListCell *cell; + + /* Start a transaction so our commands have one to play into. */ + StartTransactionCommand(); + + /* Memory context where cross-transaction state is stored */ + AutovacMemCxt = AllocSetContextCreate(TopMemoryContext, + "Autovacuum context", + ALLOCSET_DEFAULT_MINSIZE, + ALLOCSET_DEFAULT_INITSIZE, + ALLOCSET_DEFAULT_MAXSIZE); + /* + * StartTransactionCommand and CommitTransactionCommand will + * automatically switch to other contexts. We need this one + * to keep the list of relations to vacuum/analyze. + */ + + check_shutdown_flag(); + + avRel = heap_open(AutovacuumRelationId, AccessShareLock); + + scan = heap_beginscan(avRel, SnapshotNow, 0, NULL); + while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL) + { + test_rel_for_autovac(dbentry, tuple, &vacuum_tables, + &analyze_tables, &update_tables); + } + + heap_endscan(scan); + heap_close(avRel, AccessShareLock); + + /* Perform operations on collected tables. */ + foreach (cell, update_tables) + { + autovac_table *tab = lfirst(cell); + + update_autovacuum_tuple(tab); + } + + CommitTransactionCommand(); + check_shutdown_flag(); + + foreach (cell, analyze_tables) + { + autovac_table *tab = lfirst(cell); + + autovacuum_do_vac_analyze(tab); + } + foreach (cell, vacuum_tables) + { + autovac_table *tab = lfirst(cell); + + autovacuum_do_vac_analyze(tab); + } + } + + /* + * test_rel_for_autovac + * + * Check whether a table needs to be vacuumed or analyzed. Add it to the + * respective list if so. + * + * A table needs to be vacuumed if the number of tuples updated and deleted + * since the last vacuum exceeds a threshold. This threshold is calculated + * as + * + * threshold = vac_base_thresh + vac_scale_factor * reltuples + * + * For analyze, the same analysis is done, except that it additionally + * considers inserted tuples since last vacuum, and it uses anl_base_thresh + * and anl_scale_factor as inputs to the threshold equation. + * + * A table whose pg_autovacuum.enabled value is false, is automatically + * skipped. Thus autovacuum can be disabled for specific tables. + * + * A table whose {vac,anl}_base_thresh value is 0, takes the base value + * from the autovacuum_default_base GUC variable. Similarly, a + * {vac,anl}_scale_factor value of 0 is substituted with the value of + * autovacuum_default_scale GUC variable. XXX I assume this works + * for floating point values. + */ + static void + test_rel_for_autovac(PgStat_StatDBEntry *dbentry, HeapTuple tuple, + List **vacuum_tables, List **analyze_tables, + List **update_tables) + { + Form_pg_autovacuum avForm = (Form_pg_autovacuum) GETSTRUCT(tuple); + Relation rel; + int vac_base_thresh, + anl_base_thresh; + float4 reltuples, + vactuples, + anltuples, + vac_scale_factor, + anl_scale_factor, + vacthresh, + anlthresh, + vac_last_cnt, + anl_last_cnt; + bool found; + autovac_table *tab; + MemoryContext old_cxt; + + if (!avForm->enabled) + return; + + /* Get data for this table from the stats system. */ + PgStat_TableEntry *entry = hash_search(dbentry->tables, + &(avForm->vacrelid), + HASH_FIND, &found); + if (!found) + { + elog(DEBUG2, "table %u not found in stat hash", avForm->vacrelid); + return; + } + + /* + * If we can't find the table in the relcache, it must have been dropped. + * (It may also mean a corrupt cache but we won't deal with that here.) + */ + rel = RelationIdGetRelation(avForm->vacrelid); + if (!rel) + return; + + reltuples = rel->rd_rel->reltuples; + RelationClose(rel); + + old_cxt = MemoryContextSwitchTo(AutovacMemCxt); + + tab = (autovac_table *) palloc0(sizeof(autovac_table)); + + vactuples = entry->t_tuples_updated + entry->t_tuples_deleted; + anltuples = entry->t_tuples_inserted + entry->t_tuples_updated + + entry->t_tuples_deleted; + + vac_scale_factor = (avForm->vac_scale_factor == -1) ? + autovacuum_vac_scale : avForm->vac_scale_factor; + vac_base_thresh = (avForm->vac_base_thresh == -1) ? + autovacuum_vac_thresh : avForm->vac_base_thresh; + + anl_scale_factor = (avForm->anl_scale_factor == -1) ? + autovacuum_anl_scale : avForm->anl_scale_factor; + anl_base_thresh = (avForm->anl_base_thresh == -1) ? + autovacuum_anl_thresh : avForm->anl_base_thresh; + + vacthresh = (float4) vac_base_thresh + vac_scale_factor * reltuples; + anlthresh = (float4) anl_base_thresh + anl_scale_factor * reltuples; + + tab->update_vactuples = false; + tab->update_anltuples = false; + tab->operation = AVOperNone; + tab->oid = avForm->vacrelid; + + /* + * Consider stat-reset: if the differences are negative, reset the + * last-seen counters. + * + * It's OK if tab->operation is rewritten later, because other operations + * take care of updating the tuple if needed. + * + * We reset the counters to 0, not their current value, because that's + * what stat-reset resets them to. So we will operate on them closer to + * the reality. Also use these numbers to determine whether to vacuum + * on this iteration. + */ + if (vactuples - avForm->vac_last_cnt < 0) + { + tab->operation = AVOperUpdateCounts; + tab->vac_last_cnt = 0; + tab->update_vactuples = true; + vac_last_cnt = 0; + } + else + vac_last_cnt = avForm->vac_last_cnt; + + if (anltuples - avForm->anl_last_cnt < 0) + { + tab->operation = AVOperUpdateCounts; + tab->anl_last_cnt = 0; + tab->update_anltuples = true; + anl_last_cnt = 0; + } + else + anl_last_cnt = avForm->anl_last_cnt; + + elog(DEBUG2, "%s: vactuples diff %f (threshold %f), anltuples diff %f (threshold %f)", + get_rel_name(tab->oid), + vactuples - vac_last_cnt, vacthresh, + anltuples - anl_last_cnt, anlthresh); + + /* Determine whether this table needs vacuum or analyze. */ + if (vactuples - vac_last_cnt > vacthresh) + { + elog(DEBUG2, "this table for vacuum"); + + tab->operation = AVOperVacuumAnalyze; + tab->anl_last_cnt = anltuples; + tab->vac_last_cnt = vactuples; + tab->update_anltuples = true; + tab->update_vactuples = true; + } + else if (anltuples - anl_last_cnt > anlthresh) + { + elog(DEBUG2, "this table for analyze"); + tab->operation = AVOperAnalyze; + tab->anl_last_cnt = anltuples; + tab->update_anltuples = true; + tab->update_vactuples = false; + } + + switch (tab->operation) + { + case AVOperNone: + pfree(tab); + break; + case AVOperAnalyze: + *analyze_tables = lappend(*analyze_tables, tab); + break; + case AVOperVacuumAnalyze: + *vacuum_tables = lappend(*vacuum_tables, tab); + break; + case AVOperUpdateCounts: + *update_tables = lappend(*update_tables, tab); + break; + } + + MemoryContextSwitchTo(old_cxt); + } + + /* + * autovacuum_do_vac_analyze + * Vacuum or analyze one table. + * + * We must not be in a transaction already, and will not be when this + * routine finishes. + * + * It'd be nice to be able to use vacuum's multitransaction machinery, to do + * all collected tables at once. We'd need a new entrypoint there. + * It'd be also nice to be able to vacuum a table by Oid rather than RangeVar. + */ + static void + autovacuum_do_vac_analyze(autovac_table *tab) + { + Oid relid = tab->oid; + VacuumStmt vacstmt; + RangeVar *rv; + + Assert(tab->operation == AVOperVacuumAnalyze || + tab->operation == AVOperAnalyze); + + MemoryContextSwitchTo(AutovacMemCxt); + rv = makeNode(RangeVar); + StartTransactionCommand(); + + /* + * What a pain to have to do all this, when we could just pass + * the relid. + */ + rv->catalogname = NULL; + rv->schemaname = get_namespace_name(get_rel_namespace(relid)); + rv->relname = get_rel_name(relid); + rv->inhOpt = INH_NO; + rv->istemp = false; + rv->alias = NULL; + + /* + * Ugly. + */ + vacstmt.vacuum = (tab->operation == AVOperVacuumAnalyze); + vacstmt.full = false; + vacstmt.analyze = true; + vacstmt.freeze = false; + vacstmt.verbose = true; + vacstmt.relation = rv; + vacstmt.va_cols = NIL; + + elog(LOG, "autovacuum: %s \"%s\".\"%s\"", + (tab->operation == AVOperVacuumAnalyze) ? + "VACUUM ANALYZE" : "ANALYZE", + get_namespace_name(get_rel_namespace(relid)), + get_rel_name(relid)); + + vacuum(&vacstmt); + update_autovacuum_tuple(tab); + + /* Release locks */ + CommitTransactionCommand(); + check_shutdown_flag(); + } + + /* + * update_autovacuum_tuple + * Update a relation's vac_tuple_cnt and anl_tuple_cnt attributes + * in pg_autovacuum. + */ + static void + update_autovacuum_tuple(autovac_table *tab) + { + Relation avRel; + HeapTuple tuple; + Form_pg_autovacuum vacForm; + MemoryContext old_cxt; + + old_cxt = MemoryContextSwitchTo(AutovacMemCxt); + + avRel = heap_open(AutovacuumRelationId, RowExclusiveLock); + + tuple = SearchSysCacheCopy(AUTOVACRELID, + ObjectIdGetDatum(tab->oid), + 0, 0, 0); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "autovacuum cache lookup for relation %u failed", tab->oid); + + vacForm = (Form_pg_autovacuum) GETSTRUCT(tuple); + + if (tab->update_vactuples) + vacForm->vac_last_cnt = tab->vac_last_cnt; + if (tab->update_anltuples) + vacForm->anl_last_cnt = tab->anl_last_cnt; + + simple_heap_update(avRel, &tuple->t_self, tuple); + + CatalogUpdateIndexes(avRel, tuple); + + heap_freetuple(tuple); + heap_close(avRel, RowExclusiveLock); + + MemoryContextSwitchTo(old_cxt); + } + + /* + * AutoVacuumingActive + * Check GUC vars and report whether the autovacuum process should be + * running. + */ + bool + AutoVacuumingActive(void) + { + if (!autovacuum_start_daemon || !pgstat_collect_startcollector || + !pgstat_collect_tuplelevel) + return false; + return true; + } + + /* + * Annoy the user if he got it wrong. + */ + void + autovac_init(void) + { + if (!autovacuum_start_daemon) + return; + + if (!pgstat_collect_startcollector || !pgstat_collect_tuplelevel) + { + ereport(WARNING, + (errmsg("autovacuum not started because of misconfiguration"), + errhint("Enable options stats_start_collector and stats_row_level."))); + /* + * Set the GUC var so we don't fork autovacuum uselessly, and also to + * help debuggin. + */ + autovacuum_start_daemon = false; + } + } + + /* + * IsAutoVacuumProcess + * Return whether this process is an autovacuum process. + */ + bool + IsAutoVacuumProcess(void) + { + return am_autovacuum; + } + + /* + * check_shutdown_flag + * Check, and act upon, the shutdown flag set by the SIGUSR2 handler. + */ + static void + check_shutdown_flag(void) + { + if (shutdown_requested) + proc_exit(0); + } + + /* + * SIGQUIT: + * + * Some backend has bought the farm, so we need to stop what we're doing + * and exit. + */ + static void + autovac_sigquit_handler(SIGNAL_ARGS) + { + PG_SETMASK(&BlockSig); + + elog(NOTICE, "autovacuum: quick die requested"); + /* + * DO NOT proc_exit() -- see equivalent comment in bg_quickdie(). + */ + exit(1); + } + + /* + * SIGHUP: set flag to reread config file. + */ + static void + autovac_sighup_handler(SIGNAL_ARGS) + { + got_SIGHUP = true; + } + + /* + * SIGUSR2: set flag to shut down. + */ + static void + autovac_sigusr2_handler(SIGNAL_ARGS) + { + elog(LOG, "autovacuum: shutdown requested"); + + shutdown_requested = true; + } Index: src/backend/postmaster/pgstat.c =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/backend/postmaster/pgstat.c,v retrieving revision 1.97 diff -c -r1.97 pgstat.c *** src/backend/postmaster/pgstat.c 28 Jun 2005 05:08:59 -0000 1.97 --- src/backend/postmaster/pgstat.c 29 Jun 2005 20:13:40 -0000 *************** *** 38,43 **** --- 38,44 ---- #include "libpq/pqsignal.h" #include "mb/pg_wchar.h" #include "miscadmin.h" + #include "postmaster/autovacuum.h" #include "postmaster/fork_process.h" #include "postmaster/postmaster.h" #include "storage/backendid.h" *************** *** 176,181 **** --- 177,183 ---- static void pgstat_recv_bestart(PgStat_MsgBestart *msg, int len); static void pgstat_recv_beterm(PgStat_MsgBeterm *msg, int len); + static void pgstat_recv_autovac(PgStat_MsgAutovac *msg, int len); static void pgstat_recv_activity(PgStat_MsgActivity *msg, int len); static void pgstat_recv_tabstat(PgStat_MsgTabstat *msg, int len); static void pgstat_recv_tabpurge(PgStat_MsgTabpurge *msg, int len); *************** *** 631,636 **** --- 633,661 ---- } + /* ---------- + * pgstat_report_autovac() - + * + * Called from autovacuum.c to inform that a database was processed + * by the autovacuum daemon. + * ---------- + */ + void + pgstat_report_autovac(time_t m_time) + { + PgStat_MsgAutovac msg; + + if (pgStatSock < 0 || !AutoVacuumingActive()) + return; + + MemSet(&(msg.m_hdr), 0, sizeof(msg.m_hdr)); + msg.m_hdr.m_type = PGSTAT_MTYPE_AUTOVAC; + msg.m_databaseid = MyDatabaseId; + msg.m_time = m_time; + + pgstat_send(&msg, sizeof(msg)); + } + /* ------------------------------------------------------------ * Public functions used by backends follow *------------------------------------------------------------ *************** *** 1653,1658 **** --- 1678,1687 ---- pgstat_recv_beterm((PgStat_MsgBeterm *) &msg, nread); break; + case PGSTAT_MTYPE_AUTOVAC: + pgstat_recv_autovac((PgStat_MsgAutovac *) &msg, nread); + break; + case PGSTAT_MTYPE_TABSTAT: pgstat_recv_tabstat((PgStat_MsgTabstat *) &msg, nread); break; *************** *** 2072,2077 **** --- 2101,2107 ---- result->n_blocks_fetched = 0; result->n_blocks_hit = 0; result->destroy = 0; + result->last_autovac_time = 0; memset(&hash_ctl, 0, sizeof(hash_ctl)); hash_ctl.keysize = sizeof(Oid); *************** *** 2342,2353 **** int mcxt_flags; /* ! * If running in the collector we use the DynaHashCxt memory context. ! * If running in a backend, we use the TopTransactionContext instead, ! * so the caller must only know the last XactId when this call ! * happened to know if his tables are still valid or already gone! */ ! if (pgStatRunningInCollector) { use_mcxt = NULL; mcxt_flags = 0; --- 2372,2384 ---- int mcxt_flags; /* ! * If running in the collector or the autovacuum process, we use the ! * DynaHashCxt memory context. If running in a backend, we use the ! * TopTransactionContext instead, so the caller must only know the last ! * XactId when this call happened to know if his tables are still valid or ! * already gone! */ ! if (pgStatRunningInCollector || IsAutoVacuumProcess()) { use_mcxt = NULL; mcxt_flags = 0; *************** *** 2587,2604 **** * * Because we store the hash tables in TopTransactionContext, the result * is good for the entire current main transaction. */ static void backend_read_statsfile(void) { ! TransactionId topXid = GetTopTransactionId(); ! ! if (!TransactionIdEquals(pgStatDBHashXact, topXid)) { Assert(!pgStatRunningInCollector); ! pgstat_read_statsfile(&pgStatDBHash, MyDatabaseId, &pgStatBeTable, &pgStatNumBackends); ! pgStatDBHashXact = topXid; } } --- 2618,2653 ---- * * Because we store the hash tables in TopTransactionContext, the result * is good for the entire current main transaction. + * + * XXX inside the autovacuum process, the statfile is assumed to be valid + * "forever" (which is one iteration, within one database, basically. + * So this means we only consider the statistics as they were when the + * autovacuum iteration started.) */ static void backend_read_statsfile(void) { ! if (IsAutoVacuumProcess()) { Assert(!pgStatRunningInCollector); ! /* already read it? */ ! if (pgStatDBHash) ! return; ! pgstat_read_statsfile(&pgStatDBHash, InvalidOid, &pgStatBeTable, &pgStatNumBackends); ! } ! else ! { ! ! TransactionId topXid = GetTopTransactionId(); ! ! if (!TransactionIdEquals(pgStatDBHashXact, topXid)) ! { ! Assert(!pgStatRunningInCollector); ! pgstat_read_statsfile(&pgStatDBHash, MyDatabaseId, ! &pgStatBeTable, &pgStatNumBackends); ! pgStatDBHashXact = topXid; ! } } } *************** *** 2641,2646 **** --- 2690,2721 ---- pgstat_sub_backend(msg->m_hdr.m_procpid); } + /* ---------- + * pgstat_recv_autovac() - + * + * Process an autovacuum signalling message. + */ + static void + pgstat_recv_autovac(PgStat_MsgAutovac *msg, int len) + { + PgStat_StatDBEntry *dbentry; + + /* + * Lookup the database in the hashtable. + * + * XXX this creates the entry if it doesn't exist. Is this a problem? (We + * could leak an entry if we send an autovac message and the database is + * later destroyed, _and_ the messages are rearranged. Doesn't seem very + * likely though.) Not sure what to do about it. + */ + dbentry = pgstat_get_db_entry(msg->m_databaseid); + + /* + * Store the last autovacuum time in the database entry. + */ + dbentry->last_autovac_time = msg->m_time; + } + /* ---------- * pgstat_recv_activity() - Index: src/backend/postmaster/postmaster.c =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/backend/postmaster/postmaster.c,v retrieving revision 1.455 diff -c -r1.455 postmaster.c *** src/backend/postmaster/postmaster.c 28 Jun 2005 05:08:59 -0000 1.455 --- src/backend/postmaster/postmaster.c 29 Jun 2005 20:13:40 -0000 *************** *** 106,111 **** --- 106,112 ---- #include "miscadmin.h" #include "nodes/nodes.h" #include "pgstat.h" + #include "postmaster/autovacuum.h" #include "postmaster/fork_process.h" #include "postmaster/pgarch.h" #include "postmaster/postmaster.h" *************** *** 205,210 **** --- 206,212 ---- /* PIDs of special child processes; 0 when not running */ static pid_t StartupPID = 0, + AutoVacPID = 0, BgWriterPID = 0, PgArchPID = 0, PgStatPID = 0, *************** *** 869,875 **** * CAUTION: when changing this list, check for side-effects on the signal * handling setup of child processes. See tcop/postgres.c, * bootstrap/bootstrap.c, postmaster/bgwriter.c, postmaster/pgarch.c, ! * postmaster/pgstat.c, and postmaster/syslogger.c. */ pqinitmask(); PG_SETMASK(&BlockSig); --- 871,877 ---- * CAUTION: when changing this list, check for side-effects on the signal * handling setup of child processes. See tcop/postgres.c, * bootstrap/bootstrap.c, postmaster/bgwriter.c, postmaster/pgarch.c, ! * postmaster/pgstat.c, postmaster/autovacuum.c and postmaster/syslogger.c. */ pqinitmask(); PG_SETMASK(&BlockSig); *************** *** 927,932 **** --- 929,939 ---- StartTimeSec = GetCurrentAbsoluteTimeUsec(&StartTimeUSec); StartTime = AbsoluteTimeUsecToTimestampTz(StartTimeSec, StartTimeUSec); + /* + * Initialize the autovacuum daemon + */ + autovac_init(); + status = ServerLoop(); /* *************** *** 1259,1264 **** --- 1266,1281 ---- PgStatPID = pgstat_start(); /* + * Start a new autovacuum process, if there isn't one running already. + * (It'll die relatively quickly.) We check that it's not started + * too frequently in autovac_start. + */ + if (AutoVacPID == 0 && + StartupPID == 0 && !FatalError && Shutdown == NoShutdown && + AutoVacuumingActive()) + AutoVacPID = autovac_start(); + + /* * Touch the socket and lock file every 58 minutes, to * ensure that they are not removed by overzealous /tmp-cleaning * tasks. We assume no one runs cleaners with cutoff times of *************** *** 1819,1824 **** --- 1836,1843 ---- kill(PgArchPID, SIGHUP); if (SysLoggerPID != 0) kill(SysLoggerPID, SIGHUP); + if (AutoVacPID != 0) + kill(AutoVacPID, SIGHUP); /* PgStatPID does not currently need SIGHUP */ /* Reload authentication config files too */ *************** *** 1874,1879 **** --- 1893,1901 ---- */ if (StartupPID != 0 || FatalError) break; /* let reaper() handle this */ + /* Tell autovacuum to shut down */ + if (AutoVacPID != 0) + kill(AutoVacPID, SIGUSR2); /* Start the bgwriter if not running */ if (BgWriterPID == 0) BgWriterPID = StartBackgroundWriter(); *************** *** 1902,1907 **** --- 1924,1933 ---- ereport(LOG, (errmsg("received fast shutdown request"))); + /* Shut down autovacuum */ + if (AutoVacPID != 0) + kill(AutoVacPID, SIGQUIT); + if (DLGetHead(BackendList)) { if (!FatalError) *************** *** 1946,1951 **** --- 1972,1979 ---- */ ereport(LOG, (errmsg("received immediate shutdown request"))); + if (AutoVacPID != 0) + kill(AutoVacPID, SIGQUIT); if (StartupPID != 0) kill(StartupPID, SIGQUIT); if (BgWriterPID != 0) *************** *** 2141,2146 **** --- 2169,2188 ---- } /* + * Was it the autovacuum process? Ignore it -- it will be restarted + * at the next iteration of the postmaster's main loop, if necessary. + */ + if (AutoVacPID != 0 && pid == AutoVacPID) + { + AutoVacPID = 0; + if (exitstatus != 0) + LogChildExit(LOG, _("autovacuum process"), + pid, exitstatus); + elog(NOTICE, "autovacuum process ended"); + continue; + } + + /* * Else do standard backend child cleanup. */ CleanupBackend(pid, exitstatus); *************** *** 2153,2159 **** * StartupDataBase. (We can ignore the archiver and stats * processes here since they are not connected to shmem.) */ ! if (DLGetHead(BackendList) || StartupPID != 0 || BgWriterPID != 0) goto reaper_done; ereport(LOG, (errmsg("all server processes terminated; reinitializing"))); --- 2195,2202 ---- * StartupDataBase. (We can ignore the archiver and stats * processes here since they are not connected to shmem.) */ ! if (DLGetHead(BackendList) || StartupPID != 0 || BgWriterPID != 0 || ! AutoVacPID != 0) goto reaper_done; ereport(LOG, (errmsg("all server processes terminated; reinitializing"))); *************** *** 2170,2175 **** --- 2213,2221 ---- { if (DLGetHead(BackendList) || StartupPID != 0) goto reaper_done; + /* Tell autovacuum to shut down */ + if (AutoVacPID != 0) + kill(AutoVacPID, SIGUSR2); /* Start the bgwriter if not running */ if (BgWriterPID == 0) BgWriterPID = StartBackgroundWriter(); *************** *** 2336,2341 **** --- 2382,2388 ---- kill(PgStatPID, SIGQUIT); } + /* handle autovacuum crash */ /* We do NOT restart the syslogger */ FatalError = true; *************** *** 3155,3160 **** --- 3202,3208 ---- * can attach at the same address the postmaster used. */ if (strcmp(argv[1], "-forkbackend") == 0 || + strcmp(argv[1], "-forkautovac") == 0 || strcmp(argv[1], "-forkboot") == 0) PGSharedMemoryReAttach(); *************** *** 3206,3211 **** --- 3254,3270 ---- BootstrapMain(argc - 2, argv + 2); proc_exit(0); } + if (strcmp(arg[1], "-forkautovac") == 0) + { + /* Close the postmaster's sockets */ + ClosePostmasterPorts(false); + + /* Attached process to shared data structures */ + CreateSharedMemoryAndSemaphores(false, 0); + + AutoVacMain(argc - 2, argv + 2); + proc_exit(0); + } if (strcmp(argv[1], "-forkarch") == 0) { /* Close the postmaster's sockets */ Index: src/backend/utils/cache/syscache.c =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/backend/utils/cache/syscache.c,v retrieving revision 1.100 diff -c -r1.100 syscache.c *** src/backend/utils/cache/syscache.c 28 Jun 2005 05:09:01 -0000 1.100 --- src/backend/utils/cache/syscache.c 29 Jun 2005 20:17:04 -0000 *************** *** 29,34 **** --- 29,35 ---- #include "catalog/pg_amproc.h" #include "catalog/pg_authid.h" #include "catalog/pg_auth_members.h" + #include "catalog/pg_autovacuum.h" #include "catalog/pg_cast.h" #include "catalog/pg_conversion.h" #include "catalog/pg_index.h" *************** *** 212,217 **** --- 213,228 ---- 0, 0 }}, + {AutovacuumRelationId, /* AUTOVACRELID */ + AutovacuumRelidIndexId, + Anum_pg_autovacuum_vacrelid, + 1, + { + Anum_pg_autovacuum_vacrelid, + 0, + 0, + 0 + }}, { CastRelationId, /* CASTSOURCETARGET */ CastSourceTargetIndexId, Index: src/backend/utils/init/miscinit.c =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/backend/utils/init/miscinit.c,v retrieving revision 1.144 diff -c -r1.144 miscinit.c *** src/backend/utils/init/miscinit.c 28 Jun 2005 22:16:45 -0000 1.144 --- src/backend/utils/init/miscinit.c 29 Jun 2005 20:13:45 -0000 *************** *** 32,37 **** --- 32,38 ---- #include "catalog/pg_authid.h" #include "libpq/libpq-be.h" #include "miscadmin.h" + #include "postmaster/autovacuum.h" #include "storage/fd.h" #include "storage/ipc.h" #include "storage/pg_shmem.h" *************** *** 376,382 **** InitializeSessionUserIdStandalone(void) { /* This function should only be called in a single-user backend. */ ! AssertState(!IsUnderPostmaster); /* call only once */ AssertState(!OidIsValid(AuthenticatedUserId)); --- 377,383 ---- InitializeSessionUserIdStandalone(void) { /* This function should only be called in a single-user backend. */ ! AssertState(!IsUnderPostmaster || IsAutoVacuumProcess()); /* call only once */ AssertState(!OidIsValid(AuthenticatedUserId)); Index: src/backend/utils/init/postinit.c =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/backend/utils/init/postinit.c,v retrieving revision 1.151 diff -c -r1.151 postinit.c *** src/backend/utils/init/postinit.c 28 Jun 2005 19:51:23 -0000 1.151 --- src/backend/utils/init/postinit.c 29 Jun 2005 20:13:45 -0000 *************** *** 29,34 **** --- 29,35 ---- #include "libpq/hba.h" #include "mb/pg_wchar.h" #include "miscadmin.h" + #include "postmaster/autovacuum.h" #include "postmaster/postmaster.h" #include "storage/backendid.h" #include "storage/fd.h" *************** *** 268,274 **** * InitPostgres * Initialize POSTGRES. * ! * In bootstrap mode neither of the parameters are used. * * The return value indicates whether the userID is a superuser. (That * can only be tested inside a transaction, so we want to do it during --- 269,276 ---- * InitPostgres * Initialize POSTGRES. * ! * In bootstrap mode neither of the parameters are used. In autovacuum ! * mode, the username parameter is not used. * * The return value indicates whether the userID is a superuser. (That * can only be tested inside a transaction, so we want to do it during *************** *** 282,287 **** --- 284,290 ---- InitPostgres(const char *dbname, const char *username) { bool bootstrap = IsBootstrapProcessingMode(); + bool autovacuum = IsAutoVacuumProcess(); bool am_superuser; /* *************** *** 408,417 **** RelationCacheInitializePhase2(); /* ! * Figure out our postgres user id. In standalone mode we use a fixed ! * id, otherwise we figure it out from the authenticated user name. */ ! if (bootstrap) InitializeSessionUserIdStandalone(); else if (!IsUnderPostmaster) { --- 411,421 ---- RelationCacheInitializePhase2(); /* ! * Figure out our postgres user id. In standalone mode and in the ! * autovacuum process, we use a fixed id, otherwise we figure it out from ! * the authenticated user name. */ ! if (bootstrap || autovacuum) InitializeSessionUserIdStandalone(); else if (!IsUnderPostmaster) { *************** *** 447,453 **** /* * Check if user is a superuser. */ ! if (bootstrap) am_superuser = true; else am_superuser = superuser(); --- 451,457 ---- /* * Check if user is a superuser. */ ! if (bootstrap || autovacuum) am_superuser = true; else am_superuser = superuser(); Index: src/backend/utils/misc/guc.c =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/backend/utils/misc/guc.c,v retrieving revision 1.271 diff -c -r1.271 guc.c *** src/backend/utils/misc/guc.c 28 Jun 2005 05:09:02 -0000 1.271 --- src/backend/utils/misc/guc.c 29 Jun 2005 22:08:24 -0000 *************** *** 45,50 **** --- 45,51 ---- #include "optimizer/prep.h" #include "parser/parse_expr.h" #include "parser/parse_relation.h" + #include "postmaster/autovacuum.h" #include "postmaster/bgwriter.h" #include "postmaster/syslogger.h" #include "postmaster/postmaster.h" *************** *** 663,668 **** --- 664,677 ---- &pgstat_collect_blocklevel, false, NULL, NULL }, + { + {"autovacuum", PGC_SIGHUP, AUTOVACUUM, + gettext_noop("Starts the auto vacuum subprocess."), + NULL + }, + &autovacuum_start_daemon, + false, NULL, NULL + }, { {"trace_notify", PGC_USERSET, DEVELOPER_OPTIONS, *************** *** 1374,1379 **** --- 1383,1412 ---- &block_size, BLCKSZ, BLCKSZ, BLCKSZ, NULL, NULL }, + { + {"autovacuum_vacuum_threshold", PGC_SIGHUP, AUTOVACUUM, + gettext_noop("Minimum number of tuple updates or deletes prior to vacuum."), + NULL + }, + &autovacuum_vac_thresh, + 1000, 0, INT_MAX, NULL, NULL + }, + { + {"autovacuum_analyze_threshold", PGC_SIGHUP, AUTOVACUUM, + gettext_noop("Minimum number of tuple inserts, updates or deletes prior to analyze."), + NULL + }, + &autovacuum_anl_thresh, + 500, 0, INT_MAX, NULL, NULL + }, + { + {"autovacuum_naptime", PGC_SIGHUP, AUTOVACUUM, + gettext_noop("Time to sleep between autovacuum runs, in seconds."), + NULL + }, + &autovacuum_naptime, + 60, 0, INT_MAX, NULL, NULL + }, /* End-of-list marker */ { *************** *** 1472,1477 **** --- 1505,1528 ---- &phony_random_seed, 0.5, 0.0, 1.0, assign_random_seed, show_random_seed }, + { + {"autovacuum_vacuum_scale_factor", PGC_SIGHUP, AUTOVACUUM, + gettext_noop("Number of tuple updates or deletes prior to vacuum as a factor of reltuples."), + NULL + }, + &autovacuum_vac_scale, + /* FIXME -- figure good values for this */ + 0.4, 0.0, 100.0, NULL, NULL + }, + { + {"autovacuum_analyze_scale_factor", PGC_SIGHUP, AUTOVACUUM, + gettext_noop("Number of tuple inserts, updates or deletes prior to analyze as a factor of reltuples."), + NULL + }, + &autovacuum_anl_scale, + /* FIXME -- figure good values for this */ + 0.2, 0.0, 100.0, NULL, NULL + }, /* End-of-list marker */ { Index: src/backend/utils/misc/postgresql.conf.sample =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/backend/utils/misc/postgresql.conf.sample,v retrieving revision 1.148 diff -c -r1.148 postgresql.conf.sample *** src/backend/utils/misc/postgresql.conf.sample 26 Jun 2005 03:03:41 -0000 1.148 --- src/backend/utils/misc/postgresql.conf.sample 29 Jun 2005 18:37:34 -0000 *************** *** 278,283 **** --- 278,295 ---- #stats_row_level = false #stats_reset_on_server_start = true + #--------------------------------------------------------------------------- + # AUTOVACUUM PARAMETERS + #--------------------------------------------------------------------------- + + # FIXME - figure out good defaults for these + + #autovacuum = false + #autovacuum_naptime = 60 + #autovacuum_vacuum_threshold = 1000 + #autovacuum_analyze_threshold = 500 + #autovacuum_vacuum_scale_factor = 0.4 + #autovacuum_analyze_scale_factor = 0.2 #--------------------------------------------------------------------------- # CLIENT CONNECTION DEFAULTS Index: src/bin/initdb/initdb.c =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/bin/initdb/initdb.c,v retrieving revision 1.88 diff -c -r1.88 initdb.c *** src/bin/initdb/initdb.c 28 Jun 2005 15:38:12 -0000 1.88 --- src/bin/initdb/initdb.c 29 Jun 2005 21:04:53 -0000 *************** *** 173,178 **** --- 173,179 ---- static void get_set_pwd(void); static void unlimit_systables(void); static void setup_depend(void); + static void setup_autovacuum(void); static void setup_sysviews(void); static void setup_description(void); static void setup_conversion(void); *************** *** 1563,1568 **** --- 1564,1603 ---- } /* + * set up pg_autovacuum + */ + static void + setup_autovacuum(void) + { + PG_CMD_DECL; + char **line; + static char *pg_autovacuum_setup[] = + { + "DELETE FROM pg_autovacuum;\n", + "INSERT INTO pg_autovacuum SELECT oid, 't', -1, -1, 0, -1, -1, 0" + " FROM pg_class WHERE relkind = 'r';\n", + NULL + }; + + fputs(_("initializing pg_autovacuum ... "), stdout); + fflush(stdout); + + snprintf(cmd, sizeof(cmd), + "\"%s\" %s template1 >%s", + backend_exec, backend_options, + DEVNULL); + + PG_CMD_OPEN; + + for (line = pg_autovacuum_setup; *line != NULL; line++) + PG_CMD_PUTS(*line); + + PG_CMD_CLOSE; + + check_ok(); + } + + /* * set up system views */ static void *************** *** 2631,2636 **** --- 2666,2673 ---- setup_depend(); + setup_autovacuum(); + setup_sysviews(); setup_description(); Index: src/include/pgstat.h =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/include/pgstat.h,v retrieving revision 1.31 diff -c -r1.31 pgstat.h *** src/include/pgstat.h 28 Jun 2005 05:09:04 -0000 1.31 --- src/include/pgstat.h 29 Jun 2005 20:13:54 -0000 *************** *** 23,33 **** #define PGSTAT_MTYPE_DUMMY 0 #define PGSTAT_MTYPE_BESTART 1 #define PGSTAT_MTYPE_BETERM 2 ! #define PGSTAT_MTYPE_ACTIVITY 3 ! #define PGSTAT_MTYPE_TABSTAT 4 ! #define PGSTAT_MTYPE_TABPURGE 5 ! #define PGSTAT_MTYPE_DROPDB 6 ! #define PGSTAT_MTYPE_RESETCOUNTER 7 /* ---------- * The data type used for counters. --- 23,34 ---- #define PGSTAT_MTYPE_DUMMY 0 #define PGSTAT_MTYPE_BESTART 1 #define PGSTAT_MTYPE_BETERM 2 ! #define PGSTAT_MTYPE_AUTOVAC 3 ! #define PGSTAT_MTYPE_ACTIVITY 4 ! #define PGSTAT_MTYPE_TABSTAT 5 ! #define PGSTAT_MTYPE_TABPURGE 6 ! #define PGSTAT_MTYPE_DROPDB 7 ! #define PGSTAT_MTYPE_RESETCOUNTER 8 /* ---------- * The data type used for counters. *************** *** 115,120 **** --- 116,133 ---- } PgStat_MsgBeterm; /* ---------- + * PgStat_MsgAutovacuum Sent by the autovacuum daemon to signal + * that a database was processed + * ---------- + */ + typedef struct PgStat_MsgAutovac + { + PgStat_MsgHdr m_hdr; + Oid m_databaseid; + time_t m_time; + } PgStat_MsgAutovac; + + /* ---------- * PgStat_MsgActivity Sent by the backends when they start * to parse a query. * ---------- *************** *** 200,205 **** --- 213,219 ---- PgStat_MsgTabpurge msg_tabpurge; PgStat_MsgDropdb msg_dropdb; PgStat_MsgResetcounter msg_resetcounter; + PgStat_MsgAutovac msg_autovacuum; } PgStat_Msg; *************** *** 222,227 **** --- 236,242 ---- PgStat_Counter n_blocks_fetched; PgStat_Counter n_blocks_hit; int destroy; + time_t last_autovac_time; } PgStat_StatDBEntry; *************** *** 325,330 **** --- 340,346 ---- extern void pgstat_ping(void); extern void pgstat_report_activity(const char *what); extern void pgstat_report_tabstat(void); + extern void pgstat_report_autovac(time_t m_time); extern int pgstat_vacuum_tabstat(void); extern void pgstat_reset_counters(void); Index: src/include/catalog/indexing.h =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/include/catalog/indexing.h,v retrieving revision 1.88 diff -c -r1.88 indexing.h *** src/include/catalog/indexing.h 28 Jun 2005 05:09:04 -0000 1.88 --- src/include/catalog/indexing.h 29 Jun 2005 20:13:54 -0000 *************** *** 203,208 **** --- 203,210 ---- DECLARE_UNIQUE_INDEX(pg_type_typname_nsp_index,2704, on pg_type using btree(typname name_ops, typnamespace oid_ops)); #define TypeNameNspIndexId 2704 + DECLARE_UNIQUE_INDEX(pg_autovacuum_vacrelid_index,1250, on pg_autovacuum using btree(vacrelid oid_ops)); + #define AutovacuumRelidIndexId 1250 /* last step of initialization script: build the indexes declared above */ BUILD_INDICES Index: src/include/catalog/pg_autovacuum.h =================================================================== RCS file: src/include/catalog/pg_autovacuum.h diff -N src/include/catalog/pg_autovacuum.h *** /dev/null 1 Jan 1970 00:00:00 -0000 --- src/include/catalog/pg_autovacuum.h 28 Jun 2005 15:02:07 -0000 *************** *** 0 **** --- 1,64 ---- + /*------------------------------------------------------------------------- + * + * pg_autovacuum.h + * definition of the system "autovacuum" relation (pg_autovacuum) + * + * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * $PostgreSQL$ + * + *------------------------------------------------------------------------- + */ + #ifndef PG_AUTOVACUUM_H + #define PG_AUTOVACUUM_H + + /* ---------------- + * postgres.h contains the system type definitions and the + * CATALOG(), BOOTSTRAP and DATA() sugar words so this file + * can be read by both genbki.sh and the C compiler. + * ---------------- + */ + + /* ---------------- + * pg_autovacuum definition. cpp turns this into + * typedef struct FormData_pg_autovacuum + * ---------------- + */ + #define AutovacuumRelationId 1248 + CATALOG(pg_autovacuum,1248) BKI_WITHOUT_OIDS + { + Oid vacrelid; /* OID of table */ + bool enabled; /* enabled for this table? */ + int4 vac_base_thresh; /* base threshold value */ + float4 vac_scale_factor; /* reltuples scaling factor */ + float4 vac_last_cnt; /* actual value as of last vacuum */ + int4 anl_base_thresh; /* base threshold value */ + float4 anl_scale_factor; /* reltuples scaling factor */ + float4 anl_last_cnt; /* actual value as of last analyze */ + } FormData_pg_autovacuum; + + /* ---------------- + * Form_pg_autovacuum corresponds to a pointer to a tuple with + * the format of pg_autovacuum relation. + * ---------------- + */ + typedef FormData_pg_autovacuum *Form_pg_autovacuum; + + /* ---------------- + * compiler constants for pg_autovacuum + * ---------------- + */ + #define Natts_pg_autovacuum 8 + #define Anum_pg_autovacuum_vacrelid 1 + #define Anum_pg_autovacuum_enabled 2 + #define Anum_pg_autovacuum_vac_base_thresh 3 + #define Anum_pg_autovacuum_vac_scale_factor 4 + #define Anum_pg_autovacuum_vac_last_cnt 5 + #define Anum_pg_autovacuum_anl_base_thresh 6 + #define Anum_pg_autovacuum_anl_scale_factor 7 + #define Anum_pg_autovacuum_anl_last_cnt 8 + + /* There are no preloaded tuples in pg_autovacuum.h */ + + #endif /* PG_AUTOVACUUM_H */ Index: src/include/postmaster/autovacuum.h =================================================================== RCS file: src/include/postmaster/autovacuum.h diff -N src/include/postmaster/autovacuum.h *** /dev/null 1 Jan 1970 00:00:00 -0000 --- src/include/postmaster/autovacuum.h 28 Jun 2005 23:03:25 -0000 *************** *** 0 **** --- 1,28 ---- + /* + * autovacuum.h + * + * $PostgreSQL$ + */ + + #ifndef AUTOVACUUM_H + #define AUTOVACUUM_H + + /* Minimum time between autovacuum runs, in seconds */ + /* FIXME - use the naptime instead */ + #define AUTOVACUUM_LOOP_SLEEP 10 + + /* GUC variables */ + extern bool autovacuum_start_daemon; + extern int autovacuum_naptime; + extern int autovacuum_vac_thresh; + extern double autovacuum_vac_scale; + extern int autovacuum_anl_thresh; + extern double autovacuum_anl_scale; + + /* Function to start autovacuum process, called from postmaster */ + int autovac_start(void); + void autovac_init(void); + bool AutoVacuumingActive(void); + bool IsAutoVacuumProcess(void); + + #endif /* AUTOVACUUM_H */ Index: src/include/utils/guc_tables.h =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/include/utils/guc_tables.h,v retrieving revision 1.19 diff -c -r1.19 guc_tables.h *** src/include/utils/guc_tables.h 31 Dec 2004 22:03:46 -0000 1.19 --- src/include/utils/guc_tables.h 28 Jun 2005 14:16:20 -0000 *************** *** 62,67 **** --- 62,68 ---- STATS, STATS_MONITORING, STATS_COLLECTOR, + AUTOVACUUM, CLIENT_CONN, CLIENT_CONN_STATEMENT, CLIENT_CONN_LOCALE, Index: src/include/utils/syscache.h =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/include/utils/syscache.h,v retrieving revision 1.60 diff -c -r1.60 syscache.h *** src/include/utils/syscache.h 28 Jun 2005 05:09:13 -0000 1.60 --- src/include/utils/syscache.h 29 Jun 2005 20:19:20 -0000 *************** *** 40,67 **** #define AUTHMEMROLEMEM 9 #define AUTHNAME 10 #define AUTHOID 11 ! #define CASTSOURCETARGET 12 ! #define CLAAMNAMENSP 13 ! #define CLAOID 14 ! #define CONDEFAULT 15 ! #define CONNAMENSP 16 ! #define CONOID 17 ! #define INDEXRELID 18 ! #define INHRELID 19 ! #define LANGNAME 20 ! #define LANGOID 21 ! #define NAMESPACENAME 22 ! #define NAMESPACEOID 23 ! #define OPERNAMENSP 24 ! #define OPEROID 25 ! #define PROCNAMEARGSNSP 26 ! #define PROCOID 27 ! #define RELNAMENSP 28 ! #define RELOID 29 ! #define RULERELNAME 30 ! #define STATRELATT 31 ! #define TYPENAMENSP 32 ! #define TYPEOID 33 extern void InitCatalogCache(void); extern void InitCatalogCachePhase2(void); --- 40,68 ---- #define AUTHMEMROLEMEM 9 #define AUTHNAME 10 #define AUTHOID 11 ! #define AUTOVACRELID 12 ! #define CASTSOURCETARGET 13 ! #define CLAAMNAMENSP 14 ! #define CLAOID 15 ! #define CONDEFAULT 16 ! #define CONNAMENSP 17 ! #define CONOID 18 ! #define INDEXRELID 19 ! #define INHRELID 20 ! #define LANGNAME 21 ! #define LANGOID 22 ! #define NAMESPACENAME 23 ! #define NAMESPACEOID 24 ! #define OPERNAMENSP 25 ! #define OPEROID 26 ! #define PROCNAMEARGSNSP 27 ! #define PROCOID 28 ! #define RELNAMENSP 29 ! #define RELOID 30 ! #define RULERELNAME 31 ! #define STATRELATT 32 ! #define TYPENAMENSP 33 ! #define TYPEOID 34 extern void InitCatalogCache(void); extern void InitCatalogCachePhase2(void); Index: src/test/regress/expected/sanity_check.out =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/test/regress/expected/sanity_check.out,v retrieving revision 1.25 diff -c -r1.25 sanity_check.out *** src/test/regress/expected/sanity_check.out 28 Jun 2005 05:09:14 -0000 1.25 --- src/test/regress/expected/sanity_check.out 29 Jun 2005 21:50:02 -0000 *************** *** 39,44 **** --- 39,45 ---- pg_attribute | t pg_auth_members | t pg_authid | t + pg_autovacuum | t pg_cast | t pg_class | t pg_constraint | t *************** *** 63,69 **** shighway | t tenk1 | t tenk2 | t ! (53 rows) -- -- another sanity check: every system catalog that has OIDs should have --- 64,70 ---- shighway | t tenk1 | t tenk2 | t ! (54 rows) -- -- another sanity check: every system catalog that has OIDs should have