您好,登錄后才能下訂單哦!
本篇內(nèi)容介紹了“PostgreSQL中AutoVacLauncherMain函數(shù)的實(shí)現(xiàn)邏輯是什么”的有關(guān)知識(shí),在實(shí)際案例的操作過(guò)程中,不少人都會(huì)遇到這樣的困境,接下來(lái)就讓小編帶領(lǐng)大家學(xué)習(xí)一下如何處理這些情況吧!希望大家仔細(xì)閱讀,能夠?qū)W有所成!
宏定義
#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í)用文章!
免責(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)容。