Re: Autovacuum Integration Patch Take 5 - Mailing list pgsql-patches
From | Matthew T. O'Connor |
---|---|
Subject | Re: Autovacuum Integration Patch Take 5 |
Date | |
Msg-id | 4112C3C9.6090005@zeut.net Whole thread Raw |
In response to | Autovacuum Integration Patch Take 5 ("Matthew T. O'Connor" <matthew@zeut.net>) |
Responses |
Re: Autovacuum Integration Patch Take 5
|
List | 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_tableswhere 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 ona.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 beu=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) andat 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) andat 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 isnot enabled"))); >+ if (pgstat_collect_resetonpmstart) >+ ereport(WARNING,(errmsg("pg_autovacuum: stats_reset_on_server_start should be disabled for optimalperformance"))); >+ } >+ >+ /* > * 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 frompg_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_updas 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 whenanalyze_threshold < 0 then c.analyze_base_threshold + c.analyze_scaling_factor * a.reltuples else c.analyze_thresholdend as analyze_threshold from pg_class a inner join pg_stat_all_tables b on a.oid=b.relid left outer joinpg_autovacuum c on a.oid = c.table_oid where a.relkind = 'r' and schemaname not like 'pg_temp_%' and c.table_oid is notnull" > > #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@postgresql.org so that your > message can get through to the mailing list cleanly > >
pgsql-patches by date: