溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

PostgreSQL中AutoVacLauncherMain函數(shù)的實(shí)現(xiàn)邏輯是什么

發(fā)布時(shí)間:2021-11-09 16:38:31 來(lái)源:億速云 閱讀:151 作者:iii 欄目:關(guān)系型數(shù)據(jù)庫(kù)

本篇內(nèi)容介紹了“PostgreSQL中AutoVacLauncherMain函數(shù)的實(shí)現(xiàn)邏輯是什么”的有關(guān)知識(shí),在實(shí)際案例的操作過(guò)程中,不少人都會(huì)遇到這樣的困境,接下來(lái)就讓小編帶領(lǐng)大家學(xué)習(xí)一下如何處理這些情況吧!希望大家仔細(xì)閱讀,能夠?qū)W有所成!

一、數(shù)據(jù)結(jié)構(gòu)

宏定義

#define GetProcessingMode() Mode
#define SetProcessingMode(mode) \
    do { \
        AssertArg((mode) == BootstrapProcessing || \
                  (mode) == InitProcessing || \
                  (mode) == NormalProcessing); \
        Mode = (mode); \
    } while(0)

二、源碼解讀

AutoVacLauncherMain函數(shù),autovacuum進(jìn)程主循環(huán).

/*
 * Main loop for the autovacuum launcher process.
 * autovacuum進(jìn)程主循環(huán)
 */
NON_EXEC_STATIC void
AutoVacLauncherMain(int argc, char *argv[])
{
    sigjmp_buf  local_sigjmp_buf;
    am_autovacuum_launcher = true;
    /* Identify myself via ps */
    //進(jìn)程ID
    init_ps_display(pgstat_get_backend_desc(B_AUTOVAC_LAUNCHER), "", "", "");
    ereport(DEBUG1,
            (errmsg("autovacuum launcher started")));
    if (PostAuthDelay)
        pg_usleep(PostAuthDelay * 1000000L);
    //設(shè)置進(jìn)程模式
    SetProcessingMode(InitProcessing);
    /*
     * 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.
     * 設(shè)置信號(hào)控制器.
     * autovacuum的執(zhí)行類似于普通的后臺(tái)進(jìn)程,因此使用相同的信號(hào)控制機(jī)制.
     * 參考tcop/postgres.c中的代碼.
     */
    pqsignal(SIGHUP, av_sighup_handler);
    pqsignal(SIGINT, StatementCancelHandler);
    pqsignal(SIGTERM, avl_sigterm_handler);
    pqsignal(SIGQUIT, quickdie);
    //建立SIGALRM控制器
    InitializeTimeouts();       /* establishes SIGALRM handler */
    pqsignal(SIGPIPE, SIG_IGN);//忽略SIGPIPE
    pqsignal(SIGUSR1, procsignal_sigusr1_handler);
    pqsignal(SIGUSR2, avl_sigusr2_handler);
    pqsignal(SIGFPE, FloatExceptionHandler);
    pqsignal(SIGCHLD, SIG_DFL);
    /* Early initialization */
    //基礎(chǔ)初始化
    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).
     * 在共享內(nèi)存中創(chuàng)建每個(gè)后臺(tái)進(jìn)程的PGPROC結(jié)構(gòu)體,
     *   但除了exEXEC_BACKEND這種情況,這是在SubPostmasterMain中完成的。
     */
#ifndef EXEC_BACKEND
    InitProcess();
