Re: Autovacuum Integration Patch Take 5

From: "Matthew T(dot) O'Connor" <matthew(at)zeut(dot)net>
To: "Matthew T(dot) O'Connor" <matthew(at)zeut(dot)net>
Cc: PostgreSQL Patches <pgsql-patches(at)postgresql(dot)org>
Subject: Re: Autovacuum Integration Patch Take 5
Date: 2004-08-05 23:33:29
Message-ID: 4112C3C9.6090005@zeut.net
Views: Raw Message | Whole Thread | Download mbox | Resend email
Thread:
Lists: pgsql-patches

Just to reply to my own post here, I spent a few hours last night trying
to improve the shutdown sequence for the Autovacuum patch. I think I
was headed in the right direction, but unfortunatly I wasn't able to get
anything to a high enough quality where I could submit the patch.
Basically I changed the postmaster shutdown code to signal the
autovacuum process before it signals any of it's normal child processes,
then inside the autovacuum signal handler I was issuing a query cancel
in the current database connection is there was one. For some unknown
reason, when I went to shut down the autovacuum process by editing
postgresql.conf and signaling the postmaster to reread it, I would get
one proc_exit(0) in the log followed by several hundred proc_exit(1) and
it was too late for me to track this down. I can send in what I have so
far, but it's not pretty.

Unfortunately, I am not going to be available to work on this for the
next few days. I don't know what to do.

Thanks,

Matthew O'Connor

Matthew T. O'Connor wrote:

>Well I didn't get out of the office as early as I had hoped, and I have
>stayed up longer than I had planned, but I have a patch that addresses
>many of the issues raised by Tom. Please take a look at let me know if
>I'm heading in the right direction.
>
>Issues addressed:
>* Add ability to read username and password from autovac.passwd file in
>$PGDATA (format is username:password)
>* Update patch, make sure it doesn't revert the recent write_stderr
>changes in postmaster.c
>* Remove static from functions defined in pg_autovacuum.h
>* Remove if(sigsetjmp ...) code block
>* Removed improper exit()'s, replaced with proc_exit() or
>ereport(ERROR)
>* Change elog() calls to ereport()
>* Change elog(WARNING, "pg_autovacuum: autovac is enabled, but requires
>stats_row_level which is not enabled"); to only be called once on
>postmaster startup
>* Remove unneeded code that was cut and pasted from the bgwriter example
>
>Issues not addressed in this patch:
>* Dynamic linking of libpq: I need someone else to do this.
>* Autovacuum Shutdown: I'm going to take a stab at this tonight
>(Wednesday) we will see what I come up with.
>
>
>To apply this patch:
>1) Move pg_autovacuum.[ch] from contrib to
>src/backend/postmaster/autovacuum.c and
>src/include/postmaster/autovacuum.h respectively.
>2) Place the attached pg_autovacuum.h in src/include/catelog/
>3) Apply the attached diff file
>
>Thanks again,
>
>Matthew O'Connor
>
>
>
>
>------------------------------------------------------------------------
>
>/*-------------------------------------------------------------------------
> *
> * pg_autovacuum.h
> * definition of the system "autovacuum" relation (pg_autovacuum)
> *
> * NOTE: an object is identified by the OID of the row that primarily
> * defines the object, plus the OID of the table that that row appears in.
> * For example, a function is identified by the OID of its pg_proc row
> * plus the pg_class OID of table pg_proc. This allows unique identification
> * of objects without assuming that OIDs are unique across tables.
> *
> * Since attributes don't have OIDs of their own, we identify an attribute
> * comment by the objoid+classoid of its parent table, plus an "objsubid"
> * giving the attribute column number. "objsubid" must be zero in a comment
> * for a table itself, so that it is distinct from any column comment.
> * Currently, objsubid is unused and zero for all other kinds of objects,
> * but perhaps it might be useful someday to associate comments with
> * constituent elements of other kinds of objects (arguments of a function,
> * for example).
> *
> *
> * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
> * Portions Copyright (c) 1994, Regents of the University of California
> *
> * $PostgreSQL: pgsql-server/src/include/catalog/pg_autovacuum.h,v 1.20 2003/11/29 22:40:58 pgsql Exp $
> *
> * NOTES
> * the genbki.sh script reads this file and generates .bki
> * information from the DATA() statements.
> *
> * XXX do NOT break up DATA() statements into multiple lines!
> * the scripts are not as smart as you might think...
> *
> *-------------------------------------------------------------------------
> */
>#ifndef PG_AUTOVACUUM_H
>#define PG_AUTOVACUUM_H
>
>/* ----------------
> * postgres.h contains the system type definitions and the
> * CATALOG(), BOOTSTRAP and DATA() sugar words so this file
> * can be read by both genbki.sh and the C compiler.
> * ----------------
> */
>
>/* ----------------
> * pg_autovacuum definition. cpp turns this into
> * typedef struct FormData_pg_autovacuum
> * ----------------
> */
>CATALOG(pg_autovacuum) BKI_WITHOUT_OIDS
>{
> Oid table_oid; /* OID of table */
> int4 analyze_base_threshold; /* Base Threshold value */
> int4 vacuum_base_threshold; /* Base Threshold value */
> float4 analyze_scaling_factor; /* Threshold of ins/upd/del's before analyze */
> float4 vacuum_scaling_factor; /* Threshold of ins/upd/del's before vacuuum */
> float4 analyze_threshold; /* Threshold of ins/upd/del's before analyze */
> float4 vacuum_threshold; /* Threshold of ins/upd/del's before vacuuum */
> float4 cnt_at_last_analyze; /* equal to: inserts + updates as
> * of the last analyze or initial
> * values at startup */
> float4 cnt_at_last_vacuum; /* equal to: deletes + updates as
> * of the last vacuum or initial
> * values at startup */
>} FormData_pg_autovacuum;
>
>/* ----------------
> * Form_pg_autovacuum corresponds to a pointer to a tuple with
> * the format of pg_autovacuum relation.
> * ----------------
> */
>typedef FormData_pg_autovacuum *Form_pg_autovacuum;
>
>/* ----------------
> * compiler constants for pg_autovacuum
> * ----------------
> */
>#define Natts_pg_autovacuum 9
>#define Anum_pg_autovacuum_table_oid 1
>#define Anum_pg_autovacuum_analyze_base_threshold 2
>#define Anum_pg_autovacuum_vacuum_base_threshold 3
>#define Anum_pg_autovacuum_analyze_scaling_factor 4
>#define Anum_pg_autovacuum_vacuum_scaling_factor 5
>#define Anum_pg_autovacuum_analyze_threshold 6
>#define Anum_pg_autovacuum_vacuum_threshold 7
>#define Anum_pg_autovacuum_cnt_at_last_analyze 8
>#define Anum_pg_autovacuum_cnt_at_last_vacuum 9
>
>/* ----------------
> * initial contents of pg_autovacuum
> * ----------------
> */
>
>/*
> * Because the contents of this table are taken from the other *.h files,
> * there is no initialization here. The initial contents are extracted
> * by genbki.sh and loaded during initdb.
> */
>
>#endif /* PG_AUTOVACUUM_H */
>
>
>------------------------------------------------------------------------
>
>*** ./src/backend/bootstrap/bootstrap.c.orig 2004-08-04 01:41:29.044078073 -0400
>--- ./src/backend/bootstrap/bootstrap.c 2004-08-03 00:47:35.000000000 -0400
>***************
>*** 33,38 ****
>--- 33,39 ----
> #include "libpq/pqsignal.h"
> #include "miscadmin.h"
> #include "postmaster/bgwriter.h"
>+ #include "postmaster/autovacuum.h"
> #include "storage/freespace.h"
> #include "storage/ipc.h"
> #include "storage/pg_shmem.h"
>***************
>*** 361,366 ****
>--- 362,370 ----
> case BS_XLOG_BGWRITER:
> statmsg = "writer process";
> break;
>+ case BS_XLOG_AUTOVAC:
>+ statmsg = "auto vacuum process";
>+ break;
> default:
> statmsg = "??? process";
> break;
>***************
>*** 397,402 ****
>--- 401,409 ----
> case BS_XLOG_BGWRITER:
> InitDummyProcess(DUMMY_PROC_BGWRITER);
> break;
>+ case BS_XLOG_AUTOVAC:
>+ InitDummyProcess(DUMMY_PROC_AUTOVAC);
>+ break;
>
> default:
> InitDummyProcess(DUMMY_PROC_DEFAULT);
>***************
>*** 433,438 ****
>--- 440,451 ----
> 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/catalog/Makefile.orig 2004-08-04 01:41:52.546499796 -0400
>--- ./src/backend/catalog/Makefile 2004-07-22 23:50:31.000000000 -0400
>***************
>*** 32,38 ****
> pg_language.h pg_largeobject.h pg_aggregate.h pg_statistic.h \
> pg_rewrite.h pg_trigger.h pg_listener.h pg_description.h pg_cast.h \
> pg_namespace.h pg_conversion.h pg_database.h pg_shadow.h pg_group.h \
>! pg_tablespace.h pg_depend.h indexing.h \
> )
>
> pg_includes := $(sort -I$(top_srcdir)/src/include -I$(top_builddir)/src/include)
>--- 32,38 ----
> pg_language.h pg_largeobject.h pg_aggregate.h pg_statistic.h \
> pg_rewrite.h pg_trigger.h pg_listener.h pg_description.h pg_cast.h \
> pg_namespace.h pg_conversion.h pg_database.h pg_shadow.h pg_group.h \
>! pg_tablespace.h pg_depend.h pg_autovacuum.h indexing.h \
> )
>
> pg_includes := $(sort -I$(top_srcdir)/src/include -I$(top_builddir)/src/include)
>*** ./src/backend/Makefile.orig 2004-08-04 01:41:41.458716157 -0400
>--- ./src/backend/Makefile 2004-08-03 00:47:34.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/autovacuum.c.orig 2004-06-29 09:27:14.000000000 -0400
>--- ./src/backend/postmaster/autovacuum.c 2004-08-04 02:00:32.005810127 -0400
>***************
>*** 1,153 ****
>! /* 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)
>! {
>! tbl_info *new_tbl = (tbl_info *) malloc(sizeof(tbl_info));
>
>! if (!new_tbl)
>! {
>! log_entry("init_table_info: Cannot get memory");
>! fflush(LOGOUTPUT);
>! return NULL;
>! }
>
>! if (res == NULL)
>! return NULL;
>
>- new_tbl->dbi = dbi; /* set pointer to db */
>
>! new_tbl->schema_name = (char *)
>! 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,
>! PQgetvalue(res, row, PQfnumber(res, "schemaname")));
>
>! new_tbl->table_name = (char *)
>! malloc(strlen(PQgetvalue(res, row, PQfnumber(res, "relname"))) +
>! 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;
>! }
>
> /*
>! * Put both the schema and table name in quotes so that we can work
>! * with mixed case table names
> */
>! strcpy(new_tbl->table_name, "\"");
>! strcat(new_tbl->table_name, new_tbl->schema_name);
>! strcat(new_tbl->table_name, "\".\"");
>! strcat(new_tbl->table_name, PQgetvalue(res, row, PQfnumber(res, "relname")));
>! strcat(new_tbl->table_name, "\"");
>!
>! new_tbl->CountAtLastAnalyze =
>! (atol(PQgetvalue(res, row, PQfnumber(res, "n_tup_ins"))) +
>! atol(PQgetvalue(res, row, PQfnumber(res, "n_tup_upd"))) +
>! atol(PQgetvalue(res, row, PQfnumber(res, "n_tup_del"))));
>! new_tbl->curr_analyze_count = new_tbl->CountAtLastAnalyze;
>!
>! new_tbl->CountAtLastVacuum =
>! (atol(PQgetvalue(res, row, PQfnumber(res, "n_tup_del"))) +
>! atol(PQgetvalue(res, row, PQfnumber(res, "n_tup_upd"))));
>! new_tbl->curr_vacuum_count = new_tbl->CountAtLastVacuum;
>!
>! new_tbl->relid = atooid(PQgetvalue(res, row, PQfnumber(res, "oid")));
>! new_tbl->reltuples = atof(PQgetvalue(res, row, PQfnumber(res, "reltuples")));
>! new_tbl->relpages = atooid(PQgetvalue(res, row, PQfnumber(res, "relpages")));
>!
>! if (strcmp("t", PQgetvalue(res, row, PQfnumber(res, "relisshared"))))
>! new_tbl->relisshared = 0;
>! else
>! new_tbl->relisshared = 1;
>!
>! new_tbl->analyze_threshold =
>! args->analyze_base_threshold + args->analyze_scaling_factor * new_tbl->reltuples;
>! new_tbl->vacuum_threshold =
>! args->vacuum_base_threshold + args->vacuum_scaling_factor * new_tbl->reltuples;
>
>! if (args->debug >= 2)
>! print_table_info(new_tbl);
>
>! return new_tbl;
> }
>
> /* Set thresholds = base_value + scaling_factor * reltuples
> Should be called after a vacuum since vacuum updates values in pg_class */
>! static void
>! update_table_thresholds(db_info * dbi, tbl_info * tbl, int vacuum_type)
> {
>- PGresult *res = NULL;
> int disconnect = 0;
>! char query[128];
>
> if (dbi->conn == NULL)
> {
>--- 1,188 ----
>! /*-------------------------------------------------------------------------
>! *
>! * 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 proc_exit().
>! * Emergency termination is by SIGQUIT; like any
>! * backend, the autovacuum process will simply abort and proc_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 "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/autovacuum.h"
>
> char logbuffer[4096];
>
>! /*
>! * GUC parameters
>! */
>! bool autovacuum_start_daemon = false;
>! int autovacuum_vacuum_base = 1000;
>! double autovacuum_vacuum_scaling_factor = 2;
>! int autovacuum_analyze_base = 500;
>! double autovacuum_analyze_scaling_factor = 1;
>! int PostPortNumber = 5432;
>!
>! /*
>! * These used to be taken from command lines args
>! * and might need to move to GUC, but I'm not sure.
>! */
>! char *pg_user = NULL,
>! *pg_user_password = NULL;
>!
>! /*
>! * 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 void autovac_quickdie(SIGNAL_ARGS);
>! static void AutoVacSigHupHandler(SIGNAL_ARGS);
>! static void ReqShutdownHandler(SIGNAL_ARGS);
>!
>!
>! /*
>! * Main entry point for autovacuum sub-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 proc_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.
>! */
>
>! /*
>! * 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 proc_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;
> }
>
>+
> /* Set thresholds = base_value + scaling_factor * reltuples
> Should be called after a vacuum since vacuum updates values in pg_class */
>! void
>! update_table_thresholds(db_info * dbi, Oid table_oid, int vacuum_type)
> {
> int disconnect = 0;
>! char query[255];
>
> if (dbi->conn == NULL)
> {
>***************
>*** 157,215 ****
>
> if (dbi->conn != NULL)
> {
>! snprintf(query, sizeof(query), PAGES_QUERY, tbl->relid);
>! res = send_query(query, dbi);
>! if (res != NULL)
> {
>! tbl->reltuples =
>! atof(PQgetvalue(res, 0, PQfnumber(res, "reltuples")));
>! tbl->relpages = atooid(PQgetvalue(res, 0, PQfnumber(res, "relpages")));
>!
>! /*
>! * update vacuum thresholds only of we just did a vacuum
>! * analyze
>! */
>! if (vacuum_type == VACUUM_ANALYZE)
>! {
>! tbl->vacuum_threshold =
>! (args->vacuum_base_threshold + args->vacuum_scaling_factor * tbl->reltuples);
>! tbl->CountAtLastVacuum = tbl->curr_vacuum_count;
>! }
>!
>! /* update analyze thresholds */
>! tbl->analyze_threshold =
>! (args->analyze_base_threshold + args->analyze_scaling_factor * tbl->reltuples);
>! tbl->CountAtLastAnalyze = tbl->curr_analyze_count;
>!
>! PQclear(res);
>!
>! /*
>! * If the stats collector is reporting fewer updates then we
>! * have on record then the stats were probably reset, so we
>! * need to reset also
>! */
>! if ((tbl->curr_analyze_count < tbl->CountAtLastAnalyze) ||
>! (tbl->curr_vacuum_count < tbl->CountAtLastVacuum))
>! {
>! tbl->CountAtLastAnalyze = tbl->curr_analyze_count;
>! tbl->CountAtLastVacuum = tbl->curr_vacuum_count;
>! }
> }
> }
> if (disconnect)
> db_disconnect(dbi);
> }
>
>! static void
>! update_table_list(db_info * dbi)
> {
> int disconnect = 0;
>! PGresult *res = NULL;
>! tbl_info *tbl = NULL;
>! Dlelem *tbl_elem = DLGetHead(dbi->table_list);
>! int i = 0,
>! t = 0,
>! found_match = 0;
>
> if (dbi->conn == NULL)
> {
>--- 192,244 ----
>
> if (dbi->conn != NULL)
> {
>! /*
>! * update vacuum and analyze thresholds if
>! * we did a vacuum analyze
>! */
>! if (vacuum_type == VACUUM_ANALYZE)
> {
>! sprintf(query, "update pg_autovacuum set cnt_at_last_analyze = n_tup_ins + n_tup_upd, cnt_at_last_vacuum = n_tup_upd + n_tup_del from pg_stat_all_tables where table_oid = relid and relid = %u", table_oid);
>! send_query(query, dbi);
> }
>+ /*
>+ * update only the analyze thresholds if
>+ * we only did an analyze
>+ */
>+ else
>+ {
>+ sprintf(query, "update pg_autovacuum set cnt_at_last_analyze = n_tup_ins + n_tup_upd from pg_stat_all_tables where table_oid = relid and relid = %u", table_oid);
>+ send_query(query, dbi);
>+ }
>+
>+ /* FIXME: Need to think about this and pg_stat roll over issues */
>+ /*
>+ * If the stats collector is reporting fewer updates then we
>+ * have on record then the stats were probably reset, or overflowed,
>+ * so we need to reset also our numbers also
>+ */
>+ /* if ((tbl->curr_analyze_count < tbl->CountAtLastAnalyze) ||
>+ (tbl->curr_vacuum_count < tbl->CountAtLastVacuum))
>+ {
>+ tbl->CountAtLastAnalyze = tbl->curr_analyze_count;
>+ tbl->CountAtLastVacuum = tbl->curr_vacuum_count;
>+ }*/
> }
> if (disconnect)
> db_disconnect(dbi);
> }
>
>! void
>! update_pg_autovacuum_table(db_info *dbi)
> {
>+ /*
>+ * This function will update the pg_autovacuum system table in two steps:
>+ * 1) Delete entries that are no longer in pg_class an
>+ * 2) Add zero'd out entries to the pg_autovacuum table for new tables that exist in pg_class not in pg_autovacuum
>+ */
>+
> int disconnect = 0;
>! char query[4096];
>
> if (dbi->conn == NULL)
> {
>***************
>*** 219,394 ****
>
> if (dbi->conn != NULL)
> {
> /*
>! * Get a result set that has all the information we will need to
>! * both remove tables from the list that no longer exist and add
>! * tables to the list that are new
> */
>! res = send_query((char *) TABLE_STATS_QUERY, dbi);
>! if (res != NULL)
>! {
>! t = PQntuples(res);
>!
>! /*
>! * First: use the tbl_list as the outer loop and the result set as
>! * the inner loop, this will determine what tables should be
>! * removed
>! */
>! while (tbl_elem != NULL)
>! {
>! tbl = ((tbl_info *) DLE_VAL(tbl_elem));
>! found_match = 0;
>!
>! for (i = 0; i < t; i++)
>! { /* loop through result set looking for a
>! * match */
>! if (tbl->relid == atooid(PQgetvalue(res, i, PQfnumber(res, "oid"))))
>! {
>! found_match = 1;
>! break;
>! }
>! }
>! if (found_match == 0)
>! { /* then we didn't find this tbl_elem in
>! * the result set */
>! Dlelem *elem_to_remove = tbl_elem;
>!
>! tbl_elem = DLGetSucc(tbl_elem);
>! remove_table_from_list(elem_to_remove);
>! }
>! else
>! tbl_elem = DLGetSucc(tbl_elem);
>! } /* Done removing dropped tables from the
>! * table_list */
>!
>! /*
>! * Then loop use result set as outer loop and tbl_list as the
>! * inner loop to determine what tables are new
>! */
>! for (i = 0; i < t; i++)
>! {
>! tbl_elem = DLGetHead(dbi->table_list);
>! found_match = 0;
>! while (tbl_elem != NULL)
>! {
>! tbl = ((tbl_info *) DLE_VAL(tbl_elem));
>! if (tbl->relid == atooid(PQgetvalue(res, i, PQfnumber(res, "oid"))))
>! {
>! found_match = 1;
>! break;
>! }
>! tbl_elem = DLGetSucc(tbl_elem);
>! }
>! if (found_match == 0) /* then we didn't find this result
>! * 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)
>! print_table_list(dbi->table_list);
>! if (disconnect)
>! db_disconnect(dbi);
> }
> }
>
>- /* Free memory, and remove the node from the list */
>- static void
>- remove_table_from_list(Dlelem *tbl_to_remove)
>- {
>- tbl_info *tbl = ((tbl_info *) DLE_VAL(tbl_to_remove));
>-
>- if (args->debug >= 1)
>- {
>- sprintf(logbuffer, "Removing table: %s from list.", tbl->table_name);
>- log_entry(logbuffer);
>- fflush(LOGOUTPUT);
>- }
>- DLRemove(tbl_to_remove);
>-
>- if (tbl->schema_name)
>- {
>- free(tbl->schema_name);
>- tbl->schema_name = NULL;
>- }
>- if (tbl->table_name)
>- {
>- free(tbl->table_name);
>- tbl->table_name = NULL;
>- }
>- if (tbl)
>- {
>- free(tbl);
>- tbl = NULL;
>- }
>- DLFreeElem(tbl_to_remove);
>- }
>-
>- /* Free the entire table list */
>- static void
>- free_tbl_list(Dllist *tbl_list)
>- {
>- Dlelem *tbl_elem = DLGetHead(tbl_list);
>- Dlelem *tbl_elem_to_remove = NULL;
>-
>- while (tbl_elem != NULL)
>- {
>- tbl_elem_to_remove = tbl_elem;
>- tbl_elem = DLGetSucc(tbl_elem);
>- remove_table_from_list(tbl_elem_to_remove);
>- }
>- DLFreeList(tbl_list);
>- }
>-
>- static void
>- print_table_list(Dllist *table_list)
>- {
>- Dlelem *table_elem = DLGetHead(table_list);
>-
>- while (table_elem != NULL)
>- {
>- print_table_info(((tbl_info *) DLE_VAL(table_elem)));
>- table_elem = DLGetSucc(table_elem);
>- }
>- }
>-
>- static void
>- 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 */
>
> /* Beginning of DB Management Functions */
>
> /* init_db_list() creates the db_list and initalizes template1 */
>! static Dllist *
> init_db_list()
> {
> Dllist *db_list = DLNewList();
>--- 248,276 ----
>
> if (dbi->conn != NULL)
> {
>+ /*
>+ * Delete entries in pg_autovacuum that are no longer in pg_class
>+ */
>+ send_query("DELETE from pg_autovacuum where table_oid not in (select relid from pg_stat_all_tables )",dbi);
>+
> /*
>! * Insert entires into pg_autovacuum for new tables (ones that exist in pg_class / pg_stat.., but not in pg_autovacuum)
>! * and fill in defaut values for these new tables.
>! * then add them to the table list.
> */
>! sprintf(query, "insert into pg_autovacuum (table_oid, analyze_base_threshold, vacuum_base_threshold, analyze_scaling_factor, vacuum_scaling_factor, analyze_threshold, vacuum_threshold, cnt_at_last_analyze, cnt_at_last_vacuum) select a.oid, current_setting('autovacuum_analyze_threshold_base')::int, current_setting('autovacuum_vacuum_threshold_base')::int, current_setting('autovacuum_analyze_threshold_sf')::float, current_setting('autovacuum_vacuum_threshold_sf')::float, -1, -1, 0, 0 from pg_class a inner join pg_stat_all_tables b on a.oid=b.relid left outer join pg_autovacuum c on a.oid = c.table_oid where a.relkind = 'r' and schemaname not like 'pg_temp_%%' and a.oid not in (select distinct table_oid from pg_autovacuum)");
>! elog(DEBUG5, query);
>! send_query(query,dbi);
> }
> }
>
>
> /* End of table Management Functions */
>
> /* Beginning of DB Management Functions */
>
> /* init_db_list() creates the db_list and initalizes template1 */
>! Dllist *
> init_db_list()
> {
> Dllist *db_list = DLNewList();
>***************
>*** 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;
> }
>
>--- 280,287 ----
> DLAddHead(db_list, DLNewElem(init_dbinfo((char *) "template1", 0, 0)));
> if (DLGetHead(db_list) == NULL)
> { /* Make sure init_dbinfo was successful */
>! ereport(ERROR,(errmsg("pg_autovacuum: Error initializing db_list")));
>!
> return NULL;
> }
>
>***************
>*** 419,427 ****
> dbs->age = atol(PQgetvalue(res, 0, PQfnumber(res, "age")));
> if (res)
> PQclear(res);
>-
>- if (args->debug >= 2)
>- print_db_list(db_list, 0);
> }
> else
> return NULL;
>--- 301,306 ----
>***************
>*** 431,470 ****
>
> /* Simple function to create an instance of the dbinfo struct
> Initalizes all the pointers and connects to the database */
>! static db_info *
> init_dbinfo(char *dbname, Oid oid, long age)
> {
> db_info *newdbinfo = (db_info *) malloc(sizeof(db_info));
>
>- newdbinfo->analyze_threshold = args->vacuum_base_threshold;
>- newdbinfo->vacuum_threshold = args->analyze_base_threshold;
> newdbinfo->dbname = (char *) malloc(strlen(dbname) + 1);
> strcpy(newdbinfo->dbname, dbname);
> newdbinfo->username = NULL;
>! if (args->user != NULL)
> {
>! newdbinfo->username = (char *) malloc(strlen(args->user) + 1);
>! strcpy(newdbinfo->username, args->user);
> }
> newdbinfo->password = NULL;
>! if (args->password != NULL)
> {
>! newdbinfo->password = (char *) malloc(strlen(args->password) + 1);
>! strcpy(newdbinfo->password, args->password);
> }
> newdbinfo->oid = oid;
> newdbinfo->age = age;
>- newdbinfo->table_list = DLNewList();
> newdbinfo->conn = NULL;
>
>- if (args->debug >= 2)
>- print_table_list(newdbinfo->table_list);
>-
> return newdbinfo;
> }
>
> /* Function adds and removes databases from the db_list as appropriate */
>! static void
> update_db_list(Dllist *db_list)
> {
> int disconnect = 0;
>--- 310,345 ----
>
> /* Simple function to create an instance of the dbinfo struct
> Initalizes all the pointers and connects to the database */
>! db_info *
> init_dbinfo(char *dbname, Oid oid, long age)
> {
> db_info *newdbinfo = (db_info *) malloc(sizeof(db_info));
>
> newdbinfo->dbname = (char *) malloc(strlen(dbname) + 1);
> strcpy(newdbinfo->dbname, dbname);
> newdbinfo->username = NULL;
>! if (pg_user != NULL)
> {
>! newdbinfo->username = (char *) malloc(strlen(pg_user) + 1);
>! strcpy(newdbinfo->username, pg_user);
> }
>+
> newdbinfo->password = NULL;
>! if (pg_user_password != NULL)
> {
>! newdbinfo->password = (char *) malloc(strlen(pg_user_password) + 1);
>! strcpy(newdbinfo->password, pg_user_password);
> }
>+
> newdbinfo->oid = oid;
> newdbinfo->age = age;
> newdbinfo->conn = NULL;
>
> return newdbinfo;
> }
>
> /* Function adds and removes databases from the db_list as appropriate */
>! void
> update_db_list(Dllist *db_list)
> {
> int disconnect = 0;
>***************
>*** 476,486 ****
> t = 0,
> found_match = 0;
>
>! if (args->debug >= 2)
>! {
>! log_entry("updating the database list");
>! fflush(LOGOUTPUT);
>! }
>
> if (dbi_template1->conn == NULL)
> {
>--- 351,357 ----
> t = 0,
> found_match = 0;
>
>! ereport(DEBUG2,(errmsg("pg_autovacuum: updating the database list")));
>
> if (dbi_template1->conn == NULL)
> {
>***************
>*** 563,581 ****
> (PQgetvalue(res, i, PQfnumber(res, "datname")),
> atooid(PQgetvalue(res, i, PQfnumber(res, "oid"))),
> atol(PQgetvalue(res, i, PQfnumber(res, "age"))))));
>! 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)
>! print_db_list(db_list, 0);
> if (disconnect)
> db_disconnect(dbi_template1);
> }
>--- 434,446 ----
> (PQgetvalue(res, i, PQfnumber(res, "datname")),
> atooid(PQgetvalue(res, i, PQfnumber(res, "oid"))),
> atol(PQgetvalue(res, i, PQfnumber(res, "age"))))));
>! ereport(DEBUG1,(errmsg("pg_autovacuum: added database %s", ((db_info *) DLE_VAL(DLGetTail(db_list)))->dbname)));
> }
>! } /* end of for loop that adds tables */
> }
> PQclear(res);
> res = NULL;
>! print_db_list(db_list, 0);
> if (disconnect)
> db_disconnect(dbi_template1);
> }
>***************
>*** 595,601 ****
> return 0 if nothing happened,
> return 1 if the database needed a database wide vacuum
> */
>! static int
> xid_wraparound_check(db_info * dbi)
> {
> /*
>--- 460,466 ----
> return 0 if nothing happened,
> return 1 if the database needed a database wide vacuum
> */
>! int
> xid_wraparound_check(db_info * dbi)
> {
> /*
>***************
>*** 620,636 ****
> }
>
> /* Close DB connection, free memory, and remove the node from the list */
>! static void
> remove_db_from_list(Dlelem *db_to_remove)
> {
> 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);
>--- 485,497 ----
> }
>
> /* Close DB connection, free memory, and remove the node from the list */
>! void
> remove_db_from_list(Dlelem *db_to_remove)
> {
> db_info *dbi = ((db_info *) DLE_VAL(db_to_remove));
>
>! ereport(DEBUG1,(errmsg("pg_autovacuum: Removing db: %s from list.", dbi->dbname)));
>!
> DLRemove(db_to_remove);
> if (dbi->conn)
> db_disconnect(dbi);
>***************
>*** 649,659 ****
> free(dbi->password);
> dbi->password = NULL;
> }
>- if (dbi->table_list)
>- {
>- free_tbl_list(dbi->table_list);
>- dbi->table_list = NULL;
>- }
> if (dbi)
> {
> free(dbi);
>--- 510,515 ----
>***************
>*** 664,670 ****
>
> /* Function is called before program exit to free all memory
> mostly it's just to keep valgrind happy */
>! static void
> free_db_list(Dllist *db_list)
> {
> Dlelem *db_elem = DLGetHead(db_list);
>--- 520,526 ----
>
> /* Function is called before program exit to free all memory
> mostly it's just to keep valgrind happy */
>! void
> free_db_list(Dllist *db_list)
> {
> Dlelem *db_elem = DLGetHead(db_list);
>***************
>*** 680,686 ****
> DLFreeList(db_list);
> }
>
>! static void
> print_db_list(Dllist *db_list, int print_table_lists)
> {
> Dlelem *db_elem = DLGetHead(db_list);
>--- 536,542 ----
> DLFreeList(db_list);
> }
>
>! void
> print_db_list(Dllist *db_list, int print_table_lists)
> {
> Dlelem *db_elem = DLGetHead(db_list);
>***************
>*** 692,726 ****
> }
> }
>
>! static void
> 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);
> }
>
> /* End of DB List Management Function */
>--- 548,565 ----
> }
> }
>
>! void
> print_db_info(db_info * dbi, int print_tbl_list)
> {
>! ereport(DEBUG3,(errmsg("pg_autovacuum: dbname = %s", (dbi->dbname) ? dbi->dbname : "(null)")));
>! ereport(DEBUG3,(errmsg(" oid: %u", dbi->oid)));
>! ereport(DEBUG3,(errmsg(" username: %s", (dbi->username) ? dbi->username : "(null)")));
>! ereport(DEBUG3,(errmsg(" password: %s", (dbi->password) ? dbi->password : "(null)")));
>
> if (dbi->conn != NULL)
>! ereport(DEBUG3,(errmsg(" conn is valid, (connected)")));
> else
>! ereport(DEBUG3,(errmsg(" conn is null, (not connected)")));
> }
>
> /* End of DB List Management Function */
>***************
>*** 728,753 ****
> /* Beginning of misc Functions */
>
> /* Perhaps add some test to this function to make sure that the stats we need are available */
>! static PGconn *
> db_connect(db_info * dbi)
> {
> PGconn *db_conn =
>! PQsetdbLogin(args->host, args->port, NULL, NULL, dbi->dbname,
> dbi->username, dbi->password);
>
> if (PQstatus(db_conn) != CONNECTION_OK)
> {
>! 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;
> }
> return db_conn;
> } /* end of db_connect() */
>
>! static void
> db_disconnect(db_info * dbi)
> {
> if (dbi->conn != NULL)
>--- 567,590 ----
> /* Beginning of misc Functions */
>
> /* Perhaps add some test to this function to make sure that the stats we need are available */
>! PGconn *
> db_connect(db_info * dbi)
> {
> PGconn *db_conn =
>! PQsetdbLogin(NULL,NULL, NULL, NULL, dbi->dbname,
> dbi->username, dbi->password);
>
> if (PQstatus(db_conn) != CONNECTION_OK)
> {
>! ereport(LOG,(errmsg("pg_autovacuum: Failed connection to database %s with error: %s.",
>! dbi->dbname, PQerrorMessage(db_conn))));
> PQfinish(db_conn);
> db_conn = NULL;
> }
> return db_conn;
> } /* end of db_connect() */
>
>! void
> db_disconnect(db_info * dbi)
> {
> if (dbi->conn != NULL)
>***************
>*** 757,778 ****
> }
> }
>
>! static int
>! check_stats_enabled(db_info * dbi)
>! {
>! PGresult *res;
>! int ret = 0;
>!
>! res = send_query("SHOW stats_row_level", dbi);
>! if (res != NULL)
>! {
>! ret = strcmp("on", PQgetvalue(res, 0, PQfnumber(res, "stats_row_level")));
>! PQclear(res);
>! }
>! return ret;
>! }
>!
>! static PGresult *
> send_query(const char *query, db_info * dbi)
> {
> PGresult *res;
>--- 594,600 ----
> }
> }
>
>! PGresult *
> send_query(const char *query, db_info * dbi)
> {
> PGresult *res;
>***************
>*** 780,811 ****
> 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 &&
> PQresultStatus(res) != PGRES_COMMAND_OK)
> {
>! 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;
> }
>--- 602,622 ----
> if (dbi->conn == NULL)
> return NULL;
>
>! elog(DEBUG5, query);
>
> res = PQexec(dbi->conn, query);
>
> if (!res)
> {
>! ereport(ERROR,(errmsg("pg_autovacuum: Fatal error occured while sending query (%s) to database %s; The error is [%s]",
>! query, dbi->dbname, PQresultErrorMessage(res))));
> return NULL;
> }
> if (PQresultStatus(res) != PGRES_TUPLES_OK &&
> PQresultStatus(res) != PGRES_COMMAND_OK)
> {
>! ereport(ERROR,(errmsg("pg_autovacuum: Fatal error occured while sending query (%s) to database %s; The error is [%s]",
>! query, dbi->dbname, PQresultErrorMessage(res))));
> PQclear(res);
> return NULL;
> }
>***************
>*** 813,1055 ****
> } /* End of send_query() */
>
>
> static void
>! free_cmd_args()
> {
>! if (args != NULL)
> {
>! if (args->user != NULL)
>! free(args->user);
>! if (args->password != NULL)
>! free(args->password);
>! free(args);
> }
>! }
>!
>! static cmd_args *
>! get_cmd_args(int argc, char *argv[])
>! {
>! int c;
>
>! args = (cmd_args *) malloc(sizeof(cmd_args));
>! args->sleep_base_value = SLEEPBASEVALUE;
>! args->sleep_scaling_factor = SLEEPSCALINGFACTOR;
>! args->vacuum_base_threshold = VACBASETHRESHOLD;
>! 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;
>! args->logfile = 0;
>! args->port = 0;
>
>! /*
>! * 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)
>! {
>! case 's':
>! args->sleep_base_value = atoi(optarg);
>! break;
>! case 'S':
>! args->sleep_scaling_factor = atof(optarg);
>! break;
>! case 'v':
>! args->vacuum_base_threshold = atoi(optarg);
>! break;
>! case 'V':
>! args->vacuum_scaling_factor = atof(optarg);
>! break;
>! case 'a':
>! args->analyze_base_threshold = atoi(optarg);
>! break;
>! case 'A':
>! args->analyze_scaling_factor = atof(optarg);
>! break;
>! case 'D':
>! args->daemonize++;
>! break;
>! case 'd':
>! args->debug = atoi(optarg);
>! break;
>! case 'U':
>! args->user = optarg;
>! break;
>! case 'P':
>! args->password = optarg;
>! break;
>! case 'H':
>! args->host = optarg;
>! break;
>! case 'L':
>! args->logfile = optarg;
>! break;
>! case 'p':
>! args->port = optarg;
>! break;
>! case 'h':
>! 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,
> loops = 0;
>-
>- /* int numInserts, numDeletes, */
> int sleep_secs;
> Dllist *db_list;
>! Dlelem *db_elem,
>! *tbl_elem;
> db_info *dbs;
>- tbl_info *tbl;
> PGresult *res = NULL;
> double diff;
> 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);
>- }
>-
> gettimeofday(&then, 0); /* for use later to caluculate sleep time */
>
> while (1)
>! { /* Main Loop */
> db_elem = DLGetHead(db_list); /* Reset cur_db_node to the
> * beginning of the db_list */
>
>--- 624,768 ----
> } /* End of send_query() */
>
>
>+ /* Get a password from the password file. */
> static void
>! GetAutovacuumUserAndPasswordFromFile()
> {
>! FILE *fp;
>! #define LINELEN NAMEDATALEN*2
>! char buf[LINELEN];
>! char *pgpassfile,
>! *t = buf,
>! *username,
>! *password;
>! int len;
>! struct stat stat_buf;
>!
>!
>! pgpassfile = malloc(strlen(DataDir) + 1 + strlen(AUTOVACPASSFILE) + 1);
>! if (!pgpassfile)
> {
>! printf("out of memory");
>! return;
> }
>! elog(DEBUG3,"pg_autovacuum: autovac.passwd should be: %s/%s", DataDir, AUTOVACPASSFILE);
>
>! sprintf(pgpassfile, "%s/%s", DataDir, AUTOVACPASSFILE);
>
>! /* If password file cannot be opened, ignore it. */
>! if (stat(pgpassfile, &stat_buf) == -1)
> {
>! elog(DEBUG3,"pg_autovacuum: %s cannot be opened",pgpassfile);
>! free(pgpassfile);
>! return;
>! }
>
>! #ifndef WIN32
>! /* If password file is insecure, alert the user and ignore it. */
>! if (stat_buf.st_mode & (S_IRWXG | S_IRWXO))
>! {
>! ereport(WARNING,(errmsg("pg_autovacuum: Password file %s has world or group read access; permission should be u=rw (0600)",pgpassfile)));
>! free(pgpassfile);
>! return;
> }
>! #endif
>
>! fp = fopen(pgpassfile, "r");
>! free(pgpassfile);
>! if (fp == NULL)
>! return;
>
>! fgets(buf, LINELEN - 1, fp);
>
>! len = strlen(buf);
>! if (len < 3)
>! return;
>!
>! /* Remove trailing newline */
>! if (buf[len - 1] == '\n')
>! buf[len - 1] = 0;
>
>! username = t; /* Password is always first */
>! if(*username == ':')
>! username = NULL;
>!
>! password = NULL;
>! while(*t != NULL) /* Find : since it marks the beginning of the password */
>! {
>! if (*(t+1) == ':' )
>! {
>! *(t+1) = NULL;
>! password = t+2;
>! }
>! t++;
>! }
>!
>! ereport(DEBUG3,(errmsg("pg_autovacuum: username from autovac.passwd file is: %s ",username)));
>! fclose(fp);
>!
>! /* Now if we have anything for usename and password we set the pg_user and pg_passwd globals */
>! if((username != NULL) && (strlen(username) > 1) && (strlen(username) < NAMEDATALEN))
>! {
>! pg_user = malloc(strlen(username) + 1);
>! strcpy(pg_user,username);
>! }
>! if((password != NULL) && (strlen(password) > 1) && (strlen(password) < NAMEDATALEN))
>! {
>! pg_user_password = malloc(strlen(password) + 1);
>! strcpy(pg_user_password,password);
>! }
>! return;
>!
>! #undef LINELEN
>
> }
>
>+
> /* Beginning of AutoVacuum Main Program */
>! void AutoVacLoop(void)
> {
> char buf[256];
> int j = 0,
> loops = 0;
> int sleep_secs;
> Dllist *db_list;
>! Dlelem *db_elem;
> db_info *dbs;
> PGresult *res = NULL;
> double diff;
> struct timeval now,
> then;
>!
>! GetAutovacuumUserAndPasswordFromFile();
>!
> /* Init the db list with template1 */
> db_list = init_db_list();
> if (db_list == NULL)
>! ereport(ERROR,(errmsg("pg_autovacuum: Error creating db_list, exiting.")));
>
> gettimeofday(&then, 0); /* for use later to caluculate sleep time */
>
>+ /* Main Loop */
> while (1)
>! {
>! /*
>! * 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)
>! {
>! /* Normal exit from pg_autovacuum is here */
>! proc_exit(0); /* done */
>! }
>!
> db_elem = DLGetHead(db_list); /* Reset cur_db_node to the
> * beginning of the db_list */
>
>***************
>*** 1061,1095 ****
> 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);
> }
> }
>
>! if (loops % UPDATE_INTERVAL == 0) /* Update the list if it's
>! * time */
>! update_db_list(db_list); /* Add and remove databases from
>! * the list */
>!
> while (db_elem != NULL)
>! { /* Loop through databases in list */
> dbs = ((db_info *) DLE_VAL(db_elem)); /* get pointer to
> * cur_db's db_info
> * struct */
> if (dbs->conn == NULL)
> dbs->conn = db_connect(dbs);
>!
> if (dbs->conn != NULL)
> {
>- if (loops % UPDATE_INTERVAL == 0) /* Update the list if
>- * it's time */
>- update_table_list(dbs); /* Add and remove tables
>- * from the list */
>-
> if (xid_wraparound_check(dbs) == 0)
> {
> res = send_query(TABLE_STATS_QUERY, dbs); /* Get an updated
> * snapshot of this dbs
> * table stats */
>--- 774,806 ----
> if (dbs->conn == NULL)
> { /* Serious problem: We can't connect to
> * template1 */
>! ereport(ERROR,(errmsg("pg_autovacuum: Cannot connect to template1, exiting.")));
> }
> }
>
>! update_db_list(db_list); /* Add and remove databases from
>! * the list */
>!
>! /* Loop through databases in list */
> while (db_elem != NULL)
>! {
> dbs = ((db_info *) DLE_VAL(db_elem)); /* get pointer to
> * cur_db's db_info
> * struct */
> if (dbs->conn == NULL)
> dbs->conn = db_connect(dbs);
>!
> if (dbs->conn != NULL)
> {
> if (xid_wraparound_check(dbs) == 0)
> {
>+ long curr_vacuum_count, curr_analyze_count;
>+ long cnt_at_last_vacuum, cnt_at_last_analyze;
>+ float vacuum_threshold, analyze_threshold;
>+ char table_name[512];
>+
>+ update_pg_autovacuum_table(dbs);
>+
> res = send_query(TABLE_STATS_QUERY, dbs); /* Get an updated
> * snapshot of this dbs
> * table stats */
>***************
>*** 1097,1174 ****
> {
> for (j = 0; j < PQntuples(res); j++)
> { /* loop through result set */
>! tbl_elem = DLGetHead(dbs->table_list); /* Reset tbl_elem to top
>! * of dbs->table_list */
>! while (tbl_elem != NULL)
>! { /* Loop through tables in list */
>! tbl = ((tbl_info *) DLE_VAL(tbl_elem)); /* set tbl_info =
>! * current_table */
>! if (tbl->relid == atooid(PQgetvalue(res, j, PQfnumber(res, "oid"))))
>! {
>! tbl->curr_analyze_count =
>! (atol(PQgetvalue(res, j, PQfnumber(res, "n_tup_ins"))) +
>! atol(PQgetvalue(res, j, PQfnumber(res, "n_tup_upd"))) +
>! atol(PQgetvalue(res, j, PQfnumber(res, "n_tup_del"))));
>! tbl->curr_vacuum_count =
>! (atol(PQgetvalue(res, j, PQfnumber(res, "n_tup_del"))) +
>! atol(PQgetvalue(res, j, PQfnumber(res, "n_tup_upd"))));
>!
>! /*
>! * Check numDeletes to see if we need to
>! * vacuum, if so: Run vacuum analyze
>! * (adding analyze is small so we might as
>! * well) Update table thresholds and
>! * related information if numDeletes is
>! * not big enough for vacuum then check
>! * numInserts for analyze
>! */
>! if (tbl->curr_vacuum_count - tbl->CountAtLastVacuum >= tbl->vacuum_threshold)
>! {
>! /*
>! * if relisshared = t and database !=
>! * template1 then only do an analyze
>! */
>! if (tbl->relisshared > 0 && strcmp("template1", dbs->dbname))
>! snprintf(buf, sizeof(buf), "ANALYZE %s", tbl->table_name);
>! else
>! 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);
>! if (args->debug >= 2)
>! print_table_info(tbl);
>! }
>! else if (tbl->curr_analyze_count - tbl->CountAtLastAnalyze >= tbl->analyze_threshold)
>! {
>! 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);
>! if (args->debug >= 2)
>! print_table_info(tbl);
>! }
>!
>! break; /* once we have found a match, no
>! * need to keep checking. */
>! }
>!
> /*
>! * Advance the table pointers for the next
>! * loop
> */
>! tbl_elem = DLGetSucc(tbl_elem);
>
>! } /* end for table while loop */
> } /* end for j loop (tuples in PGresult) */
> } /* end if (res != NULL) */
> } /* close of if(xid_wraparound_check()) */
>--- 808,877 ----
> {
> for (j = 0; j < PQntuples(res); j++)
> { /* loop through result set */
>! /*
>! * Check to see if we need to
>! * vacuum analyze, or just analyze
>! * if needed, do so, then update table thresholds and
>! * related information
>! */
>! curr_vacuum_count = (atol(PQgetvalue(res, j, PQfnumber(res, "curr_vacuum_count"))));
>! curr_analyze_count = (atol(PQgetvalue(res, j, PQfnumber(res, "curr_analyze_count"))));
>! cnt_at_last_vacuum = (atol(PQgetvalue(res, j, PQfnumber(res, "cnt_at_last_vacuum"))));
>! cnt_at_last_analyze = (atol(PQgetvalue(res, j, PQfnumber(res, "cnt_at_last_analyze"))));
>! vacuum_threshold = (atof(PQgetvalue(res, j, PQfnumber(res, "vacuum_threshold"))));
>! analyze_threshold = (atof(PQgetvalue(res, j, PQfnumber(res, "analyze_threshold"))));
>! strcpy(table_name,"\"");
>! strcat(table_name, PQgetvalue(res,j, PQfnumber(res, "schemaname")));
>! strcat(table_name,"\".\"");
>! strcat(table_name, PQgetvalue(res,j, PQfnumber(res, "relname")));
>! strcat(table_name,"\"");
>!
>! if ((curr_vacuum_count - cnt_at_last_vacuum) >= vacuum_threshold)
>! {
> /*
>! * if relisshared = t and database !=
>! * template1 then only do an analyze
> */
>! if ((strcmp("t",PQgetvalue(res,j, PQfnumber(res, "relisshared"))) == 0 ) && (strcmp(dbs->dbname,"template1") == 0 ))
>! snprintf(buf, sizeof(buf), "ANALYZE %s", table_name);
>! else
>! snprintf(buf, sizeof(buf), "VACUUM ANALYZE %s", table_name);
>!
>! ereport(DEBUG1,(errmsg("pg_autovacuum: Performing: %s", buf)));
>!
>! send_query(buf, dbs);
>! update_table_thresholds(dbs, atooid(PQgetvalue(res, j, PQfnumber(res, "oid"))), VACUUM_ANALYZE);
>! }
>! else if (curr_analyze_count - cnt_at_last_analyze >= analyze_threshold)
>! {
>! snprintf(buf, sizeof(buf), "ANALYZE %s", table_name);
>!
>! ereport(DEBUG1,(errmsg("pg_autovacuum: Performing: %s", buf)));
>!
>! send_query(buf, dbs);
>! update_table_thresholds(dbs, atooid(PQgetvalue(res, j, PQfnumber(res, "oid"))), ANALYZE_ONLY);
>! }
>!
>! if (curr_vacuum_count < cnt_at_last_vacuum)
>! {
>! /*
>! * this should only happen if the stats system gets reset
>! */
>! snprintf(buf, sizeof(buf), "update pg_autovacuum set cnt_at_last_vacuum = %li where table_oid = %u", curr_vacuum_count, atooid(PQgetvalue(res, j, PQfnumber(res, "oid"))));
>!
>! ereport(DEBUG1,(errmsg("pg_autovacuum: Performing: %s", buf)));
>! send_query(buf, dbs);
>! }
>! if (curr_analyze_count < cnt_at_last_analyze)
>! {
>! /*
>! * this should only happen if the stats system gets reset
>! */
>! snprintf(buf, sizeof(buf), "update pg_autovacuum set cnt_at_last_analyze = %li where table_oid = %u", curr_analyze_count, atooid(PQgetvalue(res, j, PQfnumber(res, "oid"))));
>
>! ereport(DEBUG1,(errmsg("pg_autovacuum: Performing: %s", buf)));
>! send_query(buf, dbs);
>! }
> } /* end for j loop (tuples in PGresult) */
> } /* end if (res != NULL) */
> } /* close of if(xid_wraparound_check()) */
>***************
>*** 1179,1212 ****
> }
> db_elem = DLGetSucc(db_elem); /* move on to next DB
> * regardless */
>! } /* end of db_list while loop */
>!
> /* Figure out how long to sleep etc ... */
> gettimeofday(&now, 0);
> diff = (int) (now.tv_sec - then.tv_sec) * 1000000.0 + (int) (now.tv_usec - then.tv_usec);
>!
>! 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
> * compiler / valgrind happy
> */
> free_db_list(db_list);
>! free_cmd_args();
>! return EXIT_SUCCESS;
> }
>--- 882,911 ----
> }
> db_elem = DLGetSucc(db_elem); /* move on to next DB
> * regardless */
>! } /* end of db_list while loop */
>!
> /* Figure out how long to sleep etc ... */
> gettimeofday(&now, 0);
> diff = (int) (now.tv_sec - then.tv_sec) * 1000000.0 + (int) (now.tv_usec - then.tv_usec);
>!
>! sleep_secs = SLEEPBASEVALUE + SLEEPSCALINGFACTOR * diff / 1000000.0;
> loops++;
>!
>! ereport(DEBUG2,(errmsg("pg_autovacuum: loop %i; All DBs checked in: %.0f usec, will sleep for %i secs.",
>! loops, diff, sleep_secs)));
>!
>! /* 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
> * compiler / valgrind happy
> */
> free_db_list(db_list);
>! proc_exit(0);
> }
>*** ./src/backend/postmaster/Makefile.orig 2004-08-04 01:40:53.455982146 -0400
>--- ./src/backend/postmaster/Makefile 2004-07-24 02:00:34.000000000 -0400
>***************
>*** 12,18 ****
> top_builddir = ../../..
> include $(top_builddir)/src/Makefile.global
>
>! OBJS = postmaster.o bgwriter.o pgstat.o pgarch.o
>
> all: SUBSYS.o
>
>--- 12,18 ----
> top_builddir = ../../..
> include $(top_builddir)/src/Makefile.global
>
>! OBJS = postmaster.o bgwriter.o pgstat.o pgarch.o autovacuum.o
>
> all: SUBSYS.o
>
>*** ./src/backend/postmaster/postmaster.c.orig 2004-08-04 01:40:40.707380679 -0400
>--- ./src/backend/postmaster/postmaster.c 2004-08-03 21:55:54.578203673 -0400
>***************
>*** 55,66 ****
> * The Postmaster cleans up after backends if they have an emergency
> * exit and/or core dump.
> *
>- * Error Reporting:
>- * Use write_stderr() only for reporting "interactive" errors
>- * (essentially, bogus arguments on the command line). Once the
>- * postmaster is launched, use ereport(). In particular, don't use
>- * write_stderr() for anything that occurs after pmdaemonize.
>- *
> *-------------------------------------------------------------------------
> */
>
>--- 55,60 ----
>***************
>*** 198,203 ****
>--- 192,198 ----
> /* PIDs of special child processes; 0 when not running */
> static pid_t StartupPID = 0,
> BgWriterPID = 0,
>+ AutoVacPID = 0,
> PgArchPID = 0,
> PgStatPID = 0;
>
>***************
>*** 268,273 ****
>--- 263,272 ----
> static int CountChildren(void);
> static bool CreateOptsFile(int argc, char *argv[], char *fullprogname);
> static pid_t StartChildProcess(int xlop);
>+ static void
>+ postmaster_error(const char *fmt,...)
>+ /* This lets gcc check the format string for consistency. */
>+ __attribute__((format(printf, 1, 2)));
>
> #ifdef EXEC_BACKEND
>
>***************
>*** 298,303 ****
>--- 297,303 ----
>
> #define StartupDataBase() StartChildProcess(BS_XLOG_STARTUP)
> #define StartBackgroundWriter() StartChildProcess(BS_XLOG_BGWRITER)
>+ #define StartAutoVac() StartChildProcess(BS_XLOG_AUTOVAC)
>
>
> /*
>***************
>*** 384,390 ****
> #ifdef USE_ASSERT_CHECKING
> SetConfigOption("debug_assertions", optarg, PGC_POSTMASTER, PGC_S_ARGV);
> #else
>! write_stderr("%s: assert checking is not compiled in\n", progname);
> #endif
> break;
> case 'a':
>--- 384,390 ----
> #ifdef USE_ASSERT_CHECKING
> SetConfigOption("debug_assertions", optarg, PGC_POSTMASTER, PGC_S_ARGV);
> #else
>! postmaster_error("assert checking is not compiled in");
> #endif
> break;
> case 'a':
>***************
>*** 508,515 ****
> }
>
> default:
>! write_stderr("Try \"%s --help\" for more information.\n",
>! progname);
> ExitPostmaster(1);
> }
> }
>--- 508,516 ----
> }
>
> default:
>! fprintf(stderr,
>! gettext("Try \"%s --help\" for more information.\n"),
>! progname);
> ExitPostmaster(1);
> }
> }
>***************
>*** 519,528 ****
> */
> if (optind < argc)
> {
>! write_stderr("%s: invalid argument: \"%s\"\n",
>! progname, argv[optind]);
>! write_stderr("Try \"%s --help\" for more information.\n",
>! progname);
> ExitPostmaster(1);
> }
>
>--- 520,529 ----
> */
> if (optind < argc)
> {
>! postmaster_error("invalid argument: \"%s\"", argv[optind]);
>! fprintf(stderr,
>! gettext("Try \"%s --help\" for more information.\n"),
>! progname);
> ExitPostmaster(1);
> }
>
>***************
>*** 593,605 ****
> * for lack of buffers. The specific choices here are somewhat
> * arbitrary.
> */
>! write_stderr("%s: the number of buffers (-B) must be at least twice the number of allowed connections (-N) and at least 16\n", progname);
> ExitPostmaster(1);
> }
>
> if (ReservedBackends >= MaxBackends)
> {
>! write_stderr("%s: superuser_reserved_connections must be less than max_connections\n", progname);
> ExitPostmaster(1);
> }
>
>--- 594,606 ----
> * for lack of buffers. The specific choices here are somewhat
> * arbitrary.
> */
>! postmaster_error("the number of buffers (-B) must be at least twice the number of allowed connections (-N) and at least 16");
> ExitPostmaster(1);
> }
>
> if (ReservedBackends >= MaxBackends)
> {
>! postmaster_error("superuser_reserved_connections must be less than max_connections");
> ExitPostmaster(1);
> }
>
>***************
>*** 608,614 ****
> */
> if (!CheckDateTokenTables())
> {
>! write_stderr("%s: invalid datetoken tables, please fix\n", progname);
> ExitPostmaster(1);
> }
>
>--- 609,615 ----
> */
> if (!CheckDateTokenTables())
> {
>! postmaster_error("invalid datetoken tables, please fix");
> ExitPostmaster(1);
> }
>
>***************
>*** 827,832 ****
>--- 828,835 ----
> *
> * 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/autovacuum.c.
> * bootstrap/bootstrap.c, postmaster/bgwriter.c, postmaster/pgarch.c,
> * and postmaster/pgstat.c.
> */
>***************
>*** 977,986 ****
> fp = AllocateFile(path, PG_BINARY_R);
> if (fp == NULL)
> {
>! write_stderr("%s: could not find the database system\n"
>! "Expected to find it in the directory \"%s\",\n"
>! "but could not open file \"%s\": %s\n",
>! progname, checkdir, path, strerror(errno));
> ExitPostmaster(2);
> }
> FreeFile(fp);
>--- 980,990 ----
> fp = AllocateFile(path, PG_BINARY_R);
> if (fp == NULL)
> {
>! fprintf(stderr,
>! gettext("%s: could not find the database system\n"
>! "Expected to find it in the directory \"%s\",\n"
>! "but could not open file \"%s\": %s\n"),
>! progname, checkdir, path, strerror(errno));
> ExitPostmaster(2);
> }
> FreeFile(fp);
>***************
>*** 1023,1030 ****
> pid = fork();
> if (pid == (pid_t) -1)
> {
>! write_stderr("%s: could not fork background process: %s\n",
>! progname, strerror(errno));
> ExitPostmaster(1);
> }
> else if (pid)
>--- 1027,1034 ----
> pid = fork();
> if (pid == (pid_t) -1)
> {
>! postmaster_error("could not fork background process: %s",
>! strerror(errno));
> ExitPostmaster(1);
> }
> else if (pid)
>***************
>*** 1045,1052 ****
> #ifdef HAVE_SETSID
> if (setsid() < 0)
> {
>! write_stderr("%s: could not dissociate from controlling TTY: %s\n",
>! progname, strerror(errno));
> ExitPostmaster(1);
> }
> #endif
>--- 1049,1056 ----
> #ifdef HAVE_SETSID
> if (setsid() < 0)
> {
>! postmaster_error("could not dissociate from controlling TTY: %s",
>! strerror(errno));
> ExitPostmaster(1);
> }
> #endif
>***************
>*** 1113,1121 ****
> int nSockets;
> time_t now,
> last_touch_time;
>! struct timeval earlier,
> later;
>! struct timezone tz;
>
> gettimeofday(&earlier, &tz);
> last_touch_time = time(NULL);
>--- 1117,1125 ----
> int nSockets;
> time_t now,
> last_touch_time;
>! struct timeval earlier,
> later;
>! struct timezone tz;
>
> gettimeofday(&earlier, &tz);
> last_touch_time = time(NULL);
>***************
>*** 1220,1225 ****
>--- 1224,1252 ----
> 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 (autovacuum_start_daemon)
>+ {
>+ if (pgstat_collect_tuplelevel)
>+ {
>+ if (AutoVacPID == 0 && StartupPID == 0 && !FatalError)
>+ {
>+ AutoVacPID = StartAutoVac();
>+
>+ /* If shutdown is pending, set it going */
>+ if (Shutdown > NoShutdown && AutoVacPID != 0)
>+ kill(AutoVacPID, SIGUSR2);
>+ }
>+ }
>+ }
>+ else if(AutoVacPID > 0)
>+ kill(AutoVacPID, SIGUSR2);
>+
>+
> /* If we have lost the archiver, try to start a new one */
> if (XLogArchivingActive() && PgArchPID == 0 &&
> StartupPID == 0 && !FatalError && Shutdown == NoShutdown)
>***************
>*** 1587,1592 ****
>--- 1614,1634 ----
> backendPID = (int) ntohl(canc->backendPID);
> cancelAuthCode = (long) ntohl(canc->cancelAuthCode);
>
>+ if (backendPID == BgWriterPID)
>+ {
>+ ereport(DEBUG2,
>+ (errmsg_internal("ignoring cancel request for bgwriter process %d",
>+ 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
> * can no longer access the postmaster's own backend list, and must
>***************
>*** 1768,1773 ****
>--- 1810,1817 ----
> SignalChildren(SIGHUP);
> if (BgWriterPID != 0)
> kill(BgWriterPID, SIGHUP);
>+ if (AutoVacPID != 0)
>+ kill(AutoVacPID, SIGHUP);
> if (PgArchPID != 0)
> kill(PgArchPID, SIGHUP);
> /* PgStatPID does not currently need SIGHUP */
>***************
>*** 1828,1833 ****
>--- 1872,1881 ----
> /* 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);
> /* Tell pgarch to shut down too; nothing left for it to do */
> if (PgArchPID != 0)
> kill(PgArchPID, SIGQUIT);
>***************
>*** 1875,1880 ****
>--- 1923,1931 ----
> /* And tell it to shut down */
> if (BgWriterPID != 0)
> kill(BgWriterPID, SIGUSR2);
>+ /* And tell it to shut down */
>+ if (AutoVacPID != 0)
>+ kill(AutoVacPID, SIGUSR2);
> /* Tell pgarch to shut down too; nothing left for it to do */
> if (PgArchPID != 0)
> kill(PgArchPID, SIGQUIT);
>***************
>*** 1896,1901 ****
>--- 1947,1954 ----
> kill(StartupPID, SIGQUIT);
> if (BgWriterPID != 0)
> kill(BgWriterPID, SIGQUIT);
>+ if (AutoVacPID != 0)
>+ kill(AutoVacPID, SIGQUIT);
> if (PgArchPID != 0)
> kill(PgArchPID, SIGQUIT);
> if (PgStatPID != 0)
>***************
>*** 1984,1989 ****
>--- 2037,2055 ----
> BgWriterPID = StartBackgroundWriter();
>
> /*
>+ * We won't start autovac here since we are in no hurry to start it up
>+ * But we will take this opportunity to make some noise about invalid
>+ * combinations of options in postgresql.conf
>+ */
>+ if (autovacuum_start_daemon)
>+ {
>+ if (!pgstat_collect_tuplelevel)
>+ ereport(WARNING,(errmsg("pg_autovacuum: autovac is enabled, but requires stats_row_level which is not enabled")));
>+ if (pgstat_collect_resetonpmstart)
>+ ereport(WARNING,(errmsg("pg_autovacuum: stats_reset_on_server_start should be disabled for optimal performance")));
>+ }
>+
>+ /*
> * Go to shutdown mode if a shutdown request was pending.
> * Otherwise, try to start the archiver and stats collector too.
> */
>***************
>*** 1996,2001 ****
>--- 2062,2073 ----
> PgStatPID = pgstat_start();
> }
>
>+ /*
>+ * Shutdown autovac if a shutdown request was pending.
>+ */
>+ if (Shutdown > NoShutdown && AutoVacPID != 0)
>+ kill(AutoVacPID, SIGUSR2);
>+
> continue;
> }
>
>***************
>*** 2032,2037 ****
>--- 2104,2129 ----
> }
>
> /*
>+ * Was it the autovac?
>+ */
>+ if (AutoVacPID != 0 && pid == AutoVacPID)
>+ {
>+ AutoVacPID = 0;
>+ if (exitstatus != 0)
>+ {
>+ /*
>+ * Any unexpected exit of the autovacuum is treated as a crash.
>+ * FIXME: This is useful for debugging autovac, but I think it should be
>+ * ripped out before final patch, autovac shouldn't crash the postmaster
>+ */
>+ LogChildExit(LOG, gettext("pg_autovacuum process"),
>+ pid, exitstatus);
>+ HandleChildCrash(pid, exitstatus);
>+ continue;
>+ }
>+ }
>+
>+ /*
> * Was it the archiver? If so, just try to start a new
> * one; no need to force reset of the rest of the system. (If fail,
> * we'll try again in future cycles of the main loop.)
>***************
>*** 2077,2083 ****
> * StartupDataBase. (We can ignore the archiver and stats processes
> * here since they are not connected to shmem.)
> */
>! if (DLGetHead(BackendList) || StartupPID != 0 || BgWriterPID != 0)
> goto reaper_done;
> ereport(LOG,
> (errmsg("all server processes terminated; reinitializing")));
>--- 2169,2175 ----
> * StartupDataBase. (We can ignore the archiver and stats processes
> * here since they are not connected to shmem.)
> */
>! if (DLGetHead(BackendList) || StartupPID != 0 || BgWriterPID != 0 || AutoVacPID != 0)
> goto reaper_done;
> ereport(LOG,
> (errmsg("all server processes terminated; reinitializing")));
>***************
>*** 2100,2105 ****
>--- 2192,2200 ----
> /* And tell it to shut down */
> if (BgWriterPID != 0)
> kill(BgWriterPID, SIGUSR2);
>+ /* Tell AutoVac to shut down */
>+ if (AutoVacPID != 0)
>+ kill(AutoVacPID, SIGUSR2);
> /* Tell pgarch to shut down too; nothing left for it to do */
> if (PgArchPID != 0)
> kill(PgArchPID, SIGQUIT);
>***************
>*** 2265,2270 ****
>--- 2360,2379 ----
> }
>
> FatalError = true;
>+
>+ /* Take care of the autovacuum 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;
> }
>
> /*
>***************
>*** 3256,3261 ****
>--- 3365,3374 ----
> 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")));
>***************
>*** 3310,3315 ****
>--- 3423,3446 ----
> return true;
> }
>
>+ /*
>+ * This should be used only for reporting "interactive" errors (essentially,
>+ * bogus arguments on the command line). Once the postmaster is launched,
>+ * use ereport. In particular, don't use this for anything that occurs
>+ * after pmdaemonize.
>+ */
>+ static void
>+ postmaster_error(const char *fmt,...)
>+ {
>+ va_list ap;
>+
>+ fprintf(stderr, "%s: ", progname);
>+ va_start(ap, fmt);
>+ vfprintf(stderr, gettext(fmt), ap);
>+ va_end(ap);
>+ fprintf(stderr, "\n");
>+ }
>+
>
> #ifdef EXEC_BACKEND
>
>***************
>*** 3749,3755 ****
> if (r == WAIT_OBJECT_0)
> pg_queue_signal(SIGCHLD);
> else
>! write_stderr("ERROR: failed to wait on child process handle: %d\n",
> (int) GetLastError());
> CloseHandle(procHandle);
> return 0;
>--- 3880,3886 ----
> if (r == WAIT_OBJECT_0)
> pg_queue_signal(SIGCHLD);
> else
>! fprintf(stderr, "ERROR: failed to wait on child process handle: %d\n",
> (int) GetLastError());
> CloseHandle(procHandle);
> return 0;
>*** ./src/backend/utils/misc/guc.c.orig 2004-08-04 01:41:20.000000000 -0400
>--- ./src/backend/utils/misc/guc.c 2004-08-03 00:47:41.000000000 -0400
>***************
>*** 43,49 ****
>--- 43,51 ----
> #include "optimizer/prep.h"
> #include "parser/parse_expr.h"
> #include "parser/parse_relation.h"
>+ #include "pgstat.h"
> #include "postmaster/bgwriter.h"
>+ #include "postmaster/autovacuum.h"
> #include "postmaster/postmaster.h"
> #include "storage/bufmgr.h"
> #include "storage/fd.h"
>***************
>*** 55,61 ****
> #include "utils/builtins.h"
> #include "utils/memutils.h"
> #include "utils/pg_locale.h"
>- #include "pgstat.h"
>
> char *guc_pgdata;
> char *guc_hbafile;
>--- 57,62 ----
>***************
>*** 642,647 ****
>--- 643,656 ----
> &pgstat_collect_blocklevel,
> false, NULL, NULL
> },
>+ {
>+ {"autovacuum", PGC_SIGHUP, AUTOVACUUM,
>+ gettext_noop("Starts the auto vacuum subprocess."),
>+ NULL
>+ },
>+ &autovacuum_start_daemon,
>+ false, NULL, NULL
>+ },
>
> {
> {"trace_notify", PGC_USERSET, DEVELOPER_OPTIONS,
>***************
>*** 1284,1289 ****
>--- 1293,1322 ----
> &block_size,
> BLCKSZ, BLCKSZ, BLCKSZ, NULL, NULL
> },
>+ {
>+ {"autovacuum_vacuum_threshold_base", PGC_SIGHUP, AUTOVACUUM,
>+ gettext_noop("Minimum number of tuple updates or deletes prior to vacuum."),
>+ NULL
>+ },
>+ &autovacuum_vacuum_base,
>+ 1000, 0, INT_MAX, NULL, NULL
>+ },
>+ {
>+ {"autovacuum_analyze_threshold_base", PGC_SIGHUP, AUTOVACUUM,
>+ gettext_noop("Minimum number of tuple updates or deletes prior to analyze."),
>+ NULL
>+ },
>+ &autovacuum_analyze_base,
>+ 500, 0, INT_MAX, NULL, NULL
>+ },
>+ {
>+ {"autovacuum_naptime", PGC_SIGHUP, AUTOVACUUM,
>+ gettext_noop("Minimum number of tuple updates or deletes prior to analyze."),
>+ NULL
>+ },
>+ &autovacuum_analyze_base,
>+ 500, 0, INT_MAX, NULL, NULL
>+ },
>
> /* End-of-list marker */
> {
>***************
>*** 1364,1369 ****
>--- 1397,1418 ----
> &phony_random_seed,
> 0.5, 0.0, 1.0, assign_random_seed, show_random_seed
> },
>+ {
>+ {"autovacuum_vacuum_threshold_sf", PGC_SIGHUP, AUTOVACUUM,
>+ gettext_noop("Numer of tuple updates or deletes prior to vacuum as a factor of reltuples."),
>+ NULL
>+ },
>+ &autovacuum_vacuum_scaling_factor,
>+ 2, 0, 100, NULL, NULL
>+ },
>+ {
>+ {"autovacuum_analyze_threshold_sf", PGC_SIGHUP, AUTOVACUUM,
>+ gettext_noop("Numer of tuple updates or deletes prior to analyze as a factor of reltuples."),
>+ NULL
>+ },
>+ &autovacuum_analyze_scaling_factor,
>+ 1, 0, 100, NULL, NULL
>+ },
>
> /* End-of-list marker */
> {
>*** ./src/backend/utils/misc/postgresql.conf.sample.orig 2004-08-04 01:41:06.000000000 -0400
>--- ./src/backend/utils/misc/postgresql.conf.sample 2004-07-22 23:50:32.000000000 -0400
>***************
>*** 238,243 ****
>--- 238,253 ----
>
>
> #---------------------------------------------------------------------------
>+ # VACUUM DAEMON
>+ #---------------------------------------------------------------------------
>+
>+ #autovacuum = false # requires stats_row_level = true
>+ #autovacuum_vacuum_threshold_base = 1000
>+ #autovacuum_vacuum_threshold_sf = 2
>+ #autovacuum_analyze_threshold_base = 500
>+ #autovacuum_analyze_threshold_sf = 1
>+
>+ #---------------------------------------------------------------------------
> # CLIENT CONNECTION DEFAULTS
> #---------------------------------------------------------------------------
>
>*** ./src/include/bootstrap/bootstrap.h.orig 2004-08-04 01:42:35.927740714 -0400
>--- ./src/include/bootstrap/bootstrap.h 2004-07-22 23:50:32.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/autovacuum.h.orig 2004-06-29 09:29:39.000000000 -0400
>--- ./src/include/postmaster/autovacuum.h 2004-08-04 00:46:59.293859961 -0400
>***************
>*** 1,42 ****
>! /* pg_autovacuum.h
>! * Header file for pg_autovacuum.c
> * (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'"
>--- 1,35 ----
>! /* autovacuum.h
>! * Header file for autovacuum.c
> * (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 <sys/stat.h>
>
>! #include "../../interfaces/libpq/libpq-fe.h"
> #include "lib/dllist.h"
>!
>! /* default settings defined here
>! * These could be changed into GUC variables, but I'm not sure we need to. */
>! #define SLEEPBASEVALUE 15
> #define SLEEPSCALINGFACTOR 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 AUTOVACPASSFILE "autovac.passwd"
>
>! /* 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, c.cnt_at_last_analyze, c.cnt_at_last_vacuum, b.n_tup_ins + b.n_tup_upd as curr_analyze_count, b.n_tup_upd + b.n_tup_del as curr_vacuum_count, case when vacuum_threshold < 0 then c.vacuum_base_threshold + c.vacuum_scaling_factor * a.reltuples else c.vacuum_threshold end as vacuum_threshold, case when analyze_threshold < 0 then c.analyze_base_threshold + c.analyze_scaling_factor * a.reltuples else c.analyze_threshold end as analyze_threshold from pg_class a inner join pg_stat_all_tables b on a.oid=b.relid left outer join pg_autovacuum c on a.oid = c.table_oid where a.relkind = 'r' and schemaname not like 'pg_temp_%' and c.table_oid is not null"
>
> #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'"
>***************
>*** 44,70 ****
> /* define atooid */
> #define atooid(x) ((Oid) strtoul((x), NULL, 10))
>
>- /* define cmd_args stucture */
>- struct cmdargs
>- {
>- int vacuum_base_threshold,
>- analyze_base_threshold,
>- sleep_base_value,
>- debug,
>- daemonize;
>- float vacuum_scaling_factor,
>- analyze_scaling_factor,
>- sleep_scaling_factor;
>- char *user,
>- *password,
>- *host,
>- *logfile,
>- *port;
>- };
>- typedef struct cmdargs cmd_args;
>
>! /* define cmd_args as global so we can get to them everywhere */
>! cmd_args *args;
>
> /* Might need to add a time value for last time the whold database was vacuumed.
> I think we need to guarantee this happens approx every 1Million TX's */
>--- 37,53 ----
> /* define atooid */
> #define atooid(x) ((Oid) strtoul((x), NULL, 10))
>
>
>! /* ----------
>! * GUC parameters
>! * ----------
>! */
>! extern bool autovacuum_start_daemon;
>! extern int autovacuum_vacuum_base;
>! extern double autovacuum_vacuum_scaling_factor;
>! extern int autovacuum_analyze_base;
>! extern double autovacuum_analyze_scaling_factor;
>!
>
> /* Might need to add a time value for last time the whold database was vacuumed.
> I think we need to guarantee this happens approx every 1Million TX's */
>***************
>*** 72,142 ****
> {
> Oid oid;
> long age;
>- long analyze_threshold,
>- vacuum_threshold; /* Use these as defaults for table
>- * thresholds */
> PGconn *conn;
> char *dbname,
> *username,
> *password;
>- Dllist *table_list;
> };
> typedef struct dbinfo db_info;
>
>- struct tableinfo
>- {
>- char *schema_name,
>- *table_name;
>- float reltuples;
>- int relisshared;
>- Oid relid,
>- relpages;
>- long analyze_threshold,
>- vacuum_threshold;
>- long CountAtLastAnalyze; /* equal to: inserts + updates as
>- * of the last analyze or initial
>- * values at startup */
>- long CountAtLastVacuum; /* equal to: deletes + updates as
>- * of the last vacuum or initial
>- * values at startup */
>- long curr_analyze_count,
>- curr_vacuum_count; /* Latest values from stats system */
>- db_info *dbi; /* pointer to the database that this table
>- * belongs to */
>- };
>- 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);
> static db_info *init_dbinfo(char *dbname, Oid oid, long age);
>! static void update_db_list(Dllist *db_list);
>! static void remove_db_from_list(Dlelem *db_to_remove);
>! static void print_db_info(db_info * dbi, int print_table_list);
>! static void print_db_list(Dllist *db_list, int print_table_lists);
>! static int xid_wraparound_check(db_info * dbi);
>! static void free_db_list(Dllist *db_list);
>
> /* Functions for managing table lists */
>! static tbl_info *init_table_info(PGresult *conn, int row, db_info * dbi);
>! static void update_table_list(db_info * dbi);
>! static void remove_table_from_list(Dlelem *tbl_to_remove);
>! static void print_table_list(Dllist *tbl_node);
>! static void print_table_info(tbl_info * tbl);
>! static void update_table_thresholds(db_info * dbi, tbl_info * tbl, int vacuum_type);
>! static void free_tbl_list(Dllist *tbl_list);
>
> /* A few database helper functions */
>! static int check_stats_enabled(db_info * dbi);
>! static PGconn *db_connect(db_info * dbi);
>! 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);
>--- 55,88 ----
> {
> Oid oid;
> long age;
> PGconn *conn;
> char *dbname,
> *username,
> *password;
> };
> typedef struct dbinfo db_info;
>
> /* Functions for dealing with command line arguements */
>! void AutoVacMain(void);
>! void AutoVacLoop(void);
>
> /* Functions for managing database lists */
> static Dllist *init_db_list(void);
> static db_info *init_dbinfo(char *dbname, Oid oid, long age);
>! void update_db_list(Dllist *db_list);
>! void remove_db_from_list(Dlelem *db_to_remove);
>! void print_db_info(db_info * dbi, int print_table_list);
>! void print_db_list(Dllist *db_list, int print_table_lists);
>! int xid_wraparound_check(db_info * dbi);
>! void free_db_list(Dllist *db_list);
>
> /* Functions for managing table lists */
>! void update_pg_autovacuum_table(db_info * dbi);
>! void update_table_thresholds(db_info * dbi, Oid table_oid, int vacuum_type);
>
> /* A few database helper functions */
>! PGconn *db_connect(db_info * dbi);
>! void db_disconnect(db_info * dbi);
>! PGresult *send_query(const char *query, db_info * dbi);
>!
>! #endif /* _AUTOVAC_H_ */
>*** ./src/include/postmaster/postmaster.h.orig 2004-08-04 01:42:10.486531715 -0400
>--- ./src/include/postmaster/postmaster.h 2004-07-22 23:50:32.000000000 -0400
>***************
>*** 29,34 ****
>--- 29,35 ----
> extern bool Log_connections;
> extern bool log_hostname;
> extern char *rendezvous_name;
>+ extern bool autovacuum_start_daemon;
>
> #ifdef WIN32
> extern HANDLE PostmasterHandle;
>*** ./src/include/storage/proc.h.orig 2004-08-04 01:48:19.967030481 -0400
>--- ./src/include/storage/proc.h 2004-08-03 00:47:45.000000000 -0400
>***************
>*** 109,115 ****
>
> #define DUMMY_PROC_DEFAULT 0
> #define DUMMY_PROC_BGWRITER 1
>! #define NUM_DUMMY_PROCS 2
>
>
> /* configurable options */
>--- 109,116 ----
>
> #define DUMMY_PROC_DEFAULT 0
> #define DUMMY_PROC_BGWRITER 1
>! #define DUMMY_PROC_AUTOVAC 2
>! #define NUM_DUMMY_PROCS 3
>
>
> /* configurable options */
>*** ./src/include/utils/guc_tables.h.orig 2004-08-04 01:42:24.235023454 -0400
>--- ./src/include/utils/guc_tables.h 2004-07-22 23:50:32.000000000 -0400
>***************
>*** 71,76 ****
>--- 71,77 ----
> COMPAT_OPTIONS_CLIENT,
> DEVELOPER_OPTIONS,
> COMPILE_OPTIONS,
>+ AUTOVACUUM,
> CUSTOM_OPTIONS
> };
>
>
>
>------------------------------------------------------------------------
>
>
>---------------------------(end of broadcast)---------------------------
>TIP 3: if posting/reading through Usenet, please send an appropriate
> subscribe-nomail command to majordomo(at)postgresql(dot)org so that your
> message can get through to the mailing list cleanly
>
>

In response to

Responses

Browse pgsql-patches by date

  From Date Subject
Next Message Matthew T. O'Connor 2004-08-05 23:40:08 Re: Autovacuum Integration Patch Take 5
Previous Message Andreas Pflug 2004-08-05 21:44:01 Re: logger subprocess including win32