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 | 4112C558.8070908@zeut.net Whole thread Raw |
In response to | Re: Autovacuum Integration Patch Take 5 ("Matthew T. O'Connor" <matthew@zeut.net>) |
List | pgsql-patches |
BTW, I should clarify that by a few days I mean, I'll be back on Sunday evening and will be able to restart working on this then. Matthew T. O'Connor wrote: > 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@postgresql.org so that your >> message can get through to the mailing list cleanly >> >> > > > ---------------------------(end of broadcast)--------------------------- > TIP 8: explain analyze is your friend >
pgsql-patches by date: