openGauss增量备份如何找到父备份

mermaid-diagram-20220610093905.png

获取备份的列表

  1. 找到实例名的路径,backup_instance_path: /home/pjr/backup/backups/db_backup
// 拼出路径
nRet = snprintf_s(backup_instance_path,MAXPGPATH,MAXPGPATH - 1, "%s/%s/%s",
                        backup_path, BACKUPS_DIR, instance_name);
...
// 打开目录
data_dir = fio_opendir(backup_instance_path, FIO_BACKUP_HOST);
  1. 遍历目录中的每个文件,不是文件夹的就跳过。
for (; (data_ent = fio_readdir(data_dir)) != NULL; errno = 0)
{
    char    backup_conf_path[MAXPGPATH];
    char    data_path[MAXPGPATH];
    pgBackup   *backup = NULL;

    /* skip not-directory entries and hidden entries */
    if (!IsDir(backup_instance_path, data_ent->d_name, FIO_BACKUP_HOST)
        || data_ent->d_name[0] == '.')
        continue;
...
}
  1. 找到单次备份的目录,data_path: /home/pjr/backup/backups/db_backup/RD1WFX
/* open subdirectory of specific backup */
join_path_components(data_path, backup_instance_path, data_ent->d_name);
  1. 找到目录下的backup.control文件,backup_conf_path:/home/pjr/backup/backups/db_backup/RD1WFX/backup.control
nRet = snprintf_s(backup_conf_path, MAXPGPATH, MAXPGPATH - 1,"%s/%s", data_path, BACKUP_CONTROL_FILE);
  1. 将backup.control文件的信息写入结构体中
backup = readBackupControlFile(backup_conf_path);

static pgBackup *
readBackupControlFile(const char *path)
{
    ...
    ConfigOption options[] =
    {
        {'s', 0, "backup-mode",         &backup_mode, SOURCE_FILE_STRICT},
        {'u', 0, "timelineid",          &backup->tli, SOURCE_FILE_STRICT},
        {'s', 0, "start-lsn",           &start_lsn, SOURCE_FILE_STRICT},
        {'s', 0, "stop-lsn",            &stop_lsn, SOURCE_FILE_STRICT},
        {'t', 0, "start-time",          &backup->start_time, SOURCE_FILE_STRICT},
        {'t', 0, "merge-time",          &backup->merge_time, SOURCE_FILE_STRICT},
        {'t', 0, "end-time",            &backup->end_time, SOURCE_FILE_STRICT},
        {'U', 0, "recovery-xid",        &backup->recovery_xid, SOURCE_FILE_STRICT},
        {'t', 0, "recovery-time",       &backup->recovery_time, SOURCE_FILE_STRICT},
        {'t', 0, "expire-time",         &backup->expire_time, SOURCE_FILE_STRICT},
        {'I', 0, "data-bytes",          &backup->data_bytes, SOURCE_FILE_STRICT},
        {'I', 0, "wal-bytes",           &backup->wal_bytes, SOURCE_FILE_STRICT},
        {'I', 0, "uncompressed-bytes",  &backup->uncompressed_bytes, SOURCE_FILE_STRICT},
        {'I', 0, "pgdata-bytes",        &backup->pgdata_bytes, SOURCE_FILE_STRICT},
        {'u', 0, "block-size",          &backup->block_size, SOURCE_FILE_STRICT},
        {'u', 0, "xlog-block-size",     &backup->wal_block_size, SOURCE_FILE_STRICT},
        {'u', 0, "checksum-version",    &backup->checksum_version, SOURCE_FILE_STRICT},
        {'s', 0, "program-version",     &program_version, SOURCE_FILE_STRICT},
        {'s', 0, "server-version",      &server_version, SOURCE_FILE_STRICT},
        {'b', 0, "stream",              &backup->stream, SOURCE_FILE_STRICT},
        {'s', 0, "status",              &status, SOURCE_FILE_STRICT},
        {'s', 0, "parent-backup-id",    &parent_backup, SOURCE_FILE_STRICT},
        {'s', 0, "merge-dest-id",       &merge_dest_backup, SOURCE_FILE_STRICT},
        {'s', 0, "compress-alg",        &compress_alg, SOURCE_FILE_STRICT},
        {'u', 0, "compress-level",      &backup->compress_level, SOURCE_FILE_STRICT},
        {'b', 0, "from-replica",        &backup->from_replica, SOURCE_FILE_STRICT},
        {'s', 0, "external-dirs",       &backup->external_dir_str, SOURCE_FILE_STRICT},
        {'s', 0, "note",                &backup->note, SOURCE_FILE_STRICT},
        {'s', 0, "recovery-name",       &recovery_name, SOURCE_FILE_STRICT},
        {'u', 0, "content-crc",         &backup->content_crc, SOURCE_FILE_STRICT},
        {0}
    };
....
}
  1. 如果控制文件读出来的结构体为空,则自己构造一个backup结构,start_time就用目录解码出来的时间。找到的话,就再对名字和他自己start_time进行解码的时间进行一次校验。
if (!backup)
{
    backup = pgut_new(pgBackup);
    pgBackupInit(backup);
    backup->start_time = base36dec(data_ent->d_name);
}
else if (strcmp(base36enc(backup->start_time), data_ent->d_name) != 0)
{
    elog(WARNING, "backup ID in control file \"%s\" doesn't match name of the backup folder \"%s\"",
        base36enc(backup->start_time), backup_conf_path);
}
  1. 继续构造backup结构体的属性
    a. backup->root_dir: /home/pjr/backup/backups/db_backup/RD1WFX
    b. backup->database_dir: /home/pjr/backup/backups/db_backup/RD1WFX/database