#endif
    //初始化
    InitPostgres(NULL, InvalidOid, NULL, InvalidOid, NULL, false);
    //設(shè)置進(jìn)程模式
    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.
     * 創(chuàng)建內(nèi)存上下文.
     * 之所以這樣做是因?yàn)槲覀兛梢栽阱e(cuò)誤恢復(fù)中重置上下文,并且可以避免內(nèi)存泄漏.
     */
    AutovacMemCxt = AllocSetContextCreate(TopMemoryContext,
                                          "Autovacuum Launcher",
                                          ALLOCSET_DEFAULT_SIZES);
    MemoryContextSwitchTo(AutovacMemCxt);
    /*
     * If an exception is encountered, processing resumes here.
     * 如果出現(xiàn)異常,在這里重新恢復(fù).
     *
     * This code is a stripped down version of PostgresMain error recovery.
     * 這段代碼是PostgresMain錯(cuò)誤恢復(fù)的精簡(jiǎn)版。
     */
    if (sigsetjmp(local_sigjmp_buf, 1) != 0)
    {
        /* since not using PG_TRY, must reset error stack by hand */
        //由于沒(méi)有使用PG_TRY,這里必須手工重置錯(cuò)誤.
        error_context_stack = NULL;
        /* Prevents interrupts while cleaning up */
        //在清理期間禁用中斷
        HOLD_INTERRUPTS();
        /* Forget any pending QueryCancel or timeout request */
        //忽略所有QueryCancel或者超時(shí)請(qǐng)求
        disable_all_timeouts(false);
        QueryCancelPending = false; /* second to avoid race condition */
        /* Report the error to the server log */
        //在服務(wù)器日志中記錄日志.
        EmitErrorReport();
        /* Abort the current transaction in order to recover */
        //廢棄當(dāng)前事務(wù),以準(zhǔn)備恢復(fù)
        AbortCurrentTransaction();
        /*
         * Release any other resources, for the case where we were not in a
         * transaction.
         * 釋放任何其他資源,以防我們不在事務(wù)中。
         */
        LWLockReleaseAll();
        pgstat_report_wait_end();
        AbortBufferIO();
        UnlockBuffers();
        /* this is probably dead code, but let's be safe: */
        //這可能是dead code,但可以保證安全
        if (AuxProcessResourceOwner)
            ReleaseAuxProcessResources(false);
        AtEOXact_Buffers(false);
        AtEOXact_SMgr();
        AtEOXact_Files(false);
        AtEOXact_HashTables(false);
        /*
         * Now return to normal top-level context and clear ErrorContext for
         * next time.
         * 現(xiàn)在切換回正常的頂層上下文中,并為下一次的啟動(dòng)清理錯(cuò)誤上下文
         */
        MemoryContextSwitchTo(AutovacMemCxt);
        FlushErrorState();
        /* Flush any leaked data in the top-level context */
        //在top-level上下文刷新所有泄漏的數(shù)據(jù)
        MemoryContextResetAndDeleteChildren(AutovacMemCxt);
        /* don't leave dangling pointers to freed memory */
        //不要留下懸空指針來(lái)釋放內(nèi)存
        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也認(rèn)為我們的統(tǒng)計(jì)數(shù)據(jù)已經(jīng)丟棄。
         * 注意:這里不能使用autovac_refresh_stats。
         */
        pgstat_clear_snapshot();
        /* Now we can allow interrupts again */
        //可以允許中斷了
        RESUME_INTERRUPTS();
        /* if in shutdown mode, no need for anything further; just go away */
        //如處于shutdown模式,不需要繼續(xù)后續(xù)的工作了,跳轉(zhuǎn)到shutdown
        if (got_SIGTERM)
            goto shutdown;
        /*
         * 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) */
    //現(xiàn)在可以處理ereport(ERROR)了
    PG_exception_stack = &local_sigjmp_buf;
    /* must unblock signals before calling rebuild_database_list */
    //在調(diào)用rebuild_database_list前不能阻塞信號(hào)
    PG_SETMASK(&UnBlockSig);
    /*
     * Set always-secure search path.  Launcher doesn't connect to a database,
     * so this has no effect.
     * 設(shè)置安全的搜索路徑.
     * Launcher不能連接數(shù)據(jù)庫(kù),因此并沒(méi)有什么影響.
     */
    SetConfigOption("search_path", "", PGC_SUSET, PGC_S_OVERRIDE);
    /*
     * 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.
     * 在autovacuum進(jìn)程中,強(qiáng)制關(guān)閉zero_damaged_pages,即時(shí)該參數(shù)在配置文件設(shè)置為ON.
     * 我們真的不希望這樣一個(gè)危險(xiǎn)的選項(xiàng)在無(wú)需交互的情況進(jìn)行應(yīng)用.
     */
    SetConfigOption("zero_damaged_pages", "false", PGC_SUSET, PGC_S_OVERRIDE);
    /*
     * Force settable timeouts off to avoid letting these settings prevent
     * regular maintenance from being executed.
     * 強(qiáng)制關(guān)閉可設(shè)置的超時(shí),以避免這些設(shè)置妨礙常規(guī)維護(hù)的執(zhí)行。
     */
    SetConfigOption("statement_timeout", "0", PGC_SUSET, PGC_S_OVERRIDE);
    SetConfigOption("lock_timeout", "0", PGC_SUSET, PGC_S_OVERRIDE);
    SetConfigOption("idle_in_transaction_session_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.
     * 強(qiáng)制default_transaction_isolation為READ COMMITTED.
     * 我們不希望在serializable模式下增加負(fù)擔(dān),也不想增加導(dǎo)致死鎖或者其他事務(wù)延遲的風(fēng)險(xiǎn).
     */
    SetConfigOption("default_transaction_isolation", "read committed",
                    PGC_SUSET, PGC_S_OVERRIDE);
    /*
     * In emergency mode, just start a worker (unless shutdown was requested)
     * and go away.
     * 在緊急模式,啟動(dòng)一個(gè)worker(除非已請(qǐng)求shutdown)
     */
    if (!AutoVacuumingActive())
    {
        if (!got_SIGTERM)
            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).
     * 創(chuàng)建初始化數(shù)據(jù)庫(kù)鏈表.
     * 我們希望這個(gè)鏈表保持不變的是它是通過(guò)減少next_time來(lái)進(jìn)行排序.
     * 一旦條目更新到更高的時(shí)間,它就會(huì)被移動(dòng)到前面
     * (這樣處理沒(méi)有問(wèn)題,因?yàn)槲┮坏牟僮魇窍驐l目添加autovacuum_naptime,而時(shí)間總是會(huì)增加)。
     */
    rebuild_database_list(InvalidOid);
    /* loop until shutdown request */
    //循環(huán),直至請(qǐng)求shutdown
    while (!got_SIGTERM)
    {
        struct timeval nap;
        TimestampTz current_time = 0;
        bool        can_launch;
        /*
         * 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.
         * 該循環(huán)與常規(guī)的使用WaitLatch不同,因?yàn)槲覀兿M诘谝粋€(gè)子進(jìn)程啟動(dòng)前處于休眠狀態(tài).
         * 因此首先是WaitLatch,然后是ResetLatch,然后檢查并等待喚醒條件.
         */
        launcher_determine_sleep(!dlist_is_empty(&AutoVacuumShmem->av_freeWorkers),
                                 false, &nap);
        /*
         * Wait until naptime expires or we get some type of signal (all the
         * signal handlers will wake us by calling SetLatch).
         * 等待,直至naptime超時(shí)或者我們接收到某些類型的信號(hào).
         * (所有的信號(hào)控制器會(huì)通過(guò)調(diào)用SetLatch喚醒進(jìn)程)
         */
        (void) WaitLatch(MyLatch,
                         WL_LATCH_SET | WL_TIMEOUT | WL_EXIT_ON_PM_DEATH,
                         (nap.tv_sec * 1000L) + (nap.tv_usec / 1000L),
                         WAIT_EVENT_AUTOVACUUM_MAIN);
        ResetLatch(MyLatch);
        /* Process sinval catchup interrupts that happened while sleeping */
        //在休眠過(guò)程中,進(jìn)程會(huì)捕獲相關(guān)的中斷.
        ProcessCatchupInterrupt();
        /* the normal shutdown case */
        //shutdonw信號(hào)
        if (got_SIGTERM)
            break;
        if (got_SIGHUP)
        {
            //SIGHUP信號(hào)
            got_SIGHUP = false;
            ProcessConfigFile(PGC_SIGHUP);
            /* shutdown requested in config file? */
            //在配置文件中已請(qǐng)求shutdown?
            if (!AutoVacuumingActive())
                break;
            /* rebalance in case the default cost parameters changed */
            //如默認(rèn)的成本參數(shù)變化,則自動(dòng)平衡.
            LWLockAcquire(AutovacuumLock, LW_EXCLUSIVE);
            autovac_balance_cost();
            LWLockRelease(AutovacuumLock);
            /* rebuild the list in case the naptime changed */
            //如naptime出現(xiàn)變化,重建鏈表
            rebuild_database_list(InvalidOid);
        }
        /*
         * a worker finished, or postmaster signalled failure to start a
         * worker
         * 某個(gè)worker已完成,或者postmaster信號(hào)出現(xiàn)異常無(wú)法啟動(dòng)worker
         */
        if (got_SIGUSR2)
        {
            //SIGUSR2信號(hào)
            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.
                 * 如果postmaster無(wú)法啟動(dòng)新的worker,休眠一段時(shí)間,重新發(fā)送信號(hào).
                 * 新的worker的狀態(tài)仍然在內(nèi)存中,因此這樣處理是OK的.
                 * 再次之后,重新啟動(dòng)主循環(huán).
                 *
                 * 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.
                 * 是否增加重試次數(shù)的限制?XXX
                 * 我們不想太過(guò)敏感,因?yàn)槟硞€(gè)worker在未來(lái)的啟動(dòng)會(huì)以同樣的方式持續(xù)失敗.
                 */
                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 worker.  First, we need to make sure that there is a worker
         * slot available.  Second, we need to make sure that no other worker
         * failed while starting up.
         * 在嘗試啟動(dòng)worker前,有一些條件需要檢查.
         * 首先,需要確保有可用的worker slot;其次,需要確保worker在啟動(dòng)時(shí)沒(méi)有出現(xiàn)異常.
         */
        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.
             * 在某個(gè)worker仍然在啟動(dòng)時(shí),不能啟動(dòng)新的worker,因此休眠一段時(shí)間;
             * 另外一個(gè)worker在ready后會(huì)第一時(shí)間喚醒我們.
             * 只需要等待autovacuum_naptime參數(shù)設(shè)置的時(shí)間(單位秒)(最大為60s).
             * 注意,在這里不能夠連接一個(gè)特定的數(shù)據(jù)庫(kù)不存在任何問(wèn)題,因?yàn)閣orker在
             *   嘗試連接時(shí),通過(guò)startingWorker指針銷毀自己.
             * 通過(guò)postmaster檢測(cè)到問(wèn)題(如fork()失敗)會(huì)報(bào)告并且進(jìn)行不同的處理,
             *   這里唯一的問(wèn)題是可能導(dǎo)致這里的處理邏輯在AutoVacWorkerMain的早起觸發(fā)錯(cuò)誤,
             * 而且實(shí)在worker通過(guò)startingWorker指針清除WorkerInfo前.
             */
            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_sharedrel = false;
                    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 */
        //什么都做不了,繼續(xù)休眠
        if (!can_launch)
            continue;
        /* We're OK to start a new worker */
        //現(xiàn)在可以啟動(dòng)新的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).
             * 在鏈表為空時(shí)的特殊情況:正確的啟動(dòng)一個(gè)worker.
             * 這涵蓋了剛初始的情況,即pgstats中沒(méi)有數(shù)據(jù)庫(kù)(因此鏈表為空)。
             * 請(qǐng)注意,launcher_determine_sleep中的約束使我們不能過(guò)快地啟動(dòng)worker
             * (當(dāng)鏈表為空時(shí),最多一次autovacuum_naptime)。
             */
            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.
             * 因?yàn)閞ebuild_database_list首先用最遠(yuǎn)的adl_next_worker構(gòu)造了鏈表,
             *   所以我們從鏈表的尾部獲取數(shù)據(jù)庫(kù)。
             */
            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
             * 啟動(dòng)worker,如果next_worker正當(dāng)其時(shí)或者已成為過(guò)去時(shí).
             */
            if (TimestampDifferenceExceeds(avdb->adl_next_worker,
                                           current_time, 0))
                launch_worker(current_time);
        }
    }
    /* Normal exit from the autovac launcher is here */
    //常規(guī)的退出.
shutdown:
    ereport(DEBUG1,
            (errmsg("autovacuum launcher shutting down")));
    AutoVacuumShmem->av_launcherpid = 0;
    proc_exit(0);               /* done */
}

“PostgreSQL中AutoVacLauncherMain函數(shù)的實(shí)現(xiàn)邏輯是什么”的內(nèi)容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業(yè)相關(guān)的知識(shí)可以關(guān)注億速云網(wǎng)站,小編將為大家輸出更多高質(zhì)量的實(shí)用文章!

向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI