Index: access/transam/xlog.c =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/backend/access/transam/xlog.c,v retrieving revision 1.210 diff -c -r1.210 xlog.c *** access/transam/xlog.c 23 Jul 2005 15:31:16 -0000 1.210 --- access/transam/xlog.c 24 Jul 2005 17:55:51 -0000 *************** *** 465,471 **** TimeLineID endTLI, uint32 endLogId, uint32 endLogSeg); static void WriteControlFile(void); - static void ReadControlFile(void); static char *str_time(time_t tnow); static void issue_xlog_fsync(void); --- 465,470 ---- *************** *** 3383,3390 **** errmsg("could not close control file: %m"))); } ! static void ! ReadControlFile(void) { pg_crc32 crc; int fd; --- 3382,3394 ---- errmsg("could not close control file: %m"))); } ! /* ! * Read and verify the control file, filling the ControlFile struct. ! * ! * If nextXid is not NULL, the latest Checkpoint's nextXid is returned. ! */ ! void ! ReadControlFile(TransactionId *nextXid) { pg_crc32 crc; int fd; *************** *** 3525,3530 **** --- 3529,3537 ---- ControlFile->lc_ctype), errhint("It looks like you need to initdb or install locale support."))); + if (PointerIsValid(nextXid)) + *nextXid = ControlFile->checkPointCopy.nextXid; + /* Make the fixed locale settings visible as GUC variables, too */ SetConfigOption("lc_collate", ControlFile->lc_collate, PGC_INTERNAL, PGC_S_OVERRIDE); *************** *** 3650,3656 **** * for the reasons why). */ if (!IsBootstrapProcessingMode()) ! ReadControlFile(); } /* --- 3657,3663 ---- * for the reasons why). */ if (!IsBootstrapProcessingMode()) ! ReadControlFile(NULL); } /* *************** *** 4232,4238 **** * Note: in most control paths, *ControlFile is already valid and we need * not do ReadControlFile() here, but might as well do it to be sure. */ ! ReadControlFile(); if (ControlFile->logSeg == 0 || ControlFile->state < DB_SHUTDOWNED || --- 4239,4245 ---- * Note: in most control paths, *ControlFile is already valid and we need * not do ReadControlFile() here, but might as well do it to be sure. */ ! ReadControlFile(NULL); if (ControlFile->logSeg == 0 || ControlFile->state < DB_SHUTDOWNED || Index: commands/analyze.c =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/backend/commands/analyze.c,v retrieving revision 1.87 diff -c -r1.87 analyze.c *** commands/analyze.c 14 Jul 2005 05:13:39 -0000 1.87 --- commands/analyze.c 26 Jul 2005 03:51:45 -0000 *************** *** 317,323 **** * a zero-column table. */ if (!vacstmt->vacuum) ! pgstat_report_analyze(RelationGetRelid(onerel), 0, 0); vac_close_indexes(nindexes, Irel, AccessShareLock); relation_close(onerel, AccessShareLock); --- 317,325 ---- * a zero-column table. */ if (!vacstmt->vacuum) ! pgstat_report_analyze(RelationGetRelid(onerel), ! onerel->rd_rel->relisshared, ! 0, 0); vac_close_indexes(nindexes, Irel, AccessShareLock); relation_close(onerel, AccessShareLock); *************** *** 436,443 **** } /* report results to the stats collector, too */ ! pgstat_report_analyze(RelationGetRelid(onerel), totalrows, ! totaldeadrows); } /* Done with indexes */ --- 438,446 ---- } /* report results to the stats collector, too */ ! pgstat_report_analyze(RelationGetRelid(onerel), ! onerel->rd_rel->relisshared, ! totalrows, totaldeadrows); } /* Done with indexes */ Index: commands/vacuum.c =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/backend/commands/vacuum.c,v retrieving revision 1.311 diff -c -r1.311 vacuum.c *** commands/vacuum.c 14 Jul 2005 05:13:39 -0000 1.311 --- commands/vacuum.c 24 Jul 2005 15:34:23 -0000 *************** *** 41,46 **** --- 41,47 ---- #include "tcop/pquery.h" #include "utils/acl.h" #include "utils/builtins.h" + #include "utils/flatfiles.h" #include "utils/fmgroids.h" #include "utils/inval.h" #include "utils/lsyscache.h" *************** *** 712,718 **** * vac_update_dbstats() -- update statistics for one database * * Update the whole-database statistics that are kept in its pg_database ! * row. * * We violate no-overwrite semantics here by storing new values for the * statistics columns directly into the tuple that's already on the page. --- 713,719 ---- * vac_update_dbstats() -- update statistics for one database * * Update the whole-database statistics that are kept in its pg_database ! * row, and the flat-file copy of pg_database. * * We violate no-overwrite semantics here by storing new values for the * statistics columns directly into the tuple that's already on the page. *************** *** 721,728 **** * * This routine is shared by full and lazy VACUUM. Note that it is only * applied after a database-wide VACUUM operation. - * - * Note that we don't bother to update the flat-file copy of pg_database. */ static void vac_update_dbstats(Oid dbid, --- 722,727 ---- *************** *** 768,773 **** --- 767,775 ---- heap_endscan(scan); heap_close(relation, RowExclusiveLock); + + /* Mark the flat-file for update at commit */ + database_file_update_needed(); } *************** *** 1165,1172 **** vacrelstats->rel_tuples, vacrelstats->hasindex); /* report results to the stats collector, too */ ! pgstat_report_vacuum(RelationGetRelid(onerel), vacstmt->analyze, ! vacrelstats->rel_tuples); } --- 1167,1174 ---- vacrelstats->rel_tuples, vacrelstats->hasindex); /* report results to the stats collector, too */ ! pgstat_report_vacuum(RelationGetRelid(onerel), onerel->rd_rel->relisshared, ! vacstmt->analyze, vacrelstats->rel_tuples); } Index: commands/vacuumlazy.c =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/backend/commands/vacuumlazy.c,v retrieving revision 1.55 diff -c -r1.55 vacuumlazy.c *** commands/vacuumlazy.c 14 Jul 2005 05:13:40 -0000 1.55 --- commands/vacuumlazy.c 22 Jul 2005 01:08:51 -0000 *************** *** 182,189 **** hasindex); /* report results to the stats collector, too */ ! pgstat_report_vacuum(RelationGetRelid(onerel), vacstmt->analyze, ! vacrelstats->rel_tuples); } --- 182,189 ---- hasindex); /* report results to the stats collector, too */ ! pgstat_report_vacuum(RelationGetRelid(onerel), onerel->rd_rel->relisshared, ! vacstmt->analyze, vacrelstats->rel_tuples); } Index: libpq/hba.c =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/backend/libpq/hba.c,v retrieving revision 1.144 diff -c -r1.144 hba.c *** libpq/hba.c 28 Jun 2005 22:16:45 -0000 1.144 --- libpq/hba.c 22 Jul 2005 01:08:51 -0000 *************** *** 39,44 **** --- 39,45 ---- #define atooid(x) ((Oid) strtoul((x), NULL, 10)) + #define atoxid(x) ((TransactionId) strtoul((x), NULL, 10)) /* Max size of username ident server can return */ #define IDENT_USERNAME_MAX 512 *************** *** 1002,1014 **** * dbname: gets database name (must be of size NAMEDATALEN bytes) * dboid: gets database OID * dbtablespace: gets database's default tablespace's OID * * This is not much related to the other functions in hba.c, but we put it * here because it uses the next_token() infrastructure. */ bool read_pg_database_line(FILE *fp, char *dbname, ! Oid *dboid, Oid *dbtablespace) { char buf[MAX_TOKEN]; --- 1003,1017 ---- * dbname: gets database name (must be of size NAMEDATALEN bytes) * dboid: gets database OID * dbtablespace: gets database's default tablespace's OID + * dbfrozenxid: get database's frozen Xid * * This is not much related to the other functions in hba.c, but we put it * here because it uses the next_token() infrastructure. */ bool read_pg_database_line(FILE *fp, char *dbname, ! Oid *dboid, Oid *dbtablespace, ! TransactionId *dbfrozenxid) { char buf[MAX_TOKEN]; *************** *** 1027,1036 **** if (!isdigit((unsigned char) buf[0])) elog(FATAL, "bad data in flat pg_database file"); *dbtablespace = atooid(buf); - /* discard datfrozenxid */ next_token(fp, buf, sizeof(buf)); if (!isdigit((unsigned char) buf[0])) elog(FATAL, "bad data in flat pg_database file"); /* expect EOL next */ if (next_token(fp, buf, sizeof(buf))) elog(FATAL, "bad data in flat pg_database file"); --- 1030,1039 ---- if (!isdigit((unsigned char) buf[0])) elog(FATAL, "bad data in flat pg_database file"); *dbtablespace = atooid(buf); next_token(fp, buf, sizeof(buf)); if (!isdigit((unsigned char) buf[0])) elog(FATAL, "bad data in flat pg_database file"); + *dbfrozenxid = atoxid(buf); /* expect EOL next */ if (next_token(fp, buf, sizeof(buf))) elog(FATAL, "bad data in flat pg_database file"); Index: postmaster/Makefile =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/backend/postmaster/Makefile,v retrieving revision 1.21 diff -c -r1.21 Makefile *** postmaster/Makefile 14 Jul 2005 05:13:40 -0000 1.21 --- postmaster/Makefile 25 Jul 2005 23:02:00 -0000 *************** *** 17,22 **** --- 17,25 ---- all: SUBSYS.o + statdump: statdump.c + $(CC) $(CFLAGS) -I$(top_srcdir)/src/include -I$(top_builddir)/src/include $< + SUBSYS.o: $(OBJS) $(LD) $(LDREL) $(LDOUT) SUBSYS.o $(OBJS) Index: postmaster/autovacuum.c =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/backend/postmaster/autovacuum.c,v retrieving revision 1.1 diff -c -r1.1 autovacuum.c *** postmaster/autovacuum.c 14 Jul 2005 05:13:40 -0000 1.1 --- postmaster/autovacuum.c 26 Jul 2005 04:02:19 -0000 *************** *** 23,29 **** --- 23,31 ---- #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" *************** *** 41,46 **** --- 43,49 ---- #include "tcop/tcopprot.h" #include "utils/flatfiles.h" #include "utils/fmgroids.h" + #include "utils/lsyscache.h" #include "utils/memutils.h" #include "utils/ps_status.h" #include "utils/relcache.h" *************** *** 68,74 **** --- 71,79 ---- { Oid oid; char *name; + TransactionId frozenxid; PgStat_StatDBEntry *entry; + int32 age; } autovac_dbase; *************** *** 76,83 **** static pid_t autovac_forkexec(void); #endif NON_EXEC_STATIC void AutoVacMain(int argc, char *argv[]); ! static void autovac_check_wraparound(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, --- 81,87 ---- static pid_t autovac_forkexec(void); #endif NON_EXEC_STATIC void AutoVacMain(int argc, char *argv[]); ! static void do_autovacuum(bool whole_db, 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, *************** *** 194,200 **** --- 198,206 ---- ListCell *cell; List *dblist; autovac_dbase *db; + bool whole_db = false; sigjmp_buf local_sigjmp_buf; + TransactionId nextXid; /* we are a postmaster subprocess now */ IsUnderPostmaster = true; *************** *** 269,289 **** /* * 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 info about whether it needs * vacuuming before connecting to it. Perhaps look through the pgstats ! * data for the database's tables? ! * ! * XXX it is NOT good that we totally ignore databases that have no ! * pgstats entry ... */ db = NULL; foreach(cell, dblist) { ! autovac_dbase *tmp = lfirst(cell); tmp->entry = pgstat_fetch_stat_dbentry(tmp->oid); if (!tmp->entry) continue; --- 275,335 ---- /* * 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; + /* + * Get the next Xid that was current as of the last checkpoint. + * We will use it to determine whether databases are about to need + * database-wide vacuums. + */ + ReadControlFile(&nextXid); + foreach(cell, dblist) { ! autovac_dbase *tmp = lfirst(cell); ! bool this_whole_db; ! ! /* ! * We ! * look for the database that most urgently needs a database-wide ! * vacuum. As soon as one is found, any other database is ! * ignored. We decide to vacuum a little earlier than when vacuum.c's ! * vac_truncate_clog() would decide start giving warnings. ! */ ! tmp->age = (int32) (nextXid - tmp->frozenxid); ! this_whole_db = (tmp->age > (int32) ((MaxTransactionId >> 3) * 3)); ! ! if (whole_db || this_whole_db) ! { ! if (!this_whole_db) ! continue; ! if (!db || tmp->age > db->age) ! { ! db = tmp; ! whole_db = true; ! } ! continue; ! } + /* + * 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; *************** *** 316,322 **** /* * And do an appropriate amount of work on it */ ! do_autovacuum(db->entry); } /* One iteration done, go away */ --- 362,368 ---- /* * And do an appropriate amount of work on it */ ! do_autovacuum(whole_db, db->entry); } /* One iteration done, go away */ *************** *** 338,343 **** --- 384,390 ---- FILE *db_file; Oid db_id; Oid db_tablespace; + TransactionId db_frozenxid; filename = database_getflatfilename(); db_file = AllocateFile(filename, "r"); *************** *** 346,352 **** (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; --- 393,400 ---- (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)) { autovac_dbase *db; *************** *** 354,359 **** --- 402,408 ---- db->oid = db_id; db->name = pstrdup(thisname); + db->frozenxid = db_frozenxid; /* this gets set later */ db->entry = NULL; *************** *** 369,374 **** --- 418,429 ---- /* * Process a database. * + * If whole_db is true, the database is processed as a whole, and the + * dbentry parameter is ignored. If it's false, dbentry must hold a valid + * pointer to the database entry in the stats databases' hash table, and + * it will be used to determine whether vacuum or analyze is needed on a + * table per table basis. + * * Note that test_rel_for_autovac generates two separate lists, one for * vacuum and other for analyze. This is to facilitate processing all * analyzes first, and then all vacuums. *************** *** 377,383 **** * order not to ignore shutdown commands for too long. */ static void ! do_autovacuum(PgStat_StatDBEntry *dbentry) { Relation classRel, avRel; --- 432,438 ---- * order not to ignore shutdown commands for too long. */ static void ! do_autovacuum(bool whole_db, PgStat_StatDBEntry *dbentry) { Relation classRel, avRel; *************** *** 387,392 **** --- 442,449 ---- *analyze_tables = NIL; MemoryContext AutovacMemCxt; + Assert(whole_db || PointerIsValid(dbentry)); + /* Memory context where cross-transaction state is stored */ AutovacMemCxt = AllocSetContextCreate(TopMemoryContext, "Autovacuum context", *************** *** 405,485 **** */ MemoryContextSwitchTo(AutovacMemCxt); ! /* ! * If this database is old enough to need a whole-database VACUUM, ! * don't bother checking each table. If that happens, this function ! * will issue the VACUUM command and won't return. ! */ ! autovac_check_wraparound(); ! ! CHECK_FOR_INTERRUPTS(); ! ! classRel = heap_open(RelationRelationId, AccessShareLock); ! avRel = heap_open(AutovacuumRelationId, AccessShareLock); ! ! relScan = heap_beginscan(classRel, SnapshotNow, 0, NULL); ! ! /* Scan pg_class looking for tables to vacuum */ ! while ((tuple = heap_getnext(relScan, ForwardScanDirection)) != NULL) { ! Form_pg_class classForm = (Form_pg_class) GETSTRUCT(tuple); ! Form_pg_autovacuum avForm = NULL; ! PgStat_StatTabEntry *tabentry; ! SysScanDesc avScan; ! HeapTuple avTup; ! ScanKeyData entry[1]; ! Oid relid; ! ! /* Skip non-table entries. */ ! /* XXX possibly allow RELKIND_TOASTVALUE entries here too? */ ! if (classForm->relkind != RELKIND_RELATION) ! continue; ! ! relid = HeapTupleGetOid(tuple); ! ! /* See if we have a pg_autovacuum entry for this relation. */ ! ScanKeyInit(&entry[0], ! Anum_pg_autovacuum_vacrelid, ! BTEqualStrategyNumber, F_OIDEQ, ! ObjectIdGetDatum(relid)); ! ! avScan = systable_beginscan(avRel, AutovacuumRelidIndexId, true, ! SnapshotNow, 1, entry); ! ! avTup = systable_getnext(avScan); ! ! if (HeapTupleIsValid(avTup)) ! avForm = (Form_pg_autovacuum) GETSTRUCT(avTup); ! ! tabentry = hash_search(dbentry->tables, &relid, ! HASH_FIND, NULL); ! test_rel_for_autovac(relid, tabentry, classForm, avForm, ! &vacuum_tables, &analyze_tables); ! systable_endscan(avScan); ! } ! heap_endscan(relScan); ! heap_close(avRel, AccessShareLock); ! heap_close(classRel, AccessShareLock); ! CHECK_FOR_INTERRUPTS(); ! /* ! * Perform operations on collected tables. ! */ ! if (analyze_tables) ! autovacuum_do_vac_analyze(analyze_tables, false); ! CHECK_FOR_INTERRUPTS(); ! /* get back to proper context */ ! MemoryContextSwitchTo(AutovacMemCxt); ! if (vacuum_tables) ! autovacuum_do_vac_analyze(vacuum_tables, true); /* Finally close out the last transaction. */ CommitTransactionCommand(); --- 462,556 ---- */ MemoryContextSwitchTo(AutovacMemCxt); ! if (whole_db) { ! elog(DEBUG2, "autovacuum: VACUUM ANALYZE whole database"); ! autovacuum_do_vac_analyze(NIL, true); ! } ! else ! { ! /* the hash entry where pgstat stores shared relations */ ! PgStat_StatDBEntry *shared = pgstat_fetch_stat_dbentry(InvalidOid); ! CHECK_FOR_INTERRUPTS(); ! classRel = heap_open(RelationRelationId, AccessShareLock); ! avRel = heap_open(AutovacuumRelationId, AccessShareLock); ! relScan = heap_beginscan(classRel, SnapshotNow, 0, NULL); ! ! /* Scan pg_class looking for tables to vacuum */ ! while ((tuple = heap_getnext(relScan, ForwardScanDirection)) != NULL) ! { ! Form_pg_class classForm = (Form_pg_class) GETSTRUCT(tuple); ! Form_pg_autovacuum avForm = NULL; ! PgStat_StatTabEntry *tabentry; ! SysScanDesc avScan; ! HeapTuple avTup; ! ScanKeyData entry[1]; ! Oid relid; ! Oid nspOid; ! ! /* Skip non-table entries. */ ! /* XXX possibly allow RELKIND_TOASTVALUE entries here too? */ ! if (classForm->relkind != RELKIND_RELATION) ! continue; ! ! relid = HeapTupleGetOid(tuple); ! ! /* Skip temp tables (i.e. those in temp namespaces) */ ! nspOid = get_rel_namespace(relid); ! if (isTempNamespace(nspOid)) ! continue; ! ! /* See if we have a pg_autovacuum entry for this relation. */ ! ScanKeyInit(&entry[0], ! Anum_pg_autovacuum_vacrelid, ! BTEqualStrategyNumber, F_OIDEQ, ! ObjectIdGetDatum(relid)); ! ! avScan = systable_beginscan(avRel, AutovacuumRelidIndexId, true, ! SnapshotNow, 1, entry); ! ! avTup = systable_getnext(avScan); ! ! if (HeapTupleIsValid(avTup)) ! avForm = (Form_pg_autovacuum) GETSTRUCT(avTup); ! ! if (classForm->relisshared && PointerIsValid(shared)) ! tabentry = hash_search(shared->tables, &relid, ! HASH_FIND, NULL); ! else ! tabentry = hash_search(dbentry->tables, &relid, ! HASH_FIND, NULL); ! ! test_rel_for_autovac(relid, tabentry, classForm, avForm, ! &vacuum_tables, &analyze_tables); ! ! systable_endscan(avScan); ! } ! ! heap_endscan(relScan); ! heap_close(avRel, AccessShareLock); ! heap_close(classRel, AccessShareLock); ! CHECK_FOR_INTERRUPTS(); ! /* ! * Perform operations on collected tables. ! */ ! if (analyze_tables) ! autovacuum_do_vac_analyze(analyze_tables, false); ! CHECK_FOR_INTERRUPTS(); ! /* get back to proper context */ ! MemoryContextSwitchTo(AutovacMemCxt); ! if (vacuum_tables) ! autovacuum_do_vac_analyze(vacuum_tables, true); ! } /* Finally close out the last transaction. */ CommitTransactionCommand(); *************** *** 503,509 **** * analyze. This is asymmetric to the VACUUM case. * * A table whose pg_autovacuum.enabled value is false, is automatically ! * skipped. Thus autovacuum can be disabled for specific tables. * * A table whose vac_base_thresh value is <0 takes the base value from the * autovacuum_vacuum_threshold GUC variable. Similarly, a vac_scale_factor --- 574,582 ---- * analyze. This is asymmetric to the VACUUM case. * * A table whose pg_autovacuum.enabled value is false, is automatically ! * skipped. Thus autovacuum can be disabled for specific tables. Also, ! * when the stats collector does not have data about a table, it will be ! * skipped. * * A table whose vac_base_thresh value is <0 takes the base value from the * autovacuum_vacuum_threshold GUC variable. Similarly, a vac_scale_factor *************** *** 533,558 **** /* User disabled it in pg_autovacuum? */ if (avForm && !avForm->enabled) return; rel = RelationIdGetRelation(relid); /* The table was recently dropped? */ ! if (rel == NULL) ! return; ! ! /* Not found in stat hash? */ ! if (tabentry == NULL) ! { ! /* ! * Analyze this table. It will emit a stat message for the ! * collector that will initialize the entry for the next time ! * around, so we won't have to guess again. ! */ ! elog(DEBUG2, "table %s not known to stat system, will ANALYZE", ! RelationGetRelationName(rel)); ! *analyze_tables = lappend_oid(*analyze_tables, relid); ! RelationClose(rel); return; - } reltuples = rel->rd_rel->reltuples; vactuples = tabentry->n_dead_tuples; --- 606,623 ---- /* User disabled it in pg_autovacuum? */ if (avForm && !avForm->enabled) return; + /* + * Skip a table not found in stat hash. If it's not acted upon, + * there's no need to vacuum it. (Note that database-level check + * will take care of Xid wraparound.) + */ + if (!PointerIsValid(tabentry)) + return; rel = RelationIdGetRelation(relid); /* The table was recently dropped? */ ! if (!PointerIsValid(rel)) return; reltuples = rel->rd_rel->reltuples; vactuples = tabentry->n_dead_tuples; *************** *** 607,615 **** } else if (anltuples > anlthresh) { ! elog(DEBUG2, "will ANALYZE %s", ! RelationGetRelationName(rel)); ! *analyze_tables = lappend_oid(*analyze_tables, relid); } RelationClose(rel); --- 672,684 ---- } else if (anltuples > anlthresh) { ! /* ANALYZE refuses to work with pg_statistics */ ! if (relid != StatisticRelationId) ! { ! elog(DEBUG2, "will ANALYZE %s", ! RelationGetRelationName(rel)); ! *analyze_tables = lappend_oid(*analyze_tables, relid); ! } } RelationClose(rel); *************** *** 646,706 **** } /* - * autovac_check_wraparound - * Check database Xid wraparound - * - * Check pg_database to see if the last database-wide VACUUM was too long ago, - * and issue one now if so. If this comes to pass, we do not return, as there - * is no point in checking individual tables -- they will all get vacuumed - * anyway. - */ - static void - autovac_check_wraparound(void) - { - Relation relation; - ScanKeyData entry[1]; - HeapScanDesc scan; - HeapTuple tuple; - Form_pg_database dbform; - int32 age; - bool whole_db; - - relation = heap_open(DatabaseRelationId, AccessShareLock); - - /* Must use a heap scan, since there's no syscache for pg_database */ - ScanKeyInit(&entry[0], - ObjectIdAttributeNumber, - BTEqualStrategyNumber, F_OIDEQ, - ObjectIdGetDatum(MyDatabaseId)); - - scan = heap_beginscan(relation, SnapshotNow, 1, entry); - - tuple = heap_getnext(scan, ForwardScanDirection); - - if (!HeapTupleIsValid(tuple)) - elog(ERROR, "could not find tuple for database %u", MyDatabaseId); - - dbform = (Form_pg_database) GETSTRUCT(tuple); - - /* - * We decide to vacuum at the same point where vacuum.c's - * vac_truncate_clog() would decide to start giving warnings. - */ - age = (int32) (GetTopTransactionId() - dbform->datfrozenxid); - whole_db = (age > (int32) ((MaxTransactionId >> 3) * 3)); - - heap_endscan(scan); - heap_close(relation, AccessShareLock); - - if (whole_db) - { - elog(LOG, "autovacuum: VACUUM ANALYZE whole database"); - autovacuum_do_vac_analyze(NIL, true); - proc_exit(0); - } - } - - /* * AutoVacuumingActive * Check GUC vars and report whether the autovacuum process should be * running. --- 715,720 ---- Index: postmaster/pgstat.c =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/backend/postmaster/pgstat.c,v retrieving revision 1.101 diff -c -r1.101 pgstat.c *** postmaster/pgstat.c 24 Jul 2005 00:33:28 -0000 1.101 --- postmaster/pgstat.c 26 Jul 2005 04:05:14 -0000 *************** *** 119,130 **** static bool pgStatRunningInCollector = FALSE; ! static int pgStatTabstatAlloc = 0; ! static int pgStatTabstatUsed = 0; ! static PgStat_MsgTabstat **pgStatTabstatMessages = NULL; #define TABSTAT_QUANTUM 4 /* we alloc this many at a time */ static int pgStatXactCommit = 0; static int pgStatXactRollback = 0; --- 119,141 ---- static bool pgStatRunningInCollector = FALSE; ! /* ! * Place where backends store per-table info to be sent to the collector. ! * We store shared relations separately from non-shared ones, to be able to ! * send them in separate messages. ! */ ! typedef struct TabStatArray ! { ! int tsa_alloc; /* num allocated */ ! int tsa_used; /* num actually used */ ! PgStat_MsgTabstat **tsa_messages; /* the array itself */ ! } TabStatArray; #define TABSTAT_QUANTUM 4 /* we alloc this many at a time */ + static TabStatArray RegularTabStat = { 0, 0, NULL }; + static TabStatArray SharedTabStat = { 0, 0, NULL }; + static int pgStatXactCommit = 0; static int pgStatXactRollback = 0; *************** *** 158,164 **** static void pgstat_die(SIGNAL_ARGS); static void pgstat_beshutdown_hook(int code, Datum arg); ! static PgStat_StatDBEntry *pgstat_get_db_entry(Oid databaseid); static int pgstat_add_backend(PgStat_MsgHdr *msg); static void pgstat_sub_backend(int procpid); static void pgstat_drop_database(Oid databaseid); --- 169,175 ---- static void pgstat_die(SIGNAL_ARGS); static void pgstat_beshutdown_hook(int code, Datum arg); ! static PgStat_StatDBEntry *pgstat_get_db_entry(Oid databaseid, bool create); static int pgstat_add_backend(PgStat_MsgHdr *msg); static void pgstat_sub_backend(int procpid); static void pgstat_drop_database(Oid databaseid); *************** *** 614,619 **** --- 625,631 ---- if (pgStatSock < 0) return; + /* can't use pgstat_setheader() because it's not called in a backend */ MemSet(&(msg.m_hdr), 0, sizeof(msg.m_hdr)); msg.m_hdr.m_type = PGSTAT_MTYPE_BETERM; msg.m_hdr.m_procpid = pid; *************** *** 684,690 **** * --------- */ void ! pgstat_report_vacuum(Oid tableoid, bool analyze, PgStat_Counter tuples) { PgStat_MsgVacuum msg; --- 696,703 ---- * --------- */ void ! pgstat_report_vacuum(Oid tableoid, bool shared, bool analyze, ! PgStat_Counter tuples) { PgStat_MsgVacuum msg; *************** *** 692,698 **** return; pgstat_setheader(&msg.m_hdr, PGSTAT_MTYPE_VACUUM); ! msg.m_databaseid = MyDatabaseId; msg.m_tableoid = tableoid; msg.m_analyze = analyze; msg.m_tuples = tuples; --- 705,711 ---- return; pgstat_setheader(&msg.m_hdr, PGSTAT_MTYPE_VACUUM); ! msg.m_databaseid = shared ? InvalidOid : MyDatabaseId; msg.m_tableoid = tableoid; msg.m_analyze = analyze; msg.m_tuples = tuples; *************** *** 706,712 **** * -------- */ void ! pgstat_report_analyze(Oid tableoid, PgStat_Counter livetuples, PgStat_Counter deadtuples) { PgStat_MsgAnalyze msg; --- 719,725 ---- * -------- */ void ! pgstat_report_analyze(Oid tableoid, bool shared, PgStat_Counter livetuples, PgStat_Counter deadtuples) { PgStat_MsgAnalyze msg; *************** *** 715,721 **** return; pgstat_setheader(&msg.m_hdr, PGSTAT_MTYPE_ANALYZE); ! msg.m_databaseid = MyDatabaseId; msg.m_tableoid = tableoid; msg.m_live_tuples = livetuples; msg.m_dead_tuples = deadtuples; --- 728,734 ---- return; pgstat_setheader(&msg.m_hdr, PGSTAT_MTYPE_ANALYZE); ! msg.m_databaseid = shared ? InvalidOid : MyDatabaseId; msg.m_tableoid = tableoid; msg.m_live_tuples = livetuples; msg.m_dead_tuples = deadtuples; *************** *** 784,790 **** pgstat_collect_blocklevel)) { /* Not reporting stats, so just flush whatever we have */ ! pgStatTabstatUsed = 0; return; } --- 797,804 ---- pgstat_collect_blocklevel)) { /* Not reporting stats, so just flush whatever we have */ ! RegularTabStat.tsa_used = 0; ! SharedTabStat.tsa_used = 0; return; } *************** *** 792,800 **** * For each message buffer used during the last query set the header * fields and send it out. */ ! for (i = 0; i < pgStatTabstatUsed; i++) { ! PgStat_MsgTabstat *tsmsg = pgStatTabstatMessages[i]; int n; int len; --- 806,814 ---- * For each message buffer used during the last query set the header * fields and send it out. */ ! for (i = 0; i < RegularTabStat.tsa_used; i++) { ! PgStat_MsgTabstat *tsmsg = RegularTabStat.tsa_messages[i]; int n; int len; *************** *** 811,818 **** tsmsg->m_databaseid = MyDatabaseId; pgstat_send(tsmsg, len); } ! pgStatTabstatUsed = 0; } --- 825,850 ---- tsmsg->m_databaseid = MyDatabaseId; pgstat_send(tsmsg, len); } + RegularTabStat.tsa_used = 0; + + /* Ditto, for shared relations */ + for (i = 0; i < SharedTabStat.tsa_used; i++) + { + PgStat_MsgTabstat *tsmsg = SharedTabStat.tsa_messages[i]; + int n; + int len; ! n = tsmsg->m_nentries; ! len = offsetof(PgStat_MsgTabstat, m_entry[0]) + ! n * sizeof(PgStat_TableEntry); ! ! /* We don't report transaction commit/abort here */ ! ! pgstat_setheader(&tsmsg->m_hdr, PGSTAT_MTYPE_TABSTAT); ! tsmsg->m_databaseid = InvalidOid; ! pgstat_send(tsmsg, len); ! } ! SharedTabStat.tsa_used = 0; } *************** *** 1045,1081 **** } /* ! * Create or enlarge the pgStatTabstatMessages array */ static void ! more_tabstat_space(void) { PgStat_MsgTabstat *newMessages; PgStat_MsgTabstat **msgArray; ! int newAlloc = pgStatTabstatAlloc + TABSTAT_QUANTUM; int i; /* Create (another) quantum of message buffers */ newMessages = (PgStat_MsgTabstat *) MemoryContextAllocZero(TopMemoryContext, sizeof(PgStat_MsgTabstat) * TABSTAT_QUANTUM); /* Create or enlarge the pointer array */ ! if (pgStatTabstatMessages == NULL) msgArray = (PgStat_MsgTabstat **) MemoryContextAlloc(TopMemoryContext, sizeof(PgStat_MsgTabstat *) * newAlloc); else msgArray = (PgStat_MsgTabstat **) ! repalloc(pgStatTabstatMessages, sizeof(PgStat_MsgTabstat *) * newAlloc); for (i = 0; i < TABSTAT_QUANTUM; i++) ! msgArray[pgStatTabstatAlloc + i] = newMessages++; ! pgStatTabstatMessages = msgArray; ! pgStatTabstatAlloc = newAlloc; ! Assert(pgStatTabstatUsed < pgStatTabstatAlloc); } /* ---------- --- 1077,1117 ---- } /* ! * Enlarge a TabStatArray */ static void ! more_tabstat_space(TabStatArray *tsarr) { PgStat_MsgTabstat *newMessages; PgStat_MsgTabstat **msgArray; ! int newAlloc; int i; + AssertArg(PointerIsValid(tsarr)); + + newAlloc = tsarr->tsa_alloc + TABSTAT_QUANTUM; + /* Create (another) quantum of message buffers */ newMessages = (PgStat_MsgTabstat *) MemoryContextAllocZero(TopMemoryContext, sizeof(PgStat_MsgTabstat) * TABSTAT_QUANTUM); /* Create or enlarge the pointer array */ ! if (tsarr->tsa_messages == NULL) msgArray = (PgStat_MsgTabstat **) MemoryContextAlloc(TopMemoryContext, sizeof(PgStat_MsgTabstat *) * newAlloc); else msgArray = (PgStat_MsgTabstat **) ! repalloc(tsarr->tsa_messages, sizeof(PgStat_MsgTabstat *) * newAlloc); for (i = 0; i < TABSTAT_QUANTUM; i++) ! msgArray[tsarr->tsa_alloc + i] = newMessages++; ! tsarr->tsa_messages = msgArray; ! tsarr->tsa_alloc = newAlloc; ! Assert(tsarr->tsa_used < tsarr->tsa_alloc); } /* ---------- *************** *** 1092,1097 **** --- 1128,1134 ---- { Oid rel_id = rel->rd_id; PgStat_TableEntry *useent; + TabStatArray *tsarr; PgStat_MsgTabstat *tsmsg; int mb; int i; *************** *** 1112,1123 **** return; } /* * Search the already-used message slots for this relation. */ ! for (mb = 0; mb < pgStatTabstatUsed; mb++) { ! tsmsg = pgStatTabstatMessages[mb]; for (i = tsmsg->m_nentries; --i >= 0;) { --- 1149,1162 ---- return; } + tsarr = rel->rd_rel->relisshared ? &SharedTabStat : &RegularTabStat; + /* * Search the already-used message slots for this relation. */ ! for (mb = 0; mb < tsarr->tsa_used; mb++) { ! tsmsg = tsarr->tsa_messages[mb]; for (i = tsmsg->m_nentries; --i >= 0;) { *************** *** 1146,1159 **** /* * If we ran out of message buffers, we just allocate more. */ ! if (pgStatTabstatUsed >= pgStatTabstatAlloc) ! more_tabstat_space(); /* * Use the first entry of the next message buffer. */ ! mb = pgStatTabstatUsed++; ! tsmsg = pgStatTabstatMessages[mb]; tsmsg->m_nentries = 1; useent = &tsmsg->m_entry[0]; MemSet(useent, 0, sizeof(PgStat_TableEntry)); --- 1185,1198 ---- /* * If we ran out of message buffers, we just allocate more. */ ! if (tsarr->tsa_used >= tsarr->tsa_alloc) ! more_tabstat_space(tsarr); /* * Use the first entry of the next message buffer. */ ! mb = tsarr->tsa_used++; ! tsmsg = tsarr->tsa_messages[mb]; tsmsg->m_nentries = 1; useent = &tsmsg->m_entry[0]; MemSet(useent, 0, sizeof(PgStat_TableEntry)); *************** *** 1183,1195 **** * message buffer used without slots, causing the next report to tell * new xact-counters. */ ! if (pgStatTabstatAlloc == 0) ! more_tabstat_space(); ! if (pgStatTabstatUsed == 0) { ! pgStatTabstatUsed++; ! pgStatTabstatMessages[0]->m_nentries = 0; } } --- 1222,1234 ---- * message buffer used without slots, causing the next report to tell * new xact-counters. */ ! if (RegularTabStat.tsa_alloc == 0) ! more_tabstat_space(&RegularTabStat); ! if (RegularTabStat.tsa_used == 0) { ! RegularTabStat.tsa_used++; ! RegularTabStat.tsa_messages[0]->m_nentries = 0; } } *************** *** 1215,1227 **** * message buffer used without slots, causing the next report to tell * new xact-counters. */ ! if (pgStatTabstatAlloc == 0) ! more_tabstat_space(); ! if (pgStatTabstatUsed == 0) { ! pgStatTabstatUsed++; ! pgStatTabstatMessages[0]->m_nentries = 0; } } --- 1254,1266 ---- * message buffer used without slots, causing the next report to tell * new xact-counters. */ ! if (RegularTabStat.tsa_alloc == 0) ! more_tabstat_space(&RegularTabStat); ! if (RegularTabStat.tsa_used == 0) { ! RegularTabStat.tsa_used++; ! RegularTabStat.tsa_messages[0]->m_nentries = 0; } } *************** *** 2107,2124 **** /* * Lookup the hash table entry for the specified database. If no hash ! * table entry exists, initialize it. */ static PgStat_StatDBEntry * ! pgstat_get_db_entry(Oid databaseid) { PgStat_StatDBEntry *result; bool found; /* Lookup or create the hash table entry for this database */ result = (PgStat_StatDBEntry *) hash_search(pgStatDBHash, &databaseid, ! HASH_ENTER, &found); /* If not found, initialize the new one. */ if (!found) --- 2146,2168 ---- /* * Lookup the hash table entry for the specified database. If no hash ! * table entry exists, initialize it, if the create parameter is true. ! * Else, return NULL. */ static PgStat_StatDBEntry * ! pgstat_get_db_entry(Oid databaseid, bool create) { PgStat_StatDBEntry *result; bool found; + HASHACTION action = create ? HASH_ENTER : HASH_FIND; /* Lookup or create the hash table entry for this database */ result = (PgStat_StatDBEntry *) hash_search(pgStatDBHash, &databaseid, ! action, &found); ! ! if (!create && !found) ! return NULL; /* If not found, initialize the new one. */ if (!found) *************** *** 2387,2393 **** * pgstat_read_statsfile() - * * Reads in an existing statistics collector and initializes the ! * databases hash table (who's entries point to the tables hash tables) * and the current backend table. * ---------- */ --- 2431,2437 ---- * pgstat_read_statsfile() - * * Reads in an existing statistics collector and initializes the ! * databases' hash table (whose entries point to the tables' hash tables) * and the current backend table. * ---------- */ *************** *** 2738,2751 **** 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. --- 2782,2797 ---- PgStat_StatDBEntry *dbentry; /* ! * Lookup the database in the hashtable. Don't create the entry if it ! * doesn't exist, because autovacuum may be processing a template ! * database. If this isn't the case, the database is most likely to ! * have an entry already. (If it doesn't, not much harm is done ! * anyway -- it'll get created as soon as somebody actually uses ! * the database.) */ ! dbentry = pgstat_get_db_entry(msg->m_databaseid, false); ! if (dbentry == NULL) ! return; /* * Store the last autovacuum time in the database entry. *************** *** 2765,2772 **** PgStat_StatDBEntry *dbentry; PgStat_StatTabEntry *tabentry; bool found; ! dbentry = pgstat_get_db_entry(msg->m_databaseid); tabentry = hash_search(dbentry->tables, &(msg->m_tableoid), HASH_ENTER, &found); --- 2811,2829 ---- PgStat_StatDBEntry *dbentry; PgStat_StatTabEntry *tabentry; bool found; + bool create; + + /* + * If we don't know about the database, ignore the message, because it + * may be autovacuum processing a template database. If the message + * is for database InvalidOid, don't ignore it, because we are getting + * a message from vacuuming a shared relation. + */ + create = msg->m_databaseid == InvalidOid; ! dbentry = pgstat_get_db_entry(msg->m_databaseid, create); ! if (dbentry == NULL) ! return; tabentry = hash_search(dbentry->tables, &(msg->m_tableoid), HASH_ENTER, &found); *************** *** 2819,2825 **** PgStat_StatTabEntry *tabentry; bool found; ! dbentry = pgstat_get_db_entry(msg->m_databaseid); tabentry = hash_search(dbentry->tables, &(msg->m_tableoid), HASH_ENTER, &found); --- 2876,2887 ---- PgStat_StatTabEntry *tabentry; bool found; ! /* ! * Note that we do create the database entry here, as opposed to what ! * we do on AutovacStart and Vacuum messages. This is because ! * autovacuum never executes ANALYZE on template databases. ! */ ! dbentry = pgstat_get_db_entry(msg->m_databaseid, true); tabentry = hash_search(dbentry->tables, &(msg->m_tableoid), HASH_ENTER, &found); *************** *** 2902,2908 **** if (pgstat_add_backend(&msg->m_hdr) < 0) return; ! dbentry = pgstat_get_db_entry(msg->m_databaseid); /* * If the database is marked for destroy, this is a delayed UDP packet --- 2964,2970 ---- if (pgstat_add_backend(&msg->m_hdr) < 0) return; ! dbentry = pgstat_get_db_entry(msg->m_databaseid, true); /* * If the database is marked for destroy, this is a delayed UDP packet *************** *** 2994,3000 **** if (pgstat_add_backend(&msg->m_hdr) < 0) return; ! dbentry = pgstat_get_db_entry(msg->m_databaseid); /* * If the database is marked for destroy, this is a delayed UDP packet --- 3056,3062 ---- if (pgstat_add_backend(&msg->m_hdr) < 0) return; ! dbentry = pgstat_get_db_entry(msg->m_databaseid, true); /* * If the database is marked for destroy, this is a delayed UDP packet *************** *** 3037,3043 **** /* * Lookup the database in the hashtable. */ ! dbentry = pgstat_get_db_entry(msg->m_databaseid); /* * Mark the database for destruction. --- 3099,3105 ---- /* * Lookup the database in the hashtable. */ ! dbentry = pgstat_get_db_entry(msg->m_databaseid, true); /* * Mark the database for destruction. *************** *** 3067,3073 **** /* * Lookup the database in the hashtable. */ ! dbentry = pgstat_get_db_entry(msg->m_databaseid); /* * We simply throw away all the database's table entries by --- 3129,3135 ---- /* * Lookup the database in the hashtable. */ ! dbentry = pgstat_get_db_entry(msg->m_databaseid, true); /* * We simply throw away all the database's table entries by Index: postmaster/postmaster.c =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/backend/postmaster/postmaster.c,v retrieving revision 1.460 diff -c -r1.460 postmaster.c *** postmaster/postmaster.c 21 Jul 2005 03:56:11 -0000 1.460 --- postmaster/postmaster.c 24 Jul 2005 17:54:31 -0000 *************** *** 1164,1176 **** /* * Wait for something to happen. * ! * We wait at most one minute, to ensure that the other background ! * tasks handled below get done even when no requests are ! * arriving. */ memcpy((char *) &rmask, (char *) &readmask, sizeof(fd_set)); ! timeout.tv_sec = 60; timeout.tv_usec = 0; PG_SETMASK(&UnBlockSig); --- 1164,1176 ---- /* * Wait for something to happen. * ! * We wait at most one minute, or the minimum autovacuum delay, to ! * ensure that the other background tasks handled below get done ! * even when no requests are arriving. */ memcpy((char *) &rmask, (char *) &readmask, sizeof(fd_set)); ! timeout.tv_sec = Min(60, autovacuum_naptime); timeout.tv_usec = 0; PG_SETMASK(&UnBlockSig); Index: utils/init/postinit.c =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/backend/utils/init/postinit.c,v retrieving revision 1.153 diff -c -r1.153 postinit.c *** utils/init/postinit.c 14 Jul 2005 05:13:41 -0000 1.153 --- utils/init/postinit.c 24 Jul 2005 15:50:28 -0000 *************** *** 78,83 **** --- 78,84 ---- char *filename; FILE *db_file; char thisname[NAMEDATALEN]; + TransactionId frozenxid; filename = database_getflatfilename(); db_file = AllocateFile(filename, "r"); *************** *** 86,92 **** (errcode_for_file_access(), errmsg("could not open file \"%s\": %m", filename))); ! while (read_pg_database_line(db_file, thisname, db_id, db_tablespace)) { if (strcmp(thisname, name) == 0) { --- 87,94 ---- (errcode_for_file_access(), errmsg("could not open file \"%s\": %m", filename))); ! while (read_pg_database_line(db_file, thisname, db_id, db_tablespace, ! &frozenxid)) { if (strcmp(thisname, name) == 0) { *************** *** 170,179 **** /* * Also check that the database is currently allowing connections. * (We do not enforce this in standalone mode, however, so that there is ! * a way to recover from "UPDATE pg_database SET datallowconn = false;") */ dbform = (Form_pg_database) GETSTRUCT(tup); ! if (IsUnderPostmaster && !dbform->datallowconn) ereport(FATAL, (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), errmsg("database \"%s\" is not currently accepting connections", --- 172,182 ---- /* * Also check that the database is currently allowing connections. * (We do not enforce this in standalone mode, however, so that there is ! * a way to recover from "UPDATE pg_database SET datallowconn = false;". ! * We do not enforce it for the autovacuum process either.) */ dbform = (Form_pg_database) GETSTRUCT(tup); ! if (IsUnderPostmaster && !IsAutoVacuumProcess() && !dbform->datallowconn) ereport(FATAL, (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), errmsg("database \"%s\" is not currently accepting connections", Index: utils/misc/guc.c =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/backend/utils/misc/guc.c,v retrieving revision 1.277 diff -c -r1.277 guc.c *** utils/misc/guc.c 23 Jul 2005 21:05:47 -0000 1.277 --- utils/misc/guc.c 24 Jul 2005 17:55:56 -0000 *************** *** 1412,1423 **** }, { {"autovacuum_naptime", PGC_SIGHUP, AUTOVACUUM, gettext_noop("Time to sleep between autovacuum runs, in seconds."), NULL }, &autovacuum_naptime, ! 60, 0, INT_MAX, NULL, NULL }, { {"autovacuum_vacuum_threshold", PGC_SIGHUP, AUTOVACUUM, --- 1412,1427 ---- }, { + /* + * Note we set an artificial minimum of 1 sec, so that the + * postmaster's main loop does not turn into busy-wait. + */ {"autovacuum_naptime", PGC_SIGHUP, AUTOVACUUM, gettext_noop("Time to sleep between autovacuum runs, in seconds."), NULL }, &autovacuum_naptime, ! 60, 1, INT_MAX, NULL, NULL }, { {"autovacuum_vacuum_threshold", PGC_SIGHUP, AUTOVACUUM,