backup->root_dir = pgut_strdup(data_path);

backup->database_dir = (char *)pgut_malloc(MAXPGPATH);
join_path_components(backup->database_dir, backup->root_dir, DATABASE_DIR);

/* Initialize page header map */
init_header_map(backup);

/* TODO: save encoded backup id */
backup->backup_id = backup->start_time;
  1. 添加backup结构到列表
parray_append(backups, backup);
  1. 把列表按照start_time进行降序排序
parray_qsort(backups, pgBackupCompareIdDesc);
  1. 遍历所有backup结构,根据自己指向的用二分查找找到自己的祖先,也是这个列表中的一个backup结构,然后将该backup结构中的parent_backup_link指针指向自己父备份的backup结构。最后返回backups列表。
/* Link incremental backups with their ancestors.*/
for (i = 0; i < (int)parray_num(backups); i++)
{
    pgBackup   *curr = (pgBackup  *)parray_get(backups, i);
    pgBackup  **ancestor;
    pgBackup    key;

    if (curr->backup_mode == BACKUP_MODE_FULL)
        continue;

    key.start_time = curr->parent_backup;
    ancestor = (pgBackup **) parray_bsearch(backups, &key,
                pgBackupCompareIdDesc);
    if (ancestor)
        curr->parent_backup_link = *ancestor;
}

return backups;

在相同timeline里找到父备份

  1. 首先找父全量备份。遍历已经按start_time降序排序的backups列表。如果状态是OK或者DONE,backup_mode是全量备份,以及timeline相等,那么就算找到了。找不到全量备份就直接返回。
/* backup_list is sorted in order of descending ID */
for (i = 0; i < (int)parray_num(backup_list); i++)
{
    pgBackup *backup = (pgBackup *) parray_get(backup_list, i);

    if ((backup->backup_mode == BACKUP_MODE_FULL &&
        (backup->status == BACKUP_STATUS_OK ||
            backup->status == BACKUP_STATUS_DONE)) && backup->tli == tli)
    {
        full_backup = backup;
        break;
    }
}

/* Failed to find valid FULL backup to fulfill ancestor role */
if (!full_backup)
    return NULL;
  1. 找到了全量备份,就找它的最新的一个孩子。同样遍历backups列表,取一个backup,判断继承链的状态。继承链有三种状态。
#define ChainIsBroken 0
#define ChainIsInvalid 1
#define ChainIsOk 2
  1. 因为backup结构中已经使用parent_backup_link指向了自己的父备份。所以可以用以下的循环,不断的用这个指针找到自己的父备份,直到找到一个全量备份,或者找不到。这两种情况的parent_backup_link指针都为NULL。
while (target_backup->parent_backup_link)
{
    if (target_backup->status != BACKUP_STATUS_OK &&
        target_backup->status != BACKUP_STATUS_DONE)
    /* oldest invalid backup in parent chain */
    invalid_backup = target_backup;


    target_backup = target_backup->parent_backup_link;
}
  1. 如果最终找到的父备份不是全量备份,那么认为这个继承链断裂,返回ChainIsBroken。
    如果是全量备份,但是状态不OK或者DONE那么,这个备份认为是无效的,返回ChainIsInvalid。
    否则,继承链OK,返回ChainIsOk,以及找到这个全量父备份。
/* Previous loop will skip FULL backup because his parent_backup_link is NULL */
if (target_backup->backup_mode == BACKUP_MODE_FULL &&
    (target_backup->status != BACKUP_STATUS_OK &&
    target_backup->status != BACKUP_STATUS_DONE))
{
    invalid_backup = target_backup;
}

/* found chain end and oldest backup is not FULL */
if (target_backup->backup_mode != BACKUP_MODE_FULL)
{
    /* Set oldest child backup in chain */
    *result_backup = target_backup;
    return ChainIsBroken;
}

/* chain is ok, but some backups are invalid */
if (invalid_backup)
{
    *result_backup = invalid_backup;
    return ChainIsInvalid;
}

*result_backup = target_backup;
return ChainIsOk;
  1. 对无效和断裂的继承链,继续遍历backups列表中的下一个backup。对于继承链OK的backup,因为已经按照时间排过顺序了,那么这个继承就是某个全量备份的最新的孩子。这时候再最终确认一次,开始找到的全量备份和这个最新的备份之间是不是继承关系。确认方法还是用parent_backup_link指针不断的指向自己的父备份,找到最终全量备份,看这个全量备份跟我们之前找到的备份是不是同一个。确认无误后,返回全量备份的最新的孩子(也有可能是全量备份自己)。
  • 这里我感觉有点多次一举,上一步已经找到了一个result_backup,直接比较这个和开始的那个全量备份start_time就行。
/* Yes, we could call is_parent() earlier - after choosing the ancestor,
* but this way we have an opportunity to detect and report all possible
* anomalies.
*/
if (is_parent(full_backup->start_time, backup, true))
    return backup;

bool is_parent(time_t parent_backup_time, pgBackup *child_backup, bool inclusive)
{
    if (!child_backup)
        elog(ERROR, "Target backup cannot be NULL");

    if (inclusive && child_backup->start_time == parent_backup_time)
        return true;

    while (child_backup->parent_backup_link &&
        child_backup->parent_backup != parent_backup_time)
    {
        child_backup = child_backup->parent_backup_link;
    }

    if (child_backup->parent_backup == parent_backup_time)
        return true;

    return false;
}

在不同timeline里找父备份找到父备份(暂不支持)

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,013评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,205评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,370评论 0 342
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,168评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,153评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,954评论 1 283
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,271评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,916评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,382评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,877评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,989评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,624评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,209评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,199评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,418评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,401评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,700评论 2 345

推荐阅读更多精彩内容