Skip to content

Commit 577e876

Browse files
committed
[Issue #169] Refactoring, added support for external directories and bugfixes
1 parent 7f983c5 commit 577e876

File tree

6 files changed

+425
-338
lines changed

6 files changed

+425
-338
lines changed

src/catalog.c

+5
Original file line numberDiff line numberDiff line change
@@ -444,6 +444,9 @@ catalog_get_backup_list(const char *instance_name, time_t requested_backup_id)
444444
base36enc(backup->start_time), backup_conf_path);
445445
}
446446

447+
backup->root_dir = pgut_strdup(data_path);
448+
449+
/* TODO: save encoded backup id */
447450
backup->backup_id = backup->start_time;
448451
if (requested_backup_id != INVALID_BACKUP_ID
449452
&& requested_backup_id != backup->start_time)
@@ -2005,6 +2008,7 @@ pgBackupInit(pgBackup *backup)
20052008
backup->program_version[0] = '\0';
20062009
backup->server_version[0] = '\0';
20072010
backup->external_dir_str = NULL;
2011+
backup->root_dir = NULL;
20082012
}
20092013

20102014
/* free pgBackup object */
@@ -2015,6 +2019,7 @@ pgBackupFree(void *backup)
20152019

20162020
pfree(b->primary_conninfo);
20172021
pfree(b->external_dir_str);
2022+
pfree(b->root_dir);
20182023
pfree(backup);
20192024
}
20202025

src/data.c

+205-12
Original file line numberDiff line numberDiff line change
@@ -962,23 +962,88 @@ restore_data_file(const char *to_path, pgFile *file, bool allow_truncate,
962962
}
963963

