海山数据库(He3DB)源码详解:主备复制read_backup_label

# 海山数据库(He3DB)源码详解:主备复制read_backup_label

主备复制

时间点恢复——概述

   在数据库主备复制(如 PostgreSQL 的流复制等场景)中,时间点恢复是一种数据库恢复技术,它允许将数据库恢复到指定的历史时间点。

时间点恢复——read_backup_label

read_backup_label函数主要用于读取和解析 BACKUP_LABEL_FILE 文件,初始化相关输出参数,包括检查点位置、时间线 ID 等,并根据文件内容设置一些标志,同时进行错误检查和文件关闭操作,最终返回是否成功读取并解析该文件的结果。
在这里插入图片描述

  1. 初始化输出参数
    *checkPointLoc设置为InvalidXLogRecPtr,表示初始时检查点位置
    *backupLabelTLI设置为0,表示初始时时间线ID
    *backupEndRequired*backupFromStandby都设置为false,分别表示初始时不需要处理到WAL的末尾,且备份不是来自备用服务器

static bool
read_backup_label(XLogRecPtr *checkPointLoc, TimeLineID *backupLabelTLI,
				  bool *backupEndRequired, bool *backupFromStandby)
{   
	char		startxlogfilename[MAXFNAMELEN];
	TimeLineID	tli_from_walseg,
				tli_from_file;
	FILE	   *lfp;
	char		ch;
	char		backuptype[20];
	char		backupfrom[20];
	char		backuplabel[MAXPGPATH];
	char		backuptime[128];
	uint32		hi,
				lo;

	/* suppress possible uninitialized-variable warnings */
	*checkPointLoc = InvalidXLogRecPtr; //给输出参数赋初始值来避免潜在的未初始化变量警告
	*backupLabelTLI = 0;
	*backupEndRequired = false;
	*backupFromStandby = false;
  1. 尝试打开BACKUP_LABEL_FILE文件
    使用AllocateFile函数以读模式打开BACKUP_LABEL_FILE文件,并将文件指针存储在lfp
    如果文件不存在(errno == ENOENT),则函数返回false,表示没有找到文件但这不是一个错误
    如果文件存在但无法打开(errno != ENOENT),则记录一个致命错误并退出
lfp = AllocateFile(BACKUP_LABEL_FILE, "r"); //尝试以读模式打开文件 
	if (!lfp)
	{
		if (errno != ENOENT)
		// 如果错误码不是ENOENT(即文件不存在),则记录一个致命错误  
        // ereport是一个可能用于数据库系统(如PostgreSQL)的错误报告函数  
        // 它记录一个错误,可能包括错误码和错误消息  
        // errcode_for_file_access()可能是一个返回与文件访问相关错误码的函数  
        // errmsg是一个格式化字符串,用于生成错误消息,%m会被替换为系统错误消息  
        // %s会被替换为BACKUP_LABEL_FILE的值
			ereport(FATAL,
					(errcode_for_file_access(),
					 errmsg("could not read file \"%s\": %m",
							BACKUP_LABEL_FILE)));
	// 如果文件不存在(errno == ENOENT),或者我们已经处理了其他错误,则函数返回false  
    // 表示没有找到文件,但这在某些上下文中是可以接受的 		
		return false;			/* it's not there, all is fine */
	}
  1. 读取并解析文件内容
    读取并解析START WAL LOCATION行,以获取WAL的起始位置和对应的时间线ID(tli_from_walseg
    使用解析出的高32位(hi)和低32位(lo)构造64位的RedoStartLSN,并设置RedoStartTLI
    读取并解析CHECKPOINT LOCATION行,以获取检查点的位置,并更新*checkPointLoc和*backupLabelTLI
    尝试读取BACKUP METHOD行,如果备份方法是streamed,则将*backupEndRequired设置为true
    尝试读取BACKUP FROM行,如果备份来自standby,则将*backupFromStandby设置为true
    尝试读取START TIMELABEL行,并将它们作为调试信息记录
    尝试读取START TIMELINE行,并与通过WAL段获得的时间线ID进行比较,如果不匹配则记录致命错误;如果匹配,则记录调试信息
//格式应为:"START WAL LOCATION: %X/%X (file %08X%16s)%c",其中%X表示十六进制  整数,%s表示字符串,%c表示字符 
	if (fscanf(lfp, "START WAL LOCATION: %X/%X (file %08X%16s)%c",
			   &hi, &lo, &tli_from_walseg, startxlogfilename, &ch) != 5 || ch != '\n')
			   // 如果fscanf没有返回5(表示没有成功读取5个项),或者读取的最后一个字符不是换行符'\n'  
               // 则记录一个致命错误,错误码为ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE,错误消息包含文件名 
		ereport(FATAL,
				(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
				 errmsg("invalid data in file \"%s\"", BACKUP_LABEL_FILE)));
	// 使用读取的hi和lo构造64位的RedoStartLSN
	RedoStartLSN = ((uint64) hi) << 32 | lo;
	RedoStartTLI = tli_from_walseg;
	// 尝试从文件中读取检查点位置的信息  
    // 格式应为:"CHECKPOINT LOCATION: %X/%X%c",其中%X表示十六进制整数,%c表示字符 
	if (fscanf(lfp, "CHECKPOINT LOCATION: %X/%X%c",
			   &hi, &lo, &ch) != 3 || ch != '\n')
		ereport(FATAL,
				(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
				 errmsg("invalid data in file \"%s\"", BACKUP_LABEL_FILE)));
	*checkPointLoc = ((uint64) hi) << 32 | lo;
	*backupLabelTLI = tli_from_walseg;
    if (fscanf(lfp, "BACKUP METHOD: %19s\n", backuptype) == 1)
	{
		/*  
        * 尝试从文件中读取 "BACKUP METHOD:" 后面的字符串,并存储在 backuptype 中。  
        * 如果成功读取了一个字符串,则继续比较。  
        */   
		if (strcmp(backuptype, "streamed") == 0)
		        /*  
         * 如果备份类型是 "streamed",则设置 backupEndRequired 为 true,  
         * 表示需要处理到WAL的末尾。  
         */ 
			*backupEndRequired = true;
	}

	/*
	 * BACKUP FROM lets us know if this was from a primary or a standby.  If
	 * it was from a standby, we'll double-check that the control file state
	 * matches that of a standby.
	 */
	if (fscanf(lfp, "BACKUP FROM: %19s\n", backupfrom) == 1)
	{
		if (strcmp(backupfrom, "standby") == 0)
		 /*  
         * 如果备份来源是 "standby",则设置 backupFromStandby 为 true,  
         * 表示备份来自备用服务器。  
         */ 
			*backupFromStandby = true;
	}
    if (fscanf(lfp, "START TIME: %127[^\n]\n", backuptime) == 1)
		ereport(DEBUG1,
				(errmsg_internal("backup time %s in file \"%s\"",
								 backuptime, BACKUP_LABEL_FILE)));

	if (fscanf(lfp, "LABEL: %1023[^\n]\n", backuplabel) == 1)
		ereport(DEBUG1,
				(errmsg_internal("backup label %s in file \"%s\"",
								 backuplabel, BACKUP_LABEL_FILE)));

	/*
	 * START TIMELINE is new as of 11. Its parsing is not mandatory, still use
	 * it as a sanity check if present.
	 */
	// 尝试从文件中读取START TIMELINE字段,并将其存储在tli_from_file变量中
	if (fscanf(lfp, "START TIMELINE: %u\n", &tli_from_file) == 1)
	{
		// 如果从文件中解析的时间线ID与通过WAL段获得的时间线ID不匹配 
		if (tli_from_walseg != tli_from_file)
			ereport(FATAL,
					(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
					 errmsg("invalid data in file \"%s\"", BACKUP_LABEL_FILE),
					 errdetail("Timeline ID parsed is %u, but expected %u.",
							   tli_from_file, tli_from_walseg)));
        // 如果时间线ID匹配,使用DEBUG1级别记录日志
		ereport(DEBUG1,
				(errmsg_internal("backup timeline %u in file \"%s\"",
								 tli_from_file, BACKUP_LABEL_FILE)));
	}
  1. 错误检查和文件关闭
    检查文件操作是否遇到错误(使用ferror(lfp)
    尝试关闭文件(使用FreeFile(lfp)),并检查关闭操作是否遇到错误
    如果在文件操作或关闭过程中遇到错误,则记录致命错误并退出
// 检查文件操作是否有错误,或者关闭文件时是否有错误 
	if (ferror(lfp) || FreeFile(lfp))
		ereport(FATAL,
				(errcode_for_file_access(),
				 errmsg("could not read file \"%s\": %m",
						BACKUP_LABEL_FILE)));
  1. 返回成功
    如果所有步骤都成功完成,则函数返回true,表示成功读取并解析了BACKUP_LABEL_FILE文件
	return true;
}

函数调用栈

在这里插入图片描述

作者介绍

周雨慧 中移(苏州)软件技术有限公司 数据库内核开发工程师

成功完成,则函数返回true,表示成功读取并解析了BACKUP_LABEL_FILE文件

	return true;
}

函数调用栈

[外链图片转存中…(img-14UHJ2iu-1731658229710)]

作者介绍

周雨慧 中移(苏州)软件技术有限公司 数据库内核开发工程师

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值