2
* All the code for the pg_autovacuum program
3
* (c) 2003 Matthew T. O'Connor
4
* Revisions by Christopher B. Browne, Liberty RMS
5
* Win32 Service code added by Dave Page
7
* $PostgreSQL: pgsql/contrib/pg_autovacuum/pg_autovacuum.c,v 1.27.4.3 2005-04-03 00:02:03 tgl Exp $
10
#include "postgres_fe.h"
24
#include "pg_autovacuum.h"
27
SERVICE_STATUS ServiceStatus;
28
SERVICE_STATUS_HANDLE hStatus;
34
#define atooid(x) ((Oid) strtoul((x), NULL, 10))
37
static cmd_args *args;
38
static FILE *LOGOUTPUT;
39
static char logbuffer[4096];
42
/* The main program loop function */
43
static int VacuumLoop(int argc, char **argv);
45
/* Functions for dealing with command line arguements */
46
static cmd_args *get_cmd_args(int argc, char *argv[]);
47
static void print_cmd_args(void);
48
static void free_cmd_args(void);
49
static void usage(void);
51
/* Functions for managing database lists */
52
static Dllist *init_db_list(void);
53
static db_info *init_dbinfo(char *dbname, Oid oid, long age);
54
static void update_db_list(Dllist *db_list);
55
static void remove_db_from_list(Dlelem *db_to_remove);
56
static void print_db_info(db_info * dbi, int print_table_list);
57
static void print_db_list(Dllist *db_list, int print_table_lists);
58
static int xid_wraparound_check(db_info * dbi);
59
static void free_db_list(Dllist *db_list);
61
/* Functions for managing table lists */
62
static tbl_info *init_table_info(PGresult *conn, int row, db_info * dbi);
63
static void update_table_list(db_info * dbi);
64
static void remove_table_from_list(Dlelem *tbl_to_remove);
65
static void print_table_list(Dllist *tbl_node);
66
static void print_table_info(tbl_info * tbl);
67
static void update_table_thresholds(db_info * dbi, tbl_info * tbl, int vacuum_type);
68
static void free_tbl_list(Dllist *tbl_list);
70
/* A few database helper functions */
71
static int check_stats_enabled(db_info * dbi);
72
static PGconn *db_connect(db_info * dbi);
73
static void db_disconnect(db_info * dbi);
74
static PGresult *send_query(const char *query, db_info * dbi);
76
/* Other Generally needed Functions */
78
static void daemonize(void);
80
static void log_entry(const char *logentry, int level);
83
/* Windows Service related functions */
84
static void ControlHandler(DWORD request);
85
static int InstallService();
86
static int RemoveService();
91
log_entry(const char *logentry, int level)
94
* Note: Under Windows we dump the log entries to the normal
95
* stderr/logfile as well, otherwise it can be a pain to debug
96
* service install failures etc.
101
char timebuffer[128],
106
static HANDLE evtHandle = INVALID_HANDLE_VALUE;
107
static int last_level;
114
sprintf(slevel, "DEBUG: ");
118
sprintf(slevel, "INFO: ");
122
sprintf(slevel, "WARNING: ");
126
sprintf(slevel, "ERROR: ");
130
sprintf(slevel, " ");
134
sprintf(slevel, " ");
138
curtime = time(NULL);
139
loctime = localtime(&curtime);
140
strftime(timebuffer, sizeof(timebuffer), "%Y-%m-%d %H:%M:%S %Z", loctime);
141
fprintf(LOGOUTPUT, "[%s] %s%s\n", timebuffer, slevel, logentry);
145
/* Restore the previous level if this is extra info */
146
if (level == LVL_EXTRA)
153
elevel = EVENTLOG_INFORMATION_TYPE;
157
elevel = EVENTLOG_SUCCESS;
161
elevel = EVENTLOG_WARNING_TYPE;
165
elevel = EVENTLOG_ERROR_TYPE;
169
elevel = EVENTLOG_SUCCESS;
173
if (evtHandle == INVALID_HANDLE_VALUE)
175
evtHandle = RegisterEventSource(NULL, "PostgreSQL Auto Vacuum");
176
if (evtHandle == NULL)
178
evtHandle = INVALID_HANDLE_VALUE;
183
ReportEvent(evtHandle, elevel, 0, 0, NULL, 1, 0, &logentry, NULL);
188
* Function used to detach the pg_autovacuum daemon from the tty and go into
191
* This code is ripped directly from pmdaemonize in postmaster.c.
201
if (pid == (pid_t) -1)
203
log_entry("cannot disassociate from controlling TTY", LVL_ERROR);
209
/* Parent should just exit, without doing any atexit cleanup */
213
/* GH: If there's no setsid(), we hopefully don't need silent mode.
214
* Until there's a better solution.
219
log_entry("cannot disassociate from controlling TTY", LVL_ERROR);
224
i = open(NULL_DEV, O_RDWR);
232
/* Create and return tbl_info struct with initialized to values from row or res */
234
init_table_info(PGresult *res, int row, db_info * dbi)
236
tbl_info *new_tbl = (tbl_info *) malloc(sizeof(tbl_info));
240
log_entry("init_table_info: Cannot get memory", LVL_ERROR);
248
new_tbl->dbi = dbi; /* set pointer to db */
250
new_tbl->schema_name = (char *)
251
malloc(strlen(PQgetvalue(res, row, PQfnumber(res, "schemaname"))) + 1);
252
if (!new_tbl->schema_name)
254
log_entry("init_table_info: malloc failed on new_tbl->schema_name", LVL_ERROR);
258
strcpy(new_tbl->schema_name,
259
PQgetvalue(res, row, PQfnumber(res, "schemaname")));
261
new_tbl->table_name = (char *)
262
malloc(strlen(PQgetvalue(res, row, PQfnumber(res, "relname"))) +
263
strlen(new_tbl->schema_name) + 6);
264
if (!new_tbl->table_name)
266
log_entry("init_table_info: malloc failed on new_tbl->table_name", LVL_ERROR);
272
* Put both the schema and table name in quotes so that we can work
273
* with mixed case table names
275
strcpy(new_tbl->table_name, "\"");
276
strcat(new_tbl->table_name, new_tbl->schema_name);
277
strcat(new_tbl->table_name, "\".\"");
278
strcat(new_tbl->table_name, PQgetvalue(res, row, PQfnumber(res, "relname")));
279
strcat(new_tbl->table_name, "\"");
281
new_tbl->CountAtLastAnalyze =
282
(atol(PQgetvalue(res, row, PQfnumber(res, "n_tup_ins"))) +
283
atol(PQgetvalue(res, row, PQfnumber(res, "n_tup_upd"))) +
284
atol(PQgetvalue(res, row, PQfnumber(res, "n_tup_del"))));
285
new_tbl->curr_analyze_count = new_tbl->CountAtLastAnalyze;
287
new_tbl->CountAtLastVacuum =
288
(atol(PQgetvalue(res, row, PQfnumber(res, "n_tup_del"))) +
289
atol(PQgetvalue(res, row, PQfnumber(res, "n_tup_upd"))));
290
new_tbl->curr_vacuum_count = new_tbl->CountAtLastVacuum;
292
new_tbl->relid = atooid(PQgetvalue(res, row, PQfnumber(res, "oid")));
293
new_tbl->reltuples = atof(PQgetvalue(res, row, PQfnumber(res, "reltuples")));
294
new_tbl->relpages = atooid(PQgetvalue(res, row, PQfnumber(res, "relpages")));
296
if (strcmp("t", PQgetvalue(res, row, PQfnumber(res, "relisshared"))))
297
new_tbl->relisshared = 0;
299
new_tbl->relisshared = 1;
301
new_tbl->analyze_threshold =
302
args->analyze_base_threshold + args->analyze_scaling_factor * new_tbl->reltuples;
303
new_tbl->vacuum_threshold =
304
args->vacuum_base_threshold + args->vacuum_scaling_factor * new_tbl->reltuples;
306
if (args->debug >= 2)
307
print_table_info(new_tbl);
312
/* Set thresholds = base_value + scaling_factor * reltuples
313
Should be called after a vacuum since vacuum updates values in pg_class */
315
update_table_thresholds(db_info * dbi, tbl_info * tbl, int vacuum_type)
317
PGresult *res = NULL;
321
if (dbi->conn == NULL)
323
dbi->conn = db_connect(dbi);
327
if (dbi->conn != NULL)
329
snprintf(query, sizeof(query), PAGES_QUERY, tbl->relid);
330
res = send_query(query, dbi);
334
atof(PQgetvalue(res, 0, PQfnumber(res, "reltuples")));
335
tbl->relpages = atooid(PQgetvalue(res, 0, PQfnumber(res, "relpages")));
338
* update vacuum thresholds only of we just did a vacuum
341
if (vacuum_type == VACUUM_ANALYZE)
343
tbl->vacuum_threshold =
344
(args->vacuum_base_threshold + args->vacuum_scaling_factor * tbl->reltuples);
345
tbl->CountAtLastVacuum = tbl->curr_vacuum_count;
348
/* update analyze thresholds */
349
tbl->analyze_threshold =
350
(args->analyze_base_threshold + args->analyze_scaling_factor * tbl->reltuples);
351
tbl->CountAtLastAnalyze = tbl->curr_analyze_count;
356
* If the stats collector is reporting fewer updates then we
357
* have on record then the stats were probably reset, so we
360
if ((tbl->curr_analyze_count < tbl->CountAtLastAnalyze) ||
361
(tbl->curr_vacuum_count < tbl->CountAtLastVacuum))
363
tbl->CountAtLastAnalyze = tbl->curr_analyze_count;
364
tbl->CountAtLastVacuum = tbl->curr_vacuum_count;
373
update_table_list(db_info * dbi)
376
PGresult *res = NULL;
377
tbl_info *tbl = NULL;
378
Dlelem *tbl_elem = DLGetHead(dbi->table_list);
383
if (dbi->conn == NULL)
385
dbi->conn = db_connect(dbi);
389
if (dbi->conn != NULL)
392
* Get a result set that has all the information we will need to
393
* both remove tables from the list that no longer exist and add
394
* tables to the list that are new
396
res = send_query((char *) TABLE_STATS_QUERY, dbi);
402
* First: use the tbl_list as the outer loop and the result
403
* set as the inner loop, this will determine what tables
406
while (tbl_elem != NULL)
408
tbl = ((tbl_info *) DLE_VAL(tbl_elem));
411
for (i = 0; i < t; i++)
412
{ /* loop through result set looking for a
414
if (tbl->relid == atooid(PQgetvalue(res, i, PQfnumber(res, "oid"))))
420
if (found_match == 0)
421
{ /* then we didn't find this tbl_elem in
423
Dlelem *elem_to_remove = tbl_elem;
425
tbl_elem = DLGetSucc(tbl_elem);
426
remove_table_from_list(elem_to_remove);
429
tbl_elem = DLGetSucc(tbl_elem);
430
} /* Done removing dropped tables from the
434
* Then loop use result set as outer loop and tbl_list as the
435
* inner loop to determine what tables are new
437
for (i = 0; i < t; i++)
439
tbl_elem = DLGetHead(dbi->table_list);
441
while (tbl_elem != NULL)
443
tbl = ((tbl_info *) DLE_VAL(tbl_elem));
444
if (tbl->relid == atooid(PQgetvalue(res, i, PQfnumber(res, "oid"))))
449
tbl_elem = DLGetSucc(tbl_elem);
451
if (found_match == 0) /* then we didn't find this result
452
* now in the tbl_list */
454
DLAddTail(dbi->table_list, DLNewElem(init_table_info(res, i, dbi)));
455
if (args->debug >= 1)
457
sprintf(logbuffer, "added table: %s.%s", dbi->dbname,
458
((tbl_info *) DLE_VAL(DLGetTail(dbi->table_list)))->table_name);
459
log_entry(logbuffer, LVL_DEBUG);
462
} /* end of for loop that adds tables */
467
if (args->debug >= 3)
468
print_table_list(dbi->table_list);
474
/* Free memory, and remove the node from the list */
476
remove_table_from_list(Dlelem *tbl_to_remove)
478
tbl_info *tbl = ((tbl_info *) DLE_VAL(tbl_to_remove));
480
if (args->debug >= 1)
482
sprintf(logbuffer, "Removing table: %s from list.", tbl->table_name);
483
log_entry(logbuffer, LVL_DEBUG);
486
DLRemove(tbl_to_remove);
488
if (tbl->schema_name)
490
free(tbl->schema_name);
491
tbl->schema_name = NULL;
495
free(tbl->table_name);
496
tbl->table_name = NULL;
503
DLFreeElem(tbl_to_remove);
506
/* Free the entire table list */
508
free_tbl_list(Dllist *tbl_list)
510
Dlelem *tbl_elem = DLGetHead(tbl_list);
511
Dlelem *tbl_elem_to_remove = NULL;
513
while (tbl_elem != NULL)
515
tbl_elem_to_remove = tbl_elem;
516
tbl_elem = DLGetSucc(tbl_elem);
517
remove_table_from_list(tbl_elem_to_remove);
519
DLFreeList(tbl_list);
523
print_table_list(Dllist *table_list)
525
Dlelem *table_elem = DLGetHead(table_list);
527
while (table_elem != NULL)
529
print_table_info(((tbl_info *) DLE_VAL(table_elem)));
530
table_elem = DLGetSucc(table_elem);
535
print_table_info(tbl_info * tbl)
537
sprintf(logbuffer, " table name: %s.%s", tbl->dbi->dbname, tbl->table_name);
538
log_entry(logbuffer, LVL_INFO);
539
sprintf(logbuffer, " relid: %u; relisshared: %d", tbl->relid, tbl->relisshared);
540
log_entry(logbuffer, LVL_INFO);
541
sprintf(logbuffer, " reltuples: %f; relpages: %u", tbl->reltuples, tbl->relpages);
542
log_entry(logbuffer, LVL_INFO);
543
sprintf(logbuffer, " curr_analyze_count: %li; curr_vacuum_count: %li",
544
tbl->curr_analyze_count, tbl->curr_vacuum_count);
545
log_entry(logbuffer, LVL_INFO);
546
sprintf(logbuffer, " last_analyze_count: %li; last_vacuum_count: %li",
547
tbl->CountAtLastAnalyze, tbl->CountAtLastVacuum);
548
log_entry(logbuffer, LVL_INFO);
549
sprintf(logbuffer, " analyze_threshold: %li; vacuum_threshold: %li",
550
tbl->analyze_threshold, tbl->vacuum_threshold);
551
log_entry(logbuffer, LVL_INFO);
555
/* End of table Management Functions */
557
/* Beginning of DB Management Functions */
559
/* init_db_list() creates the db_list and initalizes template1 */
563
Dllist *db_list = DLNewList();
565
PGresult *res = NULL;
570
DLAddHead(db_list, DLNewElem(init_dbinfo((char *) "template1", 0, 0)));
571
if (DLGetHead(db_list) == NULL)
572
{ /* Make sure init_dbinfo was successful */
573
log_entry("init_db_list(): Error creating db_list for db: template1.", LVL_ERROR);
579
* We do this just so we can set the proper oid for the template1
582
dbs = ((db_info *) DLE_VAL(DLGetHead(db_list)));
583
dbs->conn = db_connect(dbs);
586
while (dbs->conn == NULL && !appMode && k < 10)
590
/* Pause for 30 seconds to allow the database to start up */
591
log_entry("Pausing 30 seconds to allow the database to startup completely", LVL_INFO);
593
ServiceStatus.dwWaitHint = 10;
597
ServiceStatus.dwCheckPoint++;
598
SetServiceStatus(hStatus, &ServiceStatus);
603
log_entry("Attempting to connect again.", LVL_INFO);
604
dbs->conn = db_connect(dbs);
609
if (dbs->conn != NULL)
611
res = send_query(FROZENOID_QUERY, dbs);
614
dbs->oid = atooid(PQgetvalue(res, 0, PQfnumber(res, "oid")));
615
dbs->age = atol(PQgetvalue(res, 0, PQfnumber(res, "age")));
619
if (args->debug >= 2)
620
print_db_list(db_list, 0);
628
/* Simple function to create an instance of the dbinfo struct
629
Initalizes all the pointers and connects to the database */
631
init_dbinfo(char *dbname, Oid oid, long age)
633
db_info *newdbinfo = (db_info *) malloc(sizeof(db_info));
635
newdbinfo->analyze_threshold = args->vacuum_base_threshold;
636
newdbinfo->vacuum_threshold = args->analyze_base_threshold;
637
newdbinfo->dbname = (char *) malloc(strlen(dbname) + 1);
638
strcpy(newdbinfo->dbname, dbname);
639
newdbinfo->username = NULL;
640
if (args->user != NULL)
642
newdbinfo->username = (char *) malloc(strlen(args->user) + 1);
643
strcpy(newdbinfo->username, args->user);
645
newdbinfo->password = NULL;
646
if (args->password != NULL)
648
newdbinfo->password = (char *) malloc(strlen(args->password) + 1);
649
strcpy(newdbinfo->password, args->password);
651
newdbinfo->oid = oid;
652
newdbinfo->age = age;
653
newdbinfo->table_list = DLNewList();
654
newdbinfo->conn = NULL;
656
if (args->debug >= 2)
657
print_table_list(newdbinfo->table_list);
662
/* Function adds and removes databases from the db_list as appropriate */
664
update_db_list(Dllist *db_list)
667
PGresult *res = NULL;
668
Dlelem *db_elem = DLGetHead(db_list);
670
db_info *dbi_template1 = DLE_VAL(db_elem);
675
if (args->debug >= 2)
677
log_entry("updating the database list", LVL_DEBUG);
681
if (dbi_template1->conn == NULL)
683
dbi_template1->conn = db_connect(dbi_template1);
687
if (dbi_template1->conn != NULL)
690
* Get a result set that has all the information we will need to
691
* both remove databasews from the list that no longer exist and
692
* add databases to the list that are new
694
res = send_query(FROZENOID_QUERY2, dbi_template1);
700
* First: use the db_list as the outer loop and the result set
701
* as the inner loop, this will determine what databases
704
while (db_elem != NULL)
706
dbi = ((db_info *) DLE_VAL(db_elem));
709
for (i = 0; i < t; i++)
710
{ /* loop through result set looking for a
712
if (dbi->oid == atooid(PQgetvalue(res, i, PQfnumber(res, "oid"))))
717
* update the dbi->age so that we ensure
718
* xid_wraparound won't happen
720
dbi->age = atol(PQgetvalue(res, i, PQfnumber(res, "age")));
724
if (found_match == 0)
725
{ /* then we didn't find this db_elem in the
727
Dlelem *elem_to_remove = db_elem;
729
db_elem = DLGetSucc(db_elem);
730
remove_db_from_list(elem_to_remove);
733
db_elem = DLGetSucc(db_elem);
734
} /* Done removing dropped databases from
738
* Then loop use result set as outer loop and db_list as the
739
* inner loop to determine what databases are new
741
for (i = 0; i < t; i++)
743
db_elem = DLGetHead(db_list);
745
while (db_elem != NULL)
747
dbi = ((db_info *) DLE_VAL(db_elem));
748
if (dbi->oid == atooid(PQgetvalue(res, i, PQfnumber(res, "oid"))))
753
db_elem = DLGetSucc(db_elem);
755
if (found_match == 0) /* then we didn't find this result
756
* now in the tbl_list */
758
DLAddTail(db_list, DLNewElem(init_dbinfo
759
(PQgetvalue(res, i, PQfnumber(res, "datname")),
760
atooid(PQgetvalue(res, i, PQfnumber(res, "oid"))),
761
atol(PQgetvalue(res, i, PQfnumber(res, "age"))))));
762
if (args->debug >= 1)
764
sprintf(logbuffer, "added database: %s", ((db_info *) DLE_VAL(DLGetTail(db_list)))->dbname);
765
log_entry(logbuffer, LVL_DEBUG);
768
} /* end of for loop that adds tables */
773
if (args->debug >= 3)
774
print_db_list(db_list, 0);
776
db_disconnect(dbi_template1);
780
/* xid_wraparound_check
784
With the standard freezing policy, the age column will start at one billion for a
785
freshly-vacuumed database. When the age approaches two billion, the database must
786
be vacuumed again to avoid risk of wraparound failures. Recommended practice is
787
to vacuum each database at least once every half-a-billion (500 million) transactions,
788
so as to provide plenty of safety margin.
790
So we do a full database vacuum if age > 1.5billion
791
return 0 if nothing happened,
792
return 1 if the database needed a database wide vacuum
795
xid_wraparound_check(db_info * dbi)
798
* FIXME: should probably do something better here so that we don't
799
* vacuum all the databases on the server at the same time. We have
800
* 500million xacts to work with so we should be able to spread the
801
* load of full database vacuums a bit
803
if (dbi->age > 1500000000)
805
PGresult *res = NULL;
807
res = send_query("VACUUM", dbi);
808
/* FIXME: Perhaps should add a check for PQ_COMMAND_OK */
816
/* Close DB connection, free memory, and remove the node from the list */
818
remove_db_from_list(Dlelem *db_to_remove)
820
db_info *dbi = ((db_info *) DLE_VAL(db_to_remove));
822
if (args->debug >= 1)
824
sprintf(logbuffer, "Removing db: %s from list.", dbi->dbname);
825
log_entry(logbuffer, LVL_DEBUG);
828
DLRemove(db_to_remove);
839
dbi->username = NULL;
844
dbi->password = NULL;
848
free_tbl_list(dbi->table_list);
849
dbi->table_list = NULL;
856
DLFreeElem(db_to_remove);
859
/* Function is called before program exit to free all memory
860
mostly it's just to keep valgrind happy */
862
free_db_list(Dllist *db_list)
864
Dlelem *db_elem = DLGetHead(db_list);
865
Dlelem *db_elem_to_remove = NULL;
867
while (db_elem != NULL)
869
db_elem_to_remove = db_elem;
870
db_elem = DLGetSucc(db_elem);
871
remove_db_from_list(db_elem_to_remove);
872
db_elem_to_remove = NULL;
878
print_db_list(Dllist *db_list, int print_table_lists)
880
Dlelem *db_elem = DLGetHead(db_list);
882
while (db_elem != NULL)
884
print_db_info(((db_info *) DLE_VAL(db_elem)), print_table_lists);
885
db_elem = DLGetSucc(db_elem);
890
print_db_info(db_info * dbi, int print_tbl_list)
892
sprintf(logbuffer, "dbname: %s", (dbi->dbname) ? dbi->dbname : "(null)");
893
log_entry(logbuffer, LVL_INFO);
895
sprintf(logbuffer, " oid: %u", dbi->oid);
896
log_entry(logbuffer, LVL_INFO);
898
sprintf(logbuffer, " username: %s", (dbi->username) ? dbi->username : "(null)");
899
log_entry(logbuffer, LVL_INFO);
901
sprintf(logbuffer, " password: %s", (dbi->password) ? dbi->password : "(null)");
902
log_entry(logbuffer, LVL_INFO);
904
if (dbi->conn != NULL)
905
log_entry(" conn is valid, (connected)", LVL_INFO);
907
log_entry(" conn is null, (not connected)", LVL_INFO);
909
sprintf(logbuffer, " default_analyze_threshold: %li", dbi->analyze_threshold);
910
log_entry(logbuffer, LVL_INFO);
912
sprintf(logbuffer, " default_vacuum_threshold: %li", dbi->vacuum_threshold);
913
log_entry(logbuffer, LVL_INFO);
916
if (print_tbl_list > 0)
917
print_table_list(dbi->table_list);
920
/* End of DB List Management Function */
922
/* Beginning of misc Functions */
924
/* Perhaps add some test to this function to make sure that the stats we need are available */
926
db_connect(db_info * dbi)
929
PQsetdbLogin(args->host, args->port, NULL, NULL, dbi->dbname,
930
dbi->username, dbi->password);
932
if (PQstatus(db_conn) != CONNECTION_OK)
934
sprintf(logbuffer, "Failed connection to database %s with error: %s.",
935
dbi->dbname, PQerrorMessage(db_conn));
936
log_entry(logbuffer, LVL_ERROR);
943
} /* end of db_connect() */
946
db_disconnect(db_info * dbi)
948
if (dbi->conn != NULL)
956
check_stats_enabled(db_info * dbi)
961
res = send_query("SHOW stats_row_level", dbi);
964
ret = strcmp("on", PQgetvalue(res, 0, PQfnumber(res, "stats_row_level")));
971
send_query(const char *query, db_info * dbi)
975
if (dbi->conn == NULL)
978
if (args->debug >= 4)
979
log_entry(query, LVL_DEBUG);
981
res = PQexec(dbi->conn, query);
986
"Fatal error occured while sending query (%s) to database %s",
988
log_entry(logbuffer, LVL_ERROR);
989
sprintf(logbuffer, "The error is [%s]", PQresultErrorMessage(res));
990
log_entry(logbuffer, LVL_EXTRA);
994
if (PQresultStatus(res) != PGRES_TUPLES_OK &&
995
PQresultStatus(res) != PGRES_COMMAND_OK)
998
"Can not refresh statistics information from the database %s.",
1000
log_entry(logbuffer, LVL_ERROR);
1001
sprintf(logbuffer, "The error is [%s]", PQresultErrorMessage(res));
1002
log_entry(logbuffer, LVL_EXTRA);
1008
} /* End of send_query() */
1011
* Perform either a vacuum or a vacuum analyze
1014
perform_maintenance_command(db_info * dbi, tbl_info * tbl, int operation)
1019
* Set the vacuum_cost variables if supplied on command line
1021
if (args->av_vacuum_cost_delay != -1)
1023
snprintf(buf, sizeof(buf), "set vacuum_cost_delay = %d",
1024
args->av_vacuum_cost_delay);
1025
send_query(buf, dbi);
1027
if (args->av_vacuum_cost_page_hit != -1)
1029
snprintf(buf, sizeof(buf), "set vacuum_cost_page_hit = %d",
1030
args->av_vacuum_cost_page_hit);
1031
send_query(buf, dbi);
1033
if (args->av_vacuum_cost_page_miss != -1)
1035
snprintf(buf, sizeof(buf), "set vacuum_cost_page_miss = %d",
1036
args->av_vacuum_cost_page_miss);
1037
send_query(buf, dbi);
1039
if (args->av_vacuum_cost_page_dirty != -1)
1041
snprintf(buf, sizeof(buf), "set vacuum_cost_page_dirty = %d",
1042
args->av_vacuum_cost_page_dirty);
1043
send_query(buf, dbi);
1045
if (args->av_vacuum_cost_limit != -1)
1047
snprintf(buf, sizeof(buf), "set vacuum_cost_limit = %d",
1048
args->av_vacuum_cost_limit);
1049
send_query(buf, dbi);
1053
* if ((relisshared = t and database != template1) or
1054
* if operation = ANALYZE_ONLY)
1055
* then only do an analyze
1057
if ((tbl->relisshared > 0 && strcmp("template1", dbi->dbname) != 0) ||
1058
(operation == ANALYZE_ONLY))
1059
snprintf(buf, sizeof(buf), "ANALYZE %s", tbl->table_name);
1060
else if (operation == VACUUM_ANALYZE)
1061
snprintf(buf, sizeof(buf), "VACUUM ANALYZE %s", tbl->table_name);
1065
if (args->debug >= 1)
1067
sprintf(logbuffer, "Performing: %s", buf);
1068
log_entry(logbuffer, LVL_DEBUG);
1072
send_query(buf, dbi);
1074
update_table_thresholds(dbi, tbl, operation);
1076
if (args->debug >= 2)
1077
print_table_info(tbl);
1085
if (args->user != NULL)
1087
if (args->password != NULL)
1088
free(args->password);
1094
get_cmd_args(int argc, char *argv[])
1098
args = (cmd_args *) malloc(sizeof(cmd_args));
1099
args->sleep_base_value = SLEEPBASEVALUE;
1100
args->sleep_scaling_factor = SLEEPSCALINGFACTOR;
1101
args->vacuum_base_threshold = VACBASETHRESHOLD;
1102
args->vacuum_scaling_factor = VACSCALINGFACTOR;
1103
args->analyze_base_threshold = -1;
1104
args->analyze_scaling_factor = -1;
1105
args->debug = AUTOVACUUM_DEBUG;
1107
args->daemonize = 0;
1109
args->service_dependencies = 0;
1110
args->install_as_service = 0;
1111
args->remove_as_service = 0;
1112
args->service_user = 0;
1113
args->service_password = 0;
1122
* Cost-Based Vacuum Delay Settings for pg_autovacuum
1124
args->av_vacuum_cost_delay = -1;
1125
args->av_vacuum_cost_page_hit = -1;
1126
args->av_vacuum_cost_page_miss = -1;
1127
args->av_vacuum_cost_page_dirty = -1;
1128
args->av_vacuum_cost_limit = -1;
1131
* Fixme: Should add some sanity checking such as positive integer
1135
while ((c = getopt(argc, argv, "s:S:v:V:a:A:d:U:P:H:L:p:hDc:C:m:n:l:")) != -1)
1137
while ((c = getopt(argc, argv, "s:S:v:V:a:A:d:U:P:H:L:p:hIRN:W:E:c:C:m:n:l:")) != -1)
1143
args->sleep_base_value = atoi(optarg);
1146
args->sleep_scaling_factor = atof(optarg);
1149
args->vacuum_base_threshold = atoi(optarg);
1152
args->vacuum_scaling_factor = atof(optarg);
1155
args->analyze_base_threshold = atoi(optarg);
1158
args->analyze_scaling_factor = atof(optarg);
1161
args->av_vacuum_cost_delay = atoi(optarg);
1164
args->av_vacuum_cost_page_hit = atoi(optarg);
1167
args->av_vacuum_cost_page_miss = atoi(optarg);
1170
args->av_vacuum_cost_page_dirty = atoi(optarg);
1173
args->av_vacuum_cost_limit = atoi(optarg);
1181
args->debug = atoi(optarg);
1184
args->user = optarg;
1187
args->password = optarg;
1190
args->host = optarg;
1193
args->logfile = optarg;
1196
args->port = optarg;
1204
* CreateService() expects a list of service
1205
* dependencies as a NUL-separated, double-NUL
1206
* terminated list (although we only allow the user to
1207
* specify a single dependency). So we zero out the
1208
* list first, and make sure to leave room for two NUL
1211
ZeroMemory(deps, sizeof(deps));
1212
snprintf(deps, sizeof(deps) - 2, "%s", optarg);
1213
args->service_dependencies = deps;
1216
args->install_as_service++;
1219
args->remove_as_service++;
1222
args->service_user = optarg;
1225
args->service_password = optarg;
1231
* It's here that we know that things are invalid... It is
1232
* not forcibly an error to call usage
1234
fprintf(stderr, "Error: Invalid Command Line Options.\n");
1241
* if values for insert thresholds are not specified, then they
1242
* default to 1/2 of the delete values
1244
if (args->analyze_base_threshold == -1)
1245
args->analyze_base_threshold = args->vacuum_base_threshold / 2;
1246
if (args->analyze_scaling_factor == -1)
1247
args->analyze_scaling_factor = args->vacuum_scaling_factor / 2;
1258
fprintf(stderr, "usage: pg_autovacuum \n");
1260
fprintf(stderr, " [-D] Daemonize (Detach from tty and run in the background)\n");
1262
fprintf(stderr, " [-I] Install as a Windows service\n");
1263
fprintf(stderr, " [-R] Remove as a Windows service (all other options will be ignored)\n");
1264
fprintf(stderr, " [-N] Username to run service as (only useful when installing as a Windows service)\n");
1265
fprintf(stderr, " [-W] Password to run service with (only useful when installing as a Windows service)\n");
1266
fprintf(stderr, " [-E] Dependent service that must start before this service (only useful when installing as a Windows service)\n");
1268
i = AUTOVACUUM_DEBUG;
1269
fprintf(stderr, " [-d] debug (debug level=0,1,2,3; default=%d)\n", i);
1272
fprintf(stderr, " [-s] sleep base value (default=%d)\n", i);
1273
f = SLEEPSCALINGFACTOR;
1274
fprintf(stderr, " [-S] sleep scaling factor (default=%f)\n", f);
1276
i = VACBASETHRESHOLD;
1277
fprintf(stderr, " [-v] vacuum base threshold (default=%d)\n", i);
1278
f = VACSCALINGFACTOR;
1279
fprintf(stderr, " [-V] vacuum scaling factor (default=%f)\n", f);
1281
fprintf(stderr, " [-a] analyze base threshold (default=%d)\n", i);
1283
fprintf(stderr, " [-A] analyze scaling factor (default=%f)\n", f);
1285
fprintf(stderr, " [-L] logfile (default=none)\n");
1287
fprintf(stderr, " [-c] vacuum_cost_delay (default=none)\n");
1288
fprintf(stderr, " [-C] vacuum_cost_page_hit (default=none)\n");
1289
fprintf(stderr, " [-m] vacuum_cost_page_miss (default=none)\n");
1290
fprintf(stderr, " [-n] vacuum_cost_page_dirty (default=none)\n");
1291
fprintf(stderr, " [-l] vacuum_cost_limit (default=none)\n");
1293
fprintf(stderr, " [-U] username (libpq default)\n");
1294
fprintf(stderr, " [-P] password (libpq default)\n");
1295
fprintf(stderr, " [-H] host (libpq default)\n");
1296
fprintf(stderr, " [-p] port (libpq default)\n");
1298
fprintf(stderr, " [-h] help (Show this output)\n");
1302
print_cmd_args(void)
1304
sprintf(logbuffer, "Printing command_args");
1305
log_entry(logbuffer, LVL_INFO);
1306
sprintf(logbuffer, " args->host=%s", (args->host) ? args->host : "(null)");
1307
log_entry(logbuffer, LVL_INFO);
1308
sprintf(logbuffer, " args->port=%s", (args->port) ? args->port : "(null)");
1309
log_entry(logbuffer, LVL_INFO);
1310
sprintf(logbuffer, " args->username=%s", (args->user) ? args->user : "(null)");
1311
log_entry(logbuffer, LVL_INFO);
1312
sprintf(logbuffer, " args->password=%s", (args->password) ? args->password : "(null)");
1313
log_entry(logbuffer, LVL_INFO);
1314
sprintf(logbuffer, " args->logfile=%s", (args->logfile) ? args->logfile : "(null)");
1315
log_entry(logbuffer, LVL_INFO);
1317
sprintf(logbuffer, " args->daemonize=%d", args->daemonize);
1318
log_entry(logbuffer, LVL_INFO);
1320
sprintf(logbuffer, " args->install_as_service=%d", args->install_as_service);
1321
log_entry(logbuffer, LVL_INFO);
1322
sprintf(logbuffer, " args->remove_as_service=%d", args->remove_as_service);
1323
log_entry(logbuffer, LVL_INFO);
1324
sprintf(logbuffer, " args->service_dependencies=%s", (args->service_dependencies) ? args->service_dependencies : "(null)");
1325
log_entry(logbuffer, LVL_INFO);
1326
sprintf(logbuffer, " args->service_user=%s", (args->service_user) ? args->service_user : "(null)");
1327
log_entry(logbuffer, LVL_INFO);
1328
sprintf(logbuffer, " args->service_password=%s", (args->service_password) ? args->service_password : "(null)");
1329
log_entry(logbuffer, LVL_INFO);
1332
sprintf(logbuffer, " args->sleep_base_value=%d", args->sleep_base_value);
1333
log_entry(logbuffer, LVL_INFO);
1334
sprintf(logbuffer, " args->sleep_scaling_factor=%f", args->sleep_scaling_factor);
1335
log_entry(logbuffer, LVL_INFO);
1336
sprintf(logbuffer, " args->vacuum_base_threshold=%d", args->vacuum_base_threshold);
1337
log_entry(logbuffer, LVL_INFO);
1338
sprintf(logbuffer, " args->vacuum_scaling_factor=%f", args->vacuum_scaling_factor);
1339
log_entry(logbuffer, LVL_INFO);
1340
sprintf(logbuffer, " args->analyze_base_threshold=%d", args->analyze_base_threshold);
1341
log_entry(logbuffer, LVL_INFO);
1342
sprintf(logbuffer, " args->analyze_scaling_factor=%f", args->analyze_scaling_factor);
1343
log_entry(logbuffer, LVL_INFO);
1345
if (args->av_vacuum_cost_delay != -1)
1346
sprintf(logbuffer, " args->av_vacuum_cost_delay=%d", args->av_vacuum_cost_delay);
1348
sprintf(logbuffer, " args->av_vacuum_cost_delay=(default)");
1349
log_entry(logbuffer, LVL_INFO);
1350
if (args->av_vacuum_cost_page_hit != -1)
1351
sprintf(logbuffer, " args->av_vacuum_cost_page_hit=%d", args->av_vacuum_cost_page_hit);
1353
sprintf(logbuffer, " args->av_vacuum_cost_page_hit=(default)");
1354
log_entry(logbuffer, LVL_INFO);
1355
if (args->av_vacuum_cost_page_miss != -1)
1356
sprintf(logbuffer, " args->av_vacuum_cost_page_miss=%d", args->av_vacuum_cost_page_miss);
1358
sprintf(logbuffer, " args->av_vacuum_cost_page_miss=(default)");
1359
log_entry(logbuffer, LVL_INFO);
1360
if (args->av_vacuum_cost_page_dirty != -1)
1361
sprintf(logbuffer, " args->av_vacuum_cost_page_dirty=%d", args->av_vacuum_cost_page_dirty);
1363
sprintf(logbuffer, " args->av_vacuum_cost_page_dirty=(default)");
1364
log_entry(logbuffer, LVL_INFO);
1365
if (args->av_vacuum_cost_limit != -1)
1366
sprintf(logbuffer, " args->av_vacuum_cost_limit=%d", args->av_vacuum_cost_limit);
1368
sprintf(logbuffer, " args->av_vacuum_cost_limit=(default)");
1369
log_entry(logbuffer, LVL_INFO);
1371
sprintf(logbuffer, " args->debug=%d", args->debug);
1372
log_entry(logbuffer, LVL_INFO);
1379
/* Handle control requests from the Service Control Manager */
1381
ControlHandler(DWORD request)
1385
case SERVICE_CONTROL_STOP:
1386
case SERVICE_CONTROL_SHUTDOWN:
1387
log_entry("pg_autovacuum service stopping...", LVL_INFO);
1389
ServiceStatus.dwWin32ExitCode = 0;
1390
ServiceStatus.dwCurrentState = SERVICE_STOPPED;
1391
SetServiceStatus(hStatus, &ServiceStatus);
1398
/* Report current status */
1399
SetServiceStatus(hStatus, &ServiceStatus);
1404
/* Register with the Service Control Manager */
1406
InstallService(void)
1408
SC_HANDLE schService = NULL;
1409
SC_HANDLE schSCManager = NULL;
1410
char szFilename[MAX_PATH],
1412
szCommand[MAX_PATH + 1024],
1418
* Register the service with the SCM
1420
GetModuleFileName(NULL, szFilename, MAX_PATH);
1422
/* Open the Service Control Manager on the local computer. */
1423
schSCManager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
1427
schService = CreateService(
1428
schSCManager, /* SCManager database */
1429
TEXT("pg_autovacuum"), /* Name of service */
1430
TEXT("PostgreSQL Auto Vacuum"), /* Name to display */
1431
SERVICE_ALL_ACCESS, /* Desired access */
1432
SERVICE_WIN32_OWN_PROCESS, /* Service type */
1433
SERVICE_AUTO_START, /* Start type */
1434
SERVICE_ERROR_NORMAL, /* Error control type */
1435
szFilename, /* Service binary */
1436
NULL, /* No load ordering group */
1437
NULL, /* No tag identifier */
1438
args->service_dependencies, /* Dependencies */
1439
args->service_user, /* Service account */
1440
args->service_password); /* Account password */
1446
* Rewrite the command line for the service
1448
sprintf(szKey, "SYSTEM\\CurrentControlSet\\Services\\pg_autovacuum");
1449
if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, szKey, 0, KEY_ALL_ACCESS, &hk))
1452
/* Build the command line */
1453
sprintf(szCommand, "\"%s\"", szFilename);
1455
sprintf(szCommand, "%s -H %s", szCommand, args->host);
1457
sprintf(szCommand, "%s -p %s", szCommand, args->port);
1459
sprintf(szCommand, "%s -U \"%s\"", szCommand, args->user);
1461
sprintf(szCommand, "%s -P \"%s\"", szCommand, args->password);
1463
sprintf(szCommand, "%s -L \"%s\"", szCommand, args->logfile);
1464
if (args->sleep_base_value != (int) SLEEPBASEVALUE)
1465
sprintf(szCommand, "%s -s %d", szCommand, args->sleep_base_value);
1466
if (args->sleep_scaling_factor != (float) SLEEPSCALINGFACTOR)
1467
sprintf(szCommand, "%s -S %f", szCommand, args->sleep_scaling_factor);
1468
if (args->vacuum_base_threshold != (int) VACBASETHRESHOLD)
1469
sprintf(szCommand, "%s -v %d", szCommand, args->vacuum_base_threshold);
1470
if (args->vacuum_scaling_factor != (float) VACSCALINGFACTOR)
1471
sprintf(szCommand, "%s -V %f", szCommand, args->vacuum_scaling_factor);
1472
if (args->analyze_base_threshold != (int) (VACBASETHRESHOLD / 2))
1473
sprintf(szCommand, "%s -a %d", szCommand, args->analyze_base_threshold);
1474
if (args->analyze_scaling_factor != (float) (VACSCALINGFACTOR / 2))
1475
sprintf(szCommand, "%s -A %f", szCommand, args->analyze_scaling_factor);
1476
if (args->debug != (int) AUTOVACUUM_DEBUG)
1477
sprintf(szCommand, "%s -d %d", szCommand, args->debug);
1478
if (args->av_vacuum_cost_delay != -1)
1479
sprintf(szCommand, "%s -d %d", szCommand, args->av_vacuum_cost_delay);
1480
if (args->av_vacuum_cost_page_hit != -1)
1481
sprintf(szCommand, "%s -d %d", szCommand, args->av_vacuum_cost_page_hit);
1482
if (args->av_vacuum_cost_page_miss != -1)
1483
sprintf(szCommand, "%s -d %d", szCommand, args->av_vacuum_cost_page_miss);
1484
if (args->av_vacuum_cost_page_dirty != -1)
1485
sprintf(szCommand, "%s -d %d", szCommand, args->av_vacuum_cost_page_dirty);
1486
if (args->av_vacuum_cost_limit != -1)
1487
sprintf(szCommand, "%s -d %d", szCommand, args->av_vacuum_cost_limit);
1489
/* And write the new value */
1490
if (RegSetValueEx(hk, "ImagePath", 0, REG_EXPAND_SZ, (LPBYTE) szCommand, (DWORD) strlen(szCommand) + 1))
1495
* Set the Event source for the application log
1497
sprintf(szKey, "SYSTEM\\CurrentControlSet\\Services\\EventLog\\Application\\PostgreSQL Auto Vacuum");
1498
if (RegCreateKeyEx(HKEY_LOCAL_MACHINE, szKey, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hk, NULL))
1501
/* TODO Try to find pgevent.dll, rather than hope it's in the path. ! */
1503
sprintf(szMsgDLL, "pgevent.dll");
1504
if (RegSetValueEx(hk, "EventMessageFile", 0, REG_EXPAND_SZ, (LPBYTE) szMsgDLL, (DWORD) strlen(szMsgDLL) + 1))
1507
/* Set the event types supported */
1508
dwData = EVENTLOG_ERROR_TYPE | EVENTLOG_WARNING_TYPE | EVENTLOG_INFORMATION_TYPE | EVENTLOG_SUCCESS;
1509
if (RegSetValueEx(hk, "TypesSupported", 0, REG_DWORD, (LPBYTE) & dwData, sizeof(DWORD)))
1516
/* Unregister from the Service Control Manager */
1520
SC_HANDLE schService = NULL;
1521
SC_HANDLE schSCManager = NULL;
1522
char szKey[MAX_PATH];
1526
schSCManager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
1530
/* Open the service */
1531
schService = OpenService(schSCManager, TEXT("pg_autovacuum"), SC_MANAGER_ALL_ACCESS);
1535
/* Now delete the service */
1536
if (!DeleteService(schService))
1540
* Remove the Event source from the application log
1542
sprintf(szKey, "SYSTEM\\CurrentControlSet\\Services\\EventLog\\Application");
1543
if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, szKey, 0, KEY_ALL_ACCESS, &hk))
1545
if (RegDeleteKey(hk, "PostgreSQL Auto Vacuum"))
1554
VacuumLoop(int argc, char **argv)
1559
/* int numInserts, numDeletes, */
1566
PGresult *res = NULL;
1575
log_entry("pg_autovacuum starting in Windows Application mode", LVL_INFO);
1577
log_entry("pg_autovacuum starting in Windows Service mode", LVL_INFO);
1579
ServiceStatus.dwServiceType = SERVICE_WIN32;
1580
ServiceStatus.dwCurrentState = SERVICE_START_PENDING;
1581
ServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN;
1582
ServiceStatus.dwWin32ExitCode = 0;
1583
ServiceStatus.dwServiceSpecificExitCode = 0;
1584
ServiceStatus.dwCheckPoint = 0;
1585
ServiceStatus.dwWaitHint = 0;
1589
hStatus = RegisterServiceCtrlHandler("pg_autovacuum", (LPHANDLER_FUNCTION) ControlHandler);
1590
if (hStatus == (SERVICE_STATUS_HANDLE) 0)
1595
/* Init the db list with template1 */
1596
db_list = init_db_list();
1597
if (db_list == NULL)
1600
if (check_stats_enabled(((db_info *) DLE_VAL(DLGetHead(db_list)))) != 0)
1602
log_entry("GUC variable stats_row_level must be enabled.", LVL_ERROR);
1603
log_entry(" Please fix the problems and try again.", LVL_EXTRA);
1609
gettimeofday(&then, 0); /* for use later to caluculate sleep time */
1614
/* We can now report the running status to SCM. */
1615
ServiceStatus.dwCurrentState = SERVICE_RUNNING;
1617
SetServiceStatus(hStatus, &ServiceStatus);
1619
while (ServiceStatus.dwCurrentState == SERVICE_RUNNING)
1624
db_elem = DLGetHead(db_list); /* Reset cur_db_node to the
1625
* beginning of the db_list */
1627
dbs = ((db_info *) DLE_VAL(db_elem)); /* get pointer to cur_db's
1629
if (dbs->conn == NULL)
1631
dbs->conn = db_connect(dbs);
1632
if (dbs->conn == NULL)
1633
{ /* Serious problem: We can't connect to
1635
log_entry("Cannot connect to template1, exiting.", LVL_ERROR);
1639
ServiceStatus.dwCurrentState = SERVICE_STOPPED;
1640
ServiceStatus.dwWin32ExitCode = ERROR_SERVICE_SPECIFIC_ERROR;
1641
ServiceStatus.dwServiceSpecificExitCode = -1;
1643
SetServiceStatus(hStatus, &ServiceStatus);
1649
if (loops % UPDATE_INTERVAL == 0) /* Update the list if it's
1651
update_db_list(db_list); /* Add and remove databases from
1654
while (db_elem != NULL)
1655
{ /* Loop through databases in list */
1656
dbs = ((db_info *) DLE_VAL(db_elem)); /* get pointer to
1659
if (dbs->conn == NULL)
1660
dbs->conn = db_connect(dbs);
1662
if (dbs->conn != NULL)
1664
if (loops % UPDATE_INTERVAL == 0) /* Update the list if
1666
update_table_list(dbs); /* Add and remove tables
1669
if (xid_wraparound_check(dbs) == 0)
1671
res = send_query(TABLE_STATS_QUERY, dbs); /* Get an updated
1672
* snapshot of this dbs
1676
for (j = 0; j < PQntuples(res); j++)
1677
{ /* loop through result set */
1678
tbl_elem = DLGetHead(dbs->table_list); /* Reset tbl_elem to top
1679
* of dbs->table_list */
1680
while (tbl_elem != NULL)
1681
{ /* Loop through tables in list */
1682
tbl = ((tbl_info *) DLE_VAL(tbl_elem)); /* set tbl_info =
1684
if (tbl->relid == atooid(PQgetvalue(res, j, PQfnumber(res, "oid"))))
1686
tbl->curr_analyze_count =
1687
(atol(PQgetvalue(res, j, PQfnumber(res, "n_tup_ins"))) +
1688
atol(PQgetvalue(res, j, PQfnumber(res, "n_tup_upd"))) +
1689
atol(PQgetvalue(res, j, PQfnumber(res, "n_tup_del"))));
1690
tbl->curr_vacuum_count =
1691
(atol(PQgetvalue(res, j, PQfnumber(res, "n_tup_del"))) +
1692
atol(PQgetvalue(res, j, PQfnumber(res, "n_tup_upd"))));
1695
* Check numDeletes to see if we need
1696
* to vacuum, if so: Run vacuum
1697
* analyze (adding analyze is small so
1698
* we might as well) Update table
1699
* thresholds and related information
1700
* if numDeletes is not big enough for
1701
* vacuum then check numInserts for
1704
if (tbl->curr_vacuum_count - tbl->CountAtLastVacuum >= tbl->vacuum_threshold)
1705
perform_maintenance_command(dbs, tbl, VACUUM_ANALYZE);
1706
else if (tbl->curr_analyze_count - tbl->CountAtLastAnalyze >= tbl->analyze_threshold)
1707
perform_maintenance_command(dbs, tbl, ANALYZE_ONLY);
1709
break; /* We found a match, no need to keep looping. */
1713
* Advance the table pointers for the next
1716
tbl_elem = DLGetSucc(tbl_elem);
1718
} /* end for table while loop */
1719
} /* end for j loop (tuples in PGresult) */
1720
} /* end if (res != NULL) */
1721
} /* close of if (xid_wraparound_check()) */
1722
/* Done working on this db, Clean up, then advance cur_db */
1727
db_elem = DLGetSucc(db_elem); /* move on to next DB
1729
} /* end of db_list while loop */
1731
/* Figure out how long to sleep etc ... */
1732
gettimeofday(&now, 0);
1733
diff = (int) (now.tv_sec - then.tv_sec) * 1000000.0 + (int) (now.tv_usec - then.tv_usec);
1735
sleep_secs = args->sleep_base_value + args->sleep_scaling_factor * diff / 1000000.0;
1737
if (args->debug >= 2)
1740
"%d All DBs checked in: %.0f usec, will sleep for %d secs.",
1741
loops, diff, sleep_secs);
1742
log_entry(logbuffer, LVL_DEBUG);
1746
pg_usleep(sleep_secs * 1000000); /* Larger Pause between outer loops */
1748
gettimeofday(&then, 0); /* Reset time counter */
1750
} /* end of while loop */
1753
* program is exiting, this should never run, but is here to make
1754
* compiler / valgrind happy
1756
free_db_list(db_list);
1761
/* Beginning of AutoVacuum Main Program */
1763
main(int argc, char *argv[])
1768
SERVICE_TABLE_ENTRY ServiceTable[2];
1771
args = get_cmd_args(argc, argv); /* Get Command Line Args and put
1772
* them in the args struct */
1774
/* Dameonize if requested */
1775
if (args->daemonize == 1)
1781
LOGOUTPUT = fopen(args->logfile, "a");
1784
fprintf(stderr, "Could not open log file - [%s]\n", args->logfile);
1790
if (args->debug >= 2)
1794
/* Install as a Windows service if required */
1795
if (args->install_as_service)
1797
if (InstallService() != 0)
1799
FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR) & lpMsgBuf, 0, NULL);
1800
fprintf(stderr, "Error: %s\n", (char *) lpMsgBuf);
1805
fprintf(stderr, "Successfully installed pg_autovacuum as a service.\n");
1810
/* Remove as a Windows service if required */
1811
if (args->remove_as_service)
1813
if (RemoveService() != 0)
1815
FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR) & lpMsgBuf, 0, NULL);
1816
fprintf(stderr, "Error: %s\n", (char *) lpMsgBuf);
1821
fprintf(stderr, "Successfully removed pg_autovacuum as a service.\n");
1826
/* Normal service startup */
1827
ServiceTable[0].lpServiceName = "pg_autovacuum";
1828
ServiceTable[0].lpServiceProc = (LPSERVICE_MAIN_FUNCTION) VacuumLoop;
1830
ServiceTable[1].lpServiceName = NULL;
1831
ServiceTable[1].lpServiceProc = NULL;
1833
/* Start the control dispatcher thread for our service */
1834
if (!StartServiceCtrlDispatcher(ServiceTable))
1837
VacuumLoop(0, NULL);
1842
/* Call the main program loop. */
1843
VacuumLoop(0, NULL);
1846
return EXIT_SUCCESS;