Header And Logo

PostgreSQL
| The world's most advanced open source database.

Data Structures | Defines | Typedefs | Enumerations | Functions | Variables

autovacuum.c File Reference

#include "postgres.h"
#include <signal.h>
#include <sys/types.h>
#include <sys/time.h>
#include <unistd.h>
#include "access/heapam.h"
#include "access/htup_details.h"
#include "access/multixact.h"
#include "access/reloptions.h"
#include "access/transam.h"
#include "access/xact.h"
#include "catalog/dependency.h"
#include "catalog/namespace.h"
#include "catalog/pg_database.h"
#include "commands/dbcommands.h"
#include "commands/vacuum.h"
#include "lib/ilist.h"
#include "libpq/pqsignal.h"
#include "miscadmin.h"
#include "pgstat.h"
#include "postmaster/autovacuum.h"
#include "postmaster/fork_process.h"
#include "postmaster/postmaster.h"
#include "storage/bufmgr.h"
#include "storage/ipc.h"
#include "storage/latch.h"
#include "storage/pmsignal.h"
#include "storage/proc.h"
#include "storage/procsignal.h"
#include "storage/sinvaladt.h"
#include "tcop/tcopprot.h"
#include "utils/fmgroids.h"
#include "utils/lsyscache.h"
#include "utils/memutils.h"
#include "utils/ps_status.h"
#include "utils/rel.h"
#include "utils/snapmgr.h"
#include "utils/syscache.h"
#include "utils/timeout.h"
#include "utils/timestamp.h"
#include "utils/tqual.h"
Include dependency graph for autovacuum.c:

Go to the source code of this file.

Data Structures

struct  avl_dbase
struct  avw_dbase
struct  av_relation
struct  autovac_table
struct  WorkerInfoData
struct  AutoVacuumShmemStruct

Defines

#define STATS_READ_DELAY   1000
#define MIN_AUTOVAC_SLEEPTIME   100.0
#define MAX_AUTOVAC_ACTIV_LEN   (NAMEDATALEN * 2 + 56)

Typedefs

typedef struct avl_dbase avl_dbase
typedef struct avw_dbase avw_dbase
typedef struct av_relation av_relation
typedef struct autovac_table autovac_table
typedef struct WorkerInfoData WorkerInfoData
typedef struct WorkerInfoDataWorkerInfo

Enumerations

enum  AutoVacuumSignal { AutoVacForkFailed, AutoVacRebalance, AutoVacNumSignals }

Functions

NON_EXEC_STATIC void AutoVacWorkerMain (int argc, char *argv[]) __attribute__((noreturn))
NON_EXEC_STATIC void AutoVacLauncherMain (int argc, char *argv[]) __attribute__((noreturn))
static Oid do_start_worker (void)
static void launcher_determine_sleep (bool canlaunch, bool recursing, struct timeval *nap)
static void launch_worker (TimestampTz now)
static Listget_database_list (void)
static void rebuild_database_list (Oid newdb)
static int db_comparator (const void *a, const void *b)
static void autovac_balance_cost (void)
static void do_autovacuum (void)
static void FreeWorkerInfo (int code, Datum arg)
static autovac_tabletable_recheck_autovac (Oid relid, HTAB *table_toast_map, TupleDesc pg_class_desc)
static void relation_needs_vacanalyze (Oid relid, AutoVacOpts *relopts, Form_pg_class classForm, PgStat_StatTabEntry *tabentry, bool *dovacuum, bool *doanalyze, bool *wraparound)
static void autovacuum_do_vac_analyze (autovac_table *tab, BufferAccessStrategy bstrategy)
static AutoVacOptsextract_autovac_opts (HeapTuple tup, TupleDesc pg_class_desc)
static PgStat_StatTabEntryget_pgstat_tabentry_relid (Oid relid, bool isshared, PgStat_StatDBEntry *shared, PgStat_StatDBEntry *dbentry)
static void autovac_report_activity (autovac_table *tab)
static void avl_sighup_handler (SIGNAL_ARGS)
static void avl_sigusr2_handler (SIGNAL_ARGS)
static void avl_sigterm_handler (SIGNAL_ARGS)
static void autovac_refresh_stats (void)
int StartAutoVacLauncher (void)
void AutoVacWorkerFailed (void)
int StartAutoVacWorker (void)
void AutoVacuumUpdateDelay (void)
bool AutoVacuumingActive (void)
void autovac_init (void)
bool IsAutoVacuumLauncherProcess (void)
bool IsAutoVacuumWorkerProcess (void)
Size AutoVacuumShmemSize (void)
void AutoVacuumShmemInit (void)

Variables

bool autovacuum_start_daemon = false
int autovacuum_max_workers
int autovacuum_naptime
int autovacuum_vac_thresh
double autovacuum_vac_scale
int autovacuum_anl_thresh
double autovacuum_anl_scale
int autovacuum_freeze_max_age
int autovacuum_vac_cost_delay
int autovacuum_vac_cost_limit
int Log_autovacuum_min_duration = -1
static bool am_autovacuum_launcher = false
static bool am_autovacuum_worker = false
static volatile sig_atomic_t got_SIGHUP = false
static volatile sig_atomic_t got_SIGUSR2 = false
static volatile sig_atomic_t got_SIGTERM = false
static TransactionId recentXid
static MultiXactId recentMulti
static int default_freeze_min_age
static int default_freeze_table_age
static MemoryContext AutovacMemCxt
static AutoVacuumShmemStructAutoVacuumShmem
static dlist_head DatabaseList = DLIST_STATIC_INIT(DatabaseList)
static MemoryContext DatabaseListCxt = NULL
static WorkerInfo MyWorkerInfo = NULL
int AutovacuumLauncherPid = 0

Define Documentation

#define MAX_AUTOVAC_ACTIV_LEN   (NAMEDATALEN * 2 + 56)

Referenced by autovac_report_activity().

#define MIN_AUTOVAC_SLEEPTIME   100.0

Definition at line 129 of file autovacuum.c.

Referenced by launcher_determine_sleep(), and rebuild_database_list().

#define STATS_READ_DELAY   1000

Definition at line 126 of file autovacuum.c.

Referenced by autovac_refresh_stats().


Typedef Documentation

typedef struct autovac_table autovac_table
typedef struct av_relation av_relation
typedef struct avl_dbase avl_dbase
typedef struct avw_dbase avw_dbase
typedef struct WorkerInfoData* WorkerInfo

Definition at line 225 of file autovacuum.c.


Enumeration Type Documentation

Enumerator:
AutoVacForkFailed 
AutoVacRebalance 
AutoVacNumSignals 

Definition at line 232 of file autovacuum.c.

{
    AutoVacForkFailed,          /* failed trying to start a worker */
    AutoVacRebalance,           /* rebalance the cost limits */
    AutoVacNumSignals           /* must be last */
}   AutoVacuumSignal;


Function Documentation

static void autovac_balance_cost ( void   )  [static]

Definition at line 1753 of file autovacuum.c.

References autovacuum_vac_cost_delay, autovacuum_vac_cost_limit, AutoVacuumShmemStruct::av_runningWorkers, dlist_iter::cur, DEBUG2, dlist_container, dlist_foreach, elog, Max, Min, NULL, PGPROC::pid, VacuumCostDelay, VacuumCostLimit, WorkerInfoData::wi_cost_delay, WorkerInfoData::wi_cost_limit, WorkerInfoData::wi_cost_limit_base, WorkerInfoData::wi_dboid, WorkerInfoData::wi_proc, and WorkerInfoData::wi_tableoid.

Referenced by AutoVacLauncherMain(), and do_autovacuum().

{
    /*
     * The idea here is that we ration out I/O equally.  The amount of I/O
     * that a worker can consume is determined by cost_limit/cost_delay, so we
     * try to equalize those ratios rather than the raw limit settings.
     *
     * note: in cost_limit, zero also means use value from elsewhere, because
     * zero is not a valid value.
     */
    int         vac_cost_limit = (autovacuum_vac_cost_limit > 0 ?
                                autovacuum_vac_cost_limit : VacuumCostLimit);
    int         vac_cost_delay = (autovacuum_vac_cost_delay >= 0 ?
                                autovacuum_vac_cost_delay : VacuumCostDelay);
    double      cost_total;
    double      cost_avail;
    dlist_iter  iter;

    /* not set? nothing to do */
    if (vac_cost_limit <= 0 || vac_cost_delay <= 0)
        return;

    /* caculate the total base cost limit of active workers */
    cost_total = 0.0;
    dlist_foreach(iter, &AutoVacuumShmem->av_runningWorkers)
    {
        WorkerInfo worker = dlist_container(WorkerInfoData, wi_links, iter.cur);

        if (worker->wi_proc != NULL &&
            worker->wi_cost_limit_base > 0 && worker->wi_cost_delay > 0)
            cost_total +=
                (double) worker->wi_cost_limit_base / worker->wi_cost_delay;
    }
    /* there are no cost limits -- nothing to do */
    if (cost_total <= 0)
        return;

    /*
     * Adjust cost limit of each active worker to balance the total of cost
     * limit to autovacuum_vacuum_cost_limit.
     */
    cost_avail = (double) vac_cost_limit / vac_cost_delay;
    dlist_foreach(iter, &AutoVacuumShmem->av_runningWorkers)
    {
        WorkerInfo worker = dlist_container(WorkerInfoData, wi_links, iter.cur);

        if (worker->wi_proc != NULL &&
            worker->wi_cost_limit_base > 0 && worker->wi_cost_delay > 0)
        {
            int         limit = (int)
            (cost_avail * worker->wi_cost_limit_base / cost_total);

            /*
             * We put a lower bound of 1 on the cost_limit, to avoid division-
             * by-zero in the vacuum code.  Also, in case of roundoff trouble
             * in these calculations, let's be sure we don't ever set
             * cost_limit to more than the base value.
             */
            worker->wi_cost_limit = Max(Min(limit,
                                            worker->wi_cost_limit_base),
                                        1);

            elog(DEBUG2, "autovac_balance_cost(pid=%u db=%u, rel=%u, cost_limit=%d, cost_limit_base=%d, cost_delay=%d)",
                 worker->wi_proc->pid, worker->wi_dboid, worker->wi_tableoid,
                 worker->wi_cost_limit, worker->wi_cost_limit_base,
                 worker->wi_cost_delay);
        }
    }
}

void autovac_init ( void   ) 

Definition at line 2831 of file autovacuum.c.

References autovacuum_start_daemon, ereport, errhint(), errmsg(), pgstat_track_counts, and WARNING.

Referenced by PostmasterMain().

{
    if (autovacuum_start_daemon && !pgstat_track_counts)
        ereport(WARNING,
                (errmsg("autovacuum not started because of misconfiguration"),
                 errhint("Enable the \"track_counts\" option.")));
}

static void autovac_refresh_stats ( void   )  [static]

Definition at line 2927 of file autovacuum.c.

References GetCurrentTimestamp(), IsAutoVacuumLauncherProcess(), pgstat_clear_snapshot(), STATS_READ_DELAY, and TimestampDifferenceExceeds().

Referenced by do_start_worker(), rebuild_database_list(), and table_recheck_autovac().

{
    if (IsAutoVacuumLauncherProcess())
    {
        static TimestampTz last_read = 0;
        TimestampTz current_time;

        current_time = GetCurrentTimestamp();

        if (!TimestampDifferenceExceeds(last_read, current_time,
                                        STATS_READ_DELAY))
            return;

        last_read = current_time;
    }

    pgstat_clear_snapshot();
}

static void autovac_report_activity ( autovac_table tab  )  [static]

Definition at line 2781 of file autovacuum.c.

References autovac_table::at_doanalyze, autovac_table::at_dovacuum, autovac_table::at_nspname, autovac_table::at_relname, autovac_table::at_wraparound, MAX_AUTOVAC_ACTIV_LEN, pgstat_report_activity(), SetCurrentStatementStartTimestamp(), snprintf(), and STATE_RUNNING.

Referenced by autovacuum_do_vac_analyze().

{
#define MAX_AUTOVAC_ACTIV_LEN (NAMEDATALEN * 2 + 56)
    char        activity[MAX_AUTOVAC_ACTIV_LEN];
    int         len;

    /* Report the command and possible options */
    if (tab->at_dovacuum)
        snprintf(activity, MAX_AUTOVAC_ACTIV_LEN,
                 "autovacuum: VACUUM%s",
                 tab->at_doanalyze ? " ANALYZE" : "");
    else
        snprintf(activity, MAX_AUTOVAC_ACTIV_LEN,
                 "autovacuum: ANALYZE");

    /*
     * Report the qualified name of the relation.
     */
    len = strlen(activity);

    snprintf(activity + len, MAX_AUTOVAC_ACTIV_LEN - len,
             " %s.%s%s", tab->at_nspname, tab->at_relname,
             tab->at_wraparound ? " (to prevent wraparound)" : "");

    /* Set statement_timestamp() to current time for pg_stat_activity */
    SetCurrentStatementStartTimestamp();

    pgstat_report_activity(STATE_RUNNING, activity);
}

NON_EXEC_STATIC void AutoVacLauncherMain ( int  argc,
char *  argv[] 
)

Definition at line 399 of file autovacuum.c.

