PostgreSQL 소스 코드 판독 (106) - WAL \ # 3 (삽입 & WAL - heap insert 함수 \ # 3)

30946 단어
이 절 은 데 이 터 를 삽입 할 때 WAL 과 관련 된 처리 논 리 를 소개 하 는데 주로 힙insert - > XLogInsert 함수.
데이터 구조
정적 변수 프로 세 스 의 전역 공유
/*
 * An array of XLogRecData structs, to hold registered data.
 * XLogRecData     ,        
 */
static XLogRecData *rdatas;
//      
static int  num_rdatas;         /* entries currently used */
//        
static int  max_rdatas;         /* allocated size */
//    XLogBeginInsert  
static bool begininsert_called = false;


매크로 정의

typedef char* Pointer;//  
typedef Pointer Page;//Page

#define XLOG_HEAP_INSERT   0x00

/*
 * Pointer to a location in the XLOG.  These pointers are 64 bits wide,
 * because we don't want them ever to overflow.
 *   XLOG    .
 *        64bit,         .
 */
typedef uint64 XLogRecPtr;


/*
 * Additional macros for access to page headers. (Beware multiple evaluation
 * of the arguments!)
 */
#define PageGetLSN(page) \
    PageXLogRecPtrGet(((PageHeader) (page))->pd_lsn)
#define PageSetLSN(page, lsn) \
    PageXLogRecPtrSet(((PageHeader) (page))->pd_lsn, lsn)

/* Buffer size required to store a compressed version of backup block image */
//                    
#define PGLZ_MAX_BLCKSZ PGLZ_MAX_OUTPUT(BLCKSZ)

/*
 * Fake spinlock implementation using semaphores --- slow and prone
 * to fall foul of kernel limits on number of semaphores, so don't use this
 * unless you must!  The subroutines appear in spin.c.
 *             ——                   ,
 *         ,       !
 *            spin.c 。
 */
typedef int slock_t;


XLogCtl XLog 의 모든 공유 메모리 상태 정보
/*
 * Total shared-memory state for XLOG.
 * XLOG           
 */
typedef struct XLogCtlData
{
    XLogCtlInsert Insert;//     

    /* Protected by info_lck: */
    //------   info_lck   
    XLogwrtRqst LogwrtRqst;
    //Insert->RedoRecPtr     
    XLogRecPtr  RedoRecPtr;     /* a recent copy of Insert->RedoRecPtr */
    //   checkpoint nextXID & epoch
    uint32      ckptXidEpoch;   /* nextXID & epoch of latest checkpoint */
    TransactionId ckptXid;
    //      /   LSN
    XLogRecPtr  asyncXactLSN;   /* LSN of newest async commit/abort */
    //slot    " " LSN
    XLogRecPtr  replicationSlotMinLSN;  /* oldest LSN needed by any slot */
    //    /   XLOG 
    XLogSegNo   lastRemovedSegNo;   /* latest removed/recycled XLOG segment */

    /* Fake LSN counter, for unlogged relations. Protected by ulsn_lck. */
    //---- "  " LSN   ,            .  ulsn_lck   
    XLogRecPtr  unloggedLSN;
    slock_t     ulsn_lck;

    /* Time and LSN of last xlog segment switch. Protected by WALWriteLock. */
    //----       xlog      LSN,  WALWriteLock   
    pg_time_t   lastSegSwitchTime;
    XLogRecPtr  lastSegSwitchLSN;

    /*
     * Protected by info_lck and WALWriteLock (you must hold either lock to
     * read it, but both to update)
     *   info_lck WALWriteLock  
     * (            ,          )
     */
    XLogwrtResult LogwrtResult;

    /*
     * Latest initialized page in the cache (last byte position + 1).
     *           page(         + 1)
     * 
     * To change the identity of a buffer (and InitializedUpTo), you need to
     * hold WALBufMappingLock.  To change the identity of a buffer that's
     * still dirty, the old page needs to be written out first, and for that
     * you need WALWriteLock, and you need to ensure that there are no
     * in-progress insertions to the page by calling
     * WaitXLogInsertionsToFinish().
     *           (  InitializedUpTo),    WALBufMappingLock .
     *      dirty        ,  page      ,      WALWriteLock ,
     *                 WaitXLogInsertionsToFinish()        page  
     */
    XLogRecPtr  InitializedUpTo;

    /*
     * These values do not change after startup, although the pointed-to pages
     * and xlblocks values certainly do.  xlblock values are protected by
     * WALBufMappingLock.
     *            ,  pointed-to pages xlblocks      .
     * xlblock    WALBufMappingLock   .
     */
    //    XLOG pages   
    char       *pages;          /* buffers for unwritten XLOG pages */
    //ptr-s       + XLOG_BLCKSZ
    XLogRecPtr *xlblocks;       /* 1st byte ptr-s + XLOG_BLCKSZ */
    //    xlog        
    int         XLogCacheBlck;  /* highest allocated xlog buffer index */

    /*
     * Shared copy of ThisTimeLineID. Does not change after end-of-recovery.
     * If we created a new timeline when the system was started up,
     * PrevTimeLineID is the old timeline's ID that we forked off from.
     * Otherwise it's equal to ThisTimeLineID.
     * ThisTimeLineID     .
     *           .
     *                   ,PrevTimeLineID         ID.
     *   ,PrevTimeLineID = ThisTimeLineID
     */
    TimeLineID  ThisTimeLineID;
    TimeLineID  PrevTimeLineID;

    /*
     * SharedRecoveryInProgress indicates if we're still in crash or archive
     * recovery.  Protected by info_lck.
     * SharedRecoveryInProgress               ,  info_lck   .
     */
    bool        SharedRecoveryInProgress;

    /*
     * SharedHotStandbyActive indicates if we're still in crash or archive
     * recovery.  Protected by info_lck.
     * SharedHotStandbyActive               ,  info_lck   .
     */
    bool        SharedHotStandbyActive;

    /*
     * WalWriterSleeping indicates whether the WAL writer is currently in
     * low-power mode (and hence should be nudged if an async commit occurs).
     * Protected by info_lck.
     * WalWriterSleeping  WAL writer      "  "  
     * (  ,        ,         ).
     *   info_lck   .
     */
    bool        WalWriterSleeping;

    /*
     * recoveryWakeupLatch is used to wake up the startup process to continue
     * WAL replay, if it is waiting for WAL to arrive or failover trigger file
     * to appear.
     * recoveryWakeupLatch  WAL arrive  failover      ,
     *                 WAL  .
     * 
     */
    Latch       recoveryWakeupLatch;

    /*
     * During recovery, we keep a copy of the latest checkpoint record here.
     * lastCheckPointRecPtr points to start of checkpoint record and
     * lastCheckPointEndPtr points to end+1 of checkpoint record.  Used by the
     * checkpointer when it wants to create a restartpoint.
     *      ,                   .
     * lastCheckPointRecPtr          
     * lastCheckPointEndPtr           +1  
     *  checkpointer                 .
     *
     * Protected by info_lck.
     *   info_lck   .
     */
    XLogRecPtr  lastCheckPointRecPtr;
    XLogRecPtr  lastCheckPointEndPtr;
    CheckPoint  lastCheckPoint;

    /*
     * lastReplayedEndRecPtr points to end+1 of the last record successfully
     * replayed. When we're currently replaying a record, ie. in a redo
     * function, replayEndRecPtr points to the end+1 of the record being
     * replayed, otherwise it's equal to lastReplayedEndRecPtr.
     * lastReplayedEndRecPtr                  + 1   .
     *      redo        ,  replayEndRecPtr              + 1   ,
     *   replayEndRecPtr = lastReplayedEndRecPtr
     */
    XLogRecPtr  lastReplayedEndRecPtr;
    TimeLineID  lastReplayedTLI;
    XLogRecPtr  replayEndRecPtr;
    TimeLineID  replayEndTLI;
    /* timestamp of last COMMIT/ABORT record replayed (or being replayed) */
    //   COMMIT/ABORT  (     )      
    TimestampTz recoveryLastXTime;

    /*
     * timestamp of when we started replaying the current chunk of WAL data,
     * only relevant for replication or archive recovery
     *          WAL chunk    (           )
     */
    TimestampTz currentChunkStartTime;
    /* Are we requested to pause recovery? */
    //        
    bool        recoveryPause;

    /*
     * lastFpwDisableRecPtr points to the start of the last replayed
     * XLOG_FPW_CHANGE record that instructs full_page_writes is disabled.
     * lastFpwDisableRecPtr        XLOG_FPW_CHANGE  (           )    .
     */
    XLogRecPtr  lastFpwDisableRecPtr;
    //   
    slock_t     info_lck;       /* locks shared variables shown above */
} XLogCtlData;

