1. 程式人生 > >Postgresql - 原始碼 - WalWriter process

Postgresql - 原始碼 - WalWriter process

啟動時執行兩個函式

InitXLOGAccess(); 程式碼位置 src/backend/access/transam/xlog.c

WalWriterMain(); 程式碼位置 src/backend/postmaster/walwriter.c

 

InitXLOGAccess()的解釋是:在建立WAR記錄之前,必須在後端過程中呼叫。我們需要初始化ThisTimeLineID 和 RedoRecPtr 的本地副本。

程式碼不長,是對LOG訪問的初始化。

void InitXLOGAccess(void)

{

    XLogCtlInsert *Insert = &XLogCtl->Insert;

 

    /* ThisTimeLineID doesn't change so we need no lock to copy it */

    ThisTimeLineID = XLogCtl->ThisTimeLineID;

    Assert(ThisTimeLineID != 0 || IsBootstrapProcessingMode());

 

    /* set wal_segment_size */

    wal_segment_size = ControlFile->xlog_seg_size;

 

    /* Use GetRedoRecPtr to copy the RedoRecPtr safely */

    (void) GetRedoRecPtr();

    /* Also update our copy of doPageWrites. */

    doPageWrites = (Insert->fullPageWrites || Insert->forcePageWrites);

 

    /* Also initialize the working areas for constructing WAL records */

    InitXLogInsert();

}

 

對於WalWriter process,主入口函式是WalWriterMain(void),我們來看一下它的程式碼。

void

WalWriterMain(void)

