PostgreSQL源码分析—WAL日志读取函数ReadPageInternal

PostgreSQL源码分析—WAL日志读取函数ReadPageInternal

我们使用test_decoding对ReadPageInternal()函数代码进行探究,代码选自pg12,xlogreader.c


```c
 /*
     * Read a single xlog page including at least [pageptr, reqLen] of valid data
     * via the read_page() callback.
     *
     * Returns -1 if the required page cannot be read for some reason; errormsg_buf
     * is set in that case (unless the error occurs in the read_page callback).
     *
     * We fetch the page from a reader-local cache if we know we have the required
     * data and if there hasn't been any error since caching the data.
     */
    static int
    ReadPageInternal(XLogReaderState *state, XLogRecPtr pageptr, int reqLen)
    {
    	int			readLen;
    	uint32		targetPageOff;
    	XLogSegNo	targetSegNo;
    	XLogPageHeader hdr;
    
    	Assert((pageptr % XLOG_BLCKSZ) == 0);
    
    	XLByteToSeg(pageptr, targetSegNo, state->wal_segment_size);
    	targetPageOff = XLogSegmentOffset(pageptr, state->wal_segment_size);
    
    	/* check whether we have all the requested data already */
    	if (targetSegNo == state->readSegNo && targetPageOff == state->readOff &&
    		reqLen <= state->readLen)
    		return state->readLen;
    
    	/*
    	 * Data is not in our buffer.
    	 *
    	 * Every time we actually read the page, even if we looked at parts of it
    	 * before, we need to do verification as the read_page callback might now
    	 * be rereading data from a different source.
    	 *
    	 * Whenever switching to a new WAL segment, we read the first page of the
    	 * file and validate its header, even if that's not where the target
    	 * record is.  This is so that we can check the additional identification
    	 * info that is present in the first page's "long" header.
    	 */
    	if (targetSegNo != state->readSegNo && targetPageOff != 0)
    	{
    		XLogRecPtr	targetSegmentPtr = pageptr - targetPageOff;
    
    		readLen = state->read_page(state, targetSegmentPtr, XLOG_BLCKSZ,
    								   state->currRecPtr,
    								   state->readBuf, &state->readPageTLI);
    		if (readLen < 0)
    			goto err;
    
    		/* we can be sure to have enough WAL available, we scrolled back */
    		Assert(readLen == XLOG_BLCKSZ);
    
    		if (!XLogReaderValidatePageHeader(state, targetSegmentPtr,
    										  state->readBuf))
    			goto err;
    	}
    
    	/*
    	 * First, read the requested data length, but at least a short page header
    	 * so that we can validate it.
    	 */
    	readLen = state->read_page(state, pageptr, Max(reqLen, SizeOfXLogShortPHD),
    							   state->currRecPtr,
    							   state->readBuf, &state->readPageTLI);
    	if (readLen < 0)
    		goto err;
    
    	Assert(readLen <= XLOG_BLCKSZ);
    
    	/* Do we have enough data to check the header length? */
    	if (readLen <= SizeOfXLogShortPHD)
    		goto err;
    
    	Assert(readLen >= reqLen);
    
    	hdr = (XLogPageHeader) state->readBuf;
    
    	/* still not enough */
    	if (readLen < XLogPageHeaderSize(hdr))
    	{
    		readLen = state->read_page(state, pageptr, XLogPageHeaderSize(hdr),
    								   state->currRecPtr,
    								   state->readBuf, &state->readPageTLI);
    		if (readLen < 0)
    			goto err;
    	}
    
    	/*
    	 * Now that we know we have the full header, validate it.
    	 */
    	if (!XLogReaderValidatePageHeader(state, pageptr, (char *) hdr))
    		goto err;
    
    	/* update read state information */
    	state->readSegNo = targetSegNo;
    	state->readOff = targetPageOff;
    	state->readLen = readLen;
    
    	return readLen;
    
    err:
    	XLogReaderInvalReadState(state);
    	return -1;
    }
 ReadPageInternal()函数通过read_page()回调函数读取包含至少[pageptr, reqLen]有效数据的单个xlog页面。 如果由于某种原因无法读取所需页面,则返回-1;在这种情况下会设置errormsg_buf(除非错误发生在read_page回调中)。 如果我们知道我们有所需的数据,并且自从缓存这些数据以来没有发生任何错误,那么我们会从读取器本地的缓存中获取该页面。

这里read_page指向logical_read_local_xlog_page()函数

1、首先检查我们是否已经有了所有请求的数据

if (targetSegNo == state->readSegNo && targetPageOff == state->readOff &&
	reqLen <= state->readLen)

2、若数据不在我们的缓冲区中。每次我们实际读取页面时,即使我们之前已经查看过其中的部分数据,我们仍需要进行验证,因为read_page回调函数可能现在正在从不同的源重新读取数据。每当切换到新的WAL段时,我们会读取文件的第一个页面并验证其头部,即使目标记录并不在那里。这样做是为了检查第一个页面的“长”头部中包含的附加识别信息。

  if (targetSegNo != state->readSegNo && targetPageOff != 0)
    	{
    		XLogRecPtr	targetSegmentPtr = pageptr - targetPageOff;
    
    		readLen = state->read_page(state, targetSegmentPtr, XLOG_BLCKSZ,
    								   state->currRecPtr,
    								   state->readBuf, &state->readPageTLI);
    		if (readLen < 0)
    			goto err;
    
    		/* we can be sure to have enough WAL available, we scrolled back */
    		Assert(readLen == XLOG_BLCKSZ);
    
    		if (!XLogReaderValidatePageHeader(state, targetSegmentPtr,
    										  state->readBuf))
    			goto err;
    	}

3、之后读取请求的数据长度,但至少读取一个短的页面头部,方便我们严重,这样可以确保数据的完整性和准确性

readLen = state->read_page(state, pageptr, Max(reqLen, SizeOfXLogShortPHD),
						   state->currRecPtr,
						   state->readBuf, &state->readPageTLI);
if (readLen < 0)
	goto err;

Assert(readLen <= XLOG_BLCKSZ);

4、判断我们是否有足够的数据来检查头部信息

if (readLen <= SizeOfXLogShortPHD)
	goto err;

5、数据不够的话,继续读取数据

if (readLen < XLogPageHeaderSize(hdr))
{
	readLen = state->read_page(state, pageptr, XLogPageHeaderSize(hdr),
							   state->currRecPtr,
							   state->readBuf, &state->readPageTLI);
	if (readLen < 0)
		goto err;
}

6、有完整的数据页头部信息后,对其进行验证

if (!XLogReaderValidatePageHeader(state, pageptr, (char *) hdr))
	goto err;

7、更新XLogReaderState的信息

state->readSegNo = targetSegNo;
state->readOff = targetPageOff;
state->readLen = readLen;
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值