
mermaid-diagram-20220610093905.png
獲取備份的列表
- 找到實例名的路徑,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);
- 遍歷目錄中的每個文件,不是文件夾的就跳過。
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;
...
}
- 找到單次備份的目錄,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);
- 找到目錄下的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);
- 將backup.control文件的信息寫入結(jié)構(gòu)體中
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}
};
....
}
- 如果控制文件讀出來的結(jié)構(gòu)體為空,則自己構(gòu)造一個backup結(jié)構(gòu),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);
}
- 繼續(xù)構(gòu)造backup結(jié)構(gòu)體的屬性
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;
- 添加backup結(jié)構(gòu)到列表
parray_append(backups, backup);
- 把列表按照start_time進行降序排序
parray_qsort(backups, pgBackupCompareIdDesc);
- 遍歷所有backup結(jié)構(gòu),根據(jù)自己指向的用二分查找找到自己的祖先,也是這個列表中的一個backup結(jié)構(gòu),然后將該backup結(jié)構(gòu)中的parent_backup_link指針指向自己父備份的backup結(jié)構(gòu)。最后返回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里找到父備份
- 首先找父全量備份。遍歷已經(jīng)按start_time降序排序的backups列表。如果狀態(tài)是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;
- 找到了全量備份,就找它的最新的一個孩子。同樣遍歷backups列表,取一個backup,判斷繼承鏈的狀態(tài)。繼承鏈有三種狀態(tài)。
#define ChainIsBroken 0
#define ChainIsInvalid 1
#define ChainIsOk 2
- 因為backup結(jié)構(gòu)中已經(jīng)使用parent_backup_link指向了自己的父備份。所以可以用以下的循環(huán),不斷的用這個指針找到自己的父備份,直到找到一個全量備份,或者找不到。這兩種情況的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;
}
- 如果最終找到的父備份不是全量備份,那么認為這個繼承鏈斷裂,返回ChainIsBroken。
如果是全量備份,但是狀態(tài)不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;
- 對無效和斷裂的繼承鏈,繼續(xù)遍歷backups列表中的下一個backup。對于繼承鏈OK的backup,因為已經(jīng)按照時間排過順序了,那么這個繼承就是某個全量備份的最新的孩子。這時候再最終確認一次,開始找到的全量備份和這個最新的備份之間是不是繼承關(guān)系。確認方法還是用parent_backup_link指針不斷的指向自己的父備份,找到最終全量備份,看這個全量備份跟我們之前找到的備份是不是同一個。確認無誤后,返回全量備份的最新的孩子(也有可能是全量備份自己)。
- 這里我感覺有點多次一舉,上一步已經(jīng)找到了一個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里找父備份找到父備份(暫不支持)