{

    sigjmp_buf  local_sigjmp_buf;

    MemoryContext walwriter_context;

    int         left_till_hibernate;

    bool        hibernating;

 

    /* 正確接受或忽略 postmaster 可能傳送給我們的訊號 */

    pqsignal(SIGHUP, WalSigHupHandler); /* set flag to read config file */

    pqsignal(SIGINT, WalShutdownHandler);   /* request shutdown */

    pqsignal(SIGTERM, WalShutdownHandler);  /* request shutdown */

    pqsignal(SIGQUIT, wal_quickdie);    /* hard crash time */

    pqsignal(SIGALRM, SIG_IGN);

    pqsignal(SIGPIPE, SIG_IGN);

    pqsignal(SIGUSR1, walwriter_sigusr1_handler);

    pqsignal(SIGUSR2, SIG_IGN); /* not used */

 

    /*

     * Reset some signals that are accepted by postmaster but not here

     */

    pqsignal(SIGCHLD, SIG_DFL);

    pqsignal(SIGTTIN, SIG_DFL);

    pqsignal(SIGTTOU, SIG_DFL);

    pqsignal(SIGCONT, SIG_DFL);

    pqsignal(SIGWINCH, SIG_DFL);

 

    /* We allow SIGQUIT (quickdie) at all times */

    sigdelset(&BlockSig, SIGQUIT);

 

    /* 建立一個 resource owner 來跟蹤我們的資源 */

    CurrentResourceOwner = ResourceOwnerCreate(NULL, "Wal Writer");

 

    /* 建立一個記憶體上下文,我們將完成所有的工作。我們這樣做,以便在錯誤恢復過程中可以重置上下文,從而避免可能的記憶體洩漏。以前,這段程式碼只是在TopMemoryContext執行,但是重新設定這將是一個非常糟糕的想法。 */

    walwriter_context = AllocSetContextCreate(TopMemoryContext,

                                             "Wal Writer",

                                             ALLOCSET_DEFAULT_SIZES);

    MemoryContextSwitchTo(walwriter_context);

 

    /* 如果遇到異常,則在此恢復處理。 */

    if (sigsetjmp(local_sigjmp_buf, 1) != 0)

    {

        /* 由於不使用 PG_TRY,必須手動重置錯誤堆疊。 */

        error_context_stack = NULL;

 

        /* 清理時防止中斷 */

        HOLD_INTERRUPTS();

 

        /* Report the error to the server log */

        EmitErrorReport();

 

        /* 這些操作實際上只是AbortTransaction() 的最小子集。我們在walwriter沒有太多的資源需要擔心,但我們確實有LWLocks ,也許還有緩衝區?*/

        LWLockReleaseAll();

        ConditionVariableCancelSleep();

        pgstat_report_wait_end();

        AbortBufferIO();

        UnlockBuffers();

        /* buffer pins are released here: */

        ResourceOwnerRelease(CurrentResourceOwner,

                             RESOURCE_RELEASE_BEFORE_LOCKS,

                             false, true);

        /* 我們不必擔心其他 ResourceOwnerRelease 階段。 */

        AtEOXact_Buffers(false);

        AtEOXact_SMgr();

        AtEOXact_Files(false);

        AtEOXact_HashTables(false);

 

        /* 現在返回正常的頂級上下文,下次清除錯誤上下文。*/

        MemoryContextSwitchTo(walwriter_context);

        FlushErrorState();

 

        /* 在頂層上下文中清除任何洩漏的資料 */

        MemoryContextResetAndDeleteChildren(walwriter_context);

 

        /* 現在我們可以再次中斷 */

        RESUME_INTERRUPTS();

 

        /* 在任何錯誤之後睡眠至少1秒。一個寫錯誤很可能會被重複,並且我們不想以儘可能快的速度填充錯誤日誌。*/

        pg_usleep(1000000L);

 

        /* 在所有錯誤之後關閉所有開啟的檔案。這在Windows上是有用的,在那裡儲存刪除的檔案會導致各種奇怪的錯誤。目前還不清楚我們需要在別處,但不應該傷害。 */

        smgrcloseall();

    }

 

    /* We can now handle ereport(ERROR) */

    PG_exception_stack = &local_sigjmp_buf;

 

    /*

     * Unblock signals (they were blocked when the postmaster forked us)

     */

    PG_SETMASK(&UnBlockSig);

 

    /*

     * Reset hibernation state after any error.

     */

    left_till_hibernate = LOOPS_UNTIL_HIBERNATE;

    hibernating = false;

    SetWalWriterSleeping(false);

 

    /* 告訴閂鎖,當我們睡覺的時候後臺可以隨時叫醒我們。 */

    ProcGlobal->walwriterLatch = &MyProc->procLatch;

 

    /* 迴圈 */

    for (;;)

    {

        long        cur_timeout;

        int         rc;

 

        /* 通知我們是否可以在這個週期休眠。在重置鎖存器之前這樣做,以確保如果任何非同步提交可能需要喚醒我們,它們將看到標誌集,並且我們不會錯過它們傳送給我們的任何訊號。(如果我們在休眠之前的最後一個週期中發現需要做的工作,那麼全域性標誌將被不必要地設定,但是傷害很小。)但是如果不需要更改,則避免觸控全域性標誌。  */

        if (hibernating != (left_till_hibernate <= 1))

        {

            hibernating = (left_till_hibernate <= 1);

            SetWalWriterSleeping(hibernating);

        }

 

        /* 清除任何已掛起的喚醒 */

        ResetLatch(MyLatch);

 

        /* 處理最近收到的任何請求或訊號 */

        if (got_SIGHUP)

        {

            got_SIGHUP = false;

            ProcessConfigFile(PGC_SIGHUP);

        }

        if (shutdown_requested)

        {

            /* Normal exit from the walwriter is here */

            proc_exit(0);       /* done */

        }

 

        /* 做我們在這裡做的事情;然後,如果XLogBackgroundFlush() 找到有用的工作要做,請重新設定休眠計數器。 */

        if (XLogBackgroundFlush())

            left_till_hibernate = LOOPS_UNTIL_HIBERNATE;

        else if (left_till_hibernate > 0)

            left_till_hibernate--;

 

        /* 休眠直到我們發出訊號或 WalWriterDelay 已經過去。如果我們已經相當長一段時間沒有做任何有用的事情了,延長休眠時間以減少伺服器的空閒功耗。 */

        if (left_till_hibernate > 0)

            cur_timeout = WalWriterDelay;   /* in ms */

        else

            cur_timeout = WalWriterDelay * HIBERNATE_FACTOR;

 

        rc = WaitLatch(MyLatch,

                     WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH,

                     cur_timeout,

                     WAIT_EVENT_WAL_WRITER_MAIN);

 

        /* 如果postmaster 程序死了,將緊急救助。這是為了避免對所有postmaster的子程序進行手工清理。 */

        if (rc & WL_POSTMASTER_DEATH)

            exit(1);

    }

}