1010 *
1111 *
1212 * IDENTIFICATION
13- * $PostgreSQL: pgsql/src/backend/postmaster/autovacuum.c,v 1.3 2005/08/11 21:11:44 tgl Exp $
13+ * $PostgreSQL: pgsql/src/backend/postmaster/autovacuum.c,v 1.4 2005/08/15 16:25:17 tgl Exp $
1414 *
1515 *-------------------------------------------------------------------------
1616 */
1717#include "postgres.h"
1818
1919#include <signal.h>
20- #include <time.h>
2120#include <sys/types.h>
21+ #include <time.h>
2222#include <unistd.h>
2323
2424#include "access/genam.h"
@@ -86,6 +86,7 @@ typedef struct autovac_dbase
8686typedef struct autovac_table
8787{
8888Oid relid ;
89+ Oid toastrelid ;
8990bool dovacuum ;
9091bool doanalyze ;
9192int vacuum_cost_delay ;
@@ -101,8 +102,10 @@ static void process_whole_db(void);
101102static void do_autovacuum (PgStat_StatDBEntry * dbentry );
102103static List * autovac_get_database_list (void );
103104static void test_rel_for_autovac (Oid relid ,PgStat_StatTabEntry * tabentry ,
104- Form_pg_class classForm ,Form_pg_autovacuum avForm ,
105- List * * vacuum_tables );
105+ Form_pg_class classForm ,
106+ Form_pg_autovacuum avForm ,
107+ List * * vacuum_tables ,
108+ List * * toast_table_ids );
106109static void autovacuum_do_vac_analyze (List * relids ,bool dovacuum ,
107110bool doanalyze ,bool freeze );
108111
@@ -386,12 +389,21 @@ AutoVacMain(int argc, char *argv[])
386389
387390if (db )
388391{
392+ /*
393+ * Report autovac startup to the stats collector. We deliberately
394+ * do this before InitPostgres, so that the last_autovac_time will
395+ * get updated even if the connection attempt fails. This is to
396+ * prevent autovac from getting "stuck" repeatedly selecting an
397+ * unopenable database, rather than making any progress on stuff
398+ * it can connect to.
399+ */
400+ pgstat_report_autovac (db -> oid );
401+
389402/*
390403 * Connect to the selected database
391404 */
392405InitPostgres (db -> name ,NULL );
393406SetProcessingMode (NormalProcessing );
394- pgstat_report_autovac ();
395407set_ps_display (db -> name );
396408ereport (LOG ,
397409(errmsg ("autovacuum: processing database \"%s\"" ,db -> name )));
@@ -538,6 +550,7 @@ do_autovacuum(PgStat_StatDBEntry *dbentry)
538550HeapTuple tuple ;
539551HeapScanDesc relScan ;
540552List * vacuum_tables = NIL ;
553+ List * toast_table_ids = NIL ;
541554ListCell * cell ;
542555PgStat_StatDBEntry * shared ;
543556
@@ -558,9 +571,25 @@ do_autovacuum(PgStat_StatDBEntry *dbentry)
558571classRel = heap_open (RelationRelationId ,AccessShareLock );
559572avRel = heap_open (AutovacuumRelationId ,AccessShareLock );
560573
574+ /*
575+ * Scan pg_class and determine which tables to vacuum.
576+ *
577+ * The stats subsystem collects stats for toast tables independently
578+ * of the stats for their parent tables. We need to check those stats
579+ * since in cases with short, wide tables there might be proportionally
580+ * much more activity in the toast table than in its parent.
581+ *
582+ * Since we can only issue VACUUM against the parent table, we need to
583+ * transpose a decision to vacuum a toast table into a decision to vacuum
584+ * its parent. There's no point in considering ANALYZE on a toast table,
585+ * either. To support this, we keep a list of OIDs of toast tables that
586+ * need vacuuming alongside the list of regular tables. Regular tables
587+ * will be entered into the table list even if they appear not to need
588+ * vacuuming; we go back and re-mark them after finding all the
589+ * vacuumable toast tables.
590+ */
561591relScan = heap_beginscan (classRel ,SnapshotNow ,0 ,NULL );
562592
563- /* Scan pg_class looking for tables to vacuum */
564593while ((tuple = heap_getnext (relScan ,ForwardScanDirection ))!= NULL )
565594{
566595Form_pg_class classForm = (Form_pg_class )GETSTRUCT (tuple );
@@ -571,9 +600,9 @@ do_autovacuum(PgStat_StatDBEntry *dbentry)
571600ScanKeyData entry [1 ];
572601Oid relid ;
573602
574- /*Skip non-table entries . */
575- /* XXX possibly allow RELKIND_TOASTVALUE entries here too? */
576- if ( classForm -> relkind != RELKIND_RELATION )
603+ /*Consider only regular and toast tables . */
604+ if ( classForm -> relkind != RELKIND_RELATION &&
605+ classForm -> relkind != RELKIND_TOASTVALUE )
577606continue ;
578607
579608/*
@@ -607,7 +636,7 @@ do_autovacuum(PgStat_StatDBEntry *dbentry)
607636HASH_FIND ,NULL );
608637
609638test_rel_for_autovac (relid ,tabentry ,classForm ,avForm ,
610- & vacuum_tables );
639+ & vacuum_tables , & toast_table_ids );
611640
612641systable_endscan (avScan );
613642}
@@ -625,6 +654,22 @@ do_autovacuum(PgStat_StatDBEntry *dbentry)
625654
626655CHECK_FOR_INTERRUPTS ();
627656
657+ /*
658+ * Check to see if we need to force vacuuming of this table because
659+ * its toast table needs it.
660+ */
661+ if (OidIsValid (tab -> toastrelid )&& !tab -> dovacuum &&
662+ list_member_oid (toast_table_ids ,tab -> toastrelid ))
663+ {
664+ tab -> dovacuum = true;
665+ elog (DEBUG2 ,"autovac: VACUUM %u because of TOAST table" ,
666+ tab -> relid );
667+ }
668+
669+ /* Otherwise, ignore table if it needs no work */
670+ if (!tab -> dovacuum && !tab -> doanalyze )
671+ continue ;
672+
628673/* Set the vacuum cost parameters for this table */
629674VacuumCostDelay = tab -> vacuum_cost_delay ;
630675VacuumCostLimit = tab -> vacuum_cost_limit ;
@@ -643,7 +688,7 @@ do_autovacuum(PgStat_StatDBEntry *dbentry)
643688 * test_rel_for_autovac
644689 *
645690 * Check whether a table needs to be vacuumed or analyzed. Add it to the
646- * output list if so.
691+ *appropriate output list if so.
647692 *
648693 * A table needs to be vacuumed if the number of dead tuples exceeds a
649694 * threshold. This threshold is calculated as
@@ -670,7 +715,8 @@ static void
670715test_rel_for_autovac (Oid relid ,PgStat_StatTabEntry * tabentry ,
671716Form_pg_class classForm ,
672717Form_pg_autovacuum avForm ,
673- List * * vacuum_tables )
718+ List * * vacuum_tables ,
719+ List * * toast_table_ids )
674720{
675721Relation rel ;
676722float4 reltuples ;/* pg_class.reltuples */
@@ -764,12 +810,10 @@ test_rel_for_autovac(Oid relid, PgStat_StatTabEntry *tabentry,
764810 * will be reset too.
765811 */
766812
767- elog (DEBUG2 ,"%s: vac: %.0f (threshold %.0f), anl: %.0f (threshold %.0f)" ,
813+ elog (DEBUG3 ,"%s: vac: %.0f (threshold %.0f), anl: %.0f (threshold %.0f)" ,
768814RelationGetRelationName (rel ),
769815vactuples ,vacthresh ,anltuples ,anlthresh );
770816
771- Assert (CurrentMemoryContext == AutovacMemCxt );
772-
773817/* Determine if this table needs vacuum or analyze. */
774818dovacuum = (vactuples > vacthresh );
775819doanalyze = (anltuples > anlthresh );
@@ -778,23 +822,40 @@ test_rel_for_autovac(Oid relid, PgStat_StatTabEntry *tabentry,
778822if (relid == StatisticRelationId )
779823doanalyze = false;
780824
781- if (dovacuum || doanalyze )
825+ Assert (CurrentMemoryContext == AutovacMemCxt );
826+
827+ if (classForm -> relkind == RELKIND_RELATION )
782828{
783- autovac_table * tab ;
829+ if (dovacuum || doanalyze )
830+ elog (DEBUG2 ,"autovac: will%s%s %s" ,
831+ (dovacuum ?" VACUUM" :"" ),
832+ (doanalyze ?" ANALYZE" :"" ),
833+ RelationGetRelationName (rel ));
784834
785- elog (DEBUG2 ,"will%s%s %s" ,
786- (dovacuum ?" VACUUM" :"" ),
787- (doanalyze ?" ANALYZE" :"" ),
788- RelationGetRelationName (rel ));
835+ /*
836+ * we must record tables that have a toast table, even if we currently
837+ * don't think they need vacuuming.
838+ */
839+ if (dovacuum || doanalyze || OidIsValid (classForm -> reltoastrelid ))
840+ {
841+ autovac_table * tab ;
789842
790- tab = (autovac_table * )palloc (sizeof (autovac_table ));
791- tab -> relid = relid ;
792- tab -> dovacuum = dovacuum ;
793- tab -> doanalyze = doanalyze ;
794- tab -> vacuum_cost_limit = vac_cost_limit ;
795- tab -> vacuum_cost_delay = vac_cost_delay ;
843+ tab = (autovac_table * )palloc (sizeof (autovac_table ));
844+ tab -> relid = relid ;
845+ tab -> toastrelid = classForm -> reltoastrelid ;
846+ tab -> dovacuum = dovacuum ;
847+ tab -> doanalyze = doanalyze ;
848+ tab -> vacuum_cost_limit = vac_cost_limit ;
849+ tab -> vacuum_cost_delay = vac_cost_delay ;
796850
797- * vacuum_tables = lappend (* vacuum_tables ,tab );
851+ * vacuum_tables = lappend (* vacuum_tables ,tab );
852+ }
853+ }
854+ else
855+ {
856+ Assert (classForm -> relkind == RELKIND_TOASTVALUE );
857+ if (dovacuum )
858+ * toast_table_ids = lappend_oid (* toast_table_ids ,relid );
798859}
799860
800861RelationClose (rel );