964964
/*
965-
* Restore files in the from_root directory to the to_root directory with
966-
* same relative path.
967-
*
968-
* If write_header is true then we add header to each restored block, currently
969-
* it is used for MERGE command.
970-
*
971-
* to_fullpath and from_fullpath are provided strictly for ERROR reporting
965+
* Iterate over parent backup chain and lookup given destination file in
966+
* filelist of every chain member starting with FULL backup.
967+
* Apply changed blocks to destination file from every backup in parent chain.
972968
*/
973969
void
974-
restore_data_file_new(FILE *in, FILE *out, pgFile *file, uint32 backup_version,
970+
restore_data_file_new(parray *parent_chain, pgFile *dest_file, FILE *out, const char *to_fullpath)
971+
{
972+
int i;
973+
974+
for (i = parray_num(parent_chain) - 1; i >= 0; i--)
975+
{
976+
char from_root[MAXPGPATH];
977+
char from_fullpath[MAXPGPATH];
978+
FILE *in = NULL;
979+
980+
pgFile **res_file = NULL;
981+
pgFile *tmp_file = NULL;
982+
983+
pgBackup *backup = (pgBackup *) parray_get(parent_chain, i);
984+
985+
/* check for interrupt */
986+
if (interrupted || thread_interrupted)
987+
elog(ERROR, "Interrupted during restore");
988+
989+
/* lookup file in intermediate backup */
990+
res_file = parray_bsearch(backup->files, dest_file, pgFileCompareRelPathWithExternal);
991+
tmp_file = (res_file) ? *res_file : NULL;
992+
993+
/* Destination file is not exists yet at this moment */
994+
if (tmp_file == NULL)
995+
continue;
996+
997+
/*
998+
* Skip file if it haven't changed since previous backup
999+
* and thus was not backed up.
1000+
*/
1001+
if (tmp_file->write_size == BYTES_INVALID)
1002+
{
1003+
// elog(VERBOSE, "The file didn`t change. Skip restore: \"%s\"", tmp_file->rel_path);
1004+
continue;
1005+
}
1006+
1007+
/*
1008+
* At this point we are sure, that something is going to be copied
1009+
* Open source file.
1010+
*/
1011+
join_path_components(from_root, backup->root_dir, DATABASE_DIR);
1012+
join_path_components(from_fullpath, from_root, tmp_file->rel_path);
1013+
1014+
in = fopen(from_fullpath, PG_BINARY_R);
1015+
if (in == NULL)
1016+
{
1017+
elog(INFO, "Cannot open backup file \"%s\": %s", from_fullpath,
1018+
strerror(errno));
1019+
Assert(0);
1020+
}
1021+
1022+
/*
1023+
* restore the file.
1024+
* Datafiles are backed up block by block and every block
1025+
* have BackupPageHeader with meta information, so we cannot just
1026+
* copy the file from backup.
1027+
*/
1028+
restore_data_file_internal(in, out, tmp_file,
1029+
parse_program_version(backup->program_version),
1030+
from_fullpath, to_fullpath, dest_file->n_blocks);
1031+
1032+
if (fio_fclose(in) != 0)
1033+
elog(ERROR, "Cannot close file \"%s\": %s", from_fullpath,
1034+
strerror(errno));
1035+
}
1036+
}
1037+
1038+
void
1039+
restore_data_file_internal(FILE *in, FILE *out, pgFile *file, uint32 backup_version,
9751040
const char *from_fullpath, const char *to_fullpath, int nblocks)
9761041
{
9771042
BackupPageHeader header;
9781043
BlockNumber blknum = 0;
9791044
size_t write_len = 0;
9801045

981-
while (true)
1046+
for (;;)
9821047
{
9831048
off_t write_pos;
9841049
size_t read_len;
@@ -1016,9 +1081,44 @@ restore_data_file_new(FILE *in, FILE *out, pgFile *file, uint32 backup_version,
10161081

10171082
blknum = header.block;
10181083

1084+
/*
1085+
* Backupward compatibility kludge: in the good old days
1086+
* n_blocks attribute was available only in DELTA backups.
1087+
* File truncate in PAGE and PTRACK happened on the fly when
1088+
* special value PageIsTruncated is encountered.
1089+
* It is inefficient.
1090+
*
1091+
* Nowadays every backup type has n_blocks, so instead
1092+
* writing and then truncating redundant data, writing
1093+
* is not happening in the first place.
1094+
* TODO: remove in 3.0.0
1095+
*/
1096+
if (header.compressed_size == PageIsTruncated)
1097+
{
1098+
/*
1099+
* Block header contains information that this block was truncated.
1100+
* We need to truncate file to this length.
1101+
*/
1102+
1103+
elog(VERBOSE, "Truncate file \"%s\" to block %u", to_fullpath, header.block);
1104+
1105+
/* To correctly truncate file, we must first flush STDIO buffers */
1106+
if (fio_fflush(out) != 0)
1107+
elog(ERROR, "Cannot flush file \"%s\": %s", to_fullpath, strerror(errno));
1108+
1109+
/* Set position to the start of file */
1110+
if (fio_fseek(out, 0) < 0)
1111+
elog(ERROR, "Cannot seek to the start of file \"%s\": %s", to_fullpath, strerror(errno));
1112+
1113+
if (fio_ftruncate(out, header.block * BLCKSZ) != 0)
1114+
elog(ERROR, "Cannot truncate file \"%s\": %s", to_fullpath, strerror(errno));
1115+
1116+
break;
1117+
}
1118+
10191119
/* no point in writing redundant data */
10201120
if (nblocks > 0 && blknum >= nblocks)
1021-
return;
1121+
break;
10221122

10231123
if (header.compressed_size > BLCKSZ)
10241124
elog(ERROR, "Size of a blknum %i exceed BLCKSZ", blknum);
@@ -1061,7 +1161,6 @@ restore_data_file_new(FILE *in, FILE *out, pgFile *file, uint32 backup_version,
10611161

10621162
/*
10631163
* Seek and write the restored page.
1064-
* TODO: invent fio_pwrite().
10651164
*/
10661165
if (fio_fseek(out, write_pos) < 0)
10671166
elog(ERROR, "Cannot seek block %u of \"%s\": %s",
@@ -1096,7 +1195,7 @@ restore_data_file_new(FILE *in, FILE *out, pgFile *file, uint32 backup_version,
10961195
* it is either small control file or already compressed cfs file.
10971196
*/
10981197
void
1099-
restore_non_data_file(FILE *in, FILE *out, pgFile *file,
1198+
restore_non_data_file_internal(FILE *in, FILE *out, pgFile *file,
11001199
const char *from_fullpath, const char *to_fullpath)
11011200
{
11021201
size_t read_len = 0;
@@ -1148,6 +1247,100 @@ restore_non_data_file(FILE *in, FILE *out, pgFile *file,
11481247
elog(VERBOSE, "Copied file \"%s\": %lu bytes", from_fullpath, file->write_size);
11491248
}
11501249

1250+
void
1251+
restore_non_data_file(parray *parent_chain, pgBackup *dest_backup,
1252+
pgFile *dest_file, FILE *out, const char *to_fullpath)
1253+
{
1254+
int i;
1255+
char from_root[MAXPGPATH];
1256+
char from_fullpath[MAXPGPATH];
1257+
FILE *in = NULL;
1258+
1259+
pgFile *tmp_file = NULL;
1260+
pgBackup *tmp_backup = NULL;
1261+
1262+
/* Check if full copy of destination file is available in destination backup */
1263+
if (dest_file->write_size > 0)
1264+
{
1265+
tmp_file = dest_file;
1266+
tmp_backup = dest_backup;
1267+
}
1268+
else
1269+
{
1270+
/*
1271+
* Iterate over parent chain starting from direct parent of destination
1272+
* backup to oldest backup in chain, and look for the first
1273+
* full copy of destination file.
1274+
* Full copy is latest possible destination file with size equal or
1275+
* greater than zero.
1276+
*/
1277+
for (i = 1; i < parray_num(parent_chain); i++)
1278+
{
1279+
pgFile **res_file = NULL;
1280+
1281+
tmp_backup = (pgBackup *) parray_get(parent_chain, i);
1282+
1283+
/* lookup file in intermediate backup */
1284+
res_file = parray_bsearch(tmp_backup->files, dest_file, pgFileCompareRelPathWithExternal);
1285+
tmp_file = (res_file) ? *res_file : NULL;
1286+
1287+
/*
1288+
* It should not be possible not to find destination file in intermediate
1289+
* backup, without encountering full copy first.
1290+
*/
1291+
if (!tmp_file)
1292+
{
1293+
elog(ERROR, "Failed to locate non-data file \"%s\" in backup %s",
1294+
dest_file->rel_path, base36enc(tmp_backup->start_time));
1295+
continue;
1296+
}
1297+
1298+
/* Full copy is found and it is null sized, nothing to do here */
1299+
if (tmp_file->write_size == 0)
1300+
return;
1301+
1302+
/* Full copy is found */
1303+
if (tmp_file->write_size > 0)
1304+
break;
1305+
}
1306+
}
1307+
1308+
/* sanity */
1309+
if (!tmp_backup)
1310+
elog(ERROR, "Failed to found a backup containing full copy of non-data file \"%s\"",
1311+
to_fullpath);
1312+
1313+
if (!tmp_file)
1314+
elog(ERROR, "Failed to locate a full copy of non-data file \"%s\"", to_fullpath);
1315+
1316+
if (tmp_file->external_dir_num == 0)
1317+
// pgBackupGetPath(tmp_backup, from_root, lengthof(from_root), DATABASE_DIR);
1318+
join_path_components(from_root, tmp_backup->root_dir, DATABASE_DIR);
1319+
else
1320+
{
1321+
// get external prefix for tmp_backup
1322+
char external_prefix[MAXPGPATH];
1323+
1324+
join_path_components(external_prefix, tmp_backup->root_dir, EXTERNAL_DIR);
1325+
makeExternalDirPathByNum(from_root, external_prefix, tmp_file->external_dir_num);
1326+
}
1327+
1328+
join_path_components(from_fullpath, from_root, dest_file->rel_path);
1329+
1330+
in = fopen(from_fullpath, PG_BINARY_R);
1331+
if (in == NULL)
1332+
{
1333+
elog(ERROR, "Cannot open backup file \"%s\": %s", from_fullpath,
1334+
strerror(errno));
1335+
}
1336+
1337+
restore_non_data_file_internal(in, out, tmp_file, from_fullpath, to_fullpath);
1338+
1339+
if (fio_fclose(in) != 0)
1340+
elog(ERROR, "Cannot close file \"%s\": %s", from_fullpath,
1341+
strerror(errno));
1342+
}
1343+
11511344
/*
11521345
* Copy file to backup.
11531346
* We do not apply compression to these files, because

src/dir.c

+1-2
Original file line numberDiff line numberDiff line change
@@ -1643,8 +1643,7 @@ free_dir_list(parray *list)
16431643

16441644
/* Append to string "path_prefix" int "dir_num" */
16451645
void
1646-
makeExternalDirPathByNum(char *ret_path, const char *path_prefix,
1647-
const int dir_num)
1646+
makeExternalDirPathByNum(char *ret_path, const char *path_prefix, const int dir_num)
16481647
{
16491648
sprintf(ret_path, "%s%d", path_prefix, dir_num);
16501649
}

src/pg_probackup.h

+9-3
Original file line numberDiff line numberDiff line change
@@ -374,6 +374,8 @@ struct pgBackup
374374
* separated by ':' */
375375
parray *files; /* list of files belonging to this backup
376376
* must be populated by calling backup_populate() */
377+
char *root_dir; /* Full path for root backup directory:
378+
backup_path/instance_name/backup_id */
377379
};
378380

379381
/* Recovery target for restore and validate subcommands */
@@ -837,10 +839,14 @@ extern void restore_data_file(const char *to_path,
837839
pgFile *file, bool allow_truncate,
838840
bool write_header,
839841
uint32 backup_version);
840-
extern void restore_data_file_new(FILE *in, FILE *out, pgFile *file, uint32 backup_version,
842+
extern void restore_data_file_new(parray *parent_chain, pgFile *dest_file,
843+
FILE *out, const char *to_fullpath);
844+
extern void restore_data_file_internal(FILE *in, FILE *out, pgFile *file, uint32 backup_version,
841845
const char *from_fullpath, const char *to_fullpath, int nblocks);
842-
extern void restore_non_data_file(FILE *in, FILE *out, pgFile *file,
843-
const char *from_fullpath, const char *to_fullpath);
846+
extern void restore_non_data_file(parray *parent_chain, pgBackup *dest_backup,
847+
pgFile *dest_file, FILE *out, const char *to_fullpath);
848+
extern void restore_non_data_file_internal(FILE *in, FILE *out, pgFile *file,
849+
const char *from_fullpath, const char *to_fullpath);
844850
extern bool copy_file(fio_location from_location, const char *to_root,
845851
fio_location to_location, pgFile *file, bool missing_ok);
846852
extern bool create_empty_file(fio_location from_location, const char *to_root,

0 commit comments

Comments
 (0)