I have been working on integrating pg_autovacuum into the backend. I
have used Tom's recent work on the bgwriter as an example. This patch
accomplishes the following:
* pg_autovacuum is now started and stopped by the postmaster
* pg_autovacuum is no longer a stand alone executable
* pg_autovacuum now uses elog
I am submitting this patch for review to make sure that I'm headed in
the right direction, so please give it a cursory glance and tell me if
you see any glaring problems.
I am planning on doing much more in the next few days including:
* creating pg_autovacuum related guc variables
* create a new system table for pg_autovacuum settings
* alter pg_class (or create another new system table) to store
additional per table pg_autovacuum data which will allow per table
settings and persistent data
To use this patch, move pg_autovacuum.c into src/backend/postmaster,
move pg_autovacuum.h into src/include/postmaster and apply the patch.
Matthew O'Connor
ps, I am hoping to get this work in before feature freeze.
*** ./src/backend/bootstrap/bootstrap.c.orig 2004-06-05 15:32:02.000000000 -0400
--- ./src/backend/bootstrap/bootstrap.c 2004-06-05 15:33:13.000000000 -0400
***************
*** 34,39 ****
--- 34,40 ----
#include "libpq/pqsignal.h"
#include "miscadmin.h"
#include "postmaster/bgwriter.h"
+ #include "postmaster/pg_autovacuum.h"
#include "storage/freespace.h"
#include "storage/ipc.h"
#include "storage/pg_shmem.h"
***************
*** 358,363 ****
--- 359,367 ----
case BS_XLOG_BGWRITER:
statmsg = "writer process";
break;
+ case BS_XLOG_AUTOVAC:
+ statmsg = "auto vacuum process";
+ break;
default:
statmsg = "??? process";
break;
***************
*** 394,399 ****
--- 398,406 ----
case BS_XLOG_BGWRITER:
InitDummyProcess(DUMMY_PROC_BGWRITER);
break;
+ case BS_XLOG_AUTOVAC:
+ InitDummyProcess(DUMMY_PROC_AUTOVAC);
+ break;
default:
InitDummyProcess(DUMMY_PROC_DEFAULT);
***************
*** 430,435 ****
--- 437,448 ----
BackgroundWriterMain();
proc_exit(1); /* should never return */
+ case BS_XLOG_AUTOVAC:
+ /* don't set signals, autovac has its own agenda */
+ InitXLOGAccess();
+ AutoVacMain();
+ proc_exit(1); /* should never return */
+
default:
elog(PANIC, "unrecognized XLOG op: %d", xlogop);
proc_exit(1);
*** ./src/backend/Makefile.orig 2004-06-07 01:21:43.515373849 -0400
--- ./src/backend/Makefile 2004-06-05 13:46:24.000000000 -0400
***************
*** 29,41 ****
##########################################################################
! all: submake-libpgport postgres $(POSTGRES_IMP)
ifneq ($(PORTNAME), cygwin)
ifneq ($(PORTNAME), win32)
postgres: $(OBJS)
! $(CC) $(CFLAGS) $(LDFLAGS) $(export_dynamic) $^ $(LIBS) -o $@
endif
endif
--- 29,41 ----
##########################################################################
! all: submake-libpgport submake-libpq postgres $(POSTGRES_IMP)
ifneq ($(PORTNAME), cygwin)
ifneq ($(PORTNAME), win32)
postgres: $(OBJS)
! $(CC) $(CFLAGS) $(LDFLAGS) -I $(libpq_srcdir) $(export_dynamic) $^ $(LIBS) $(libpq) -o $@
endif
endif
*** ./src/backend/postmaster/Makefile.orig 2004-06-05 00:58:08.000000000 -0400
--- ./src/backend/postmaster/Makefile 2004-06-05 13:45:20.000000000 -0400
***************
*** 12,18 ****
top_builddir = ../../..
include $(top_builddir)/src/Makefile.global
! OBJS = postmaster.o bgwriter.o pgstat.o
all: SUBSYS.o
--- 12,18 ----
top_builddir = ../../..
include $(top_builddir)/src/Makefile.global
! OBJS = postmaster.o bgwriter.o pgstat.o pg_autovacuum.o
all: SUBSYS.o
*** ./src/backend/postmaster/pg_autovacuum.c.orig 2004-06-07 00:39:57.000000000 -0400
--- ./src/backend/postmaster/pg_autovacuum.c 2004-06-07 00:35:51.000000000 -0400
***************
*** 1,65 ****
! /* pg_autovacuum.c
* All the code for the pg_autovacuum program
* (c) 2003 Matthew T. O'Connor
* Revisions by Christopher B. Browne, Liberty RMS
*/
! #include "pg_autovacuum.h"
- FILE *LOGOUTPUT;
char logbuffer[4096];
! static void
! log_entry(const char *logentry)
{
! time_t curtime;
! struct tm *loctime;
! char timebuffer[128];
!
! curtime = time(NULL);
! loctime = localtime(&curtime);
! strftime(timebuffer, sizeof(timebuffer), "%Y-%m-%d %H:%M:%S %Z", loctime);
! fprintf(LOGOUTPUT, "[%s] %s\n", timebuffer, logentry);
}
/*
! * Function used to detach the pg_autovacuum daemon from the tty and go into
! * the background.
*
! * This code is mostly ripped directly from pm_dameonize in postmaster.c with
! * unneeded code removed.
*/
static void
! daemonize()
{
! pid_t pid;
! pid = fork();
! if (pid == (pid_t) -1)
! {
! log_entry("Error: cannot disassociate from controlling TTY");
! fflush(LOGOUTPUT);
! _exit(1);
! }
! else if (pid)
! { /* parent */
! /* Parent should just exit, without doing any atexit cleanup */
! _exit(0);
! }
! /* GH: If there's no setsid(), we hopefully don't need silent mode.
! * Until there's a better solution. */
! #ifdef HAVE_SETSID
! if (setsid() < 0)
! {
! log_entry("Error: cannot disassociate from controlling TTY");
! fflush(LOGOUTPUT);
! _exit(1);
! }
! #endif
}
/* Create and return tbl_info struct with initialized to values from row or res */
static tbl_info *
init_table_info(PGresult *res, int row, db_info * dbi)
--- 1,223 ----
! /*-------------------------------------------------------------------------
! *
! * pg_autovacuum.c
! *
! * The background autovacuum daemon was in 7.4 contribis but is being newly
! * integrated into 7.5. It monitors database activity using data from the
! * stats system (though at some point is should also look at FSM data) so
! * as to perform vacuum commands on specific tables when and only when
! * a sufficient amount activity has been performed on that table.
! *
! * The autovacuum process is started by the postmaster on startup.
! * It remains alive until the postmaster commands it to terminate. Normal
! * termination is by SIGUSR2, which instructs the autovacuum process to exit(0).
! * Emergency termination is by SIGQUIT; like any
! * backend, the autovacuum process will simply abort and exit on SIGQUIT.
! *
* All the code for the pg_autovacuum program
* (c) 2003 Matthew T. O'Connor
* Revisions by Christopher B. Browne, Liberty RMS
+ *-------------------------------------------------------------------------
*/
+ #include "postgres.h"
+
+ #include <signal.h>
+ #include <time.h>
! #include "access/xlog.h"
! #include "libpq/pqsignal.h"
! #include "miscadmin.h"
! #include "postmaster/bgwriter.h"
! #include "storage/bufmgr.h"
! #include "storage/freespace.h"
! #include "storage/ipc.h"
! #include "storage/pmsignal.h"
! #include "storage/smgr.h"
! #include "tcop/tcopprot.h"
! #include "utils/guc.h"
! #include "postmaster/pg_autovacuum.h"
char logbuffer[4096];
! /*
! * GUC parameters
! */
! /*
! int BgWriterDelay = 200;
! int BgWriterPercent = 1;
! int BgWriterMaxPages = 100;
!
! int CheckPointTimeout = 300;
! int CheckPointWarning = 30;
! */
!
! /*
! * Flags set by interrupt handlers for later service in the main loop.
! */
! static volatile sig_atomic_t got_SIGHUP = false;
! static volatile sig_atomic_t shutdown_requested = false;
!
! /*
! * Private state
! */
! static bool am_autovac = false;
!
! /* static bool ckpt_active = false; */
!
! /* static time_t last_checkpoint_time; */
!
!
! static void autovac_quickdie(SIGNAL_ARGS);
! static void AutoVacSigHupHandler(SIGNAL_ARGS);
! static void ReqShutdownHandler(SIGNAL_ARGS);
!
!
! /*
! * Main entry point for bgwriter process
! *
! * This is invoked from BootstrapMain, which has already created the basic
! * execution environment, but not enabled signals yet.
! */
! void
! AutoVacMain(void)
{
! am_autovac = true;
!
! /*
! * Properly accept or ignore signals the postmaster might send us
! *
! * Note: we deliberately ignore SIGTERM, because during a standard Unix
! * system shutdown cycle, init will SIGTERM all processes at once. We
! * want to wait for the backends to exit, whereupon the postmaster will
! * tell us it's okay to shut down (via SIGUSR2).
! *
! * SIGUSR1 is presently unused; keep it spare in case someday we want
! * this process to participate in sinval messaging.
! */
! pqsignal(SIGHUP, AutoVacSigHupHandler); /* set flag to read config file */
! pqsignal(SIGTERM, SIG_IGN); /* ignore SIGTERM */
! pqsignal(SIGQUIT, autovac_quickdie); /* hard crash time */
! pqsignal(SIGALRM, SIG_IGN);
! pqsignal(SIGPIPE, SIG_IGN);
! pqsignal(SIGUSR1, SIG_IGN); /* reserve for sinval */
! pqsignal(SIGUSR2, ReqShutdownHandler); /* request shutdown */
!
! /*
! * Reset some signals that are accepted by postmaster but not here
! */
! pqsignal(SIGCHLD, SIG_DFL);
! pqsignal(SIGTTIN, SIG_DFL);
! pqsignal(SIGTTOU, SIG_DFL);
! pqsignal(SIGCONT, SIG_DFL);
! pqsignal(SIGWINCH, SIG_DFL);
!
! /* We allow SIGQUIT (quickdie) at all times */
! #ifdef HAVE_SIGPROCMASK
! sigdelset(&BlockSig, SIGQUIT);
! #else
! BlockSig &= ~(sigmask(SIGQUIT));
! #endif
!
! /*
! * If an exception is encountered, processing resumes here.
! */
! if (sigsetjmp(Warn_restart, 1) != 0)
! {
! /*
! * Make sure we're not interrupted while cleaning up. Also forget
! * any pending QueryCancel request, since we're aborting anyway.
! * Force InterruptHoldoffCount to a known state in case we
! * ereport'd from inside a holdoff section.
! */
! ImmediateInterruptOK = false;
! QueryCancelPending = false;
! InterruptHoldoffCount = 1;
! CritSectionCount = 0; /* should be unnecessary, but... */
!
! /*
! * These operations are really just a minimal subset of
! * AbortTransaction(). We don't have very many resources
! * to worry about in bgwriter, but we do have LWLocks and buffers.
! */
! LWLockReleaseAll();
! AbortBufferIO();
! UnlockBuffers();
!
! /*
! * Clear flag to indicate that we got out of error recovery mode
! * successfully. (Flag was set in elog.c before longjmp().)
! */
! InError = false;
!
! /*
! * Exit interrupt holdoff section we implicitly established above.
! */
! RESUME_INTERRUPTS();
!
! /*
! * Sleep at least 1 second after any error. A write error is
! * likely to be repeated, and we don't want to be filling the
! * error logs as fast as we can. (XXX think about ways to make
! * progress when the LRU dirty buffer cannot be written...)
! */
! pg_usleep(1000000L);
! }
!
! Warn_restart_ready = true; /* we can now handle ereport(ERROR) */
!
! /*
! * Unblock signals (they were blocked when the postmaster forked us)
! */
! PG_SETMASK(&UnBlockSig);
!
! AutoVacLoop();
}
+
+ /* --------------------------------
+ * signal handler routines
+ * --------------------------------
+ */
+
/*
! * autovac_quickdie() occurs when signalled SIGQUIT by the postmaster.
*
! * Some backend has bought the farm,
! * so we need to stop what we're doing and exit.
*/
static void
! autovac_quickdie(SIGNAL_ARGS)
{
! PG_SETMASK(&BlockSig);
! /*
! * DO NOT proc_exit() -- we're here because shared memory may be
! * corrupted, so we don't want to try to clean up our transaction.
! * Just nail the windows shut and get out of town.
! *
! * Note we do exit(1) not exit(0). This is to force the postmaster into
! * a system reset cycle if some idiot DBA sends a manual SIGQUIT to a
! * random backend. This is necessary precisely because we don't clean
! * up our shared memory state.
! */
! exit(1);
! }
! /* SIGHUP: set flag to re-read config file at next convenient time */
! static void
! AutoVacSigHupHandler(SIGNAL_ARGS)
! {
! got_SIGHUP = true;
! }
+ /* SIGUSR2: set flag to run a shutdown checkpoint and exit */
+ static void
+ ReqShutdownHandler(SIGNAL_ARGS)
+ {
+ shutdown_requested = true;
}
+
/* Create and return tbl_info struct with initialized to values from row or res */
static tbl_info *
init_table_info(PGresult *res, int row, db_info * dbi)
***************
*** 68,75 ****
if (!new_tbl)
{
! log_entry("init_table_info: Cannot get memory");
! fflush(LOGOUTPUT);
return NULL;
}
--- 226,232 ----
if (!new_tbl)
{
! elog(ERROR, "pg_autovacuum: init_table_info: Cannot get memory");
return NULL;
}
***************
*** 82,89 ****
malloc(strlen(PQgetvalue(res, row, PQfnumber(res, "schemaname"))) + 1);
if (!new_tbl->schema_name)
{
! log_entry("init_table_info: malloc failed on new_tbl->schema_name");
! fflush(LOGOUTPUT);
return NULL;
}
strcpy(new_tbl->schema_name,
--- 239,245 ----
malloc(strlen(PQgetvalue(res, row, PQfnumber(res, "schemaname"))) + 1);
if (!new_tbl->schema_name)
{
! elog(ERROR, "pg_autovacuum: init_table_info: malloc failed on new_tbl->schema_name");
return NULL;
}
strcpy(new_tbl->schema_name,
***************
*** 94,101 ****
strlen(new_tbl->schema_name) + 6);
if (!new_tbl->table_name)
{
! log_entry("init_table_info: malloc failed on new_tbl->table_name");
! fflush(LOGOUTPUT);
return NULL;
}
--- 250,256 ----
strlen(new_tbl->schema_name) + 6);
if (!new_tbl->table_name)
{
! elog(ERROR, "pg_autovacuum: init_table_info: malloc failed on new_tbl->table_name");
return NULL;
}
***************
*** 283,298 ****
* now in the tbl_list */
{
DLAddTail(dbi->table_list, DLNewElem(init_table_info(res, i, dbi)));
! if (args->debug >= 1)
! {
! sprintf(logbuffer, "added table: %s.%s", dbi->dbname,
! ((tbl_info *) DLE_VAL(DLGetTail(dbi->table_list)))->table_name);
! log_entry(logbuffer);
! }
}
} /* end of for loop that adds tables */
}
- fflush(LOGOUTPUT);
PQclear(res);
res = NULL;
if (args->debug >= 3)
--- 438,449 ----
* now in the tbl_list */
{
DLAddTail(dbi->table_list, DLNewElem(init_table_info(res, i, dbi)));
! sprintf(logbuffer, "pg_autovacuum: added table: %s.%s", dbi->dbname,
! ((tbl_info *) DLE_VAL(DLGetTail(dbi->table_list)))->table_name);
! elog(DEBUG1, logbuffer);
}
} /* end of for loop that adds tables */
}
PQclear(res);
res = NULL;
if (args->debug >= 3)
***************
*** 310,318 ****
if (args->debug >= 1)
{
! sprintf(logbuffer, "Removing table: %s from list.", tbl->table_name);
! log_entry(logbuffer);
! fflush(LOGOUTPUT);
}
DLRemove(tbl_to_remove);
--- 461,468 ----
if (args->debug >= 1)
{
! sprintf(logbuffer, "pg_autovacuum: Removing table: %s from list.", tbl->table_name);
! elog(DEBUG1, logbuffer);
}
DLRemove(tbl_to_remove);
***************
*** 366,386 ****
print_table_info(tbl_info * tbl)
{
sprintf(logbuffer, " table name: %s.%s", tbl->dbi->dbname, tbl->table_name);
! log_entry(logbuffer);
sprintf(logbuffer, " relid: %u; relisshared: %i", tbl->relid, tbl->relisshared);
! log_entry(logbuffer);
sprintf(logbuffer, " reltuples: %f; relpages: %u", tbl->reltuples, tbl->relpages);
! log_entry(logbuffer);
sprintf(logbuffer, " curr_analyze_count: %li; curr_vacuum_count: %li",
tbl->curr_analyze_count, tbl->curr_vacuum_count);
! log_entry(logbuffer);
sprintf(logbuffer, " last_analyze_count: %li; last_vacuum_count: %li",
tbl->CountAtLastAnalyze, tbl->CountAtLastVacuum);
! log_entry(logbuffer);
sprintf(logbuffer, " analyze_threshold: %li; vacuum_threshold: %li",
tbl->analyze_threshold, tbl->vacuum_threshold);
! log_entry(logbuffer);
! fflush(LOGOUTPUT);
}
/* End of table Management Functions */
--- 516,540 ----
print_table_info(tbl_info * tbl)
{
sprintf(logbuffer, " table name: %s.%s", tbl->dbi->dbname, tbl->table_name);
! elog(LOG, logbuffer);
!
sprintf(logbuffer, " relid: %u; relisshared: %i", tbl->relid, tbl->relisshared);
! elog(LOG, logbuffer);
!
sprintf(logbuffer, " reltuples: %f; relpages: %u", tbl->reltuples, tbl->relpages);
! elog(LOG, logbuffer);
!
sprintf(logbuffer, " curr_analyze_count: %li; curr_vacuum_count: %li",
tbl->curr_analyze_count, tbl->curr_vacuum_count);
! elog(LOG, logbuffer);
!
sprintf(logbuffer, " last_analyze_count: %li; last_vacuum_count: %li",
tbl->CountAtLastAnalyze, tbl->CountAtLastVacuum);
! elog(LOG, logbuffer);
!
sprintf(logbuffer, " analyze_threshold: %li; vacuum_threshold: %li",
tbl->analyze_threshold, tbl->vacuum_threshold);
! elog(LOG, logbuffer);
}
/* End of table Management Functions */
***************
*** 398,405 ****
DLAddHead(db_list, DLNewElem(init_dbinfo((char *) "template1", 0, 0)));
if (DLGetHead(db_list) == NULL)
{ /* Make sure init_dbinfo was successful */
! log_entry("init_db_list(): Error creating db_list for db: template1.");
! fflush(LOGOUTPUT);
return NULL;
}
--- 552,558 ----
DLAddHead(db_list, DLNewElem(init_dbinfo((char *) "template1", 0, 0)));
if (DLGetHead(db_list) == NULL)
{ /* Make sure init_dbinfo was successful */
! elog(ERROR, logbuffer);
return NULL;
}
***************
*** 476,486 ****
t = 0,
found_match = 0;
! if (args->debug >= 2)
! {
! log_entry("updating the database list");
! fflush(LOGOUTPUT);
! }
if (dbi_template1->conn == NULL)
{
--- 629,635 ----
t = 0,
found_match = 0;
! elog(DEBUG2, "pg_autovacuum: updating the database list");
if (dbi_template1->conn == NULL)
{
***************
*** 566,577 ****
if (args->debug >= 1)
{
sprintf(logbuffer, "added database: %s", ((db_info *) DLE_VAL(DLGetTail(db_list)))->dbname);
! log_entry(logbuffer);
}
}
! } /* end of for loop that adds tables */
}
- fflush(LOGOUTPUT);
PQclear(res);
res = NULL;
if (args->debug >= 3)
--- 715,725 ----
if (args->debug >= 1)
{
sprintf(logbuffer, "added database: %s", ((db_info *) DLE_VAL(DLGetTail(db_list)))->dbname);
! elog(DEBUG1, logbuffer);
}
}
! } /* end of for loop that adds tables */
}
PQclear(res);
res = NULL;
if (args->debug >= 3)
***************
*** 625,636 ****
{
db_info *dbi = ((db_info *) DLE_VAL(db_to_remove));
! if (args->debug >= 1)
! {
! sprintf(logbuffer, "Removing db: %s from list.", dbi->dbname);
! log_entry(logbuffer);
! fflush(LOGOUTPUT);
! }
DLRemove(db_to_remove);
if (dbi->conn)
db_disconnect(dbi);
--- 773,781 ----
{
db_info *dbi = ((db_info *) DLE_VAL(db_to_remove));
! sprintf(logbuffer, "pg_autovacuum: Removing db: %s from list.", dbi->dbname);
! elog(DEBUG1, logbuffer);
!
DLRemove(db_to_remove);
if (dbi->conn)
db_disconnect(dbi);
***************
*** 696,724 ****
print_db_info(db_info * dbi, int print_tbl_list)
{
sprintf(logbuffer, "dbname: %s", (dbi->dbname) ? dbi->dbname : "(null)");
! log_entry(logbuffer);
sprintf(logbuffer, " oid: %u", dbi->oid);
! log_entry(logbuffer);
sprintf(logbuffer, " username: %s", (dbi->username) ? dbi->username : "(null)");
! log_entry(logbuffer);
sprintf(logbuffer, " password: %s", (dbi->password) ? dbi->password : "(null)");
! log_entry(logbuffer);
if (dbi->conn != NULL)
! log_entry(" conn is valid, (connected)");
else
! log_entry(" conn is null, (not connected)");
sprintf(logbuffer, " default_analyze_threshold: %li", dbi->analyze_threshold);
! log_entry(logbuffer);
sprintf(logbuffer, " default_vacuum_threshold: %li", dbi->vacuum_threshold);
! log_entry(logbuffer);
- fflush(LOGOUTPUT);
if (print_tbl_list > 0)
print_table_list(dbi->table_list);
}
--- 841,868 ----
print_db_info(db_info * dbi, int print_tbl_list)
{
sprintf(logbuffer, "dbname: %s", (dbi->dbname) ? dbi->dbname : "(null)");
! elog(LOG, logbuffer);
sprintf(logbuffer, " oid: %u", dbi->oid);
! elog(LOG, logbuffer);
sprintf(logbuffer, " username: %s", (dbi->username) ? dbi->username : "(null)");
! elog(LOG, logbuffer);
sprintf(logbuffer, " password: %s", (dbi->password) ? dbi->password : "(null)");
! elog(LOG, logbuffer);
if (dbi->conn != NULL)
! elog(LOG, " conn is valid, (connected)");
else
! elog(LOG, " conn is null, (not connected)");
sprintf(logbuffer, " default_analyze_threshold: %li", dbi->analyze_threshold);
! elog(LOG, logbuffer);
sprintf(logbuffer, " default_vacuum_threshold: %li", dbi->vacuum_threshold);
! elog(LOG, logbuffer);
if (print_tbl_list > 0)
print_table_list(dbi->table_list);
}
***************
*** 739,746 ****
{
sprintf(logbuffer, "Failed connection to database %s with error: %s.",
dbi->dbname, PQerrorMessage(db_conn));
! log_entry(logbuffer);
! fflush(LOGOUTPUT);
PQfinish(db_conn);
db_conn = NULL;
}
--- 883,889 ----
{
sprintf(logbuffer, "Failed connection to database %s with error: %s.",
dbi->dbname, PQerrorMessage(db_conn));
! elog(LOG, logbuffer);
PQfinish(db_conn);
db_conn = NULL;
}
***************
*** 780,799 ****
if (dbi->conn == NULL)
return NULL;
! if (args->debug >= 4)
! log_entry(query);
res = PQexec(dbi->conn, query);
if (!res)
{
sprintf(logbuffer,
! "Fatal error occured while sending query (%s) to database %s",
query, dbi->dbname);
! log_entry(logbuffer);
sprintf(logbuffer, "The error is [%s]", PQresultErrorMessage(res));
! log_entry(logbuffer);
! fflush(LOGOUTPUT);
return NULL;
}
if (PQresultStatus(res) != PGRES_TUPLES_OK &&
--- 923,940 ----
if (dbi->conn == NULL)
return NULL;
! elog(DEBUG3, query);
res = PQexec(dbi->conn, query);
if (!res)
{
sprintf(logbuffer,
! "pg_autovacuum: Fatal error occured while sending query (%s) to database %s",
query, dbi->dbname);
! elog(ERROR, logbuffer);
sprintf(logbuffer, "The error is [%s]", PQresultErrorMessage(res));
! elog(ERROR, logbuffer);
return NULL;
}
if (PQresultStatus(res) != PGRES_TUPLES_OK &&
***************
*** 802,811 ****
sprintf(logbuffer,
"Can not refresh statistics information from the database %s.",
dbi->dbname);
! log_entry(logbuffer);
sprintf(logbuffer, "The error is [%s]", PQresultErrorMessage(res));
! log_entry(logbuffer);
! fflush(LOGOUTPUT);
PQclear(res);
return NULL;
}
--- 943,951 ----
sprintf(logbuffer,
"Can not refresh statistics information from the database %s.",
dbi->dbname);
! elog(ERROR, logbuffer);
sprintf(logbuffer, "The error is [%s]", PQresultErrorMessage(res));
! elog(ERROR, logbuffer);
PQclear(res);
return NULL;
}
***************
*** 829,835 ****
static cmd_args *
get_cmd_args(int argc, char *argv[])
{
! int c;
args = (cmd_args *) malloc(sizeof(cmd_args));
args->sleep_base_value = SLEEPBASEVALUE;
--- 969,975 ----
static cmd_args *
get_cmd_args(int argc, char *argv[])
{
! // int c;
args = (cmd_args *) malloc(sizeof(cmd_args));
args->sleep_base_value = SLEEPBASEVALUE;
***************
*** 838,845 ****
args->vacuum_scaling_factor = VACSCALINGFACTOR;
args->analyze_base_threshold = -1;
args->analyze_scaling_factor = -1;
! args->debug = AUTOVACUUM_DEBUG;
! args->daemonize = 0;
args->user = 0;
args->password = 0;
args->host = 0;
--- 978,985 ----
args->vacuum_scaling_factor = VACSCALINGFACTOR;
args->analyze_base_threshold = -1;
args->analyze_scaling_factor = -1;
! // args->debug = AUTOVACUUM_DEBUG;
! args->debug = 2;
args->user = 0;
args->password = 0;
args->host = 0;
***************
*** 850,856 ****
* Fixme: Should add some sanity checking such as positive integer
* values etc
*/
! while ((c = getopt(argc, argv, "s:S:v:V:a:A:d:U:P:H:L:p:hD")) != -1)
{
switch (c)
{
--- 990,996 ----
* Fixme: Should add some sanity checking such as positive integer
* values etc
*/
! /* while ((c = getopt(argc, argv, "s:S:v:V:a:A:d:U:P:H:L:p:hD")) != -1)
{
switch (c)
{
***************
*** 872,880 ****
case 'A':
args->analyze_scaling_factor = atof(optarg);
break;
- case 'D':
- args->daemonize++;
- break;
case 'd':
args->debug = atoi(optarg);
break;
--- 1012,1017 ----
***************
*** 897,999 ****
usage();
exit(0);
default:
!
/*
* It's here that we know that things are invalid... It is
* not forcibly an error to call usage
*/
! fprintf(stderr, "Error: Invalid Command Line Options.\n");
usage();
exit(1);
break;
}
-
- /*
- * if values for insert thresholds are not specified, then they
- * default to 1/2 of the delete values
- */
- if (args->analyze_base_threshold == -1)
- args->analyze_base_threshold = args->vacuum_base_threshold / 2;
- if (args->analyze_scaling_factor == -1)
- args->analyze_scaling_factor = args->vacuum_scaling_factor / 2;
}
return args;
}
- static void
- usage()
- {
- int i = 0;
- float f = 0;
-
- fprintf(stderr, "usage: pg_autovacuum \n");
- fprintf(stderr, " [-D] Daemonize (Detach from tty and run in the background)\n");
- i = AUTOVACUUM_DEBUG;
- fprintf(stderr, " [-d] debug (debug level=0,1,2,3; default=%i)\n", i);
-
- i = SLEEPBASEVALUE;
- fprintf(stderr, " [-s] sleep base value (default=%i)\n", i);
- f = SLEEPSCALINGFACTOR;
- fprintf(stderr, " [-S] sleep scaling factor (default=%f)\n", f);
-
- i = VACBASETHRESHOLD;
- fprintf(stderr, " [-v] vacuum base threshold (default=%i)\n", i);
- f = VACSCALINGFACTOR;
- fprintf(stderr, " [-V] vacuum scaling factor (default=%f)\n", f);
- i = i / 2;
- fprintf(stderr, " [-a] analyze base threshold (default=%i)\n", i);
- f = f / 2;
- fprintf(stderr, " [-A] analyze scaling factor (default=%f)\n", f);
-
- fprintf(stderr, " [-L] logfile (default=none)\n");
-
- fprintf(stderr, " [-U] username (libpq default)\n");
- fprintf(stderr, " [-P] password (libpq default)\n");
- fprintf(stderr, " [-H] host (libpq default)\n");
- fprintf(stderr, " [-p] port (libpq default)\n");
-
- fprintf(stderr, " [-h] help (Show this output)\n");
- }
-
- static void
- print_cmd_args()
- {
- sprintf(logbuffer, "Printing command_args");
- log_entry(logbuffer);
- sprintf(logbuffer, " args->host=%s", (args->host) ? args->host : "(null)");
- log_entry(logbuffer);
- sprintf(logbuffer, " args->port=%s", (args->port) ? args->port : "(null)");
- log_entry(logbuffer);
- sprintf(logbuffer, " args->username=%s", (args->user) ? args->user : "(null)");
- log_entry(logbuffer);
- sprintf(logbuffer, " args->password=%s", (args->password) ? args->password : "(null)");
- log_entry(logbuffer);
- sprintf(logbuffer, " args->logfile=%s", (args->logfile) ? args->logfile : "(null)");
- log_entry(logbuffer);
- sprintf(logbuffer, " args->daemonize=%i", args->daemonize);
- log_entry(logbuffer);
-
- sprintf(logbuffer, " args->sleep_base_value=%i", args->sleep_base_value);
- log_entry(logbuffer);
- sprintf(logbuffer, " args->sleep_scaling_factor=%f", args->sleep_scaling_factor);
- log_entry(logbuffer);
- sprintf(logbuffer, " args->vacuum_base_threshold=%i", args->vacuum_base_threshold);
- log_entry(logbuffer);
- sprintf(logbuffer, " args->vacuum_scaling_factor=%f", args->vacuum_scaling_factor);
- log_entry(logbuffer);
- sprintf(logbuffer, " args->analyze_base_threshold=%i", args->analyze_base_threshold);
- log_entry(logbuffer);
- sprintf(logbuffer, " args->analyze_scaling_factor=%f", args->analyze_scaling_factor);
- log_entry(logbuffer);
- sprintf(logbuffer, " args->debug=%i", args->debug);
- log_entry(logbuffer);
-
- fflush(LOGOUTPUT);
- }
/* Beginning of AutoVacuum Main Program */
! int
! main(int argc, char *argv[])
{
char buf[256];
int j = 0,
--- 1034,1065 ----
usage();
exit(0);
default:
! */
/*
* It's here that we know that things are invalid... It is
* not forcibly an error to call usage
*/
! /* fprintf(stderr, "Error: Invalid Command Line Options.\n");
usage();
exit(1);
break;
}
}
+ */
+ /*
+ * if values for insert thresholds are not specified, then they
+ * default to 1/2 of the delete values
+ */
+ if (args->analyze_base_threshold == -1)
+ args->analyze_base_threshold = args->vacuum_base_threshold / 2;
+ if (args->analyze_scaling_factor == -1)
+ args->analyze_scaling_factor = args->vacuum_scaling_factor / 2;
return args;
}
/* Beginning of AutoVacuum Main Program */
! void AutoVacLoop(void)
{
char buf[256];
int j = 0,
***************
*** 1011,1048 ****
struct timeval now,
then;
! args = get_cmd_args(argc, argv); /* Get Command Line Args and put
* them in the args struct */
- /* Dameonize if requested */
- if (args->daemonize == 1)
- daemonize();
-
- if (args->logfile)
- {
- LOGOUTPUT = fopen(args->logfile, "a");
- if (!LOGOUTPUT)
- {
- fprintf(stderr, "Could not open log file - [%s]\n", args->logfile);
- exit(-1);
- }
- }
- else
- LOGOUTPUT = stderr;
- if (args->debug >= 2)
- print_cmd_args();
-
/* Init the db list with template1 */
db_list = init_db_list();
if (db_list == NULL)
! return 1;
if (check_stats_enabled(((db_info *) DLE_VAL(DLGetHead(db_list)))) != 0)
{
! log_entry("Error: GUC variable stats_row_level must be enabled.");
! log_entry(" Please fix the problems and try again.");
! fflush(LOGOUTPUT);
!
exit(1);
}
--- 1077,1095 ----
struct timeval now,
then;
! // args = get_cmd_args(argc, argv); /* Get Command Line Args and put
! args = get_cmd_args(0, NULL); /* Get Command Line Args and put
* them in the args struct */
/* Init the db list with template1 */
db_list = init_db_list();
if (db_list == NULL)
! exit(1);
if (check_stats_enabled(((db_info *) DLE_VAL(DLGetHead(db_list)))) != 0)
{
! elog(ERROR, "pg_autovacuum: GUC variable stats_row_level must be enabled.");
! elog(ERROR, " Please fix the problems and try again.");
exit(1);
}
***************
*** 1050,1055 ****
--- 1097,1123 ----
while (1)
{ /* Main Loop */
+
+ /*
+ * Emergency bailout if postmaster has died. This is to avoid the
+ * necessity for manual cleanup of all postmaster children.
+ */
+ if (!PostmasterIsAlive(true))
+ exit(1);
+
+ if (got_SIGHUP)
+ {
+ got_SIGHUP = false;
+ ProcessConfigFile(PGC_SIGHUP);
+ }
+ if (shutdown_requested)
+ {
+ ShutdownXLOG(0, 0);
+ DumpFreeSpaceMap(0, 0);
+ /* Normal exit from the bgwriter is here */
+ proc_exit(0); /* done */
+ }
+
db_elem = DLGetHead(db_list); /* Reset cur_db_node to the
* beginning of the db_list */
***************
*** 1061,1069 ****
if (dbs->conn == NULL)
{ /* Serious problem: We can't connect to
* template1 */
! log_entry("Error: Cannot connect to template1, exiting.");
! fflush(LOGOUTPUT);
! fclose(LOGOUTPUT);
exit(1);
}
}
--- 1129,1135 ----
if (dbs->conn == NULL)
{ /* Serious problem: We can't connect to
* template1 */
! elog(ERROR, "pg_autovacuum: Cannot connect to template1, exiting.");
exit(1);
}
}
***************
*** 1134,1142 ****
snprintf(buf, sizeof(buf), "VACUUM ANALYZE %s", tbl->table_name);
if (args->debug >= 1)
{
! sprintf(logbuffer, "Performing: %s", buf);
! log_entry(logbuffer);
! fflush(LOGOUTPUT);
}
send_query(buf, dbs);
update_table_thresholds(dbs, tbl, VACUUM_ANALYZE);
--- 1200,1207 ----
snprintf(buf, sizeof(buf), "VACUUM ANALYZE %s", tbl->table_name);
if (args->debug >= 1)
{
! sprintf(logbuffer, "pg_autovacuum: Performing: %s", buf);
! elog(DEBUG1, logbuffer);
}
send_query(buf, dbs);
update_table_thresholds(dbs, tbl, VACUUM_ANALYZE);
***************
*** 1148,1156 ****
snprintf(buf, sizeof(buf), "ANALYZE %s", tbl->table_name);
if (args->debug >= 1)
{
! sprintf(logbuffer, "Performing: %s", buf);
! log_entry(logbuffer);
! fflush(LOGOUTPUT);
}
send_query(buf, dbs);
update_table_thresholds(dbs, tbl, ANALYZE_ONLY);
--- 1213,1220 ----
snprintf(buf, sizeof(buf), "ANALYZE %s", tbl->table_name);
if (args->debug >= 1)
{
! sprintf(logbuffer, "pg_autovacuum: Performing: %s", buf);
! elog(DEBUG1, logbuffer);
}
send_query(buf, dbs);
update_table_thresholds(dbs, tbl, ANALYZE_ONLY);
***************
*** 1187,1206 ****
sleep_secs = args->sleep_base_value + args->sleep_scaling_factor * diff / 1000000.0;
loops++;
! if (args->debug >= 2)
! {
! sprintf(logbuffer,
! "%i All DBs checked in: %.0f usec, will sleep for %i secs.",
! loops, diff, sleep_secs);
! log_entry(logbuffer);
! fflush(LOGOUTPUT);
! }
!
! sleep(sleep_secs); /* Larger Pause between outer loops */
gettimeofday(&then, 0); /* Reset time counter */
! } /* end of while loop */
/*
* program is exiting, this should never run, but is here to make
--- 1251,1269 ----
sleep_secs = args->sleep_base_value + args->sleep_scaling_factor * diff / 1000000.0;
loops++;
!
! sprintf(logbuffer,
! "pg_autovacuum: loop %i; All DBs checked in: %.0f usec, will sleep for %i secs.",
! loops, diff, sleep_secs);
! elog(DEBUG1, logbuffer);
!
! /* Larger Pause between outer loops */
! if (!(got_SIGHUP || shutdown_requested))
! pg_usleep((long)(sleep_secs * 1000000L));
gettimeofday(&then, 0); /* Reset time counter */
! } /* end of while loop */
/*
* program is exiting, this should never run, but is here to make
***************
*** 1208,1212 ****
*/
free_db_list(db_list);
free_cmd_args();
! return EXIT_SUCCESS;
}
--- 1271,1275 ----
*/
free_db_list(db_list);
free_cmd_args();
! exit(0);
}
*** ./src/backend/postmaster/postmaster.c.orig 2004-06-05 15:05:18.000000000 -0400
--- ./src/backend/postmaster/postmaster.c 2004-06-05 15:37:48.000000000 -0400
***************
*** 191,197 ****
/* PIDs of special child processes; 0 when not running */
static pid_t StartupPID = 0,
! BgWriterPID = 0;
/* Startup/shutdown state */
#define NoShutdown 0
--- 191,198 ----
/* PIDs of special child processes; 0 when not running */
static pid_t StartupPID = 0,
! BgWriterPID = 0,
! AutoVacPID = 0;
/* Startup/shutdown state */
#define NoShutdown 0
***************
*** 293,298 ****
--- 294,300 ----
#define StartupDataBase() StartChildProcess(BS_XLOG_STARTUP)
#define StartBackgroundWriter() StartChildProcess(BS_XLOG_BGWRITER)
+ #define StartAutoVac() StartChildProcess(BS_XLOG_AUTOVAC)
/*
***************
*** 780,786 ****
*
* CAUTION: when changing this list, check for side-effects on the signal
* handling setup of child processes. See tcop/postgres.c,
! * bootstrap/bootstrap.c, postmaster/bgwriter.c, and postmaster/pgstat.c.
*/
pqinitmask();
PG_SETMASK(&BlockSig);
--- 782,789 ----
*
* CAUTION: when changing this list, check for side-effects on the signal
* handling setup of child processes. See tcop/postgres.c,
! * bootstrap/bootstrap.c, postmaster/bgwriter.c, postmaster/pgstat.c,
! * and postmaster/pg_autovacuum.c.
*/
pqinitmask();
PG_SETMASK(&BlockSig);
***************
*** 1144,1149 ****
--- 1147,1165 ----
kill(BgWriterPID, SIGUSR2);
}
+ /*
+ * If no AutoVacuum process is running, and we are not in
+ * a state that prevents it, start one. It doesn't matter if this
+ * fails, we'll just try again later.
+ */
+ if (AutoVacPID == 0 && StartupPID == 0 && !FatalError)
+ {
+ AutoVacPID = StartAutoVac();
+ /* If shutdown is pending, set it going */
+ if (Shutdown > NoShutdown && AutoVacPID != 0)
+ kill(AutoVacPID, SIGUSR2);
+ }
+
/* If we have lost the stats collector, try to start a new one */
if (!pgstat_is_running)
pgstat_start();
***************
*** 1512,1517 ****
--- 1528,1540 ----
backendPID)));
return;
}
+ if (backendPID == AutoVacPID)
+ {
+ ereport(DEBUG2,
+ (errmsg_internal("ignoring cancel request for autovacuum process %d",
+ backendPID)));
+ return;
+ }
/*
* See if we have a matching backend. In the EXEC_BACKEND case, we
***************
*** 1694,1699 ****
--- 1717,1724 ----
SignalChildren(SIGHUP);
if (BgWriterPID != 0)
kill(BgWriterPID, SIGHUP);
+ if (AutoVacPID != 0)
+ kill(AutoVacPID, SIGHUP);
load_hba();
load_ident();
***************
*** 1751,1756 ****
--- 1776,1785 ----
/* And tell it to shut down */
if (BgWriterPID != 0)
kill(BgWriterPID, SIGUSR2);
+ /* I don't think we need to Start the autovac process if not running */
+ /* And tell it to shut down */
+ if (AutoVacPID != 0)
+ kill(AutoVacPID, SIGUSR2);
break;
case SIGINT:
***************
*** 1792,1797 ****
--- 1821,1829 ----
/* And tell it to shut down */
if (BgWriterPID != 0)
kill(BgWriterPID, SIGUSR2);
+ /* And tell it to shut down */
+ if (AutoVacPID != 0)
+ kill(AutoVacPID, SIGUSR2);
break;
case SIGQUIT:
***************
*** 1807,1812 ****
--- 1839,1846 ----
kill(StartupPID, SIGQUIT);
if (BgWriterPID != 0)
kill(BgWriterPID, SIGQUIT);
+ if (AutoVacPID != 0)
+ kill(AutoVacPID, SIGQUIT);
if (DLGetHead(BackendList))
SignalChildren(SIGQUIT);
ExitPostmaster(0);
***************
*** 1909,1914 ****
--- 1943,1961 ----
if (Shutdown > NoShutdown && BgWriterPID != 0)
kill(BgWriterPID, SIGUSR2);
+ /*
+ * Crank up the AutoVac. It doesn't matter if this
+ * fails, we'll just try again later.
+ */
+ Assert(AutoVacPID == 0);
+ AutoVacPID = StartAutoVac();
+
+ /*
+ * Go to shutdown mode if a shutdown request was pending.
+ */
+ if (Shutdown > NoShutdown && AutoVacPID != 0)
+ kill(AutoVacPID, SIGUSR2);
+
continue;
}
***************
*** 1950,1956 ****
* Wait for all children exit, then reset shmem and
* StartupDataBase.
*/
! if (DLGetHead(BackendList) || StartupPID != 0 || BgWriterPID != 0)
goto reaper_done;
ereport(LOG,
(errmsg("all server processes terminated; reinitializing")));
--- 1997,2003 ----
* Wait for all children exit, then reset shmem and
* StartupDataBase.
*/
! if (DLGetHead(BackendList) || StartupPID != 0 || BgWriterPID != 0 || AutoVacPID != 0)
goto reaper_done;
ereport(LOG,
(errmsg("all server processes terminated; reinitializing")));
***************
*** 1973,1978 ****
--- 2020,2028 ----
/* And tell it to shut down */
if (BgWriterPID != 0)
kill(BgWriterPID, SIGUSR2);
+ /* Tell AutoVac to shut down */
+ if (AutoVacPID != 0)
+ kill(AutoVacPID, SIGUSR2);
}
reaper_done:
***************
*** 2110,2115 ****
--- 2160,2179 ----
}
FatalError = true;
+
+ /* Take care of the autovac too */
+ if (pid == AutoVacPID)
+ AutoVacPID = 0;
+ else if (AutoVacPID != 0 && !FatalError)
+ {
+ ereport(DEBUG2,
+ (errmsg_internal("sending %s to process %d",
+ (SendStop ? "SIGSTOP" : "SIGQUIT"),
+ (int) AutoVacPID)));
+ kill(AutoVacPID, (SendStop ? SIGSTOP : SIGQUIT));
+ }
+
+ FatalError = true;
}
/*
***************
*** 3074,3079 ****
--- 3138,3147 ----
ereport(LOG,
(errmsg("could not fork background writer process: %m")));
break;
+ case BS_XLOG_AUTOVAC:
+ ereport(LOG,
+ (errmsg("could not fork auto vacuum process: %m")));
+ break;
default:
ereport(LOG,
(errmsg("could not fork process: %m")));
*** ./src/include/bootstrap/bootstrap.h.orig 2004-06-05 15:07:32.000000000 -0400
--- ./src/include/bootstrap/bootstrap.h 2004-06-05 15:07:04.000000000 -0400
***************
*** 59,63 ****
--- 59,64 ----
#define BS_XLOG_BOOTSTRAP 1
#define BS_XLOG_STARTUP 2
#define BS_XLOG_BGWRITER 3
+ #define BS_XLOG_AUTOVAC 4
#endif /* BOOTSTRAP_H */
*** ./src/include/postmaster/pg_autovacuum.h.orig 2004-06-07 00:40:16.000000000 -0400
--- ./src/include/postmaster/pg_autovacuum.h 2004-06-07 00:37:45.000000000 -0400
***************
*** 3,42 ****
* (c) 2003 Matthew T. O'Connor
*/
#include "postgres_fe.h"
#include <unistd.h>
- #ifdef HAVE_GETOPT_H
- #include <getopt.h>
- #endif
#include <time.h>
#include <sys/time.h>
! /* These next two lines are correct when pg_autovaccum is compiled
! from within the postgresql source tree */
! #include "libpq-fe.h"
#include "lib/dllist.h"
- /* Had to change the last two lines to compile on
- Redhat outside of postgresql source tree */
- /*
- #include "/usr/include/libpq-fe.h"
- #include "/usr/include/pgsql/server/lib/dllist.h"
- */
#define AUTOVACUUM_DEBUG 1
#define VACBASETHRESHOLD 1000
#define VACSCALINGFACTOR 2
! #define SLEEPBASEVALUE 300
#define SLEEPSCALINGFACTOR 2
! #define UPDATE_INTERVAL 2
/* these two constants are used to tell update_table_stats what operation we just perfomred */
#define VACUUM_ANALYZE 0
#define ANALYZE_ONLY 1
! #define TABLE_STATS_QUERY "select a.oid,a.relname,a.relnamespace,a.relpages,a.relisshared,a.reltuples,b.schemaname,b.n_tup_ins,b.n_tup_upd,b.n_tup_del from pg_class a, pg_stat_all_tables b where a.oid=b.relid and a.relkind = 'r' and schemaname not like 'pg_temp_%'"
!
! #define FRONTEND
#define PAGES_QUERY "select oid,reltuples,relpages from pg_class where oid=%u"
#define FROZENOID_QUERY "select oid,age(datfrozenxid) from pg_database where datname = 'template1'"
#define FROZENOID_QUERY2 "select oid,datname,age(datfrozenxid) from pg_database where datname!='template0'"
--- 3,35 ----
* (c) 2003 Matthew T. O'Connor
*/
+ #ifndef _AUTOVAC_H
+ #define _AUTOVAC_H
+
+ #define FRONTEND
#include "postgres_fe.h"
#include <unistd.h>
#include <time.h>
#include <sys/time.h>
! #include "../../interfaces/libpq/libpq-fe.h"
#include "lib/dllist.h"
+ /* default settings defined here */
#define AUTOVACUUM_DEBUG 1
#define VACBASETHRESHOLD 1000
#define VACSCALINGFACTOR 2
! #define SLEEPBASEVALUE 15
#define SLEEPSCALINGFACTOR 2
! #define UPDATE_INTERVAL 1
/* these two constants are used to tell update_table_stats what operation we just perfomred */
#define VACUUM_ANALYZE 0
#define ANALYZE_ONLY 1
! /* define the main queries */
! #define TABLE_STATS_QUERY "select a.oid, a.relname, a.relnamespace, a.relpages, a.relisshared, a.reltuples, b.schemaname, b.n_tup_ins, b.n_tup_upd, b.n_tup_del from pg_class a, pg_stat_all_tables b where a.oid=b.relid and a.relkind = 'r' and schemaname not like 'pg_temp_%'"
#define PAGES_QUERY "select oid,reltuples,relpages from pg_class where oid=%u"
#define FROZENOID_QUERY "select oid,age(datfrozenxid) from pg_database where datname = 'template1'"
#define FROZENOID_QUERY2 "select oid,datname,age(datfrozenxid) from pg_database where datname!='template0'"
***************
*** 107,116 ****
typedef struct tableinfo tbl_info;
/* Functions for dealing with command line arguements */
static cmd_args *get_cmd_args(int argc, char *argv[]);
- static void print_cmd_args(void);
static void free_cmd_args(void);
- static void usage(void);
/* Functions for managing database lists */
static Dllist *init_db_list(void);
--- 100,109 ----
typedef struct tableinfo tbl_info;
/* Functions for dealing with command line arguements */
+ void AutoVacMain(void);
+ void AutoVacLoop(void);
static cmd_args *get_cmd_args(int argc, char *argv[]);
static void free_cmd_args(void);
/* Functions for managing database lists */
static Dllist *init_db_list(void);
***************
*** 137,142 ****
static void db_disconnect(db_info * dbi);
static PGresult *send_query(const char *query, db_info * dbi);
! /* Other Generally needed Functions */
! static void daemonize(void);
! static void log_entry(const char *logentry);
--- 130,133 ----
static void db_disconnect(db_info * dbi);
static PGresult *send_query(const char *query, db_info * dbi);
! #endif /* _AUTOVAC_H_ */
*** ./src/include/storage/proc.h.orig 2004-06-05 15:31:36.000000000 -0400
--- ./src/include/storage/proc.h 2004-06-05 15:32:20.000000000 -0400
***************
*** 88,94 ****
#define DUMMY_PROC_DEFAULT 0
#define DUMMY_PROC_BGWRITER 1
! #define NUM_DUMMY_PROCS 2
/* configurable options */
--- 88,95 ----
#define DUMMY_PROC_DEFAULT 0
#define DUMMY_PROC_BGWRITER 1
! #define DUMMY_PROC_AUTOVAC 2
! #define NUM_DUMMY_PROCS 3
/* configurable options */
---------------------------(end of broadcast)---------------------------
TIP 7: don't forget to increase your free space map settings