初步学习pg_control文件之十二

接前问,初步学习pg_control文件之十一,再来看下面这个

XLogRecPtr    minRecoveryPoint;

看其注释:

     * minRecoveryPoint is updated to the latest replayed LSN whenever we
     * flush a data change during archive recovery. That guards against
     * starting archive recovery, aborting it, and restarting with an earlier
     * stop location. If we've already flushed data changes from WAL record X
     * to disk, we mustn't start up until we reach X again. Zero when not
     * doing archive recovery.

看来,是为了防止出现重复执行重做日志...它强调的是在 archive recovery的情况下。

启动时,如果处于Recovery状态,则要进行设置:

/*                                    
 * This must be called ONCE during postmaster or standalone-backend startup                                    
 */                                    
void                                    
StartupXLOG(void)                                    
{                                    
    …                                
    CheckPoint    checkPoint;                            
    …                                
    /*                                
     * Read control file and check XLOG status looks valid.    
     * Note: in most control paths, *ControlFile is already valid and we need  
     * not do ReadControlFile() here, but might as well do it to be sure.                                
     */                                
    ReadControlFile();                                
                                    
    if (ControlFile->state < DB_SHUTDOWNED ||                                
        ControlFile->state > DB_IN_PRODUCTION ||                            
        !XRecOffIsValid(ControlFile->checkPoint.xrecoff))                            
        ereport(FATAL,                            
                (errmsg("control file contains invalid data")));                    
    …                                
                                    
    /* REDO */                                
    if (InRecovery)                                
    {                                
        …                            
        if (InArchiveRecovery)                            
        {                            
            /* initialize minRecoveryPoint if not set yet */                        
            if (XLByteLT(ControlFile->minRecoveryPoint, checkPoint.redo))                        
                ControlFile->minRecoveryPoint = checkPoint.redo;                    
        }                            
    …                                
    }                                
    …                                
}                                    

在xlogredo函数中得到处理:

/*                                    
 * XLOG resource manager's routines                                    
 *                                    
 * Definitions of info values are in include/catalog/pg_control.h, though                                    
 * not all record types are related to control file updates.                                    
 */                                    
void                                    
xlog_redo(XLogRecPtr lsn, XLogRecord *record)                                    
{                                    
    …                                
    if (info == XLOG_NEXTOID)                                
    {                                
        …                            
    }                                
    …                                
    else if (info == XLOG_BACKUP_END)                                
    {                                
        …                            
        if (XLByteEQ(ControlFile->backupStartPoint, startpoint))                            
        {                            
            /*                        
             * We have reached the end of base backup, the point where                        
             * pg_stop_backup() was done. The data on disk is now consistent.                        
             * Reset backupStartPoint, and update minRecoveryPoint to make                        
             * sure we don't allow starting up at an earlier point even if                        
             * recovery is stopped and restarted soon after this.                        
             */                        
            …                        
            LWLockAcquire(ControlFileLock, LW_EXCLUSIVE);                        
                                    
            if (XLByteLT(ControlFile->minRecoveryPoint, lsn))                        
                ControlFile->minRecoveryPoint = lsn;                    
            MemSet(&ControlFile->backupStartPoint, 0, sizeof(XLogRecPtr));                        
            UpdateControlFile();                        
                                    
            LWLockRelease(ControlFileLock);                        
        }                            
    }                                
    else if (info == XLOG_PARAMETER_CHANGE)                                
    {                                
        …                            
        LWLockAcquire(ControlFileLock, LW_EXCLUSIVE);                            
        ControlFile->MaxConnections = xlrec.MaxConnections;                            
        ControlFile->max_prepared_xacts = xlrec.max_prepared_xacts;                            
        ControlFile->max_locks_per_xact = xlrec.max_locks_per_xact;                            
        ControlFile->wal_level = xlrec.wal_level;                            
                                    
        /*                            
         * Update minRecoveryPoint to ensure that if recovery is aborted, we                            
         * recover back up to this point before allowing hot standby again.                            
         * This is particularly important if wal_level was set to 'archive'                            
         * before, and is now 'hot_standby', to ensure you don't run queries                            
         * against the WAL preceding the wal_level change. Same applies to                            
         * decreasing max_* settings.                            
         */                            
        minRecoveryPoint = ControlFile->minRecoveryPoint;                            
        if ((minRecoveryPoint.xlogid != 0 || minRecoveryPoint.xrecoff != 0)                            
            && XLByteLT(minRecoveryPoint, lsn))                        
        {                            
            ControlFile->minRecoveryPoint = lsn;                        
        }                            
                                    
        UpdateControlFile();                            
        LWLockRelease(ControlFileLock);                            
        …                            
    }                                
}                                    

再就是checkpoint发生的时候,也做了处理:

CreateCheckPoint -->XLogFlush--> UpdateMinRecoveryPoint

/*                                
 * Perform a checkpoint --- either during shutdown, or on-the-fly                                
 *                                
 * flags is a bitwise OR of the following:                                
 *    CHECKPOINT_IS_SHUTDOWN: checkpoint is for database shutdown.                            
 *    CHECKPOINT_END_OF_RECOVERY: checkpoint is for end of WAL recovery.                            
 *    CHECKPOINT_IMMEDIATE: finish the checkpoint ASAP,                            
 *        ignoring checkpoint_completion_target parameter.                        
 *    CHECKPOINT_FORCE: force a checkpoint even if no XLOG activity has occured                            
 *        since the last one (implied by CHECKPOINT_IS_SHUTDOWN or                        
 *        CHECKPOINT_END_OF_RECOVERY).                        
 *                                
 * Note: flags contains other bits, of interest here only for logging purposes.                                
 * In particular note that this routine is synchronous and does not pay                                
 * attention to CHECKPOINT_WAIT.                                
 */                                
void                                
CreateCheckPoint(int flags)                                
{                                
    …                            
    recptr = XLogInsert(RM_XLOG_ID,                            
                shutdown ? XLOG_CHECKPOINT_SHUTDOWN :                
                XLOG_CHECKPOINT_ONLINE,                
                &rdata);                
                                
    XLogFlush(recptr);                            
    …                            
}                                
/*                            
 * Ensure that all XLOG data through the given position is flushed to disk.                            
 *                            
 * NOTE: this differs from XLogWrite mainly in that the WALWriteLock is not                            
 * already held, and we try to avoid acquiring it if possible.                            
 */                            
void                            
XLogFlush(XLogRecPtr record)                            
{                            
    XLogRecPtr        WriteRqstPtr;                
    XLogwrtRqst WriteRqst;                        
                            
    /*                        
     * During REDO, we are reading not writing WAL.  Therefore, instead of                        
     * trying to flush the WAL, we should update minRecoveryPoint instead. We                        
     * test XLogInsertAllowed(), not InRecovery, because we need the bgwriter                        
     * to act this way too, and because when the bgwriter tries to write the                        
     * end-of-recovery checkpoint, it should indeed flush.                        
     */                        
    if (!XLogInsertAllowed())                        
    {                        
        UpdateMinRecoveryPoint(record, false);                    
        return;                    
    }                        
                            
    /* Quick exit if already known flushed */                        
    if (XLByteLE(record, LogwrtResult.Flush))                        
        return;                    
                            
    …                        
}                            
/*                                            
 * Advance minRecoveryPoint in control file.  
 * If we crash during recovery, we must reach this point again before the                                            
 * database is consistent.                   
 * If 'force' is true, 'lsn' argument is ignored. Otherwise, minRecoveryPoint 
 * is only updated if it's not already greater than or equal to 'lsn'. 
 */                                            
static void                                            
UpdateMinRecoveryPoint(XLogRecPtr lsn, bool force)                                            
{                                            
    …                                        
    /*                                        
     * An invalid minRecoveryPoint means that we need to recover all the WAL,  
     * i.e., we're doing crash recovery.  We never modify the control file's
     * value in that case, so we can short-circuit future checks here too. 
     */                                        
    if (minRecoveryPoint.xlogid == 0 && minRecoveryPoint.xrecoff == 0)
        updateMinRecoveryPoint = false;                                    
    else if (force || XLByteLT(minRecoveryPoint, lsn))                                        
    {                                        
        /* use volatile pointer to prevent code rearrangement */                                    
        volatile XLogCtlData *xlogctl = XLogCtl;                                    
        XLogRecPtr        newMinRecoveryPoint;                            
                                            
        /*                                    
         * To avoid having to update the control file too often, we update it  
         * all the way to the last record being replayed, even though 'lsn'
         * would suffice for correctness.  This also allows the 'force' case  
         * to not need a valid 'lsn' value.                                    
         *                                    
         * Another important reason for doing it this way is that the passed  
         * 'lsn' value could be bogus, i.e., past the end of available WAL, if  
         * the caller got it from a corrupted heap page.  Accepting such a   
         * value as the min recovery point would prevent us from coming up at  
         * all.  Instead, we just log a warning and continue with recovery.  
         * (See also the comments about corrupt LSNs in XLogFlush.)                                    
         */                                    
        SpinLockAcquire(&xlogctl->info_lck);                                    
        newMinRecoveryPoint = xlogctl->replayEndRecPtr;                                    
        SpinLockRelease(&xlogctl->info_lck);                                    
                                            
        if (!force && XLByteLT(newMinRecoveryPoint, lsn))                                    
            elog(WARNING,                                
               "xlog min recovery request %X/%X is past current point %X/%X", 
                 lsn.xlogid, lsn.xrecoff,                            
                 newMinRecoveryPoint.xlogid, newMinRecoveryPoint.xrecoff);                            
                                            
        /* update control file */                                    
        if (XLByteLT(ControlFile->minRecoveryPoint, newMinRecoveryPoint))                                    
        {                                    
            ControlFile->minRecoveryPoint = newMinRecoveryPoint;                                
            UpdateControlFile();                                
            minRecoveryPoint = newMinRecoveryPoint;                                
                                            
            ereport(DEBUG2,                                
                    (errmsg("updated min recovery point to %X/%X",                        
                        minRecoveryPoint.xlogid, minRecoveryPoint.xrecoff)));                    
        }                                    
    }                                        
    LWLockRelease(ControlFileLock);                                        
}                                            

 

posted @ 2013-08-01 17:06  健哥的数据花园  阅读(995)  评论(0编辑  收藏  举报