static XLogCtlData *XLogCtl = NULL;


2. 소스 코드 해독
heap_insert 의 주요 구현 논 리 는 원 그룹 을 더미 에 삽입 하 는 것 입 니 다. WAL (XLog) 을 처리 하 는 부분 이 있 습 니 다. PostgreSQL 소스 코드 해석 (104) - WAL \ # 1 (Insert & WAL - heap insert 함수 \ # 1 참조)
XLogInsert 는 지정 한 RMID 와 info 바이트 가 있 는 XLOG 기록 을 삽입 합 니 다. 이 기록 의 주 체 는 이전에 XLogRegister * 를 통 해 등 록 된 데이터 와 버퍼 참조 입 니 다.
/*
 * Insert an XLOG record having the specified RMID and info bytes, with the
 * body of the record being the data and buffer references registered earlier
 * with XLogRegister* calls.
 *          RMID info   XLOG  ,
 *              XLogRegister*             。
 *
 * Returns XLOG pointer to end of record (beginning of next record).
 * This can be used as LSN for data pages affected by the logged action.
 * (LSN is the XLOG point up to which the XLOG must be flushed to disk
 * before the data page can be written out.  This implements the basic
 * WAL rule "write the log before the data".)
 *   XLOG         (        )。
 *                  LSN。
 * (LSN    XLOG             XLOG 。
 *         WAL  :“        ”。)
 */
XLogRecPtr
XLogInsert(RmgrId rmid, uint8 info)
{
    XLogRecPtr  EndPos;//uint64

    /* XLogBeginInsert() must have been called. */
    //   ,XLogBeginInsert()     
    if (!begininsert_called)
        elog(ERROR, "XLogBeginInsert was not called");

    /*
     * The caller can set rmgr bits, XLR_SPECIAL_REL_UPDATE and
     * XLR_CHECK_CONSISTENCY; the rest are reserved for use by me.
     *        rmgr :XLR_SPECIAL_REL_UPDATE & XLR_CHECK_CONSISTENCY.
     *          
     */
    if ((info & ~(XLR_RMGR_INFO_MASK |
                  XLR_SPECIAL_REL_UPDATE |
                  XLR_CHECK_CONSISTENCY)) != 0)
        elog(PANIC, "invalid xlog info mask %02X", info);

    TRACE_POSTGRESQL_WAL_INSERT(rmid, info);

    /*
     * In bootstrap mode, we don't actually log anything but XLOG resources;
     * return a phony record pointer.
     *  bootstrap  ,  XLOG   ,         .
     *          .
     */
    if (IsBootstrapProcessingMode() && rmid != RM_XLOG_ID)
    {
        XLogResetInsertion();
        EndPos = SizeOfXLogLongPHD; /*        ;start of 1st chkpt record */
        return EndPos;
    }

    do
    {
        //  
        XLogRecPtr  RedoRecPtr;
        bool        doPageWrites;
        XLogRecPtr  fpw_lsn;
        XLogRecData *rdt;

        /*
         * Get values needed to decide whether to do full-page writes. Since
         * we don't yet have an insertion lock, these could change under us,
         * but XLogInsertRecord will recheck them once it has a lock.
         *                 。
         *           ,                  ,
         *     XLogInsertRecord     ,        。
         */
        GetFullPageWriteInfo(&RedoRecPtr, &doPageWrites);

        rdt = XLogRecordAssemble(rmid, info, RedoRecPtr, doPageWrites,
                                 &fpw_lsn);
        //curinsert_flags   uint8
        EndPos = XLogInsertRecord(rdt, fpw_lsn, curinsert_flags);
    } while (EndPos == InvalidXLogRecPtr);

    XLogResetInsertion();

    return EndPos;
}