References AbortCurrentTransaction(), avl_dbase::adl_next_worker, ALLOCSET_DEFAULT_INITSIZE, ALLOCSET_DEFAULT_MAXSIZE, ALLOCSET_DEFAULT_MINSIZE, AllocSetContextCreate(), am_autovacuum_launcher, autovac_balance_cost(), AutoVacForkFailed, AutoVacRebalance, autovacuum_naptime, AutoVacuumingActive(), AutovacuumLock, AutoVacuumShmemStruct::av_freeWorkers, AutoVacuumShmemStruct::av_launcherpid, AutoVacuumShmemStruct::av_signal, AutoVacuumShmemStruct::av_startingWorker, avl_sighup_handler(), avl_sigterm_handler(), avl_sigusr2_handler(), BaseInit(), disable_all_timeouts(), DisableCatchupInterrupt(), dlist_init(), dlist_is_empty(), dlist_push_head(), dlist_tail_element, do_start_worker(), elog, EmitErrorReport(), EnableCatchupInterrupt(), ereport, errmsg(), error_context_stack, FATAL, FloatExceptionHandler(), FlushErrorState(), GetCurrentTimestamp(), got_SIGHUP, got_SIGTERM, got_SIGUSR2, HOLD_INTERRUPTS, init_ps_display(), InitializeTimeouts(), InitPostgres(), InitProcess(), InitProcessing, InvalidOid, IsUnderPostmaster, launch_worker(), launcher_determine_sleep(), LOG, LW_EXCLUSIVE, LW_SHARED, LWLockAcquire(), LWLockRelease(), MemoryContextResetAndDeleteChildren(), MemoryContextSwitchTo(), Min, MyProc, MyProcPid, MyStartTime, NormalProcessing, NULL, PG_exception_stack, PG_SETMASK, pg_usleep(), PGC_S_OVERRIDE, PGC_SIGHUP, PGC_SUSET, pgstat_clear_snapshot(), PMSIGNAL_START_AUTOVAC_WORKER, PostAuthDelay, pqsignal(), proc_exit(), ProcessConfigFile(), PGPROC::procLatch, procsignal_sigusr1_handler(), QueryCancelPending, quickdie(), rebuild_database_list(), ResetLatch(), RESUME_INTERRUPTS, SendPostmasterSignal(), SetConfigOption(), SetProcessingMode, SIG_DFL, SIG_IGN, SIGCHLD, SIGHUP, sigjmp_buf, SIGPIPE, SIGQUIT, sigsetjmp, SIGUSR1, SIGUSR2, StatementCancelHandler(), TimestampDifferenceExceeds(), TopMemoryContext, UnBlockSig, WaitLatch(), waittime, WARNING, WorkerInfoData::wi_dboid, WorkerInfoData::wi_launchtime, WorkerInfoData::wi_links, WorkerInfoData::wi_proc, WorkerInfoData::wi_tableoid, WL_LATCH_SET, WL_POSTMASTER_DEATH, and WL_TIMEOUT.

Referenced by StartAutoVacLauncher().

{
    sigjmp_buf  local_sigjmp_buf;

    /* we are a postmaster subprocess now */
    IsUnderPostmaster = true;
    am_autovacuum_launcher = true;

    /* reset MyProcPid */
    MyProcPid = getpid();

    /* record Start Time for logging */
    MyStartTime = time(NULL);

    /* Identify myself via ps */
    init_ps_display("autovacuum launcher process", "", "", "");

    ereport(LOG,
            (errmsg("autovacuum launcher started")));

    if (PostAuthDelay)
        pg_usleep(PostAuthDelay * 1000000L);

    SetProcessingMode(InitProcessing);

    /*
     * If possible, make this process a group leader, so that the postmaster
     * can signal any child processes too.  (autovacuum probably never has any
     * child processes, but for consistency we make all postmaster child
     * processes do this.)
     */
#ifdef HAVE_SETSID
    if (setsid() < 0)
        elog(FATAL, "setsid() failed: %m");
#endif

    /*
     * Set up signal handlers.  We operate on databases much like a regular
     * backend, so we use the same signal handling.  See equivalent code in
     * tcop/postgres.c.
     */
    pqsignal(SIGHUP, avl_sighup_handler);
    pqsignal(SIGINT, StatementCancelHandler);
    pqsignal(SIGTERM, avl_sigterm_handler);

    pqsignal(SIGQUIT, quickdie);
    InitializeTimeouts();       /* establishes SIGALRM handler */

    pqsignal(SIGPIPE, SIG_IGN);
    pqsignal(SIGUSR1, procsignal_sigusr1_handler);
    pqsignal(SIGUSR2, avl_sigusr2_handler);
    pqsignal(SIGFPE, FloatExceptionHandler);
    pqsignal(SIGCHLD, SIG_DFL);

    /* Early initialization */
    BaseInit();

    /*
     * Create a per-backend PGPROC struct in shared memory, except in the
     * EXEC_BACKEND case where this was done in SubPostmasterMain. We must do
     * this before we can use LWLocks (and in the EXEC_BACKEND case we already
     * had to do some stuff with LWLocks).
     */
#ifndef EXEC_BACKEND
    InitProcess();
#endif

    InitPostgres(NULL, InvalidOid, NULL, NULL);

    SetProcessingMode(NormalProcessing);

    /*
     * Create a memory context that we will do all our work in.  We do this so
     * that we can reset the context during error recovery and thereby avoid
     * possible memory leaks.
     */
    AutovacMemCxt = AllocSetContextCreate(TopMemoryContext,
                                          "Autovacuum Launcher",
                                          ALLOCSET_DEFAULT_MINSIZE,
                                          ALLOCSET_DEFAULT_INITSIZE,
                                          ALLOCSET_DEFAULT_MAXSIZE);
    MemoryContextSwitchTo(AutovacMemCxt);

    /*
     * If an exception is encountered, processing resumes here.
     *
     * This code is a stripped down version of PostgresMain error recovery.
     */
    if (sigsetjmp(local_sigjmp_buf, 1) != 0)
    {
        /* since not using PG_TRY, must reset error stack by hand */
        error_context_stack = NULL;

        /* Prevents interrupts while cleaning up */
        HOLD_INTERRUPTS();

        /* Forget any pending QueryCancel or timeout request */
        QueryCancelPending = false;
        disable_all_timeouts(false);
        QueryCancelPending = false;     /* again in case timeout occurred */

        /* Report the error to the server log */
        EmitErrorReport();

        /* Abort the current transaction in order to recover */
        AbortCurrentTransaction();

        /*
         * Now return to normal top-level context and clear ErrorContext for
         * next time.
         */
        MemoryContextSwitchTo(AutovacMemCxt);
        FlushErrorState();

        /* Flush any leaked data in the top-level context */
        MemoryContextResetAndDeleteChildren(AutovacMemCxt);

        /* don't leave dangling pointers to freed memory */
        DatabaseListCxt = NULL;
        dlist_init(&DatabaseList);

        /*
         * Make sure pgstat also considers our stat data as gone.  Note: we
         * mustn't use autovac_refresh_stats here.
         */
        pgstat_clear_snapshot();

        /* Now we can allow interrupts again */
        RESUME_INTERRUPTS();

        /*
         * Sleep at least 1 second after any error.  We don't want to be
         * filling the error logs as fast as we can.
         */
        pg_usleep(1000000L);
    }

    /* We can now handle ereport(ERROR) */
    PG_exception_stack = &local_sigjmp_buf;

    /* must unblock signals before calling rebuild_database_list */
    PG_SETMASK(&UnBlockSig);

    /*
     * Force zero_damaged_pages OFF in the autovac process, even if it is set
     * in postgresql.conf.  We don't really want such a dangerous option being
     * applied non-interactively.
     */
    SetConfigOption("zero_damaged_pages", "false", PGC_SUSET, PGC_S_OVERRIDE);

    /*
     * Force statement_timeout and lock_timeout to zero to avoid letting these
     * settings prevent regular maintenance from being executed.
     */
    SetConfigOption("statement_timeout", "0", PGC_SUSET, PGC_S_OVERRIDE);
    SetConfigOption("lock_timeout", "0", PGC_SUSET, PGC_S_OVERRIDE);

    /*
     * Force default_transaction_isolation to READ COMMITTED.  We don't want
     * to pay the overhead of serializable mode, nor add any risk of causing
     * deadlocks or delaying other transactions.
     */
    SetConfigOption("default_transaction_isolation", "read committed",
                    PGC_SUSET, PGC_S_OVERRIDE);

    /* in emergency mode, just start a worker and go away */
    if (!AutoVacuumingActive())
    {
        do_start_worker();
        proc_exit(0);           /* done */
    }

    AutoVacuumShmem->av_launcherpid = MyProcPid;

    /*
     * Create the initial database list.  The invariant we want this list to
     * keep is that it's ordered by decreasing next_time.  As soon as an entry
     * is updated to a higher time, it will be moved to the front (which is
     * correct because the only operation is to add autovacuum_naptime to the
     * entry, and time always increases).
     */
    rebuild_database_list(InvalidOid);

    for (;;)
    {
        struct timeval nap;
        TimestampTz current_time = 0;
        bool        can_launch;
        int         rc;

        /*
         * This loop is a bit different from the normal use of WaitLatch,
         * because we'd like to sleep before the first launch of a child
         * process.  So it's WaitLatch, then ResetLatch, then check for
         * wakening conditions.
         */

        launcher_determine_sleep(!dlist_is_empty(&AutoVacuumShmem->av_freeWorkers),
                                 false, &nap);

        /* Allow sinval catchup interrupts while sleeping */
        EnableCatchupInterrupt();

        /*
         * Wait until naptime expires or we get some type of signal (all the
         * signal handlers will wake us by calling SetLatch).
         */
        rc = WaitLatch(&MyProc->procLatch,
                       WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH,
                       (nap.tv_sec * 1000L) + (nap.tv_usec / 1000L));

        ResetLatch(&MyProc->procLatch);

        DisableCatchupInterrupt();

        /*
         * Emergency bailout if postmaster has died.  This is to avoid the
         * necessity for manual cleanup of all postmaster children.
         */
        if (rc & WL_POSTMASTER_DEATH)
            proc_exit(1);

        /* the normal shutdown case */
        if (got_SIGTERM)
            break;

        if (got_SIGHUP)
        {
            got_SIGHUP = false;
            ProcessConfigFile(PGC_SIGHUP);

            /* shutdown requested in config file? */
            if (!AutoVacuumingActive())
                break;

            /* rebalance in case the default cost parameters changed */
            LWLockAcquire(AutovacuumLock, LW_EXCLUSIVE);
            autovac_balance_cost();
            LWLockRelease(AutovacuumLock);

            /* rebuild the list in case the naptime changed */
            rebuild_database_list(InvalidOid);
        }

        /*
         * a worker finished, or postmaster signalled failure to start a
         * worker
         */
        if (got_SIGUSR2)
        {
            got_SIGUSR2 = false;

            /* rebalance cost limits, if needed */
            if (AutoVacuumShmem->av_signal[AutoVacRebalance])
            {
                LWLockAcquire(AutovacuumLock, LW_EXCLUSIVE);
                AutoVacuumShmem->av_signal[AutoVacRebalance] = false;
                autovac_balance_cost();
                LWLockRelease(AutovacuumLock);
            }

            if (AutoVacuumShmem->av_signal[AutoVacForkFailed])
            {
                /*
                 * If the postmaster failed to start a new worker, we sleep
                 * for a little while and resend the signal.  The new worker's
                 * state is still in memory, so this is sufficient.  After
                 * that, we restart the main loop.
                 *
                 * XXX should we put a limit to the number of times we retry?
                 * I don't think it makes much sense, because a future start
                 * of a worker will continue to fail in the same way.
                 */
                AutoVacuumShmem->av_signal[AutoVacForkFailed] = false;
                pg_usleep(1000000L);    /* 1s */
                SendPostmasterSignal(PMSIGNAL_START_AUTOVAC_WORKER);
                continue;
            }
        }

        /*
         * There are some conditions that we need to check before trying to
         * start a launcher.  First, we need to make sure that there is a
         * launcher slot available.  Second, we need to make sure that no
         * other worker failed while starting up.
         */

        current_time = GetCurrentTimestamp();
        LWLockAcquire(AutovacuumLock, LW_SHARED);

        can_launch = !dlist_is_empty(&AutoVacuumShmem->av_freeWorkers);

        if (AutoVacuumShmem->av_startingWorker != NULL)
        {
            int         waittime;
            WorkerInfo  worker = AutoVacuumShmem->av_startingWorker;

            /*
             * We can't launch another worker when another one is still
             * starting up (or failed while doing so), so just sleep for a bit
             * more; that worker will wake us up again as soon as it's ready.
             * We will only wait autovacuum_naptime seconds (up to a maximum
             * of 60 seconds) for this to happen however.  Note that failure
             * to connect to a particular database is not a problem here,
             * because the worker removes itself from the startingWorker
             * pointer before trying to connect.  Problems detected by the
             * postmaster (like fork() failure) are also reported and handled
             * differently.  The only problems that may cause this code to
             * fire are errors in the earlier sections of AutoVacWorkerMain,
             * before the worker removes the WorkerInfo from the
             * startingWorker pointer.
             */
            waittime = Min(autovacuum_naptime, 60) * 1000;
            if (TimestampDifferenceExceeds(worker->wi_launchtime, current_time,
                                           waittime))
            {
                LWLockRelease(AutovacuumLock);
                LWLockAcquire(AutovacuumLock, LW_EXCLUSIVE);

                /*
                 * No other process can put a worker in starting mode, so if
                 * startingWorker is still INVALID after exchanging our lock,
                 * we assume it's the same one we saw above (so we don't
                 * recheck the launch time).
                 */
                if (AutoVacuumShmem->av_startingWorker != NULL)
                {
                    worker = AutoVacuumShmem->av_startingWorker;
                    worker->wi_dboid = InvalidOid;
                    worker->wi_tableoid = InvalidOid;
                    worker->wi_proc = NULL;
                    worker->wi_launchtime = 0;
                    dlist_push_head(&AutoVacuumShmem->av_freeWorkers,
                                    &worker->wi_links);
                    AutoVacuumShmem->av_startingWorker = NULL;
                    elog(WARNING, "worker took too long to start; canceled");
                }
            }
            else
                can_launch = false;
        }
        LWLockRelease(AutovacuumLock);  /* either shared or exclusive */

        /* if we can't do anything, just go back to sleep */
        if (!can_launch)
            continue;

        /* We're OK to start a new worker */

        if (dlist_is_empty(&DatabaseList))
        {
            /*
             * Special case when the list is empty: start a worker right away.
             * This covers the initial case, when no database is in pgstats
             * (thus the list is empty).  Note that the constraints in
             * launcher_determine_sleep keep us from starting workers too
             * quickly (at most once every autovacuum_naptime when the list is
             * empty).
             */
            launch_worker(current_time);
        }
        else
        {
            /*
             * because rebuild_database_list constructs a list with most
             * distant adl_next_worker first, we obtain our database from the
             * tail of the list.
             */
            avl_dbase  *avdb;

            avdb = dlist_tail_element(avl_dbase, adl_node, &DatabaseList);

            /*
             * launch a worker if next_worker is right now or it is in the
             * past
             */
            if (TimestampDifferenceExceeds(avdb->adl_next_worker,
                                           current_time, 0))
                launch_worker(current_time);
        }
    }

    /* Normal exit from the autovac launcher is here */
    ereport(LOG,
            (errmsg("autovacuum launcher shutting down")));
    AutoVacuumShmem->av_launcherpid = 0;

    proc_exit(0);               /* done */
}

static void autovacuum_do_vac_analyze ( autovac_table tab,
BufferAccessStrategy  bstrategy 
) [static]

Definition at line 2736 of file autovacuum.c.

References autovac_table::at_doanalyze, autovac_table::at_dovacuum, autovac_table::at_freeze_min_age, autovac_table::at_freeze_table_age, autovac_table::at_nspname, autovac_table::at_relid, autovac_table::at_relname, autovac_table::at_wraparound, autovac_report_activity(), VacuumStmt::freeze_min_age, VacuumStmt::freeze_table_age, RangeVar::location, MemSet, VacuumStmt::options, VacuumStmt::relation, RangeVar::relname, RangeVar::schemaname, VacuumStmt::type, VacuumStmt::va_cols, and vacuum().

Referenced by do_autovacuum().

{
    VacuumStmt  vacstmt;
    RangeVar    rangevar;

    /* Set up command parameters --- use local variables instead of palloc */
    MemSet(&vacstmt, 0, sizeof(vacstmt));
    MemSet(&rangevar, 0, sizeof(rangevar));

    rangevar.schemaname = tab->at_nspname;
    rangevar.relname = tab->at_relname;
    rangevar.location = -1;

    vacstmt.type = T_VacuumStmt;
    if (!tab->at_wraparound)
        vacstmt.options = VACOPT_NOWAIT;
    if (tab->at_dovacuum)
        vacstmt.options |= VACOPT_VACUUM;
    if (tab->at_doanalyze)
        vacstmt.options |= VACOPT_ANALYZE;
    vacstmt.freeze_min_age = tab->at_freeze_min_age;
    vacstmt.freeze_table_age = tab->at_freeze_table_age;
    /* we pass the OID, but might need this anyway for an error message */
    vacstmt.relation = &rangevar;
    vacstmt.va_cols = NIL;

    /* Let pgstat know what we're doing */
    autovac_report_activity(tab);

    vacuum(&vacstmt, tab->at_relid, false, bstrategy, tab->at_wraparound, true);
}

bool AutoVacuumingActive ( void   ) 

Definition at line 2817 of file autovacuum.c.

References autovacuum_start_daemon, and pgstat_track_counts.

Referenced by AutoVacLauncherMain(), reaper(), and ServerLoop().

{
    if (!autovacuum_start_daemon || !pgstat_track_counts)
        return false;
    return true;
}

void AutoVacuumShmemInit ( void   ) 
Size AutoVacuumShmemSize ( void   ) 

Definition at line 2862 of file autovacuum.c.

References add_size(), autovacuum_max_workers, MAXALIGN, and mul_size().

Referenced by AutoVacuumShmemInit(), and CreateSharedMemoryAndSemaphores().

{
    Size        size;

    /*
     * Need the fixed struct and the array of WorkerInfoData.
     */
    size = sizeof(AutoVacuumShmemStruct);
    size = MAXALIGN(size);
    size = add_size(size, mul_size(autovacuum_max_workers,
                                   sizeof(WorkerInfoData)));
    return size;
}

void AutoVacuumUpdateDelay ( void   ) 
void AutoVacWorkerFailed ( void   ) 

Definition at line 1348 of file autovacuum.c.

References AutoVacuumShmemStruct::av_signal.

Referenced by StartAutovacuumWorker().

{
    AutoVacuumShmem->av_signal[AutoVacForkFailed] = true;
}

NON_EXEC_STATIC void AutoVacWorkerMain ( int  argc,
char *  argv[] 
)

Definition at line 1474 of file autovacuum.c.

References am_autovacuum_worker, AutovacuumLock, AutoVacuumShmemStruct::av_launcherpid, AutoVacuumShmemStruct::av_runningWorkers, AutoVacuumShmemStruct::av_startingWorker, BaseInit(), DEBUG1, die(), dlist_push_head(), do_autovacuum(), elog, EmitErrorReport(), ereport, errmsg(), FATAL, FloatExceptionHandler(), FreeWorkerInfo(), HOLD_INTERRUPTS, init_ps_display(), InitializeTimeouts(), InitPostgres(), InitProcess(), InitProcessing, IsUnderPostmaster, LW_EXCLUSIVE, LWLockAcquire(), LWLockRelease(), MyProc, MyProcPid, MyStartTime, NormalProcessing, NULL, OidIsValid, on_shmem_exit(), PG_exception_stack, PG_SETMASK, pg_usleep(), PGC_S_OVERRIDE, PGC_SUSET, pgstat_report_autovac(), PostAuthDelay, pqsignal(), proc_exit(), procsignal_sigusr1_handler(), quickdie(), ReadNewTransactionId(), ReadNextMultiXactId(), recentMulti, recentXid, set_ps_display(), SetConfigOption(), SetProcessingMode, SIG_DFL, SIG_IGN, SIGCHLD, SIGHUP, sigjmp_buf, SIGPIPE, SIGQUIT, sigsetjmp, SIGUSR1, SIGUSR2, StatementCancelHandler(), synchronous_commit, SYNCHRONOUS_COMMIT_LOCAL_FLUSH, UnBlockSig, WARNING, WorkerInfoData::wi_dboid, WorkerInfoData::wi_links, and WorkerInfoData::wi_proc.

Referenced by StartAutoVacWorker().

{
    sigjmp_buf  local_sigjmp_buf;
    Oid         dbid;

    /* we are a postmaster subprocess now */
    IsUnderPostmaster = true;
    am_autovacuum_worker = true;

    /* reset MyProcPid */
    MyProcPid = getpid();

    /* record Start Time for logging */
    MyStartTime = time(NULL);

    /* Identify myself via ps */
    init_ps_display("autovacuum worker process", "", "", "");

    SetProcessingMode(InitProcessing);

    /*
     * If possible, make this process a group leader, so that the postmaster
     * can signal any child processes too.  (autovacuum probably never has any
     * child processes, but for consistency we make all postmaster child
     * processes do this.)
     */
#ifdef HAVE_SETSID
    if (setsid() < 0)
        elog(FATAL, "setsid() failed: %m");
#endif

    /*
     * Set up signal handlers.  We operate on databases much like a regular
     * backend, so we use the same signal handling.  See equivalent code in
     * tcop/postgres.c.
     *
     * Currently, we don't pay attention to postgresql.conf changes that
     * happen during a single daemon iteration, so we can ignore SIGHUP.
     */
    pqsignal(SIGHUP, SIG_IGN);

    /*
     * SIGINT is used to signal canceling the current table's vacuum; SIGTERM
     * means abort and exit cleanly, and SIGQUIT means abandon ship.
     */
    pqsignal(SIGINT, StatementCancelHandler);
    pqsignal(SIGTERM, die);
    pqsignal(SIGQUIT, quickdie);
    InitializeTimeouts();       /* establishes SIGALRM handler */

    pqsignal(SIGPIPE, SIG_IGN);
    pqsignal(SIGUSR1, procsignal_sigusr1_handler);
    pqsignal(SIGUSR2, SIG_IGN);
    pqsignal(SIGFPE, FloatExceptionHandler);
    pqsignal(SIGCHLD, SIG_DFL);

    /* Early initialization */
    BaseInit();

    /*
     * Create a per-backend PGPROC struct in shared memory, except in the
     * EXEC_BACKEND case where this was done in SubPostmasterMain. We must do
     * this before we can use LWLocks (and in the EXEC_BACKEND case we already
     * had to do some stuff with LWLocks).
     */
#ifndef EXEC_BACKEND
    InitProcess();
#endif

    /*
     * If an exception is encountered, processing resumes here.
     *
     * See notes in postgres.c about the design of this coding.
     */
    if (sigsetjmp(local_sigjmp_buf, 1) != 0)
    {
        /* Prevents interrupts while cleaning up */
        HOLD_INTERRUPTS();

        /* Report the error to the server log */
        EmitErrorReport();

        /*
         * We can now go away.  Note that because we called InitProcess, a
         * callback was registered to do ProcKill, which will clean up
         * necessary state.
         */
        proc_exit(0);
    }

    /* We can now handle ereport(ERROR) */
    PG_exception_stack = &local_sigjmp_buf;

    PG_SETMASK(&UnBlockSig);

    /*
     * Force zero_damaged_pages OFF in the autovac process, even if it is set
     * in postgresql.conf.  We don't really want such a dangerous option being
     * applied non-interactively.
     */
    SetConfigOption("zero_damaged_pages", "false", PGC_SUSET, PGC_S_OVERRIDE);

    /*
     * Force statement_timeout and lock_timeout to zero to avoid letting these
     * settings prevent regular maintenance from being executed.
     */
    SetConfigOption("statement_timeout", "0", PGC_SUSET, PGC_S_OVERRIDE);
    SetConfigOption("lock_timeout", "0", PGC_SUSET, PGC_S_OVERRIDE);

    /*
     * Force default_transaction_isolation to READ COMMITTED.  We don't want
     * to pay the overhead of serializable mode, nor add any risk of causing
     * deadlocks or delaying other transactions.
     */
    SetConfigOption("default_transaction_isolation", "read committed",
                    PGC_SUSET, PGC_S_OVERRIDE);

    /*
     * Force synchronous replication off to allow regular maintenance even if
     * we are waiting for standbys to connect. This is important to ensure we
     * aren't blocked from performing anti-wraparound tasks.
     */
    if (synchronous_commit > SYNCHRONOUS_COMMIT_LOCAL_FLUSH)
        SetConfigOption("synchronous_commit", "local",
                        PGC_SUSET, PGC_S_OVERRIDE);

    /*
     * Get the info about the database we're going to work on.
     */
    LWLockAcquire(AutovacuumLock, LW_EXCLUSIVE);

    /*
     * beware of startingWorker being INVALID; this should normally not
     * happen, but if a worker fails after forking and before this, the
     * launcher might have decided to remove it from the queue and start
     * again.
     */
    if (AutoVacuumShmem->av_startingWorker != NULL)
    {
        MyWorkerInfo = AutoVacuumShmem->av_startingWorker;
        dbid = MyWorkerInfo->wi_dboid;
        MyWorkerInfo->wi_proc = MyProc;

        /* insert into the running list */
        dlist_push_head(&AutoVacuumShmem->av_runningWorkers,
                        &MyWorkerInfo->wi_links);

        /*
         * remove from the "starting" pointer, so that the launcher can start
         * a new worker if required
         */
        AutoVacuumShmem->av_startingWorker = NULL;
        LWLockRelease(AutovacuumLock);

        on_shmem_exit(FreeWorkerInfo, 0);

        /* wake up the launcher */
        if (AutoVacuumShmem->av_launcherpid != 0)
            kill(AutoVacuumShmem->av_launcherpid, SIGUSR2);
    }
    else
    {
        /* no worker entry for me, go away */
        elog(WARNING, "autovacuum worker started without a worker entry");
        dbid = InvalidOid;
        LWLockRelease(AutovacuumLock);
    }

    if (OidIsValid(dbid))
    {
        char        dbname[NAMEDATALEN];

        /*
         * Report autovac startup to the stats collector.  We deliberately do
         * this before InitPostgres, so that the last_autovac_time will get
         * updated even if the connection attempt fails.  This is to prevent
         * autovac from getting "stuck" repeatedly selecting an unopenable
         * database, rather than making any progress on stuff it can connect
         * to.
         */
        pgstat_report_autovac(dbid);

        /*
         * Connect to the selected database
         *
         * Note: if we have selected a just-deleted database (due to using
         * stale stats info), we'll fail and exit here.
         */
        InitPostgres(NULL, dbid, NULL, dbname);
        SetProcessingMode(NormalProcessing);
        set_ps_display(dbname, false);
        ereport(DEBUG1,
                (errmsg("autovacuum: processing database \"%s\"", dbname)));

        if (PostAuthDelay)
            pg_usleep(PostAuthDelay * 1000000L);

        /* And do an appropriate amount of work */
        recentXid = ReadNewTransactionId();
        recentMulti = ReadNextMultiXactId();
        do_autovacuum();
    }

    /*
     * The launcher will be notified of my death in ProcKill, *if* we managed
     * to get a worker slot at all
     */

    /* All done, go away */
    proc_exit(0);
}

static void avl_sighup_handler ( SIGNAL_ARGS   )  [static]

Definition at line 1355 of file autovacuum.c.

References got_SIGHUP, MyProc, PGPROC::procLatch, and SetLatch().

Referenced by AutoVacLauncherMain().

{
    int         save_errno = errno;

    got_SIGHUP = true;
    if (MyProc)
        SetLatch(&MyProc->procLatch);

    errno = save_errno;
}

static void avl_sigterm_handler ( SIGNAL_ARGS   )  [static]

Definition at line 1381 of file autovacuum.c.

References got_SIGTERM, MyProc, PGPROC::procLatch, and SetLatch().

Referenced by AutoVacLauncherMain().

{
    int         save_errno = errno;

    got_SIGTERM = true;
    if (MyProc)
        SetLatch(&MyProc->procLatch);

    errno = save_errno;
}

static void avl_sigusr2_handler ( SIGNAL_ARGS   )  [static]

Definition at line 1368 of file autovacuum.c.

References got_SIGUSR2, MyProc, PGPROC::procLatch, and SetLatch().

Referenced by AutoVacLauncherMain().

{
    int         save_errno = errno;

    got_SIGUSR2 = true;
    if (MyProc)
        SetLatch(&MyProc->procLatch);

    errno = save_errno;
}

static int db_comparator ( const void *  a,
const void *  b 
) [static]

Definition at line 1058 of file autovacuum.c.

Referenced by rebuild_database_list().

{
    if (((const avl_dbase *) a)->adl_score == ((const avl_dbase *) b)->adl_score)
        return 0;
    else
        return (((const avl_dbase *) a)->adl_score < ((const avl_dbase *) b)->adl_score) ? 1 : -1;
}

static void do_autovacuum ( void   )  [static]

Definition at line 1902 of file autovacuum.c.

References AbortOutOfAnyTransaction(), AccessShareLock, ALLOCSET_DEFAULT_INITSIZE, ALLOCSET_DEFAULT_MAXSIZE, ALLOCSET_DEFAULT_MINSIZE, AllocSetContextCreate(), Anum_pg_class_relkind, av_relation::ar_hasrelopts, av_relation::ar_relid, av_relation::ar_reloptions, autovac_table::at_datname, autovac_table::at_dovacuum, autovac_table::at_nspname, autovac_table::at_relid, autovac_table::at_relname, autovac_table::at_vacuum_cost_delay, autovac_table::at_vacuum_cost_limit, autovac_balance_cost(), autovacuum_do_vac_analyze(), AutovacuumLock, AutovacuumScheduleLock, AutoVacuumUpdateDelay(), AutoVacuumShmemStruct::av_runningWorkers, BackendIdGetProc(), BAS_VACUUM, BTEqualStrategyNumber, CharGetDatum, CHECK_FOR_INTERRUPTS, CommitTransactionCommand(), CreateTupleDescCopy(), dlist_iter::cur, DATABASEOID, default_freeze_min_age, default_freeze_table_age, dlist_container, dlist_foreach, DROP_CASCADE, elog, EmitErrorReport(), HASHCTL::entrysize, ereport, errcontext, errmsg(), ERROR, extract_autovac_opts(), FlushErrorState(), ForwardScanDirection, get_database_name(), get_namespace_name(), get_pgstat_tabentry_relid(), get_rel_name(), get_rel_namespace(), GetAccessStrategy(), GETSTRUCT, GetTempNamespaceBackendId(), HASHCTL::hash, hash_create(), HASH_ELEM, HASH_ENTER, HASH_FIND, HASH_FUNCTION, hash_search(), heap_beginscan(), heap_close, heap_endscan(), heap_getnext(), heap_open(), HeapTupleGetOid, HeapTupleIsValid, HOLD_INTERRUPTS, InvalidOid, HASHCTL::keysize, lappend_oid(), lfirst_oid, LOG, LW_EXCLUSIVE, LW_SHARED, LWLockAcquire(), LWLockRelease(), MemoryContextResetAndDeleteChildren(), MemoryContextSwitchTo(), MemSet, MyBackendId, MyDatabaseId, NameStr, NULL, ObjectIdGetDatum, OidIsValid, PERFORM_DELETION_INTERNAL, performDeletion(), pfree(), PG_CATCH, PG_END_TRY, PG_TRY, pgstat_fetch_stat_dbentry(), pgstat_vacuum_stat(), PortalContext, QueryCancelPending, relation_needs_vacanalyze(), RelationGetDescr, RelationRelationId, ReleaseSysCache(), RELKIND_MATVIEW, RELKIND_RELATION, RELKIND_TOASTVALUE, RELPERSISTENCE_TEMP, RESUME_INTERRUPTS, ScanKeyInit(), SearchSysCache1, SnapshotNow, StartTransactionCommand(), table_recheck_autovac(), TopMemoryContext, TopTransactionContext, vac_update_datfrozenxid(), vacuum_freeze_min_age, vacuum_freeze_table_age, VacuumCostDelay, VacuumCostLimit, WorkerInfoData::wi_cost_delay, WorkerInfoData::wi_cost_limit, WorkerInfoData::wi_cost_limit_base, WorkerInfoData::wi_dboid, and WorkerInfoData::wi_tableoid.

Referenced by AutoVacWorkerMain().

{
    Relation    classRel;
    HeapTuple   tuple;
    HeapScanDesc relScan;
    Form_pg_database dbForm;
    List       *table_oids = NIL;
    HASHCTL     ctl;
    HTAB       *table_toast_map;
    ListCell   *volatile cell;
    PgStat_StatDBEntry *shared;
    PgStat_StatDBEntry *dbentry;
    BufferAccessStrategy bstrategy;
    ScanKeyData key;
    TupleDesc   pg_class_desc;

    /*
     * StartTransactionCommand and CommitTransactionCommand will automatically
     * switch to other contexts.  We need this one to keep the list of
     * relations to vacuum/analyze across transactions.
     */
    AutovacMemCxt = AllocSetContextCreate(TopMemoryContext,
                                          "AV worker",
                                          ALLOCSET_DEFAULT_MINSIZE,
                                          ALLOCSET_DEFAULT_INITSIZE,
                                          ALLOCSET_DEFAULT_MAXSIZE);
    MemoryContextSwitchTo(AutovacMemCxt);

    /*
     * may be NULL if we couldn't find an entry (only happens if we are
     * forcing a vacuum for anti-wrap purposes).
     */
    dbentry = pgstat_fetch_stat_dbentry(MyDatabaseId);

    /* Start a transaction so our commands have one to play into. */
    StartTransactionCommand();

    /*
     * Clean up any dead statistics collector entries for this DB. We always
     * want to do this exactly once per DB-processing cycle, even if we find
     * nothing worth vacuuming in the database.
     */
    pgstat_vacuum_stat();

    /*
     * Find the pg_database entry and select the default freeze ages. We use
     * zero in template and nonconnectable databases, else the system-wide
     * default.
     */
    tuple = SearchSysCache1(DATABASEOID, ObjectIdGetDatum(MyDatabaseId));
    if (!HeapTupleIsValid(tuple))
        elog(ERROR, "cache lookup failed for database %u", MyDatabaseId);
    dbForm = (Form_pg_database) GETSTRUCT(tuple);

    if (dbForm->datistemplate || !dbForm->datallowconn)
    {
        default_freeze_min_age = 0;
        default_freeze_table_age = 0;
    }
    else
    {
        default_freeze_min_age = vacuum_freeze_min_age;
        default_freeze_table_age = vacuum_freeze_table_age;
    }

    ReleaseSysCache(tuple);

    /* StartTransactionCommand changed elsewhere */
    MemoryContextSwitchTo(AutovacMemCxt);

    /* The database hash where pgstat keeps shared relations */
    shared = pgstat_fetch_stat_dbentry(InvalidOid);

    classRel = heap_open(RelationRelationId, AccessShareLock);

    /* create a copy so we can use it after closing pg_class */
    pg_class_desc = CreateTupleDescCopy(RelationGetDescr(classRel));

    /* create hash table for toast <-> main relid mapping */
    MemSet(&ctl, 0, sizeof(ctl));
    ctl.keysize = sizeof(Oid);
    ctl.entrysize = sizeof(av_relation);
    ctl.hash = oid_hash;

    table_toast_map = hash_create("TOAST to main relid map",
                                  100,
                                  &ctl,
                                  HASH_ELEM | HASH_FUNCTION);

    /*
     * Scan pg_class to determine which tables to vacuum.
     *
     * We do this in two passes: on the first one we collect the list of plain
     * relations and materialized views, and on the second one we collect
     * TOAST tables. The reason for doing the second pass is that during it we
     * want to use the main relation's pg_class.reloptions entry if the TOAST
     * table does not have any, and we cannot obtain it unless we know
     * beforehand what's the main  table OID.
     *
     * We need to check TOAST tables separately because in cases with short,
     * wide tables there might be proportionally much more activity in the
     * TOAST table than in its parent.
     */
    relScan = heap_beginscan(classRel, SnapshotNow, 0, NULL);

    /*
     * On the first pass, we collect main tables to vacuum, and also the main
     * table relid to TOAST relid mapping.
     */
    while ((tuple = heap_getnext(relScan, ForwardScanDirection)) != NULL)
    {
        Form_pg_class classForm = (Form_pg_class) GETSTRUCT(tuple);
        PgStat_StatTabEntry *tabentry;
        AutoVacOpts *relopts;
        Oid         relid;
        bool        dovacuum;
        bool        doanalyze;
        bool        wraparound;

        if (classForm->relkind != RELKIND_RELATION &&
            classForm->relkind != RELKIND_MATVIEW)
            continue;

        relid = HeapTupleGetOid(tuple);

        /* Fetch reloptions and the pgstat entry for this table */
        relopts = extract_autovac_opts(tuple, pg_class_desc);
        tabentry = get_pgstat_tabentry_relid(relid, classForm->relisshared,
                                             shared, dbentry);

        /* Check if it needs vacuum or analyze */
        relation_needs_vacanalyze(relid, relopts, classForm, tabentry,
                                  &dovacuum, &doanalyze, &wraparound);

        /*
         * Check if it is a temp table (presumably, of some other backend's).
         * We cannot safely process other backends' temp tables.
         */
        if (classForm->relpersistence == RELPERSISTENCE_TEMP)
        {
            int         backendID;

            backendID = GetTempNamespaceBackendId(classForm->relnamespace);

            /* We just ignore it if the owning backend is still active */
            if (backendID == MyBackendId || BackendIdGetProc(backendID) == NULL)
            {
                /*
                 * We found an orphan temp table (which was probably left
                 * behind by a crashed backend).  If it's so old as to need
                 * vacuum for wraparound, forcibly drop it.  Otherwise just
                 * log a complaint.
                 */
                if (wraparound)
                {
                    ObjectAddress object;

                    ereport(LOG,
                            (errmsg("autovacuum: dropping orphan temp table \"%s\".\"%s\" in database \"%s\"",
                                 get_namespace_name(classForm->relnamespace),
                                    NameStr(classForm->relname),
                                    get_database_name(MyDatabaseId))));
                    object.classId = RelationRelationId;
                    object.objectId = relid;
                    object.objectSubId = 0;
                    performDeletion(&object, DROP_CASCADE, PERFORM_DELETION_INTERNAL);
                }
                else
                {
                    ereport(LOG,
                            (errmsg("autovacuum: found orphan temp table \"%s\".\"%s\" in database \"%s\"",
                                 get_namespace_name(classForm->relnamespace),
                                    NameStr(classForm->relname),
                                    get_database_name(MyDatabaseId))));
                }
            }
        }
        else
        {
            /* relations that need work are added to table_oids */
            if (dovacuum || doanalyze)
                table_oids = lappend_oid(table_oids, relid);

            /*
             * Remember the association for the second pass.  Note: we must do
             * this even if the table is going to be vacuumed, because we
             * don't automatically vacuum toast tables along the parent table.
             */
            if (OidIsValid(classForm->reltoastrelid))
            {
                av_relation *hentry;
                bool        found;

                hentry = hash_search(table_toast_map,
                                     &classForm->reltoastrelid,
                                     HASH_ENTER, &found);

                if (!found)
                {
                    /* hash_search already filled in the key */
                    hentry->ar_relid = relid;
                    hentry->ar_hasrelopts = false;
                    if (relopts != NULL)
                    {
                        hentry->ar_hasrelopts = true;
                        memcpy(&hentry->ar_reloptions, relopts,
                               sizeof(AutoVacOpts));
                    }
                }
            }
        }
    }

    heap_endscan(relScan);

    /* second pass: check TOAST tables */
    ScanKeyInit(&key,
                Anum_pg_class_relkind,
                BTEqualStrategyNumber, F_CHAREQ,
                CharGetDatum(RELKIND_TOASTVALUE));

    relScan = heap_beginscan(classRel, SnapshotNow, 1, &key);
    while ((tuple = heap_getnext(relScan, ForwardScanDirection)) != NULL)
    {
        Form_pg_class classForm = (Form_pg_class) GETSTRUCT(tuple);
        PgStat_StatTabEntry *tabentry;
        Oid         relid;
        AutoVacOpts *relopts = NULL;
        bool        dovacuum;
        bool        doanalyze;
        bool        wraparound;

        /*
         * We cannot safely process other backends' temp tables, so skip 'em.
         */
        if (classForm->relpersistence == RELPERSISTENCE_TEMP)
            continue;

        relid = HeapTupleGetOid(tuple);

        /*
         * fetch reloptions -- if this toast table does not have them, try the
         * main rel
         */
        relopts = extract_autovac_opts(tuple, pg_class_desc);
        if (relopts == NULL)
        {
            av_relation *hentry;
            bool        found;

            hentry = hash_search(table_toast_map, &relid, HASH_FIND, &found);
            if (found && hentry->ar_hasrelopts)
                relopts = &hentry->ar_reloptions;
        }

        /* Fetch the pgstat entry for this table */
        tabentry = get_pgstat_tabentry_relid(relid, classForm->relisshared,
                                             shared, dbentry);

        relation_needs_vacanalyze(relid, relopts, classForm, tabentry,
                                  &dovacuum, &doanalyze, &wraparound);

        /* ignore analyze for toast tables */
        if (dovacuum)
            table_oids = lappend_oid(table_oids, relid);
    }

    heap_endscan(relScan);
    heap_close(classRel, AccessShareLock);

    /*
     * Create a buffer access strategy object for VACUUM to use.  We want to
     * use the same one across all the vacuum operations we perform, since the
     * point is for VACUUM not to blow out the shared cache.
     */
    bstrategy = GetAccessStrategy(BAS_VACUUM);

    /*
     * create a memory context to act as fake PortalContext, so that the
     * contexts created in the vacuum code are cleaned up for each table.
     */
    PortalContext = AllocSetContextCreate(AutovacMemCxt,
                                          "Autovacuum Portal",
                                          ALLOCSET_DEFAULT_INITSIZE,
                                          ALLOCSET_DEFAULT_MINSIZE,
                                          ALLOCSET_DEFAULT_MAXSIZE);

    /*
     * Perform operations on collected tables.
     */
    foreach(cell, table_oids)
    {
        Oid         relid = lfirst_oid(cell);
        autovac_table *tab;
        bool        skipit;
        int         stdVacuumCostDelay;
        int         stdVacuumCostLimit;
        dlist_iter  iter;

        CHECK_FOR_INTERRUPTS();

        /*
         * hold schedule lock from here until we're sure that this table still
         * needs vacuuming.  We also need the AutovacuumLock to walk the
         * worker array, but we'll let go of that one quickly.
         */
        LWLockAcquire(AutovacuumScheduleLock, LW_EXCLUSIVE);
        LWLockAcquire(AutovacuumLock, LW_SHARED);

        /*
         * Check whether the table is being vacuumed concurrently by another
         * worker.
         */
        skipit = false;
        dlist_foreach(iter, &AutoVacuumShmem->av_runningWorkers)
        {
            WorkerInfo  worker = dlist_container(WorkerInfoData, wi_links, iter.cur);

            /* ignore myself */
            if (worker == MyWorkerInfo)
                continue;

            /* ignore workers in other databases */
            if (worker->wi_dboid != MyDatabaseId)
                continue;

            if (worker->wi_tableoid == relid)
            {
                skipit = true;
                break;
            }
        }
        LWLockRelease(AutovacuumLock);
        if (skipit)
        {
            LWLockRelease(AutovacuumScheduleLock);
            continue;
        }

        /*
         * Check whether pgstat data still says we need to vacuum this table.
         * It could have changed if something else processed the table while
         * we weren't looking.
         *
         * Note: we have a special case in pgstat code to ensure that the
         * stats we read are as up-to-date as possible, to avoid the problem
         * that somebody just finished vacuuming this table.  The window to
         * the race condition is not closed but it is very small.
         */
        MemoryContextSwitchTo(AutovacMemCxt);
        tab = table_recheck_autovac(relid, table_toast_map, pg_class_desc);
        if (tab == NULL)
        {
            /* someone else vacuumed the table, or it went away */
            LWLockRelease(AutovacuumScheduleLock);
            continue;
        }

        /*
         * Ok, good to go.  Store the table in shared memory before releasing
         * the lock so that other workers don't vacuum it concurrently.
         */
        MyWorkerInfo->wi_tableoid = relid;
        LWLockRelease(AutovacuumScheduleLock);

        /*
         * Remember the prevailing values of the vacuum cost GUCs.  We have to
         * restore these at the bottom of the loop, else we'll compute wrong
         * values in the next iteration of autovac_balance_cost().
         */
        stdVacuumCostDelay = VacuumCostDelay;
        stdVacuumCostLimit = VacuumCostLimit;

        /* Must hold AutovacuumLock while mucking with cost balance info */
        LWLockAcquire(AutovacuumLock, LW_EXCLUSIVE);

        /* advertise my cost delay parameters for the balancing algorithm */
        MyWorkerInfo->wi_cost_delay = tab->at_vacuum_cost_delay;
        MyWorkerInfo->wi_cost_limit = tab->at_vacuum_cost_limit;
        MyWorkerInfo->wi_cost_limit_base = tab->at_vacuum_cost_limit;

        /* do a balance */
        autovac_balance_cost();

        /* set the active cost parameters from the result of that */
        AutoVacuumUpdateDelay();

        /* done */
        LWLockRelease(AutovacuumLock);

        /* clean up memory before each iteration */
        MemoryContextResetAndDeleteChildren(PortalContext);

        /*
         * Save the relation name for a possible error message, to avoid a
         * catalog lookup in case of an error.  If any of these return NULL,
         * then the relation has been dropped since last we checked; skip it.
         * Note: they must live in a long-lived memory context because we call
         * vacuum and analyze in different transactions.
         */

        tab->at_relname = get_rel_name(tab->at_relid);
        tab->at_nspname = get_namespace_name(get_rel_namespace(tab->at_relid));
        tab->at_datname = get_database_name(MyDatabaseId);
        if (!tab->at_relname || !tab->at_nspname || !tab->at_datname)
            goto deleted;

        /*
         * We will abort vacuuming the current table if something errors out,
         * and continue with the next one in schedule; in particular, this
         * happens if we are interrupted with SIGINT.
         */
        PG_TRY();
        {
            /* have at it */
            MemoryContextSwitchTo(TopTransactionContext);
            autovacuum_do_vac_analyze(tab, bstrategy);

            /*
             * Clear a possible query-cancel signal, to avoid a late reaction
             * to an automatically-sent signal because of vacuuming the
             * current table (we're done with it, so it would make no sense to
             * cancel at this point.)
             */
            QueryCancelPending = false;
        }
        PG_CATCH();
        {
            /*
             * Abort the transaction, start a new one, and proceed with the
             * next table in our list.
             */
            HOLD_INTERRUPTS();
            if (tab->at_dovacuum)
                errcontext("automatic vacuum of table \"%s.%s.%s\"",
                           tab->at_datname, tab->at_nspname, tab->at_relname);
            else
                errcontext("automatic analyze of table \"%s.%s.%s\"",
                           tab->at_datname, tab->at_nspname, tab->at_relname);
            EmitErrorReport();

            /* this resets the PGXACT flags too */
            AbortOutOfAnyTransaction();
            FlushErrorState();
            MemoryContextResetAndDeleteChildren(PortalContext);

            /* restart our transaction for the following operations */
            StartTransactionCommand();
            RESUME_INTERRUPTS();
        }
        PG_END_TRY();

        /* the PGXACT flags are reset at the next end of transaction */

        /* be tidy */
deleted:
        if (tab->at_datname != NULL)
            pfree(tab->at_datname);
        if (tab->at_nspname != NULL)
            pfree(tab->at_nspname);
        if (tab->at_relname != NULL)
            pfree(tab->at_relname);
        pfree(tab);

        /*
         * Remove my info from shared memory.  We could, but intentionally
         * don't, clear wi_cost_limit and friends --- this is on the
         * assumption that we probably have more to do with similar cost
         * settings, so we don't want to give up our share of I/O for a very
         * short interval and thereby thrash the global balance.
         */
        LWLockAcquire(AutovacuumLock, LW_EXCLUSIVE);
        MyWorkerInfo->wi_tableoid = InvalidOid;
        LWLockRelease(AutovacuumLock);

        /* restore vacuum cost GUCs for the next iteration */
        VacuumCostDelay = stdVacuumCostDelay;
        VacuumCostLimit = stdVacuumCostLimit;
    }

    /*
     * We leak table_toast_map here (among other things), but since we're
     * going away soon, it's not a problem.
     */

    /*
     * Update pg_database.datfrozenxid, and truncate pg_clog if possible. We
     * only need to do this once, not after each table.
     */
    vac_update_datfrozenxid();

    /* Finally close out the last transaction. */
    CommitTransactionCommand();
}

static Oid do_start_worker ( void   )  [static]

Definition at line 1078 of file autovacuum.c.

References avl_dbase::adl_datid, avl_dbase::adl_next_worker, avw_dbase::adw_datid, avw_dbase::adw_entry, avw_dbase::adw_frozenmulti, avw_dbase::adw_frozenxid, ALLOCSET_DEFAULT_INITSIZE, ALLOCSET_DEFAULT_MAXSIZE, ALLOCSET_DEFAULT_MINSIZE, AllocSetContextCreate(), autovac_refresh_stats(), autovacuum_freeze_max_age, autovacuum_naptime, AutovacuumLock, AutoVacuumShmemStruct::av_freeWorkers, AutoVacuumShmemStruct::av_startingWorker, dlist_iter::cur, CurrentMemoryContext, dblist, dlist_container, dlist_is_empty(), dlist_pop_head_node(), dlist_reverse_foreach, FirstMultiXactId, FirstNormalTransactionId, get_database_list(), GetCurrentTimestamp(), InvalidOid, PgStat_StatDBEntry::last_autovac_time, lfirst, LW_EXCLUSIVE, LW_SHARED, LWLockAcquire(), LWLockRelease(), MemoryContextDelete(), MemoryContextSwitchTo(), MultiXactIdPrecedes(), NULL, pgstat_fetch_stat_dbentry(), PMSIGNAL_START_AUTOVAC_WORKER, ReadNewTransactionId(), ReadNextMultiXactId(), rebuild_database_list(), recentMulti, recentXid, SendPostmasterSignal(), TimestampDifferenceExceeds(), TransactionIdPrecedes(), WorkerInfoData::wi_dboid, WorkerInfoData::wi_launchtime, and WorkerInfoData::wi_proc.

Referenced by AutoVacLauncherMain(), and launch_worker().

{
    List       *dblist;
    ListCell   *cell;
    TransactionId xidForceLimit;
    MultiXactId multiForceLimit;
    bool        for_xid_wrap;
    bool        for_multi_wrap;
    avw_dbase  *avdb;
    TimestampTz current_time;
    bool        skipit = false;
    Oid         retval = InvalidOid;
    MemoryContext tmpcxt,
                oldcxt;

    /* return quickly when there are no free workers */
    LWLockAcquire(AutovacuumLock, LW_SHARED);
    if (dlist_is_empty(&AutoVacuumShmem->av_freeWorkers))
    {
        LWLockRelease(AutovacuumLock);
        return InvalidOid;
    }
    LWLockRelease(AutovacuumLock);

    /*
     * Create and switch to a temporary context to avoid leaking the memory
     * allocated for the database list.
     */
    tmpcxt = AllocSetContextCreate(CurrentMemoryContext,
                                   "Start worker tmp cxt",
                                   ALLOCSET_DEFAULT_MINSIZE,
                                   ALLOCSET_DEFAULT_INITSIZE,
                                   ALLOCSET_DEFAULT_MAXSIZE);
    oldcxt = MemoryContextSwitchTo(tmpcxt);

    /* use fresh stats */
    autovac_refresh_stats();

    /* Get a list of databases */
    dblist = get_database_list();

    /*
     * Determine the oldest datfrozenxid/relfrozenxid that we will allow to
     * pass without forcing a vacuum.  (This limit can be tightened for
     * particular tables, but not loosened.)
     */
    recentXid = ReadNewTransactionId();
    xidForceLimit = recentXid - autovacuum_freeze_max_age;
    /* ensure it's a "normal" XID, else TransactionIdPrecedes misbehaves */
    /* this can cause the limit to go backwards by 3, but that's OK */
    if (xidForceLimit < FirstNormalTransactionId)
        xidForceLimit -= FirstNormalTransactionId;

    /* Also determine the oldest datminmxid we will consider. */
    recentMulti = ReadNextMultiXactId();
    multiForceLimit = recentMulti - autovacuum_freeze_max_age;
    if (multiForceLimit < FirstMultiXactId)
        multiForceLimit -= FirstMultiXactId;

    /*
     * Choose a database to connect to.  We pick the database that was least
     * recently auto-vacuumed, or one that needs vacuuming to prevent Xid
     * wraparound-related data loss.  If any db at risk of Xid wraparound is
     * found, we pick the one with oldest datfrozenxid, independently of
     * autovacuum times; similarly we pick the one with the oldest datminmxid
     * if any is in MultiXactId wraparound.  Note that those in Xid wraparound
     * danger are given more priority than those in multi wraparound danger.
     *
     * Note that a database with no stats entry is not considered, except for
     * Xid wraparound purposes.  The theory is that if no one has ever
     * connected to it since the stats were last initialized, it doesn't need
     * vacuuming.
     *
     * XXX This could be improved if we had more info about whether it needs
     * vacuuming before connecting to it.  Perhaps look through the pgstats
     * data for the database's tables?  One idea is to keep track of the
     * number of new and dead tuples per database in pgstats.  However it
     * isn't clear how to construct a metric that measures that and not cause
     * starvation for less busy databases.
     */
    avdb = NULL;
    for_xid_wrap = false;
    for_multi_wrap = false;
    current_time = GetCurrentTimestamp();
    foreach(cell, dblist)
    {
        avw_dbase  *tmp = lfirst(cell);
        dlist_iter iter;

        /* Check to see if this one is at risk of wraparound */
        if (TransactionIdPrecedes(tmp->adw_frozenxid, xidForceLimit))
        {
            if (avdb == NULL ||
                TransactionIdPrecedes(tmp->adw_frozenxid,
                                      avdb->adw_frozenxid))
                avdb = tmp;
            for_xid_wrap = true;
            continue;
        }
        else if (for_xid_wrap)
            continue;           /* ignore not-at-risk DBs */
        else if (MultiXactIdPrecedes(tmp->adw_frozenmulti, multiForceLimit))
        {
            if (avdb == NULL ||
                MultiXactIdPrecedes(tmp->adw_frozenmulti,
                                    avdb->adw_frozenmulti))
                avdb = tmp;
            for_multi_wrap = true;
            continue;
        }
        else if (for_multi_wrap)
            continue;           /* ignore not-at-risk DBs */

        /* Find pgstat entry if any */
        tmp->adw_entry = pgstat_fetch_stat_dbentry(tmp->adw_datid);

        /*
         * Skip a database with no pgstat entry; it means it hasn't seen any
         * activity.
         */
        if (!tmp->adw_entry)
            continue;

        /*
         * Also, skip a database that appears on the database list as having
         * been processed recently (less than autovacuum_naptime seconds ago).
         * We do this so that we don't select a database which we just
         * selected, but that pgstat hasn't gotten around to updating the last
         * autovacuum time yet.
         */
        skipit = false;

        dlist_reverse_foreach(iter, &DatabaseList)
        {
            avl_dbase  *dbp = dlist_container(avl_dbase, adl_node, iter.cur);

            if (dbp->adl_datid == tmp->adw_datid)
            {
                /*
                 * Skip this database if its next_worker value falls between
                 * the current time and the current time plus naptime.
                 */
                if (!TimestampDifferenceExceeds(dbp->adl_next_worker,
                                                current_time, 0) &&
                    !TimestampDifferenceExceeds(current_time,
                                                dbp->adl_next_worker,
                                                autovacuum_naptime * 1000))
                    skipit = true;

                break;
            }
        }
        if (skipit)
            continue;

        /*
         * Remember the db with oldest autovac time.  (If we are here, both
         * tmp->entry and db->entry must be non-null.)
         */
        if (avdb == NULL ||
            tmp->adw_entry->last_autovac_time < avdb->adw_entry->last_autovac_time)
            avdb = tmp;
    }

    /* Found a database -- process it */
    if (avdb != NULL)
    {
        WorkerInfo  worker;
        dlist_node *wptr;

        LWLockAcquire(AutovacuumLock, LW_EXCLUSIVE);

        /*
         * Get a worker entry from the freelist.  We checked above, so there
         * really should be a free slot.
         */
        wptr = dlist_pop_head_node(&AutoVacuumShmem->av_freeWorkers);

        worker = dlist_container(WorkerInfoData, wi_links, wptr);
        worker->wi_dboid = avdb->adw_datid;
        worker->wi_proc = NULL;
        worker->wi_launchtime = GetCurrentTimestamp();

        AutoVacuumShmem->av_startingWorker = worker;

        LWLockRelease(AutovacuumLock);

        SendPostmasterSignal(PMSIGNAL_START_AUTOVAC_WORKER);

        retval = avdb->adw_datid;
    }
    else if (skipit)
    {
        /*
         * If we skipped all databases on the list, rebuild it, because it
         * probably contains a dropped database.
         */
        rebuild_database_list(InvalidOid);
    }

    MemoryContextSwitchTo(oldcxt);
    MemoryContextDelete(tmpcxt);

    return retval;
}

static AutoVacOpts * extract_autovac_opts ( HeapTuple  tup,
TupleDesc  pg_class_desc 
) [static]

Definition at line 2404 of file autovacuum.c.

References Assert, extractRelOptions(), GETSTRUCT, InvalidOid, NULL, palloc(), pfree(), RELKIND_MATVIEW, RELKIND_RELATION, and RELKIND_TOASTVALUE.

Referenced by do_autovacuum(), and table_recheck_autovac().

{
    bytea      *relopts;
    AutoVacOpts *av;

    Assert(((Form_pg_class) GETSTRUCT(tup))->relkind == RELKIND_RELATION ||
           ((Form_pg_class) GETSTRUCT(tup))->relkind == RELKIND_MATVIEW ||
           ((Form_pg_class) GETSTRUCT(tup))->relkind == RELKIND_TOASTVALUE);

    relopts = extractRelOptions(tup, pg_class_desc, InvalidOid);
    if (relopts == NULL)
        return NULL;

    av = palloc(sizeof(AutoVacOpts));
    memcpy(av, &(((StdRdOptions *) relopts)->autovacuum), sizeof(AutoVacOpts));
    pfree(relopts);

    return av;
}

static void FreeWorkerInfo ( int  code,
Datum  arg 
) [static]

Definition at line 1690 of file autovacuum.c.

References AutovacuumLauncherPid, AutovacuumLock, AutoVacuumShmemStruct::av_freeWorkers, AutoVacuumShmemStruct::av_launcherpid, AutoVacuumShmemStruct::av_signal, dlist_delete(), dlist_push_head(), LW_EXCLUSIVE, LWLockAcquire(), LWLockRelease(), NULL, WorkerInfoData::wi_cost_delay, WorkerInfoData::wi_cost_limit, WorkerInfoData::wi_cost_limit_base, WorkerInfoData::wi_dboid, WorkerInfoData::wi_launchtime, WorkerInfoData::wi_links, WorkerInfoData::wi_proc, and WorkerInfoData::wi_tableoid.

Referenced by AutoVacWorkerMain().

{
    if (MyWorkerInfo != NULL)
    {
        LWLockAcquire(AutovacuumLock, LW_EXCLUSIVE);

        /*
         * Wake the launcher up so that he can launch a new worker immediately
         * if required.  We only save the launcher's PID in local memory here;
         * the actual signal will be sent when the PGPROC is recycled.  Note
         * that we always do this, so that the launcher can rebalance the cost
         * limit setting of the remaining workers.
         *
         * We somewhat ignore the risk that the launcher changes its PID
         * between us reading it and the actual kill; we expect ProcKill to be
         * called shortly after us, and we assume that PIDs are not reused too
         * quickly after a process exits.
         */
        AutovacuumLauncherPid = AutoVacuumShmem->av_launcherpid;

        dlist_delete(&MyWorkerInfo->wi_links);
        MyWorkerInfo->wi_dboid = InvalidOid;
        MyWorkerInfo->wi_tableoid = InvalidOid;
        MyWorkerInfo->wi_proc = NULL;
        MyWorkerInfo->wi_launchtime = 0;
        MyWorkerInfo->wi_cost_delay = 0;
        MyWorkerInfo->wi_cost_limit = 0;
        MyWorkerInfo->wi_cost_limit_base = 0;
        dlist_push_head(&AutoVacuumShmem->av_freeWorkers,
                        &MyWorkerInfo->wi_links);
        /* not mine anymore */
        MyWorkerInfo = NULL;

        /*
         * now that we're inactive, cause a rebalancing of the surviving
         * workers
         */
        AutoVacuumShmem->av_signal[AutoVacRebalance] = true;
        LWLockRelease(AutovacuumLock);
    }
}

static List * get_database_list ( void   )  [static]

Definition at line 1836 of file autovacuum.c.

References AccessShareLock, avw_dbase::adw_datid, avw_dbase::adw_entry, avw_dbase::adw_frozenmulti, avw_dbase::adw_frozenxid, avw_dbase::adw_name, CommitTransactionCommand(), CurrentMemoryContext, DatabaseRelationId, dblist, ForwardScanDirection, GETSTRUCT, GetTransactionSnapshot(), heap_beginscan(), heap_close, heap_endscan(), heap_getnext(), heap_open(), HeapTupleGetOid, HeapTupleIsValid, lappend(), MemoryContextSwitchTo(), NameStr, NULL, palloc(), pstrdup(), SnapshotNow, and StartTransactionCommand().

Referenced by do_start_worker(), and rebuild_database_list().

{
    List       *dblist = NIL;
    Relation    rel;
    HeapScanDesc scan;
    HeapTuple   tup;
    MemoryContext resultcxt;

    /* This is the context that we will allocate our output data in */
    resultcxt = CurrentMemoryContext;

    /*
     * Start a transaction so we can access pg_database, and get a snapshot.
     * We don't have a use for the snapshot itself, but we're interested in
     * the secondary effect that it sets RecentGlobalXmin.  (This is critical
     * for anything that reads heap pages, because HOT may decide to prune
     * them even if the process doesn't attempt to modify any tuples.)
     */
    StartTransactionCommand();
    (void) GetTransactionSnapshot();

    rel = heap_open(DatabaseRelationId, AccessShareLock);
    scan = heap_beginscan(rel, SnapshotNow, 0, NULL);

    while (HeapTupleIsValid(tup = heap_getnext(scan, ForwardScanDirection)))
    {
        Form_pg_database pgdatabase = (Form_pg_database) GETSTRUCT(tup);
        avw_dbase  *avdb;
        MemoryContext oldcxt;

        /*
         * Allocate our results in the caller's context, not the
         * transaction's. We do this inside the loop, and restore the original
         * context at the end, so that leaky things like heap_getnext() are
         * not called in a potentially long-lived context.
         */
        oldcxt = MemoryContextSwitchTo(resultcxt);

        avdb = (avw_dbase *) palloc(sizeof(avw_dbase));

        avdb->adw_datid = HeapTupleGetOid(tup);
        avdb->adw_name = pstrdup(NameStr(pgdatabase->datname));
        avdb->adw_frozenxid = pgdatabase->datfrozenxid;
        avdb->adw_frozenmulti = pgdatabase->datminmxid;
        /* this gets set later: */
        avdb->adw_entry = NULL;

        dblist = lappend(dblist, avdb);
        MemoryContextSwitchTo(oldcxt);
    }

    heap_endscan(scan);
    heap_close(rel, AccessShareLock);

    CommitTransactionCommand();

    return dblist;
}

static PgStat_StatTabEntry * get_pgstat_tabentry_relid ( Oid  relid,
bool  isshared,
PgStat_StatDBEntry shared,
PgStat_StatDBEntry dbentry 
) [static]

Definition at line 2430 of file autovacuum.c.

References HASH_FIND, hash_search(), NULL, PointerIsValid, and PgStat_StatDBEntry::tables.

Referenced by do_autovacuum(), and table_recheck_autovac().

{
    PgStat_StatTabEntry *tabentry = NULL;

    if (isshared)
    {
        if (PointerIsValid(shared))
            tabentry = hash_search(shared->tables, &relid,
                                   HASH_FIND, NULL);
    }
    else if (PointerIsValid(dbentry))
        tabentry = hash_search(dbentry->tables, &relid,
                               HASH_FIND, NULL);

    return tabentry;
}

bool IsAutoVacuumLauncherProcess ( void   ) 
bool IsAutoVacuumWorkerProcess ( void   ) 
static void launch_worker ( TimestampTz  now  )  [static]

Definition at line 1296 of file autovacuum.c.

References avl_dbase::adl_datid, avl_dbase::adl_next_worker, autovacuum_naptime, dlist_iter::cur, dlist_container, dlist_foreach, dlist_move_head(), do_start_worker(), OidIsValid, rebuild_database_list(), and TimestampTzPlusMilliseconds.

Referenced by AutoVacLauncherMain().

