Skip to content

Commit b696669

Browse files
committed
[Issue #66] Incremental restore, initial commit
1 parent f2d2bab commit b696669

File tree

8 files changed

+362
-27
lines changed

8 files changed

+362
-27
lines changed

src/data.c

Lines changed: 122 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -862,7 +862,7 @@ backup_non_data_file(pgFile *file, pgFile *prev_file,
862862
*/
863863
size_t
864864
restore_data_file(parray *parent_chain, pgFile *dest_file, FILE *out,
865-
const char *to_fullpath, bool use_bitmap)
865+
const char *to_fullpath, bool use_bitmap, uint16 *checksum_map)
866866
{
867867
size_t total_write_len = 0;
868868
char *in_buf = pgut_malloc(STDIO_BUFSIZE);
@@ -941,7 +941,8 @@ restore_data_file(parray *parent_chain, pgFile *dest_file, FILE *out,
941941
total_write_len += restore_data_file_internal(in, out, tmp_file,
942942
parse_program_version(backup->program_version),
943943
from_fullpath, to_fullpath, dest_file->n_blocks,
944-
use_bitmap ? &(dest_file)->pagemap : NULL);
944+
use_bitmap ? &(dest_file)->pagemap : NULL,
945+
checksum_map, backup->checksum_version);
945946

946947
if (fclose(in) != 0)
947948
elog(ERROR, "Cannot close file \"%s\": %s", from_fullpath,
@@ -962,7 +963,7 @@ restore_data_file(parray *parent_chain, pgFile *dest_file, FILE *out,
962963
size_t
963964
restore_data_file_internal(FILE *in, FILE *out, pgFile *file, uint32 backup_version,
964965
const char *from_fullpath, const char *to_fullpath, int nblocks,
965-
datapagemap_t *map)
966+
datapagemap_t *map, uint16 *checksum_map, int checksum_version)
966967
{
967968
BackupPageHeader header;
968969
BlockNumber blknum = 0;
@@ -1100,6 +1101,43 @@ restore_data_file_internal(FILE *in, FILE *out, pgFile *file, uint32 backup_vers
11001101
is_compressed = true;
11011102
}
11021103

1104+
/* Incremental restore */
1105+
if (checksum_map && checksum_map[blknum] != 0)
1106+
{
1107+
uint16 page_crc = 0;
1108+
1109+
if (is_compressed)
1110+
{
1111+
char uncompressed_buf[BLCKSZ];
1112+
fio_decompress(uncompressed_buf, page.data, compressed_size, file->compress_alg);
1113+
1114+
/* If checksumms are enabled, then we can trust checksumm in header */
1115+
if (checksum_version)
1116+
page_crc = ((PageHeader) uncompressed_buf)->pd_checksum;
1117+
else
1118+
page_crc = pg_checksum_page(uncompressed_buf, file->segno * RELSEG_SIZE + blknum);
1119+
1120+
// page_crc_1 = pg_checksum_page(uncompressed_buf, file->segno * RELSEG_SIZE + blknum);
1121+
// Assert(page_crc == page_crc_1);
1122+
}
1123+
else
1124+
{
1125+
/* if checksumms are enabled, then we can trust checksumm in header */
1126+
if (checksum_version)
1127+
page_crc = ((PageHeader) page.data)->pd_checksum;
1128+
else
1129+
page_crc = pg_checksum_page(page.data, file->segno + blknum);
1130+
}
1131+
1132+
/* the heart of incremental restore */
1133+
if (page_crc == checksum_map[blknum])
1134+
{
1135+
if (map)
1136+
datapagemap_add(map, blknum);
1137+
continue;
1138+
}
1139+
}
1140+
11031141
/*
11041142
* Seek and write the restored page.
11051143
* When restoring file from FULL backup, pages are written sequentially,
@@ -1189,7 +1227,8 @@ restore_non_data_file_internal(FILE *in, FILE *out, pgFile *file,
11891227

11901228
size_t
11911229
restore_non_data_file(parray *parent_chain, pgBackup *dest_backup,
1192-
pgFile *dest_file, FILE *out, const char *to_fullpath)
1230+
pgFile *dest_file, FILE *out, const char *to_fullpath,
1231+
bool already_exists)
11931232
{
11941233
// int i;
11951234
char from_root[MAXPGPATH];
@@ -1259,6 +1298,20 @@ restore_non_data_file(parray *parent_chain, pgBackup *dest_backup,
12591298
"Metadata corruption in backup %s in file: \"%s\"",
12601299
base36enc(tmp_backup->start_time), to_fullpath);
12611300

1301+
/* incremental restore */
1302+
if (already_exists)
1303+
{
1304+
/* compare checksumms of remote and local files */
1305+
pg_crc32 file_crc = fio_get_crc32(to_fullpath, FIO_DB_HOST, false);
1306+
1307+
if (file_crc == tmp_file->crc)
1308+
{
1309+
elog(VERBOSE, "Remote nondata file \"%s\" is unchanged, skip restore",
1310+
to_fullpath);
1311+
return 0;
1312+
}
1313+
}
1314+
12621315
if (tmp_file->external_dir_num == 0)
12631316
join_path_components(from_root, tmp_backup->root_dir, DATABASE_DIR);
12641317
else
@@ -1757,3 +1810,68 @@ check_file_pages(pgFile *file, const char *fullpath, XLogRecPtr stop_lsn,
17571810

17581811
return is_valid;
17591812
}
1813+
1814+
/* read local data file and construct map with block checksums */
1815+
uint16 *get_checksum_map(const char *fullpath, uint32 checksum_version,
1816+
int n_blocks, XLogRecPtr dest_stop_lsn, BlockNumber segmentno)
1817+
{
1818+
uint16 *checksum_map = NULL;
1819+
FILE *in = NULL;
1820+
BlockNumber blknum = 0;
1821+
XLogRecPtr page_lsn = 0;
1822+
char read_buffer[BLCKSZ];
1823+
char in_buf[STDIO_BUFSIZE];
1824+
1825+
/* truncate up to blocks */
1826+
if (truncate(fullpath, n_blocks * BLCKSZ) != 0)
1827+
elog(ERROR, "Cannot truncate file to blknum %u \"%s\": %s",
1828+
n_blocks, fullpath, strerror(errno));
1829+
1830+
/* open file */
1831+
in = fopen(fullpath, PG_BINARY_R);
1832+
if (!in)
1833+
elog(ERROR, "Cannot open source file \"%s\": %s", fullpath, strerror(errno));
1834+
setvbuf(in, in_buf, _IOFBF, STDIO_BUFSIZE);
1835+
1836+
/* initialize array of checksums */
1837+
checksum_map = pgut_malloc(n_blocks * sizeof(uint16));
1838+
memset(checksum_map, 0, n_blocks * sizeof(uint16));
1839+
1840+
for (blknum = 0; blknum < n_blocks; blknum++)
1841+
{
1842+
size_t read_len = fread(read_buffer, 1, BLCKSZ, in);
1843+
page_lsn = InvalidXLogRecPtr;
1844+
1845+
/* report error */
1846+
if (ferror(in))
1847+
elog(ERROR, "Cannot read block %u of \"%s\": %s",
1848+
blknum, fullpath, strerror(errno));
1849+
1850+
if (read_len == BLCKSZ)
1851+
{
1852+
int rc = validate_one_page(read_buffer, segmentno + blknum,
1853+
dest_stop_lsn, &page_lsn, checksum_version);
1854+
1855+
if (rc == PAGE_IS_VALID)
1856+
{
1857+
if (checksum_version)
1858+
checksum_map[blknum] = ((PageHeader) read_buffer)->pd_checksum;
1859+
else
1860+
checksum_map[blknum] = pg_checksum_page(read_buffer, segmentno + blknum);
1861+
}
1862+
}
1863+
else
1864+
elog(ERROR, "Failed to read blknum %u from file \"%s\"", blknum, fullpath);
1865+
1866+
if (feof(in))
1867+
break;
1868+
1869+
if (interrupted)
1870+
elog(ERROR, "Interrupted during page reading");
1871+
}
1872+
1873+
if (in)
1874+
fclose(in);
1875+
1876+
return checksum_map;
1877+
}

src/dir.c

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -248,6 +248,38 @@ pgFileDelete(pgFile *file, const char *full_path)
248248
}
249249
}
250250

251+
/*
252+
* Delete file pointed by the pgFile.
253+
* If the pgFile points directory, the directory must be empty.
254+
*/
255+
void
256+
fio_pgFileDelete(pgFile *file, const char *full_path)
257+
{
258+
if (S_ISDIR(file->mode))
259+
{
260+
if (fio_unlink(full_path, FIO_DB_HOST) == -1)
261+
{
262+
if (errno == ENOENT)
263+
return;
264+
else if (errno == ENOTDIR) /* could be symbolic link */
265+
goto delete_file;
266+
267+
elog(ERROR, "Cannot remove directory \"%s\": %s",
268+
full_path, strerror(errno));
269+
}
270+
return;
271+
}
272+
273+
delete_file:
274+
if (fio_unlink(full_path, FIO_DB_HOST) == -1)
275+
{
276+
if (errno == ENOENT)
277+
return;
278+
elog(ERROR, "Cannot remove file \"%s\": %s", full_path,
279+
strerror(errno));
280+
}
281+
}
282+
251283
/*
252284
* Read the local file to compute its CRC.
253285
* We cannot make decision about file decompression because

src/merge.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1161,7 +1161,7 @@ merge_data_file(parray *parent_chain, pgBackup *full_backup,
11611161
setvbuf(out, buffer, _IOFBF, STDIO_BUFSIZE);
11621162

11631163
/* restore file into temp file */
1164-
tmp_file->size = restore_data_file(parent_chain, dest_file, out, to_fullpath_tmp1, use_bitmap);
1164+
tmp_file->size = restore_data_file(parent_chain, dest_file, out, to_fullpath_tmp1, use_bitmap, NULL);
11651165
fclose(out);
11661166
pg_free(buffer);
11671167

src/pg_probackup.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,7 @@ static pgRestoreParams *restore_params = NULL;
9898
time_t current_time = 0;
9999
bool restore_as_replica = false;
100100
bool no_validate = false;
101+
bool incremental_restore = false;
101102

102103
bool skip_block_validation = false;
103104
bool skip_external_dirs = false;
@@ -203,6 +204,7 @@ static ConfigOption cmd_options[] =
203204
{ 'b', 'R', "restore-as-replica", &restore_as_replica, SOURCE_CMD_STRICT },
204205
{ 's', 160, "primary-conninfo", &primary_conninfo, SOURCE_CMD_STRICT },
205206
{ 's', 'S', "primary-slot-name",&replication_slot, SOURCE_CMD_STRICT },
207+
{ 'b', 161, "incremental", &incremental_restore, SOURCE_CMD_STRICT },
206208
/* checkdb options */
207209
{ 'b', 195, "amcheck", &need_amcheck, SOURCE_CMD_STRICT },
208210
{ 'b', 196, "heapallindexed", &heapallindexed, SOURCE_CMD_STRICT },
@@ -703,6 +705,7 @@ main(int argc, char *argv[])
703705
restore_params->partial_db_list = NULL;
704706
restore_params->partial_restore_type = NONE;
705707
restore_params->primary_conninfo = primary_conninfo;
708+
restore_params->incremental = incremental_restore;
706709

707710
/* handle partial restore parameters */
708711
if (datname_exclude_list && datname_include_list)

src/pg_probackup.h

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -180,7 +180,7 @@ typedef struct pgFile
180180
Oid relOid; /* relOid extracted from path, if applicable */
181181
ForkName forkName; /* forkName extracted from path, if applicable */
182182
int segno; /* Segment number for ptrack */
183-
BlockNumber n_blocks; /* size of the data file in blocks */
183+
int n_blocks; /* size of the data file in blocks */
184184
bool is_cfs; /* Flag to distinguish files compressed by CFS*/
185185
bool is_database; /* Flag used strictly by ptrack 1.x backup */
186186
int external_dir_num; /* Number of external directory. 0 if not external */
@@ -441,6 +441,7 @@ typedef struct pgRestoreParams
441441
bool restore_as_replica;
442442
bool skip_external_dirs;
443443
bool skip_block_validation; //Start using it
444+
bool incremental;
444445
const char *restore_command;
445446
const char *primary_slot_name;
446447

@@ -902,6 +903,7 @@ extern pgFile *pgFileNew(const char *path, const char *rel_path,
902903
fio_location location);
903904
extern pgFile *pgFileInit(const char *rel_path);
904905
extern void pgFileDelete(pgFile *file, const char *full_path);
906+
extern void fio_pgFileDelete(pgFile *file, const char *full_path);
905907

906908
extern void pgFileFree(void *file);
907909

@@ -934,18 +936,22 @@ extern void backup_non_data_file_internal(const char *from_fullpath,
934936
const char *to_fullpath, pgFile *file,
935937
bool missing_ok);
936938

937-
extern size_t restore_data_file(parray *parent_chain, pgFile *dest_file,
938-
FILE *out, const char *to_fullpath, bool use_bitmap);
939+
extern size_t restore_data_file(parray *parent_chain, pgFile *dest_file, FILE *out,
940+
const char *to_fullpath, bool use_bitmap, uint16 *checksum_map);
939941
extern size_t restore_data_file_internal(FILE *in, FILE *out, pgFile *file, uint32 backup_version,
940942
const char *from_fullpath, const char *to_fullpath, int nblocks,
941-
datapagemap_t *map);
943+
datapagemap_t *map, uint16 *checksum_map, int checksum_version);
942944
extern size_t restore_non_data_file(parray *parent_chain, pgBackup *dest_backup,
943-
pgFile *dest_file, FILE *out, const char *to_fullpath);
945+
pgFile *dest_file, FILE *out, const char *to_fullpath,
946+
bool already_exists);
944947
extern void restore_non_data_file_internal(FILE *in, FILE *out, pgFile *file,
945948
const char *from_fullpath, const char *to_fullpath);
946949
extern bool create_empty_file(fio_location from_location, const char *to_root,
947950
fio_location to_location, pgFile *file);
948951

952+
extern uint16 *get_checksum_map(const char *fullpath, uint32 checksum_version,
953+
int n_blocks, XLogRecPtr dest_stop_lsn, BlockNumber segmentno);
954+
949955
extern bool check_file_pages(pgFile *file, const char *fullpath, XLogRecPtr stop_lsn,
950956
uint32 checksum_version, uint32 backup_version);
951957
/* parsexlog.c */
@@ -1040,6 +1046,11 @@ extern void fio_list_dir(parray *files, const char *root, bool exclude, bool fol
10401046

10411047
extern bool pgut_rmtree(const char *path, bool rmtopdir, bool strict);
10421048

1049+
extern uint16 *fio_get_checksum_map(const char *fullpath, uint32 checksum_version,
1050+
int n_blocks, XLogRecPtr dest_stop_lsn, BlockNumber segmentno);
1051+
1052+
extern int32 fio_decompress(void* dst, void const* src, size_t size, int compress_alg);
1053+
10431054
/* return codes for fio_send_pages() and fio_send_file() */
10441055
#define SEND_OK (0)
10451056
#define FILE_MISSING (-1)

0 commit comments

Comments
 (0)