XLogInsertRecord 는 이미 구 성 된 데이터 chunks 체인 이 표시 하 는 XLOG 기록 을 삽입 합 니 다.
/*
 * Insert an XLOG record represented by an already-constructed chain of data
 * chunks.  This is a low-level routine; to construct the WAL record header
 * and data, use the higher-level routines in xloginsert.c.
 *             chunks    XLOG  。
 *                ,
 *     xloginsert.c         WAL        
 *
 * If 'fpw_lsn' is valid, it is the oldest LSN among the pages that this
 * WAL record applies to, that were not included in the record as full page
 * images.  If fpw_lsn <= RedoRecPtr, the function does not perform the
 * insertion and returns InvalidXLogRecPtr.  The caller can then recalculate
 * which pages need a full-page image, and retry.  If fpw_lsn is invalid, the
 * record is always inserted.
 *  "fpw_lsn"    ,         WAL     pages    LSN,
 *                .
 *  fpw_lsn <= RedoRecPtr,              InvalidXLogRecPtr.
 *            pages  full-page image      .
 *   fpw_lsn  ,        .
 *
 * 'flags' gives more in-depth control on the record being inserted. See
 * XLogSetRecordFlags() for details.
 * "flags"                     .
 *     XLogSetRecordFlags()         .
 *
 * The first XLogRecData in the chain must be for the record header, and its
 * data must be MAXALIGNed.  XLogInsertRecord fills in the xl_prev and
 * xl_crc fields in the header, the rest of the header must already be filled
 * by the caller.
 *       XLogRecData        ,      MAXALIGNed.
 * XLogInsertRecord      xl_prev xl_crc  ,
 *                 .
 *
 * Returns XLOG pointer to end of record (beginning of next record).
 * This can be used as LSN for data pages affected by the logged action.
 * (LSN is the XLOG point up to which the XLOG must be flushed to disk
 * before the data page can be written out.  This implements the basic
 * WAL rule "write the log before the data".)
 *   XLOG  ,         (        ).
 *                  LSN。
 * (LSN    XLOG              XLOG 。
 *      WAL     "        ")
 */
