您好,登錄后才能下訂單哦!
本篇內容介紹了“PostgreSQL中AutoVacLauncherMain函數的實現邏輯是什么”的有關知識,在實際案例的操作過程中,不少人都會遇到這樣的困境,接下來就讓小編帶領大家學習一下如何處理這些情況吧!希望大家仔細閱讀,能夠學有所成!
宏定義
#define GetProcessingMode() Mode #define SetProcessingMode(mode) \ do { \ AssertArg((mode) == BootstrapProcessing || \ (mode) == InitProcessing || \ (mode) == NormalProcessing); \ Mode = (mode); \ } while(0)
AutoVacLauncherMain函數,autovacuum進程主循環.
/* * Main loop for the autovacuum launcher process. * autovacuum進程主循環 */ NON_EXEC_STATIC void AutoVacLauncherMain(int argc, char *argv[]) { sigjmp_buf local_sigjmp_buf; am_autovacuum_launcher = true; /* Identify myself via ps */ //進程ID init_ps_display(pgstat_get_backend_desc(B_AUTOVAC_LAUNCHER), "", "", ""); ereport(DEBUG1, (errmsg("autovacuum launcher started"))); if (PostAuthDelay) pg_usleep(PostAuthDelay * 1000000L); //設置進程模式 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. * 設置信號控制器. * autovacuum的執行類似于普通的后臺進程,因此使用相同的信號控制機制. * 參考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 */ //基礎初始化 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). * 在共享內存中創建每個后臺進程的PGPROC結構體, * 但除了exEXEC_BACKEND這種情況,這是在SubPostmasterMain中完成的。 */ #ifndef EXEC_BACKEND InitProcess(); #endif //初始化 InitPostgres(NULL, InvalidOid, NULL, InvalidOid, NULL, false); //設置進程模式 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_SIZES); MemoryContextSwitchTo(AutovacMemCxt); /* * If an exception is encountered, processing resumes here. * 如果出現異常,在這里重新恢復. * * This code is a stripped down version of PostgresMain error recovery. * 這段代碼是PostgresMain錯誤恢復的精簡版。 */ if (sigsetjmp(local_sigjmp_buf, 1) != 0) { /* since not using PG_TRY, must reset error stack by hand */ //由于沒有使用PG_TRY,這里必須手工重置錯誤. error_context_stack = NULL; /* Prevents interrupts while cleaning up */ //在清理期間禁用中斷 HOLD_INTERRUPTS(); /* Forget any pending QueryCancel or timeout request */ //忽略所有QueryCancel或者超時請求 disable_all_timeouts(false); QueryCancelPending = false; /* second to avoid race condition */ /* Report the error to the server log */ //在服務器日志中記錄日志. EmitErrorReport(); /* Abort the current transaction in order to recover */ //廢棄當前事務,以準備恢復 AbortCurrentTransaction(); /* * Release any other resources, for the case where we were not in a * transaction. * 釋放任何其他資源,以防我們不在事務中。 */ 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. * 現在切換回正常的頂層上下文中,并為下一次的啟動清理錯誤上下文 */ MemoryContextSwitchTo(AutovacMemCxt); FlushErrorState(); /* Flush any leaked data in the top-level context */ //在top-level上下文刷新所有泄漏的數據 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也認為我們的統計數據已經丟棄。 * 注意:這里不能使用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模式,不需要繼續后續的工作了,跳轉到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) */ //現在可以處理ereport(ERROR)了 PG_exception_stack = &local_sigjmp_buf; /* must unblock signals before calling rebuild_database_list */ //在調用rebuild_database_list前不能阻塞信號 PG_SETMASK(&UnBlockSig); /* * Set always-secure search path. Launcher doesn't connect to a database, * so this has no effect. * 設置安全的搜索路徑. * Launcher不能連接數據庫,因此并沒有什么影響. */ 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進程中,強制關閉zero_damaged_pages,即時該參數在配置文件設置為ON. * 我們真的不希望這樣一個危險的選項在無需交互的情況進行應用. */ 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. * 強制關閉可設置的超時,以避免這些設置妨礙常規維護的執行。 */ 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. * 強制default_transaction_isolation為READ COMMITTED. * 我們不希望在serializable模式下增加負擔,也不想增加導致死鎖或者其他事務延遲的風險. */ 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. * 在緊急模式,啟動一個worker(除非已請求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). * 創建初始化數據庫鏈表. * 我們希望這個鏈表保持不變的是它是通過減少next_time來進行排序. * 一旦條目更新到更高的時間,它就會被移動到前面 * (這樣處理沒有問題,因為惟一的操作是向條目添加autovacuum_naptime,而時間總是會增加)。 */ rebuild_database_list(InvalidOid); /* loop until shutdown request */ //循環,直至請求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. * 該循環與常規的使用WaitLatch不同,因為我們希望在第一個子進程啟動前處于休眠狀態. * 因此首先是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超時或者我們接收到某些類型的信號. * (所有的信號控制器會通過調用SetLatch喚醒進程) */ (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 */ //在休眠過程中,進程會捕獲相關的中斷. ProcessCatchupInterrupt(); /* the normal shutdown case */ //shutdonw信號 if (got_SIGTERM) break; if (got_SIGHUP) { //SIGHUP信號 got_SIGHUP = false; ProcessConfigFile(PGC_SIGHUP); /* shutdown requested in config file? */ //在配置文件中已請求shutdown? 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 */ //如naptime出現變化,重建鏈表 rebuild_database_list(InvalidOid); } /* * a worker finished, or postmaster signalled failure to start a * worker * 某個worker已完成,或者postmaster信號出現異常無法啟動worker */ if (got_SIGUSR2) { //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. * 如果postmaster無法啟動新的worker,休眠一段時間,重新發送信號. * 新的worker的狀態仍然在內存中,因此這樣處理是OK的. * 再次之后,重新啟動主循環. * * 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. * 是否增加重試次數的限制?XXX * 我們不想太過敏感,因為某個worker在未來的啟動會以同樣的方式持續失敗. */ 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. * 在嘗試啟動worker前,有一些條件需要檢查. * 首先,需要確保有可用的worker slot;其次,需要確保worker在啟動時沒有出現異常. */ 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. * 在某個worker仍然在啟動時,不能啟動新的worker,因此休眠一段時間; * 另外一個worker在ready后會第一時間喚醒我們. * 只需要等待autovacuum_naptime參數設置的時間(單位秒)(最大為60s). * 注意,在這里不能夠連接一個特定的數據庫不存在任何問題,因為worker在 * 嘗試連接時,通過startingWorker指針銷毀自己. * 通過postmaster檢測到問題(如fork()失敗)會報告并且進行不同的處理, * 這里唯一的問題是可能導致這里的處理邏輯在AutoVacWorkerMain的早起觸發錯誤, * 而且實在worker通過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 */ //什么都做不了,繼續休眠 if (!can_launch) continue; /* We're OK to start a new worker */ //現在可以啟動新的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). * 在鏈表為空時的特殊情況:正確的啟動一個worker. * 這涵蓋了剛初始的情況,即pgstats中沒有數據庫(因此鏈表為空)。 * 請注意,launcher_determine_sleep中的約束使我們不能過快地啟動worker * (當鏈表為空時,最多一次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. * 因為rebuild_database_list首先用最遠的adl_next_worker構造了鏈表, * 所以我們從鏈表的尾部獲取數據庫。 */ 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 * 啟動worker,如果next_worker正當其時或者已成為過去時. */ if (TimestampDifferenceExceeds(avdb->adl_next_worker, current_time, 0)) launch_worker(current_time); } } /* Normal exit from the autovac launcher is here */ //常規的退出. shutdown: ereport(DEBUG1, (errmsg("autovacuum launcher shutting down"))); AutoVacuumShmem->av_launcherpid = 0; proc_exit(0); /* done */ }
“PostgreSQL中AutoVacLauncherMain函數的實現邏輯是什么”的內容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業相關的知識可以關注億速云網站,小編將為大家輸出更多高質量的實用文章!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。