{
    Oid         dbid;
    dlist_iter  iter;

    dbid = do_start_worker();
    if (OidIsValid(dbid))
    {
        bool found = false;

        /*
         * Walk the database list and update the corresponding entry.  If the
         * database is not on the list, we'll recreate the list.
         */
        dlist_foreach(iter, &DatabaseList)
        {
            avl_dbase  *avdb = dlist_container(avl_dbase, adl_node, iter.cur);

            if (avdb->adl_datid == dbid)
            {
                found = true;

                /*
                 * add autovacuum_naptime seconds to the current time, and use
                 * that as the new "next_worker" field for this database.
                 */
                avdb->adl_next_worker =
                    TimestampTzPlusMilliseconds(now, autovacuum_naptime * 1000);

                dlist_move_head(&DatabaseList, iter.cur);
                break;
            }
        }

        /*
         * If the database was not present in the database list, we rebuild
         * the list.  It's possible that the database does not get into the
         * list anyway, for example if it's a database that doesn't have a
         * pgstat entry, but this is not a problem because we don't want to
         * schedule workers regularly into those in any case.
         */
        if (!found)
            rebuild_database_list(dbid);
    }
}

static void launcher_determine_sleep ( bool  canlaunch,
bool  recursing,
struct timeval *  nap 
) [static]

Definition at line 797 of file autovacuum.c.

References avl_dbase::adl_next_worker, autovacuum_naptime, dlist_is_empty(), dlist_tail_element, GetCurrentTimestamp(), InvalidOid, MIN_AUTOVAC_SLEEPTIME, rebuild_database_list(), and TimestampDifference().

Referenced by AutoVacLauncherMain().

{
    /*
     * We sleep until the next scheduled vacuum.  We trust that when the
     * database list was built, care was taken so that no entries have times
     * in the past; if the first entry has too close a next_worker value, or a
     * time in the past, we will sleep a small nominal time.
     */
    if (!canlaunch)
    {
        nap->tv_sec = autovacuum_naptime;
        nap->tv_usec = 0;
    }
    else if (!dlist_is_empty(&DatabaseList))
    {
        TimestampTz current_time = GetCurrentTimestamp();
        TimestampTz next_wakeup;
        avl_dbase  *avdb;
        long        secs;
        int         usecs;

        avdb = dlist_tail_element(avl_dbase, adl_node, &DatabaseList);

        next_wakeup = avdb->adl_next_worker;
        TimestampDifference(current_time, next_wakeup, &secs, &usecs);

        nap->tv_sec = secs;
        nap->tv_usec = usecs;
    }
    else
    {
        /* list is empty, sleep for whole autovacuum_naptime seconds  */
        nap->tv_sec = autovacuum_naptime;
        nap->tv_usec = 0;
    }

    /*
     * If the result is exactly zero, it means a database had an entry with
     * time in the past.  Rebuild the list so that the databases are evenly
     * distributed again, and recalculate the time to sleep.  This can happen
     * if there are more tables needing vacuum than workers, and they all take
     * longer to vacuum than autovacuum_naptime.
     *
     * We only recurse once.  rebuild_database_list should always return times
     * in the future, but it seems best not to trust too much on that.
     */
    if (nap->tv_sec == 0 && nap->tv_usec == 0 && !recursing)
    {
        rebuild_database_list(InvalidOid);
        launcher_determine_sleep(canlaunch, true, nap);
        return;
    }

    /* The smallest time we'll allow the launcher to sleep. */
    if (nap->tv_sec <= 0 && nap->tv_usec <= MIN_AUTOVAC_SLEEPTIME * 1000)
    {
        nap->tv_sec = 0;
        nap->tv_usec = MIN_AUTOVAC_SLEEPTIME * 1000;
    }
}

static void rebuild_database_list ( Oid  newdb  )  [static]

Definition at line 872 of file autovacuum.c.

References avl_dbase::adl_datid, avl_dbase::adl_next_worker, avl_dbase::adl_node, avl_dbase::adl_score, avw_dbase::adw_datid, ALLOCSET_DEFAULT_INITSIZE, ALLOCSET_DEFAULT_MAXSIZE, ALLOCSET_DEFAULT_MINSIZE, AllocSetContextCreate(), autovac_refresh_stats(), autovacuum_naptime, dlist_iter::cur, db_comparator(), dblist, dlist_container, dlist_foreach, dlist_init(), dlist_push_head(), HASHCTL::entrysize, get_database_list(), GetCurrentTimestamp(), HASHCTL::hash, HASH_CONTEXT, hash_create(), HASH_ELEM, HASH_ENTER, HASH_FUNCTION, hash_search(), hash_seq_init(), hash_seq_search(), HASHCTL::hcxt, i, HASHCTL::keysize, lfirst, MemoryContextDelete(), MemoryContextSwitchTo(), MIN_AUTOVAC_SLEEPTIME, NULL, OidIsValid, palloc(), pgstat_fetch_stat_dbentry(), qsort, and TimestampTzPlusMilliseconds.

Referenced by AutoVacLauncherMain(), do_start_worker(), launch_worker(), and launcher_determine_sleep().

{
    List       *dblist;
    ListCell   *cell;
    MemoryContext newcxt;
    MemoryContext oldcxt;
    MemoryContext tmpcxt;
    HASHCTL     hctl;
    int         score;
    int         nelems;
    HTAB       *dbhash;
    dlist_iter  iter;

    /* use fresh stats */
    autovac_refresh_stats();

    newcxt = AllocSetContextCreate(AutovacMemCxt,
                                   "AV dblist",
                                   ALLOCSET_DEFAULT_MINSIZE,
                                   ALLOCSET_DEFAULT_INITSIZE,
                                   ALLOCSET_DEFAULT_MAXSIZE);
    tmpcxt = AllocSetContextCreate(newcxt,
                                   "tmp AV dblist",
                                   ALLOCSET_DEFAULT_MINSIZE,
                                   ALLOCSET_DEFAULT_INITSIZE,
                                   ALLOCSET_DEFAULT_MAXSIZE);
    oldcxt = MemoryContextSwitchTo(tmpcxt);

    /*
     * Implementing this is not as simple as it sounds, because we need to put
     * the new database at the end of the list; next the databases that were
     * already on the list, and finally (at the tail of the list) all the
     * other databases that are not on the existing list.
     *
     * To do this, we build an empty hash table of scored databases.  We will
     * start with the lowest score (zero) for the new database, then
     * increasing scores for the databases in the existing list, in order, and
     * lastly increasing scores for all databases gotten via
     * get_database_list() that are not already on the hash.
     *
     * Then we will put all the hash elements into an array, sort the array by
     * score, and finally put the array elements into the new doubly linked
     * list.
     */
    hctl.keysize = sizeof(Oid);
    hctl.entrysize = sizeof(avl_dbase);
    hctl.hash = oid_hash;
    hctl.hcxt = tmpcxt;
    dbhash = hash_create("db hash", 20, &hctl,  /* magic number here FIXME */
                         HASH_ELEM | HASH_FUNCTION | HASH_CONTEXT);

    /* start by inserting the new database */
    score = 0;
    if (OidIsValid(newdb))
    {
        avl_dbase  *db;
        PgStat_StatDBEntry *entry;

        /* only consider this database if it has a pgstat entry */
        entry = pgstat_fetch_stat_dbentry(newdb);
        if (entry != NULL)
        {
            /* we assume it isn't found because the hash was just created */
            db = hash_search(dbhash, &newdb, HASH_ENTER, NULL);

            /* hash_search already filled in the key */
            db->adl_score = score++;
            /* next_worker is filled in later */
        }
    }

    /* Now insert the databases from the existing list */
    dlist_foreach(iter, &DatabaseList)
    {
        avl_dbase  *avdb = dlist_container(avl_dbase, adl_node, iter.cur);
        avl_dbase  *db;
        bool        found;
        PgStat_StatDBEntry *entry;

        /*
         * skip databases with no stat entries -- in particular, this gets
         * rid of dropped databases
         */
        entry = pgstat_fetch_stat_dbentry(avdb->adl_datid);
        if (entry == NULL)
            continue;

        db = hash_search(dbhash, &(avdb->adl_datid), HASH_ENTER, &found);

        if (!found)
        {
            /* hash_search already filled in the key */
            db->adl_score = score++;
            /* next_worker is filled in later */
        }
    }

    /* finally, insert all qualifying databases not previously inserted */
    dblist = get_database_list();
    foreach(cell, dblist)
    {
        avw_dbase  *avdb = lfirst(cell);
        avl_dbase  *db;
        bool        found;
        PgStat_StatDBEntry *entry;

        /* only consider databases with a pgstat entry */
        entry = pgstat_fetch_stat_dbentry(avdb->adw_datid);
        if (entry == NULL)
            continue;

        db = hash_search(dbhash, &(avdb->adw_datid), HASH_ENTER, &found);
        /* only update the score if the database was not already on the hash */
        if (!found)
        {
            /* hash_search already filled in the key */
            db->adl_score = score++;
            /* next_worker is filled in later */
        }
    }
    nelems = score;

    /* from here on, the allocated memory belongs to the new list */
    MemoryContextSwitchTo(newcxt);
    dlist_init(&DatabaseList);

    if (nelems > 0)
    {
        TimestampTz current_time;
        int         millis_increment;
        avl_dbase  *dbary;
        avl_dbase  *db;
        HASH_SEQ_STATUS seq;
        int         i;

        /* put all the hash elements into an array */
        dbary = palloc(nelems * sizeof(avl_dbase));

        i = 0;
        hash_seq_init(&seq, dbhash);
        while ((db = hash_seq_search(&seq)) != NULL)
            memcpy(&(dbary[i++]), db, sizeof(avl_dbase));

        /* sort the array */
        qsort(dbary, nelems, sizeof(avl_dbase), db_comparator);

        /*
         * Determine the time interval between databases in the schedule. If
         * we see that the configured naptime would take us to sleep times
         * lower than our min sleep time (which launcher_determine_sleep is
         * coded not to allow), silently use a larger naptime (but don't touch
         * the GUC variable).
         */
        millis_increment = 1000.0 * autovacuum_naptime / nelems;
        if (millis_increment <= MIN_AUTOVAC_SLEEPTIME)
            millis_increment = MIN_AUTOVAC_SLEEPTIME * 1.1;

        current_time = GetCurrentTimestamp();

        /*
         * move the elements from the array into the dllist, setting the
         * next_worker while walking the array
         */
        for (i = 0; i < nelems; i++)
        {
            avl_dbase  *db = &(dbary[i]);

            current_time = TimestampTzPlusMilliseconds(current_time,
                                                       millis_increment);
            db->adl_next_worker = current_time;

            /* later elements should go closer to the head of the list */
            dlist_push_head(&DatabaseList, &db->adl_node);
        }
    }

    /* all done, clean up memory */
    if (DatabaseListCxt != NULL)
        MemoryContextDelete(DatabaseListCxt);
    MemoryContextDelete(tmpcxt);
    DatabaseListCxt = newcxt;
    MemoryContextSwitchTo(oldcxt);
}

static void relation_needs_vacanalyze ( Oid  relid,
AutoVacOpts relopts,
Form_pg_class  classForm,
PgStat_StatTabEntry tabentry,
bool dovacuum,
bool doanalyze,
bool wraparound 
) [static]

Definition at line 2604 of file autovacuum.c.

References AutoVacOpts::analyze_scale_factor, AutoVacOpts::analyze_threshold, AssertArg, autovacuum_anl_scale, autovacuum_anl_thresh, autovacuum_freeze_max_age, autovacuum_vac_scale, autovacuum_vac_thresh, PgStat_StatTabEntry::changes_since_analyze, DEBUG3, elog, AutoVacOpts::enabled, FirstMultiXactId, FirstNormalTransactionId, AutoVacOpts::freeze_max_age, Min, MultiXactIdPrecedes(), PgStat_StatTabEntry::n_dead_tuples, NameStr, NULL, OidIsValid, PointerIsValid, recentMulti, recentXid, StatisticRelationId, TransactionIdIsNormal, TransactionIdPrecedes(), AutoVacOpts::vacuum_scale_factor, and AutoVacOpts::vacuum_threshold.

Referenced by do_autovacuum(), and table_recheck_autovac().

