Postgresql - 原始碼 - stats collector process
程式碼位置:
src/backend/postmaster/pgstat.c
所有的統計資訊收集器的東西放在一個大的醜陋的檔案。
TODO:
- Separate collector, postmaster and backend stuff into different files. 單獨收集,postmaster和backend內容到不同的檔案。
- Add some automatic call for pgstat vacuuming. 新增一些自動呼叫 pgstat vacuuming
- Add a pgstat config column to pg_database, so this entire thing can be enabled/disabled on a per db basis. 將pgstat config 列新增到pg_database,因此可以在每個DB的基礎上啟用/禁用整個事件。
從 postmaster 啟動時或存在的 collector 程序死掉是呼叫。試圖啟動一個新的statistics collector。
int
pgstat_start(void)
{
time_t curtime;
pid_t pgStatPid;
/* 檢查套接字是否存在,否則 pgstat_init 失敗,而且無能為力。 */
if (pgStatSock == PGINVALID_SOCKET)
return 0;
/* 如果上次 collector 啟動後不久,就什麼都不做。這是一個安全閥,為了防止collector 在啟動的時候死掉,連續重複嘗試,注意,因為我們將從 postmaster main loop 重新呼叫,我們稍後會得到另一個機會啟動。 */
curtime = time(NULL);
if ((unsigned int) (curtime - last_pgstat_start_time) <
(unsigned int) PGSTAT_RESTART_INTERVAL)
return 0;
last_pgstat_start_time = curtime;
/* collector 的岔口 */
#ifdef EXEC_BACKEND
switch ((pgStatPid = pgstat_forkexec()))
#else
switch ((pgStatPid = fork_process()))
#endif
{
case -1:
ereport(LOG,
(errmsg("could not fork statistics collector: %m")));
return 0;
#ifndef EXEC_BACKEND
case 0:
/* postmaster子程序 ... */
InitPostmasterChild();
/* 關閉 postmaster's 套接字 */
ClosePostmasterPorts(false);
/* 把我們連線到 postmaster 的共享記憶體 */
dsm_detach_all();
PGSharedMemoryDetach();
/* pgstat collector 主函式 */
PgstatCollectorMain(0, NULL);
break;
#endif
default:
return (int) pgStatPid;
}
/* shouldn't get here */
return 0;
}
****************************************************************************************************************************
/* 啟動統計收集器過程。這是postmaster child process 的程式碼。
* The argc/argv parameters are valid only in EXEC_BACKEND case.
*/
NON_EXEC_STATIC void
PgstatCollectorMain(int argc, char *argv[])
{
int len;
PgStat_Msg msg;
int wr;
/* 忽略所有訊號,通常繫結到postmaster 的某些行動,除了SIGHUP 和 SIGQUIT 。注意,我們不需要 SIGUSR1 處理器來支援閂鎖操作,因為我們只使用本地鎖存器。 */
pqsignal(SIGHUP, pgstat_sighup_handler);
pqsignal(SIGINT, SIG_IGN);
pqsignal(SIGTERM, SIG_IGN);
pqsignal(SIGQUIT, pgstat_exit);
pqsignal(SIGALRM, SIG_IGN);
pqsignal(SIGPIPE, SIG_IGN);
pqsignal(SIGUSR1, SIG_IGN);
pqsignal(SIGUSR2, SIG_IGN);
pqsignal(SIGCHLD, SIG_DFL);
pqsignal(SIGTTIN, SIG_DFL);
pqsignal(SIGTTOU, SIG_DFL);
pqsignal(SIGCONT, SIG_DFL);
pqsignal(SIGWINCH, SIG_DFL);
PG_SETMASK(&UnBlockSig);
/* 通過ps 確定自己 */
init_ps_display("stats collector", "", "", "");
/* 讀取現有的統計檔案或初始化統計資料為零。 */
pgStatRunningInCollector = true;
pgStatDBHash = pgstat_read_statsfiles(InvalidOid, true, true);
/* 迴圈處理訊息,直到我們得到 SIGQUIT 或檢測到我們的父程序 postmaster 死掉。出於效能原因,我們不希望在每個訊息之後都執行ResetLatch/WaitLatch ;相反,只有在 recv() 無法獲得訊息之後才這樣做。這實際上意味著,如果後端瘋狂的傳送給我們的東西,我們將不會注意到postmaster 程序死掉,直到事情有點鬆懈,這似乎是好的。為了做到這一點,我們有一個內部迴圈,只要 recv() 成功就可以迭代。我們確實在內部迴圈中識別 got_SIGHUP ,這意味著此類中斷將得到處理,但是鎖存器直到下一次動作中斷時才會被清除。*/
for (;;)
{
/* 清除任何已掛起的喚醒 */
ResetLatch(MyLatch);
/* 如果我們從postmaster得到 SIGQUIT,退出 */
if (need_exit)
break;
/* 只要我們繼續獲取訊息,沒有收到 need_exit 設定,就會一直loop。 */
while (!need_exit)
{
/* 如果我們從 postmaster 收到 SIGHUP ,重新載入配置 */
if (got_SIGHUP)
{
got_SIGHUP = false;
ProcessConfigFile(PGC_SIGHUP);
}
/* 如果一個新的請求已被現有檔案不滿足,則寫入stats 檔案。 */
if (pgstat_write_statsfile_needed())
pgstat_write_statsfiles(false, false);
/* 嘗試接收和處理訊息。由於套接字被設定為非阻塞模式,所以這不會阻塞。在Windows上,我們不得不強制與 pgwin32_recv 協作,儘管以前在套接字上使用pg_set_noblock()。這是非常不好的的,應該有一天被修復。 */
#ifdef WIN32
pgwin32_noblock = 1;
#endif
len = recv(pgStatSock, (char *) &msg,
sizeof(PgStat_Msg), 0);
#ifdef WIN32
pgwin32_noblock = 0;
#endif
if (len < 0)
{
if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR)
break; /* out of inner loop */
ereport(ERROR,
(errcode_for_socket_access(),
errmsg("could not read statistics message: %m")));
}
/* 我們忽略比我們的普通的報頭小的訊息。 */
if (len < sizeof(PgStat_MsgHdr))
continue;
/* 接收的長度必須與標題中的長度相匹配。 */
if (msg.msg_hdr.m_size != len)
continue;
/* 我們接受這個訊息。處理它。 */
switch (msg.msg_hdr.m_type)
{
case PGSTAT_MTYPE_DUMMY:
break;
case PGSTAT_MTYPE_INQUIRY:
pgstat_recv_inquiry((PgStat_MsgInquiry *) &msg, len);
break;
case PGSTAT_MTYPE_TABSTAT:
pgstat_recv_tabstat((PgStat_MsgTabstat *) &msg, len);
break;
case PGSTAT_MTYPE_TABPURGE:
pgstat_recv_tabpurge((PgStat_MsgTabpurge *) &msg, len);
break;
case PGSTAT_MTYPE_DROPDB:
pgstat_recv_dropdb((PgStat_MsgDropdb *) &msg, len);
break;
case PGSTAT_MTYPE_RESETCOUNTER:
pgstat_recv_resetcounter((PgStat_MsgResetcounter *) &msg,
len);
break;
case PGSTAT_MTYPE_RESETSHAREDCOUNTER:
pgstat_recv_resetsharedcounter(
(PgStat_MsgResetsharedcounter *) &msg,
len);
break;
case PGSTAT_MTYPE_RESETSINGLECOUNTER:
pgstat_recv_resetsinglecounter(
(PgStat_MsgResetsinglecounter *) &msg,
len);
break;
case PGSTAT_MTYPE_AUTOVAC_START:
pgstat_recv_autovac((PgStat_MsgAutovacStart *) &msg, len);
break;
case PGSTAT_MTYPE_VACUUM:
pgstat_recv_vacuum((PgStat_MsgVacuum *) &msg, len);
break;
case PGSTAT_MTYPE_ANALYZE:
pgstat_recv_analyze((PgStat_MsgAnalyze *) &msg, len);
break;
case PGSTAT_MTYPE_ARCHIVER:
pgstat_recv_archiver((PgStat_MsgArchiver *) &msg, len);
break;
case PGSTAT_MTYPE_BGWRITER:
pgstat_recv_bgwriter((PgStat_MsgBgWriter *) &msg, len);
break;
case PGSTAT_MTYPE_FUNCSTAT:
pgstat_recv_funcstat((PgStat_MsgFuncstat *) &msg, len);
break;
case PGSTAT_MTYPE_FUNCPURGE:
pgstat_recv_funcpurge((PgStat_MsgFuncpurge *) &msg, len);
break;
case PGSTAT_MTYPE_RECOVERYCONFLICT:
pgstat_recv_recoveryconflict((PgStat_MsgRecoveryConflict *) &msg, len);
break;
case PGSTAT_MTYPE_DEADLOCK:
pgstat_recv_deadlock((PgStat_MsgDeadlock *) &msg, len);
break;
case PGSTAT_MTYPE_TEMPFILE:
pgstat_recv_tempfile((PgStat_MsgTempFile *) &msg, len);
break;
default:
break;
}
} /* 內部訊息處理迴圈結束 */
/* Sleep 直到有事情要做 */
#ifndef WIN32
wr = WaitLatchOrSocket(MyLatch,
WL_LATCH_SET | WL_POSTMASTER_DEATH | WL_SOCKET_READABLE,
pgStatSock, -1L,
WAIT_EVENT_PGSTAT_MAIN);
#else
/* Windows,至少在Windows Server 2003 R2體現,有時會丟失 FD_READ 事件。喚醒並重試 recv() 修復,所以不要無限期sleep。這只是開頭,但是除非有人想除錯那裡到底發生了什麼,否則這是我們所能做的最好的事情。2秒的超時時間與我們在9.2之前的行為相匹配,並且需要足夠短,以免引發 backend_read_statsfile 的“使用陳舊的統計資料”投訴。 */
wr = WaitLatchOrSocket(MyLatch,
WL_LATCH_SET | WL_POSTMASTER_DEATH | WL_SOCKET_READABLE | WL_TIMEOUT,
pgStatSock,
2 * 1000L /* msec */ ,
WAIT_EVENT_PGSTAT_MAIN);
#endif
/* 如果 postmaster 死了,將緊急救助。這是為了避免對所有 postmaster 的子程序進行手工清理。 */
if (wr & WL_POSTMASTER_DEATH)
break;
} /* end of outer loop */
/* 在下一次啟動時儲存最終統計資料以重用。 */
pgstat_write_statsfiles(true, true);
exit(0);
}