Index: src/backend/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 *** src/backend/access/transam/xlog.c 23 Jul 2005 15:31:16 -0000 1.210 --- src/backend/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: src/backend/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 *** src/backend/commands/analyze.c 14 Jul 2005 05:13:39 -0000 1.87 --- src/backend/commands/analyze.c 22 Jul 2005 01:08:51 -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,324 ---- * 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 */ --- 437,444 ---- } /* report results to the stats collector, too */ ! pgstat_report_analyze(RelationGetRelid(onerel), onerel->rd_rel->relisshared, ! totalrows, totaldeadrows); } /* Done with indexes */ Index: src/backend/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 *** src/backend/commands/vacuum.c 14 Jul 2005 05:13:39 -0000 1.311 --- src/backend/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: src/backend/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 *** src/backend/commands/vacuumlazy.c 14 Jul 2005 05:13:40 -0000 1.55 --- src/backend/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: src/backend/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 *** src/backend/libpq/hba.c 28 Jun 2005 22:16:45 -0000 1.144 --- src/backend/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: src/backend/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 *** src/backend/postmaster/autovacuum.c 14 Jul 2005 05:13:40 -0000 1.1 --- src/backend/postmaster/autovacuum.c 24 Jul 2005 17:31:32 -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(PgStat_StatDBEntry *dbentry, bool whole_db); 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,327 ---- /* * 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.) * * 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? */ 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 decide to vacuum a little earlier than when vacuum.c's + * vac_truncate_clog() would decide start giving warnings. We + * look for the database that most urgently needs a database-wide + * vacuum. As soon as one is found, any other database is + * ignored. + */ + 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 databases 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 */ --- 354,360 ---- /* * And do an appropriate amount of work on it */ ! do_autovacuum(db->entry, whole_db); } /* One iteration done, go away */ *************** *** 338,343 **** --- 376,382 ---- 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; --- 385,392 ---- (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 **** --- 394,400 ---- db->oid = db_id; db->name = pstrdup(thisname); + db->frozenxid = db_frozenxid; /* this gets set later */ db->entry = NULL; *************** *** 369,374 **** --- 410,421 ---- /* * 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; --- 424,430 ---- * order not to ignore shutdown commands for too long. */ static void ! do_autovacuum(PgStat_StatDBEntry *dbentry, bool whole_db) { Relation classRel, avRel; *************** *** 387,392 **** --- 434,441 ---- *analyze_tables = NIL; MemoryContext AutovacMemCxt; + Assert(dbentry != NULL || whole_db); + /* 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(); --- 454,548 ---- */ MemoryContextSwitchTo(AutovacMemCxt); ! if (whole_db) { ! elog(DEBUG2, "autovacuum: VACUUM ANALYZE whole database"); ! autovacuum_do_vac_analyze(NIL, true); ! } ! else ! { ! /* Get the stat database 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) ! 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(); *************** *** 607,615 **** } else if (anltuples > anlthresh) { ! elog(DEBUG2, "will ANALYZE %s", ! RelationGetRelationName(rel)); ! *analyze_tables = lappend_oid(*analyze_tables, relid); } RelationClose(rel); --- 670,682 ---- } 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. --- 713,718 ---- Index: src/backend/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 *** src/backend/postmaster/pgstat.c 24 Jul 2005 00:33:28 -0000 1.101 --- src/backend/postmaster/pgstat.c 24 Jul 2005 17:55:54 -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; + static TabStatArray SharedTabStat; + static int pgStatXactCommit = 0; static int pgStatXactRollback = 0; *************** *** 182,187 **** --- 193,199 ---- static void pgstat_recv_vacuum(PgStat_MsgVacuum *msg, int len); static void pgstat_recv_analyze(PgStat_MsgAnalyze *msg, int len); + extern void display_statfile_contents(void); /* ------------------------------------------------------------ * Public functions called from postmaster follow *************** *** 614,619 **** --- 626,632 ---- 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; --- 697,704 ---- * --------- */ 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; --- 706,715 ---- return; pgstat_setheader(&msg.m_hdr, PGSTAT_MTYPE_VACUUM); ! if (shared) ! msg.m_databaseid = InvalidOid; ! else ! msg.m_databaseid = 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; --- 723,729 ---- * -------- */ 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; --- 732,741 ---- return; pgstat_setheader(&msg.m_hdr, PGSTAT_MTYPE_ANALYZE); ! if (shared) ! msg.m_databaseid = InvalidOid; ! else ! msg.m_databaseid = 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; } --- 804,811 ---- 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; --- 813,821 ---- * 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; } --- 832,857 ---- 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); } /* ---------- --- 1084,1124 ---- } /* ! * 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 **** --- 1135,1141 ---- { 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;) { --- 1156,1169 ---- 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)); --- 1192,1205 ---- /* * 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; } } --- 1229,1241 ---- * 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; } } --- 1261,1273 ---- * 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; } } *************** *** 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. * ---------- */ --- 2433,2439 ---- * 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. * ---------- */ Index: src/backend/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 *** src/backend/postmaster/postmaster.c 21 Jul 2005 03:56:11 -0000 1.460 --- src/backend/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: src/backend/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 *** src/backend/utils/init/postinit.c 14 Jul 2005 05:13:41 -0000 1.153 --- src/backend/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: src/backend/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 *** src/backend/utils/misc/guc.c 23 Jul 2005 21:05:47 -0000 1.277 --- src/backend/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, Index: src/include/pgstat.h =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/include/pgstat.h,v retrieving revision 1.33 diff -c -r1.33 pgstat.h *** src/include/pgstat.h 14 Jul 2005 05:13:43 -0000 1.33 --- src/include/pgstat.h 22 Jul 2005 01:08:51 -0000 *************** *** 384,393 **** extern void pgstat_report_activity(const char *what); extern void pgstat_report_tabstat(void); extern void pgstat_report_autovac(void); ! extern void pgstat_report_vacuum(Oid tableoid, bool analyze, PgStat_Counter tuples); ! extern void pgstat_report_analyze(Oid tableoid, PgStat_Counter livetuples, ! PgStat_Counter deadtuples); extern int pgstat_vacuum_tabstat(void); extern void pgstat_reset_counters(void); --- 384,393 ---- extern void pgstat_report_activity(const char *what); extern void pgstat_report_tabstat(void); extern void pgstat_report_autovac(void); ! extern void pgstat_report_vacuum(Oid tableoid, bool shared, bool analyze, PgStat_Counter tuples); ! extern void pgstat_report_analyze(Oid tableoid, bool shared, ! PgStat_Counter livetuples, PgStat_Counter deadtuples); extern int pgstat_vacuum_tabstat(void); extern void pgstat_reset_counters(void); Index: src/include/access/xlog.h =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/include/access/xlog.h,v retrieving revision 1.66 diff -c -r1.66 xlog.h *** src/include/access/xlog.h 4 Jul 2005 04:51:52 -0000 1.66 --- src/include/access/xlog.h 23 Jul 2005 02:04:33 -0000 *************** *** 155,160 **** --- 155,161 ---- extern void xlog_redo(XLogRecPtr lsn, XLogRecord *record); extern void xlog_desc(char *buf, uint8 xl_info, char *rec); + extern void ReadControlFile(TransactionId *nextXid); extern void UpdateControlFile(void); extern int XLOGShmemSize(void); extern void XLOGShmemInit(void); Index: src/include/libpq/hba.h =================================================================== RCS file: /home/alvherre/cvs/pgsql/src/include/libpq/hba.h,v retrieving revision 1.38 diff -c -r1.38 hba.h *** src/include/libpq/hba.h 28 Jun 2005 05:09:13 -0000 1.38 --- src/include/libpq/hba.h 22 Jul 2005 01:08:51 -0000 *************** *** 37,42 **** extern int hba_getauthmethod(hbaPort *port); extern int authident(hbaPort *port); extern bool read_pg_database_line(FILE *fp, char *dbname, ! Oid *dboid, Oid *dbtablespace); #endif /* HBA_H */ --- 37,43 ---- extern int hba_getauthmethod(hbaPort *port); extern int authident(hbaPort *port); extern bool read_pg_database_line(FILE *fp, char *dbname, ! Oid *dboid, Oid *dbtablespace, ! TransactionId *dbfrozenxid); #endif /* HBA_H */