{
    bool        force_vacuum;
    bool        av_enabled;
    float4      reltuples;      /* pg_class.reltuples */

    /* constants from reloptions or GUC variables */
    int         vac_base_thresh,
                anl_base_thresh;
    float4      vac_scale_factor,
                anl_scale_factor;

    /* thresholds calculated from above constants */
    float4      vacthresh,
                anlthresh;

    /* number of vacuum (resp. analyze) tuples at this time */
    float4      vactuples,
                anltuples;

    /* freeze parameters */
    int         freeze_max_age;
    TransactionId xidForceLimit;
    MultiXactId multiForceLimit;

    AssertArg(classForm != NULL);
    AssertArg(OidIsValid(relid));

    /*
     * Determine vacuum/analyze equation parameters.  We have two possible
     * sources: the passed reloptions (which could be a main table or a toast
     * table), or the autovacuum GUC variables.
     */

    /* -1 in autovac setting means use plain vacuum_cost_delay */
    vac_scale_factor = (relopts && relopts->vacuum_scale_factor >= 0)
        ? relopts->vacuum_scale_factor
        : autovacuum_vac_scale;

    vac_base_thresh = (relopts && relopts->vacuum_threshold >= 0)
        ? relopts->vacuum_threshold
        : autovacuum_vac_thresh;

    anl_scale_factor = (relopts && relopts->analyze_scale_factor >= 0)
        ? relopts->analyze_scale_factor
        : autovacuum_anl_scale;

    anl_base_thresh = (relopts && relopts->analyze_threshold >= 0)
        ? relopts->analyze_threshold
        : autovacuum_anl_thresh;

    freeze_max_age = (relopts && relopts->freeze_max_age >= 0)
        ? Min(relopts->freeze_max_age, autovacuum_freeze_max_age)
        : autovacuum_freeze_max_age;

    av_enabled = (relopts ? relopts->enabled : true);

    /* Force vacuum if table is at risk of wraparound */
    xidForceLimit = recentXid - freeze_max_age;
    if (xidForceLimit < FirstNormalTransactionId)
        xidForceLimit -= FirstNormalTransactionId;
    force_vacuum = (TransactionIdIsNormal(classForm->relfrozenxid) &&
                    TransactionIdPrecedes(classForm->relfrozenxid,
                                          xidForceLimit));
    if (!force_vacuum)
    {
        multiForceLimit = recentMulti - autovacuum_freeze_max_age;
        if (multiForceLimit < FirstMultiXactId)
            multiForceLimit -= FirstMultiXactId;
        force_vacuum = MultiXactIdPrecedes(classForm->relminmxid,
                                           multiForceLimit);
    }
    *wraparound = force_vacuum;

    /* User disabled it in pg_class.reloptions?  (But ignore if at risk) */
    if (!force_vacuum && !av_enabled)
    {
        *doanalyze = false;
        *dovacuum = false;
        return;
    }

    if (PointerIsValid(tabentry))
    {
        reltuples = classForm->reltuples;
        vactuples = tabentry->n_dead_tuples;
        anltuples = tabentry->changes_since_analyze;

        vacthresh = (float4) vac_base_thresh + vac_scale_factor * reltuples;
        anlthresh = (float4) anl_base_thresh + anl_scale_factor * reltuples;

        /*
         * Note that we don't need to take special consideration for stat
         * reset, because if that happens, the last vacuum and analyze counts
         * will be reset too.
         */
        elog(DEBUG3, "%s: vac: %.0f (threshold %.0f), anl: %.0f (threshold %.0f)",
             NameStr(classForm->relname),
             vactuples, vacthresh, anltuples, anlthresh);

        /* Determine if this table needs vacuum or analyze. */
        *dovacuum = force_vacuum || (vactuples > vacthresh);
        *doanalyze = (anltuples > anlthresh);
    }
    else
    {
        /*
         * Skip a table not found in stat hash, unless we have to force vacuum
         * for anti-wrap purposes.  If it's not acted upon, there's no need to
         * vacuum it.
         */
        *dovacuum = force_vacuum;
        *doanalyze = false;
    }

    /* ANALYZE refuses to work with pg_statistics */
    if (relid == StatisticRelationId)
        *doanalyze = false;
}

int StartAutoVacLauncher ( void   ) 

Definition at line 360 of file autovacuum.c.

References AutoVacLauncherMain(), AutoVacPID, ClosePostmasterPorts(), ereport, errmsg(), fork_process(), LOG, NULL, and on_exit_reset().

Referenced by reaper(), and ServerLoop().

{
    pid_t       AutoVacPID;

#ifdef EXEC_BACKEND
    switch ((AutoVacPID = avlauncher_forkexec()))
#else
    switch ((AutoVacPID = fork_process()))
#endif
    {
        case -1:
            ereport(LOG,
                 (errmsg("could not fork autovacuum launcher process: %m")));
            return 0;

#ifndef EXEC_BACKEND
        case 0:
            /* in postmaster child ... */
            /* Close the postmaster's sockets */
            ClosePostmasterPorts(false);

            /* Lose the postmaster's on-exit routines */
            on_exit_reset();

            AutoVacLauncherMain(0, NULL);
            break;
#endif
        default:
            return (int) AutoVacPID;
    }

    /* shouldn't get here */
    return 0;
}

int StartAutoVacWorker ( void   ) 

Definition at line 1435 of file autovacuum.c.

References AutoVacWorkerMain(), ClosePostmasterPorts(), ereport, errmsg(), fork_process(), LOG, NULL, and on_exit_reset().

Referenced by StartAutovacuumWorker().

{
    pid_t       worker_pid;

#ifdef EXEC_BACKEND
    switch ((worker_pid = avworker_forkexec()))
#else
    switch ((worker_pid = fork_process()))
#endif
    {
        case -1:
            ereport(LOG,
                    (errmsg("could not fork autovacuum worker process: %m")));
            return 0;

#ifndef EXEC_BACKEND
        case 0:
            /* in postmaster child ... */
            /* Close the postmaster's sockets */
            ClosePostmasterPorts(false);

            /* Lose the postmaster's on-exit routines */
            on_exit_reset();

            AutoVacWorkerMain(0, NULL);
            break;
#endif
        default:
            return (int) worker_pid;
    }

    /* shouldn't get here */
    return 0;
}

static autovac_table * table_recheck_autovac ( Oid  relid,
HTAB table_toast_map,
TupleDesc  pg_class_desc 
) [static]

Definition at line 2457 of file autovacuum.c.

References av_relation::ar_hasrelopts, av_relation::ar_reloptions, autovac_table::at_datname, autovac_table::at_doanalyze, autovac_table::at_dovacuum, autovac_table::at_freeze_min_age, autovac_table::at_freeze_table_age, autovac_table::at_nspname, autovac_table::at_relid, autovac_table::at_relname, autovac_table::at_vacuum_cost_delay, autovac_table::at_vacuum_cost_limit, autovac_table::at_wraparound, autovac_refresh_stats(), autovacuum_vac_cost_delay, autovacuum_vac_cost_limit, default_freeze_min_age, default_freeze_table_age, extract_autovac_opts(), AutoVacOpts::freeze_min_age, AutoVacOpts::freeze_table_age, get_pgstat_tabentry_relid(), GETSTRUCT, HASH_FIND, hash_search(), heap_freetuple(), HeapTupleIsValid, InvalidOid, MyDatabaseId, NULL, ObjectIdGetDatum, palloc(), pgstat_fetch_stat_dbentry(), relation_needs_vacanalyze(), RELKIND_TOASTVALUE, RELOID, SearchSysCacheCopy1, AutoVacOpts::vacuum_cost_delay, AutoVacOpts::vacuum_cost_limit, VacuumCostDelay, and VacuumCostLimit.

Referenced by do_autovacuum().

{
    Form_pg_class classForm;
    HeapTuple   classTup;
    bool        dovacuum;
    bool        doanalyze;
    autovac_table *tab = NULL;
    PgStat_StatTabEntry *tabentry;
    PgStat_StatDBEntry *shared;
    PgStat_StatDBEntry *dbentry;
    bool        wraparound;
    AutoVacOpts *avopts;

    /* use fresh stats */
    autovac_refresh_stats();

    shared = pgstat_fetch_stat_dbentry(InvalidOid);
    dbentry = pgstat_fetch_stat_dbentry(MyDatabaseId);

    /* fetch the relation's relcache entry */
    classTup = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relid));
    if (!HeapTupleIsValid(classTup))
        return NULL;
    classForm = (Form_pg_class) GETSTRUCT(classTup);

    /*
     * Get the applicable reloptions.  If it is a TOAST table, try to get the
     * main table reloptions if the toast table itself doesn't have.
     */
    avopts = extract_autovac_opts(classTup, pg_class_desc);
    if (classForm->relkind == RELKIND_TOASTVALUE &&
        avopts == NULL && table_toast_map != NULL)
    {
        av_relation *hentry;
        bool        found;

        hentry = hash_search(table_toast_map, &relid, HASH_FIND, &found);
        if (found && hentry->ar_hasrelopts)
            avopts = &hentry->ar_reloptions;
    }

    /* fetch the pgstat table entry */
    tabentry = get_pgstat_tabentry_relid(relid, classForm->relisshared,
                                         shared, dbentry);

    relation_needs_vacanalyze(relid, avopts, classForm, tabentry,
                              &dovacuum, &doanalyze, &wraparound);

    /* ignore ANALYZE for toast tables */
    if (classForm->relkind == RELKIND_TOASTVALUE)
        doanalyze = false;

    /* OK, it needs something done */
    if (doanalyze || dovacuum)
    {
        int         freeze_min_age;
        int         freeze_table_age;
        int         vac_cost_limit;
        int         vac_cost_delay;

        /*
         * Calculate the vacuum cost parameters and the freeze ages.  If there
         * are options set in pg_class.reloptions, use them; in the case of a
         * toast table, try the main table too.  Otherwise use the GUC
         * defaults, autovacuum's own first and plain vacuum second.
         */

        /* -1 in autovac setting means use plain vacuum_cost_delay */
        vac_cost_delay = (avopts && avopts->vacuum_cost_delay >= 0)
            ? avopts->vacuum_cost_delay
            : (autovacuum_vac_cost_delay >= 0)
            ? autovacuum_vac_cost_delay
            : VacuumCostDelay;

        /* 0 or -1 in autovac setting means use plain vacuum_cost_limit */
        vac_cost_limit = (avopts && avopts->vacuum_cost_limit > 0)
            ? avopts->vacuum_cost_limit
            : (autovacuum_vac_cost_limit > 0)
            ? autovacuum_vac_cost_limit
            : VacuumCostLimit;

        /* these do not have autovacuum-specific settings */
        freeze_min_age = (avopts && avopts->freeze_min_age >= 0)
            ? avopts->freeze_min_age
            : default_freeze_min_age;

        freeze_table_age = (avopts && avopts->freeze_table_age >= 0)
            ? avopts->freeze_table_age
            : default_freeze_table_age;

        tab = palloc(sizeof(autovac_table));
        tab->at_relid = relid;
        tab->at_dovacuum = dovacuum;
        tab->at_doanalyze = doanalyze;
        tab->at_freeze_min_age = freeze_min_age;
        tab->at_freeze_table_age = freeze_table_age;
        tab->at_vacuum_cost_limit = vac_cost_limit;
        tab->at_vacuum_cost_delay = vac_cost_delay;
        tab->at_wraparound = wraparound;
        tab->at_relname = NULL;
        tab->at_nspname = NULL;
        tab->at_datname = NULL;
    }

    heap_freetuple(classTup);

    return tab;
}


Variable Documentation

bool am_autovacuum_launcher = false [static]

Definition at line 132 of file autovacuum.c.

Referenced by AutoVacLauncherMain(), and IsAutoVacuumLauncherProcess().

bool am_autovacuum_worker = false [static]

Definition at line 133 of file autovacuum.c.

Referenced by AutoVacWorkerMain(), and IsAutoVacuumWorkerProcess().

Definition at line 149 of file autovacuum.c.

Definition at line 117 of file autovacuum.c.

Referenced by relation_needs_vacanalyze().

Definition at line 116 of file autovacuum.c.

Referenced by relation_needs_vacanalyze().

Definition at line 111 of file autovacuum.c.

Referenced by autovac_init(), and AutoVacuumingActive().

Definition at line 120 of file autovacuum.c.

Referenced by autovac_balance_cost(), and table_recheck_autovac().

Definition at line 121 of file autovacuum.c.

Referenced by autovac_balance_cost(), and table_recheck_autovac().

Definition at line 115 of file autovacuum.c.

Referenced by relation_needs_vacanalyze().

Definition at line 114 of file autovacuum.c.

Referenced by relation_needs_vacanalyze().

Definition at line 276 of file autovacuum.c.

Referenced by FreeWorkerInfo(), and ProcKill().

Definition at line 263 of file autovacuum.c.

dlist_head DatabaseList = DLIST_STATIC_INIT(DatabaseList) [static]

Definition at line 269 of file autovacuum.c.

MemoryContext DatabaseListCxt = NULL [static]

Definition at line 270 of file autovacuum.c.

int default_freeze_min_age [static]

Definition at line 145 of file autovacuum.c.

Referenced by do_autovacuum(), and table_recheck_autovac().

Definition at line 146 of file autovacuum.c.

Referenced by do_autovacuum(), and table_recheck_autovac().

volatile sig_atomic_t got_SIGHUP = false [static]

Definition at line 136 of file autovacuum.c.

Referenced by AutoVacLauncherMain(), and avl_sighup_handler().

volatile sig_atomic_t got_SIGTERM = false [static]

Definition at line 138 of file autovacuum.c.

Referenced by AutoVacLauncherMain(), and avl_sigterm_handler().

volatile sig_atomic_t got_SIGUSR2 = false [static]

Definition at line 137 of file autovacuum.c.

Referenced by AutoVacLauncherMain(), and avl_sigusr2_handler().

Definition at line 123 of file autovacuum.c.

Referenced by analyze_rel(), do_analyze_rel(), lazy_vacuum_rel(), and vacuum_rel().

WorkerInfo MyWorkerInfo = NULL [static]

Definition at line 273 of file autovacuum.c.

Definition at line 142 of file autovacuum.c.

Referenced by AutoVacWorkerMain(), do_start_worker(), and relation_needs_vacanalyze().

Definition at line 141 of file autovacuum.c.

Referenced by AutoVacWorkerMain(), do_start_worker(), and relation_needs_vacanalyze().