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:
Ok, here's a patch that deals with some of this:
- The stat collector is modified so as to keep shared relations separate
from regular ones. Also, backends sends messages separately.
Autovacuum takes advantage of this, so it correctly identifies the
appropiate time to operate on a shared relation, irrespective of the
database where they were modified. Note however that it uses each
database's pg_autovacuum settings. This means it could be vacuumed
sooner in one database than another, but I don't think it's a problem.
- Temp tables are completely ignored.
- pg_statistic is completely ignored.
- 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.
Some comments:
- Now that we have a real Xid wraparound check, we could go back to
having any table with no stat entry ignored, which was the original
coding. There's no danger of wraparound, and there'd be no work done
to a table that doesn't have any activity. We have to consider what
happens at stat reset -- AFAICS there's no problem, because as soon as
the table sees some activity, it will be picked up by pgstat.
However, it would be bad if stats are reset right after some heavy
activity on a table. Maybe the only thing we need is documentation.
- datallowcon is still ignored. Now it's safe to do so, because we have
a real Xid wraparound check. Changing it requires extending the
pg_database flat-file (should be fairly easy).
- There are stat messages emitted for a database-wide vacuum, just like
any other. This means that all tables in the database would end up in
pgstat; and also all databases, including those with datallowconn = false.
This may not be good. I'm not sure what exactly to do about it. Do
we want to disallow such stats? Disable message sending (or
collecting) in some circumstances?
- 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: 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 */
---------------------------(end of broadcast)---------------------------
TIP 9: In versions below 8.0, the planner will ignore your desire to
choose an index scan if your joining column's datatypes do not
match