XLogRecPtr
XLogInsertRecord(XLogRecData *rdata,
                 XLogRecPtr fpw_lsn,
                 uint8 flags)
{
    XLogCtlInsert *Insert = &XLogCtl->Insert;//XLOG     
    pg_crc32c   rdata_crc;//uint32
    bool        inserted;
    XLogRecord *rechdr = (XLogRecord *) rdata->data;
    uint8       info = rechdr->xl_info & ~XLR_INFO_MASK;
    bool        isLogSwitch = (rechdr->xl_rmid == RM_XLOG_ID &&
                               info == XLOG_SWITCH);
    XLogRecPtr  StartPos;
    XLogRecPtr  EndPos;
    bool        prevDoPageWrites = doPageWrites;

    /* we assume that all of the record header is in the first chunk */
    //                 chunk 
    Assert(rdata->len >= SizeOfXLogRecord);

    /* cross-check on whether we should be here or not */
    //    
    if (!XLogInsertAllowed())
        elog(ERROR, "cannot make new WAL entries during recovery");

    /*----------
     *
     * We have now done all the preparatory work we can without holding a
     * lock or modifying shared state. From here on, inserting the new WAL
     * record to the shared WAL buffer cache is a two-step process:
     *   ,              ,            。
     *      ,   WAL        WAL           :
     * 
     * 1. Reserve the right amount of space from the WAL. The current head of
     *    reserved space is kept in Insert->CurrBytePos, and is protected by
     *    insertpos_lck.
     * 1.  WAL        .          Insert->CurrBytePos ,
     *      insertpos_lck   
     *
     * 2. Copy the record to the reserved WAL space. This involves finding the
     *    correct WAL buffer containing the reserved space, and copying the
     *    record in place. This can be done concurrently in multiple processes.
     * 2.         WAL   .                 WAL   ,
     *                   .
     *               .
     *
     * To keep track of which insertions are still in-progress, each concurrent
     * inserter acquires an insertion lock. In addition to just indicating that
     * an insertion is in progress, the lock tells others how far the inserter
     * has progressed. There is a small fixed number of insertion locks,
     * determined by NUM_XLOGINSERT_LOCKS. When an inserter crosses a page
     * boundary, it updates the value stored in the lock to the how far it has
     * inserted, to allow the previous buffer to be flushed.
     *                  ,           insertion .
     *         insertion      ,                    .
     *             insertion ,    NUM_XLOGINSERT_LOCKS  .
     *           page   ,                        ,
     *              .
     *
     * Holding onto an insertion lock also protects RedoRecPtr and
     * fullPageWrites from changing until the insertion is finished.
     *           RedoRecPtr fullpagewrite           。
     * 
     * Step 2 can usually be done completely in parallel. If the required WAL
     * page is not initialized yet, you have to grab WALBufMappingLock to
     * initialize it, but the WAL writer tries to do that ahead of insertions
     * to avoid that from happening in the critical path.
     *   2          。
     *      WAL        ,     WALBufMappingLock     ,
     *     WAL writer             ,               。
     *
     *----------
     */
    START_CRIT_SECTION();
    if (isLogSwitch)
        WALInsertLockAcquireExclusive();
    else
        WALInsertLockAcquire();

    /*
     * Check to see if my copy of RedoRecPtr is out of date. If so, may have
     * to go back and have the caller recompute everything. This can only
     * happen just after a checkpoint, so it's better to be slow in this case
     * and fast otherwise.
     *      RedoRecPtr      。
     *    ,                   。
     *              ,             ,       。
     * 
     * Also check to see if fullPageWrites or forcePageWrites was just turned
     * on; if we weren't already doing full-page writes then go back and
     * recompute.
     *          fullpagewrite forcepagewrite;
     *                  ,         。
     *
     * If we aren't doing full-page writes then RedoRecPtr doesn't actually
     * affect the contents of the XLOG record, so we'll update our local copy
     * but not force a recomputation.  (If doPageWrites was just turned off,
     * we could recompute the record without full pages, but we choose not to
     * bother.)
     *                ,  RedoRecPtr       XLOG     ,
     *              ,           。
     * (  doPageWrites  ,                   ,            。)
     * 
     */
    if (RedoRecPtr != Insert->RedoRecPtr)
    {
        Assert(RedoRecPtr < Insert->RedoRecPtr);
        RedoRecPtr = Insert->RedoRecPtr;
    }
    doPageWrites = (Insert->fullPageWrites || Insert->forcePageWrites);

    if (doPageWrites &&
        (!prevDoPageWrites ||
         (fpw_lsn != InvalidXLogRecPtr && fpw_lsn <= RedoRecPtr)))
    {
        /*
         * Oops, some buffer now needs to be backed up that the caller didn't
         * back up.  Start over.
         *   ,                   。
         *         。
         */
        WALInsertLockRelease();
        END_CRIT_SECTION();
        return InvalidXLogRecPtr;
    }

    /*
     * Reserve space for the record in the WAL. This also sets the xl_prev
     * pointer.
     *  WAL      .     xl_prev  .
     * 
     */
    if (isLogSwitch)
        inserted = ReserveXLogSwitch(&StartPos, &EndPos, &rechdr->xl_prev);
    else
    {
        ReserveXLogInsertLocation(rechdr->xl_tot_len, &StartPos, &EndPos,
                                  &rechdr->xl_prev);
        inserted = true;
    }

    if (inserted)
    {
        /*
         * Now that xl_prev has been filled in, calculate CRC of the record
         * header.
         *   xl_prev     ,       CRC
         */
        rdata_crc = rechdr->xl_crc;
        COMP_CRC32C(rdata_crc, rechdr, offsetof(XLogRecord, xl_crc));
        FIN_CRC32C(rdata_crc);
        rechdr->xl_crc = rdata_crc;

        /*
         * All the record data, including the header, is now ready to be
         * inserted. Copy the record in the space reserved.
         *        ,      ,    !
         *           .
         */
        CopyXLogRecordToWAL(rechdr->xl_tot_len, isLogSwitch, rdata,
                            StartPos, EndPos);

        /*
         * Unless record is flagged as not important, update LSN of last
         * important record in the current slot. When holding all locks, just
         * update the first one.
         *            ,      slot          LSN。
         *       ,       。
         */
        if ((flags & XLOG_MARK_UNIMPORTANT) == 0)
        {
            int         lockno = holdingAllLocks ? 0 : MyLockNo;

            WALInsertLocks[lockno].l.lastImportantAt = StartPos;
        }
    }
    else
    {
        /*
         * This was an xlog-switch record, but the current insert location was
         * already exactly at the beginning of a segment, so there was no need
         * to do anything.
         *     xlog-switch  ,                   ,          。
         */
    }

    /*
     * Done! Let others know that we're finished.
     *     !               !
     */
    WALInsertLockRelease();

    MarkCurrentTransactionIdLoggedIfAny();

    END_CRIT_SECTION();

    /*
     * Update shared LogwrtRqst.Write, if we crossed page boundary.
     *     page  ,     LogwrtRqst.Write  
     */
    if (StartPos / XLOG_BLCKSZ != EndPos / XLOG_BLCKSZ)
    {
        SpinLockAcquire(&XLogCtl->info_lck);
        /* advance global request to include new block(s) */
        //        (s)
        if (XLogCtl->LogwrtRqst.Write < EndPos)
            XLogCtl->LogwrtRqst.Write = EndPos;
        /* update local result copy while I have the chance */
        //    ,         
        LogwrtResult = XLogCtl->LogwrtResult;
        SpinLockRelease(&XLogCtl->info_lck);
    }

    /*
     * If this was an XLOG_SWITCH record, flush the record and the empty
     * padding space that fills the rest of the segment, and perform
     * end-of-segment actions (eg, notifying archiver).
     *       XLOG_SWITCH  ,
     *                       ,
     *           (  ,     )。
     */
    if (isLogSwitch)
    {
        TRACE_POSTGRESQL_WAL_SWITCH();
        XLogFlush(EndPos);

        /*
         * Even though we reserved the rest of the segment for us, which is
         * reflected in EndPos, we return a pointer to just the end of the
         * xlog-switch record.
         *                 (    EndPos ),
         *             xlog-switch       。
         */
        if (inserted)
        {
            EndPos = StartPos + SizeOfXLogRecord;
            if (StartPos / XLOG_BLCKSZ != EndPos / XLOG_BLCKSZ)
            {
                uint64      offset = XLogSegmentOffset(EndPos, wal_segment_size);

                if (offset == EndPos % XLOG_BLCKSZ)
                    EndPos += SizeOfXLogLongPHD;
                else
                    EndPos += SizeOfXLogShortPHD;
            }
        }
    }

#ifdef WAL_DEBUG//DEBUG  
    if (XLOG_DEBUG)
    {
        static XLogReaderState *debug_reader = NULL;
        StringInfoData buf;
        StringInfoData recordBuf;
        char       *errormsg = NULL;
        MemoryContext oldCxt;

        oldCxt = MemoryContextSwitchTo(walDebugCxt);

        initStringInfo(&buf);
        appendStringInfo(&buf, "INSERT @ %X/%X: ",
                         (uint32) (EndPos >> 32), (uint32) EndPos);

        /*
         * We have to piece together the WAL record data from the XLogRecData
         * entries, so that we can pass it to the rm_desc function as one
         * contiguous chunk.
         */
        initStringInfo(&recordBuf);
        for (; rdata != NULL; rdata = rdata->next)
            appendBinaryStringInfo(&recordBuf, rdata->data, rdata->len);

        if (!debug_reader)
            debug_reader = XLogReaderAllocate(wal_segment_size, NULL, NULL);

        if (!debug_reader)
        {
            appendStringInfoString(&buf, "error decoding record: out of memory");
        }
        else if (!DecodeXLogRecord(debug_reader, (XLogRecord *) recordBuf.data,
                                   &errormsg))
        {
            appendStringInfo(&buf, "error decoding record: %s",
                             errormsg ? errormsg : "no error message");
        }
        else
        {
            appendStringInfoString(&buf, " - ");
            xlog_outdesc(&buf, debug_reader);
        }
        elog(LOG, "%s", buf.data);

        pfree(buf.data);
        pfree(recordBuf.data);
        MemoryContextSwitchTo(oldCxt);
    }
#endif

    /*
     * Update our global variables
     *       
     */
    ProcLastRecPtr = StartPos;
    XactLastRecEnd = EndPos;

    return EndPos;
}


3. 추적 분석
테스트 스 크 립 트 는 다음 과 같 습 니 다.
insert into t_wal_partition(c1,c2,c3) VALUES(0,'HASH0','HAHS0');


gdb 시작, 정지점 설정, XLogInsert 진입
(gdb) b XLogInsert
Breakpoint 1 at 0x5652d6: file xloginsert.c, line 420.
(gdb) c
Continuing.

Breakpoint 1, XLogInsert (rmid=10 '
', info=0 '\000') at xloginsert.c:420 420 if (!begininsert_called)

이전 에는 XLogBeginInsert () 가 호출 되 어야 합 니 다.
420     if (!begininsert_called)
(gdb) n

호출 자 는 rmgr 비트 를 설정 해 야 합 니 다: XLRSPECIAL_REL_UPDATE & XLR_CHECK_CONSISTENCY. 나머지 는 여기 서 사용 을 보류 합 니 다.
427     if ((info & ~(XLR_RMGR_INFO_MASK |
(gdb) n
432     TRACE_POSTGRESQL_WAL_INSERT(rmid, info);

순환 에 들어가다
(gdb) n
438     if (IsBootstrapProcessingMode() && rmid != RM_XLOG_ID)
(gdb) 
457         GetFullPageWriteInfo(&RedoRecPtr, &doPageWrites);

전체 페이지 기록 을 실행 할 지 여 부 를 결정 하 는 데 필요 한 값 가 져 오기
(gdb) p *RedoRecPtr
$1 = 1166604425
(gdb) p doPageWrites
$2 = false
(gdb) n
459         rdt = XLogRecordAssemble(rmid, info, RedoRecPtr, doPageWrites,
(gdb) p RedoRecPtr
$3 = 5411227832
(gdb) p doPageWrites
$4 = true

rdt 가 져 오기
(gdb) n
462         EndPos = XLogInsertRecord(rdt, fpw_lsn, curinsert_flags);
(gdb) p *rdt
$5 = {next = 0x2a911b8, data = 0x2a8f460 , len = 51}

XLogInsertRecord - > XLogInsertRecord 를 호출 하여 XLogInsertRecord 함수 fpw 에 들 어 갑 니 다.lsn=0, flags=1 '\001'
(gdb) step
XLogInsertRecord (rdata=0xf9cc70 , fpw_lsn=0, flags=1 '\001') at xlog.c:970
970     XLogCtlInsert *Insert = &XLogCtl->Insert;

XLogInsertRecord - > 삽입 관리자 가 져 오기
(gdb) n
973     XLogRecord *rechdr = (XLogRecord *) rdata->data;
(gdb) p *Insert
$6 = {insertpos_lck = 0 '\000', CurrBytePos = 5395369608, PrevBytePos = 5395369552, pad = '\000' , 
  RedoRecPtr = 5411227832, forcePageWrites = false, fullPageWrites = true, exclusiveBackupState = EXCLUSIVE_BACKUP_NONE, 
  nonExclusiveBackups = 0, lastBackupStart = 0, WALInsertLocks = 0x7fa2523d4100}

XLogInsertRecord - > 변수 할당
(gdb) n
974     uint8       info = rechdr->xl_info & ~XLR_INFO_MASK;
(gdb) 
975     bool        isLogSwitch = (rechdr->xl_rmid == RM_XLOG_ID &&
(gdb) 
979     bool        prevDoPageWrites = doPageWrites;
(gdb) 
982     Assert(rdata->len >= SizeOfXLogRecord);
(gdb) 
(gdb) p *rechdr
$7 = {xl_tot_len = 210, xl_xid = 1948, xl_prev = 0, xl_info = 0 '\000', xl_rmid = 10 '
', xl_crc = 3212449170} (gdb) p info $8 = 0 '\000' (gdb) p isLogSwitch $9 = false (gdb) p prevDoPageWrites $10 = true

XLogInsertRecord - > 관련 판단 수행, CRIT 오픈SECTION, WAL 잠 금 삽입 가 져 오기
(gdb) n
985     if (!XLogInsertAllowed())
(gdb) 
1020        START_CRIT_SECTION();
(gdb) 
1021        if (isLogSwitch)
(gdb) 
1024            WALInsertLockAcquire();
(gdb) 
1042        if (RedoRecPtr != Insert->RedoRecPtr)
(gdb) 

XLogInsertRecord - > 관련 판단 을 실행 하고 doPageWrites 업데이트
(gdb) p RedoRecPtr
$11 = 5411227832
(gdb) p Insert->RedoRecPtr
$12 = 5411227832
(gdb) n
1047        doPageWrites = (Insert->fullPageWrites || Insert->forcePageWrites);
(gdb) 
1049        if (doPageWrites &&
(gdb) p doPageWrites
$13 = true
(gdb) n
1050            (!prevDoPageWrites ||
(gdb) 
1049        if (doPageWrites &&

XLogInsert Record - > WAL 에 기록 공간 을 예약 합 니 다. xl 을 설정 합 니 다.prev 포인터.
(gdb) 
1050            (!prevDoPageWrites ||
(gdb) 
1066        if (isLogSwitch)
(gdb) 
1070            ReserveXLogInsertLocation(rechdr->xl_tot_len, &StartPos, &EndPos,
(gdb) 
1072            inserted = true;
(gdb) p rechdr->xl_tot_len
$14 = 210
(gdb) p StartPos
$15 = 5411228000
(gdb) p EndPos
$16 = 5411228216
(gdb) p *rechdr->xl_prev
Cannot access memory at address 0x14288c928
(gdb) p rechdr->xl_prev
$17 = 5411227944
(gdb) 

XLogInsertRecord - > 현재 xlprev 포인터 가 채 워 져 머리 를 기록 하 는 CRC 를 계산 합 니 다.
(gdb) n
1075        if (inserted)
(gdb) 
1081            rdata_crc = rechdr->xl_crc;
(gdb) 
1082            COMP_CRC32C(rdata_crc, rechdr, offsetof(XLogRecord, xl_crc));
(gdb) 
1083            FIN_CRC32C(rdata_crc);
(gdb) 
1084            rechdr->xl_crc = rdata_crc;
(gdb) 
1090            CopyXLogRecordToWAL(rechdr->xl_tot_len, isLogSwitch, rdata,
(gdb) p rdata_crc
$18 = 2310972234
(gdb) p *rechdr
$19 = {xl_tot_len = 210, xl_xid = 1948, xl_prev = 5411227944, xl_info = 0 '\000', xl_rmid = 10 '
', xl_crc = 2310972234} (gdb)

XLogInsert Record - > 모든 기록 데이터, 머리 데이터 포함 OK, 삽입 준비!보존 공간 에 복사 하여 기록 합 니 다. 기록 이 중요 하지 않 은 것 으로 표시 되 지 않 는 한 현재 slot 의 마지막 중요 한 기록 인 LSN 을 업데이트 합 니 다.
(gdb) n
1098            if ((flags & XLOG_MARK_UNIMPORTANT) == 0)
(gdb) 
1100                int         lockno = holdingAllLocks ? 0 : MyLockNo;
(gdb) 
(gdb) n
1102                WALInsertLocks[lockno].l.lastImportantAt = StartPos;
(gdb) 
1117        WALInsertLockRelease();

XLogInsertRecord - > 모두 완료!다른 삽입 기 에 우리 가 이미 완성 했다 는 것 을 알 게 하 세 요!page 경 계 를 넘 어서 면 공 유 된 LogwrtRqst. Write 변 수 를 업데이트 합 니 다.
(gdb) 
1117        WALInsertLockRelease();
(gdb) n
1119        MarkCurrentTransactionIdLoggedIfAny();
(gdb) 
1121        END_CRIT_SECTION();
(gdb) 
1126        if (StartPos / XLOG_BLCKSZ != EndPos / XLOG_BLCKSZ)
(gdb) 
1142        if (isLogSwitch)

XLogInsertRecord - > 전역 변 수 를 업데이트 하고 함수 반환
(gdb) 
1220        ProcLastRecPtr = StartPos;
(gdb) 
1221        XactLastRecEnd = EndPos;
(gdb) 
1223        return EndPos;
(gdb) 
1224    }

XLogInsert 되 돌리 기, 삽입 초기 화, EndPos 되 돌리 기, 종료
(gdb) 
XLogInsert (rmid=10 '
', info=0 '\000') at xloginsert.c:463 463 } while (EndPos == InvalidXLogRecPtr); (gdb) n 465 XLogResetInsertion(); (gdb) 467 return EndPos; (gdb) 468 } (gdb) p EndPos $20 = 5411228216 (gdb) $21 = 5411228216 (gdb) n heap_insert (relation=0x7fa280616228, tup=0x2b15440, cid=0, options=0, bistate=0x0) at heapam.c:2590 2590 PageSetLSN(page, recptr); (gdb)

DONE!
참고 자료
Write Ahead Logging - WAL PostgreSQL 소스 코드 판독 (4) - 데이터 삽입 \ # 3 (heap insert) PgSQL · 특성 분 석 · 데이터베이스 붕괴 복구 (상) PgSQL · 특성 분 석 · 데이터베이스 붕괴 복구 (하) PgSQL · 특성 분 석 · Write - Ahead Logging 메커니즘 분석 PostgreSQL WAL Buffers, Clog Buffers Deep Dive

좋은 웹페이지 즐겨찾기