PostgreSQL pg_ctl start超時分析

yzs87發表於2019-01-25

一、問題

pg_ctl start啟動時報錯退出:pg_ctl:server did not start in time。超時時間是多少?從什麼時候到哪個階段算超時?

二、分析:該資訊列印位置,從後面程式碼段do_start函式中可以看出

1、pg_ctl start呼叫start_postmaster啟動PG的主程式後,每隔0.1ms檢查一次postmaster.pid檔案,是否已寫入ready/standby

2、總共會檢查600次,即從啟動主程式後,最多等待60s,如果沒有寫入ready/standby則列印上述日誌並退出

3、預設等待時間是60s,如果pg_ctl start -t指定等待時間,則等待時間為該指定時間

三、什麼時候postmaster.pid檔案寫入ready/standby

1、如果是主機不管有沒有設定hot standby

    1)當startup程式恢復完成退出時,呼叫proc_exit函式向主程式傳送SIGCHLD訊號並退出

    2)主程式接收到訊號後,signal處理函式reaper呼叫AddToDataDirLockFile向postmaster.pid檔案寫入ready

2、如果是備機即data目錄下有recovery.cnf檔案,且設定了hot standby,在實際恢復前沒有到達一致性位置

    1)startup程式向主程式傳送PMSIGNAL_RECOVERY_STARTED訊號,主程式呼叫訊號處理函式sigusr1_handler,將pmState=PM_RECOVERY

    2)每次讀取下一個xlog前都會呼叫CheckRecoveryConsistency函式進行一致性檢查:

        2.1 進入一致性狀態,starup程式向主程式傳送PMSIGNAL_BEGIN_HOT_STANDBY訊號,主程式接收到訊號後呼叫sigusr1_handler->AddToDataDirLockFile向postmaster.pid檔案寫入ready

3、如果是備機即data目錄下有recovery.cnf檔案,且設定了hot standby,在實際恢復前沒有到達一致性位置

    1)startup程式向主程式傳送PMSIGNAL_RECOVERY_STARTED訊號,主程式呼叫訊號處理函式sigusr1_handler,將pmState=PM_RECOVERY

    2)每次讀取下一個xlog前都會呼叫CheckRecoveryConsistency函式進行一致性檢查。如果沒有進入一致性狀態

    3)本地日誌恢復完成,切換日誌源時同樣呼叫CheckRecoveryConsistency函式進行一致性檢查

        3.1 進入一致性狀態,starup程式向主程式傳送PMSIGNAL_BEGIN_HOT_STANDBY訊號,主程式接收到訊號後呼叫sigusr1_handler->AddToDataDirLockFile向postmaster.pid檔案寫入ready

4、如果是備機即data目錄下有recovery.cnf檔案,且設定了hot standby,在實際恢復前到達一致性位置

    1)startup程式向主程式傳送PMSIGNAL_RECOVERY_STARTED訊號,主程式呼叫訊號處理函式sigusr1_handler,將pmState=PM_RECOVERY

    2)CheckRecoveryConsistency函式進行一致性檢查,向主程式傳送PMSIGNAL_BEGIN_HOT_STANDBY訊號,主程式接收到訊號後呼叫sigusr1_handler->AddToDataDirLockFile向postmaster.pid檔案寫入ready

5、如果是備機即data目錄下有recovery.cnf檔案,沒有設定hot standby

    1)startup程式向主程式傳送PMSIGNAL_RECOVERY_STARTED訊號

    2)主程式接收到訊號後,向postmaster.將pmState=PM_RECOVERY

四、程式碼分析

1、pg_ctl start流程

do_start->
	pm_pid = start_postmaster();
	if (do_wait){
		print_msg(_("waiting for server to start..."));
		switch (wait_for_postmaster(pm_pid, false)){
			case POSTMASTER_READY:
				print_msg(_(" done\n"));
				print_msg(_("server started\n"));
				break;
			case POSTMASTER_STILL_STARTING:
				print_msg(_(" stopped waiting\n"));
				write_stderr(_("%s: server did not start in time\n"), progname);
				exit(1);
				break;
			case POSTMASTER_FAILED:
				print_msg(_(" stopped waiting\n"));
				write_stderr(_("%s: could not start server\n" "Examine the log output.\n"), progname);
				exit(1);
				break;
		}
	}else
		print_msg(_("server starting\n"));
wait_for_postmaster->
	for (i = 0; i < wait_seconds * WAITS_PER_SEC; i++){
		if ((optlines = readfile(pid_file, &numlines)) != NULL && numlines >= LOCK_FILE_LINE_PM_STATUS){
			pmpid = atol(optlines[LOCK_FILE_LINE_PID - 1]);
			pmstart = atol(optlines[LOCK_FILE_LINE_START_TIME - 1]);
			if (pmstart >= start_time - 2 && pmpid == pm_pid){
				char	   *pmstatus = optlines[LOCK_FILE_LINE_PM_STATUS - 1];
				if (strcmp(pmstatus, PM_STATUS_READY) == 0 || strcmp(pmstatus, PM_STATUS_STANDBY) == 0){
					/* postmaster is done starting up */
					free_readfile(optlines);
					return POSTMASTER_READY;
				}
			}
		}
		free_readfile(optlines);
		if (waitpid((pid_t) pm_pid, &exitstatus, WNOHANG) == (pid_t) pm_pid)
			return POSTMASTER_FAILED;
		pg_usleep(USEC_PER_SEC / WAITS_PER_SEC);
	}
	/* out of patience; report that postmaster is still starting up */
	return POSTMASTER_STILL_STARTING;

2、server主程式及訊號處理函式

PostmasterMain->
	pqsignal_no_restart(SIGUSR1, sigusr1_handler);	/* message from child process */
	pqsignal_no_restart(SIGCHLD, reaper);	/* handle child termination */
	...
	StartupXLOG();
	...
	proc_exit(0);//exit函式向主程式傳送SIGCHLD訊號
reaper->//程式終止或者停止的訊號
	AddToDataDirLockFile(LOCK_FILE_LINE_PM_STATUS, PM_STATUS_READY);
//postmaster程式接收子程式傳送的訊號:
sigusr1_handler->
	if (CheckPostmasterSignal(PMSIGNAL_RECOVERY_STARTED) &&
		pmState == PM_STARTUP && Shutdown == NoShutdown){
		CheckpointerPID = StartCheckpointer();
		BgWriterPID = StartBackgroundWriter();
		if (XLogArchivingAlways())
			PgArchPID = pgarch_start();
		//hot_standby在postgresql.conf檔案中配置TRUE
		//表示在恢復的時候允許連線
		if (!EnableHotStandby){
			//將standby寫入postmaster.pid檔案,表示up但不允許連線
			AddToDataDirLockFile(LOCK_FILE_LINE_PM_STATUS, PM_STATUS_STANDBY);
		}
		pmState = PM_RECOVERY;
	}
	if (CheckPostmasterSignal(PMSIGNAL_BEGIN_HOT_STANDBY) &&
		pmState == PM_RECOVERY && Shutdown == NoShutdown){
		PgStatPID = pgstat_start();
		//將ready寫入postmaster.pid檔案,允許連線
		AddToDataDirLockFile(LOCK_FILE_LINE_PM_STATUS, PM_STATUS_READY);
		pmState = PM_HOT_STANDBY;
	}
	...

3、Startup程式

StartupXLOG->
	ReadCheckpointRecord
	if (ArchiveRecoveryRequested && IsUnderPostmaster){//有recovery.conf檔案則ArchiveRecoveryRequested為TRUE
		//有recovery.conf檔案則ArchiveRecoveryRequested為TRUE
		PublishStartupProcessInformation();
		SetForwardFsyncRequests();
		//向master程式傳送PMSIGNAL_RECOVERY_STARTED訊號
		SendPostmasterSignal(PMSIGNAL_RECOVERY_STARTED);
		bgwriterLaunched = true;
	}
	CheckRecoveryConsistency();-->...
	|--	if (standbyState == STANDBY_SNAPSHOT_READY && !LocalHotStandbyActive &&
	|		reachedConsistency && IsUnderPostmaster){
	|		SpinLockAcquire(&XLogCtl->info_lck);
	|		XLogCtl->SharedHotStandbyActive = true;
	|		SpinLockRelease(&XLogCtl->info_lck);
	|		LocalHotStandbyActive = true;
	|		SendPostmasterSignal(PMSIGNAL_BEGIN_HOT_STANDBY);
	|--	}
	...
	回放一個record後,每次讀取下一個record前都會呼叫CheckRecoveryConsistency


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/31493717/viewspace-2564805/,如需轉載,請註明出處,否則將追究法律責任。

相關文章