On Thu, Jul 14, 2005 at 10:52:56AM -0400, Tom Lane wrote:
> I've applied Alvaro's latest integrated-autovacuum patch.  There are
> still a number of loose ends to be dealt with before beta, though:

Updated this patch:

- The stat collector is modified so as to keep shared relations separate
  from regular ones.  Autovacuum understands this.

- Temp tables are completely ignored.

- pg_statistic is ignored for analyze.  It's still candidate for vacuum.

- databases with no stat entry are still ignored, except that they are
  checked for Xid wraparound like any other.  The "oldest" one is chosen
  for vacuum in a particular autovacuum run.

- A database-wide vacuum forces a pg_database flat-file update, so that
  the wraparound check actually works.

- The postmaster's main loop sleeps Min(60, autovacuum_naptime), in
  order to be able to pick naptimes smaller than 60 seconds.  In order
  not to make the loop a busy-wait, I forced a minimum of 1 to that GUC
  var.  Yes, an argument could be made that the minimum could be higher.
  Not sure if we actually want to dictate policy on this.  The minimum
  is there only to prevent the postmaster from using 100% of a CPU the
  whole time.

- Tables with no stat entries are completely ignored.

- The stat collector ignores messages that relate to databases it
  doesn't know about.  This makes it inocuous to issue a database-wide
  vacuum on a template database.  A special case is made for database
  InvalidOid -- an entry for it is created regardless.


Two comments still apply:

- I haven't done anything yet w.r.t. the custom vacuum_delay nor sleep
  scale factor.

- There are still no docs.

-- 
Alvaro Herrera (<alvherre[a]alvh.no-ip.org>)
"Porque Kim no hacia nada, pero, eso sí,
con extraordinario éxito" ("Kim", Kipling)
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,
---------------------------(end of broadcast)---------------------------
TIP 5: don't forget to increase your free space map settings

Reply via email to