Skip to content

Commit 45995f0

Browse files
committed
[Issue #66] Add lsn-based incremental restore
1 parent 87dc977 commit 45995f0

File tree

8 files changed

+453
-109
lines changed

8 files changed

+453
-109
lines changed

src/data.c

Lines changed: 109 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -862,14 +862,19 @@ 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, uint16 *checksum_map)
865+
const char *to_fullpath, bool use_bitmap, PageState *checksum_map,
866+
XLogRecPtr horizonLsn, datapagemap_t *lsn_map)
866867
{
867868
size_t total_write_len = 0;
868869
char *in_buf = pgut_malloc(STDIO_BUFSIZE);
869870
int backup_seq = 0;
870871

871-
// FULL -> INCR -> DEST
872-
// 2 1 0
872+
/*
873+
* FULL -> INCR -> DEST
874+
* 2 1 0
875+
* Restore of backups of older versions cannot be optimized with bitmap
876+
* because of n_blocks
877+
*/
873878
if (use_bitmap)
874879
/* start with dest backup */
875880
backup_seq = 0;
@@ -942,7 +947,8 @@ restore_data_file(parray *parent_chain, pgFile *dest_file, FILE *out,
942947
parse_program_version(backup->program_version),
943948
from_fullpath, to_fullpath, dest_file->n_blocks,
944949
use_bitmap ? &(dest_file)->pagemap : NULL,
945-
checksum_map, backup->checksum_version);
950+
checksum_map, backup->checksum_version,
951+
backup->start_lsn <= horizonLsn ? lsn_map : NULL);
946952

947953
if (fclose(in) != 0)
948954
elog(ERROR, "Cannot close file \"%s\": %s", from_fullpath,
@@ -963,7 +969,8 @@ restore_data_file(parray *parent_chain, pgFile *dest_file, FILE *out,
963969
size_t
964970
restore_data_file_internal(FILE *in, FILE *out, pgFile *file, uint32 backup_version,
965971
const char *from_fullpath, const char *to_fullpath, int nblocks,
966-
datapagemap_t *map, uint16 *checksum_map, int checksum_version)
972+
datapagemap_t *map, PageState *checksum_map, int checksum_version,
973+
datapagemap_t *lsn_map)
967974
{
968975
BackupPageHeader header;
969976
BlockNumber blknum = 0;
@@ -1071,6 +1078,9 @@ restore_data_file_internal(FILE *in, FILE *out, pgFile *file, uint32 backup_vers
10711078
if (compressed_size > BLCKSZ)
10721079
elog(ERROR, "Size of a blknum %i exceed BLCKSZ", blknum);
10731080

1081+
if (lsn_map && datapagemap_is_set(lsn_map, blknum))
1082+
datapagemap_add(map, blknum);
1083+
10741084
/* if this page is marked as already restored, then skip it */
10751085
if (map && datapagemap_is_set(map, blknum))
10761086
{
@@ -1101,10 +1111,14 @@ restore_data_file_internal(FILE *in, FILE *out, pgFile *file, uint32 backup_vers
11011111
is_compressed = true;
11021112
}
11031113

1104-
/* Incremental restore */
1105-
if (checksum_map && checksum_map[blknum] != 0)
1114+
/* Incremental restore
1115+
* TODO: move to another function
1116+
*/
1117+
if (checksum_map && checksum_map[blknum].checksum != 0)
11061118
{
11071119
uint16 page_crc = 0;
1120+
XLogRecPtr page_lsn = InvalidXLogRecPtr;
1121+
PageHeader phdr;
11081122

11091123
if (is_compressed)
11101124
{
@@ -1117,8 +1131,7 @@ restore_data_file_internal(FILE *in, FILE *out, pgFile *file, uint32 backup_vers
11171131
else
11181132
page_crc = pg_checksum_page(uncompressed_buf, file->segno * RELSEG_SIZE + blknum);
11191133

1120-
// page_crc_1 = pg_checksum_page(uncompressed_buf, file->segno * RELSEG_SIZE + blknum);
1121-
// Assert(page_crc == page_crc_1);
1134+
phdr = (PageHeader) uncompressed_buf;
11221135
}
11231136
else
11241137
{
@@ -1127,10 +1140,19 @@ restore_data_file_internal(FILE *in, FILE *out, pgFile *file, uint32 backup_vers
11271140
page_crc = ((PageHeader) page.data)->pd_checksum;
11281141
else
11291142
page_crc = pg_checksum_page(page.data, file->segno + blknum);
1143+
1144+
phdr = (PageHeader) page.data;
11301145
}
11311146

1132-
/* the heart of incremental restore */
1133-
if (page_crc == checksum_map[blknum])
1147+
page_lsn = PageXLogRecPtrGet(phdr->pd_lsn);
1148+
1149+
/*
1150+
* The heart of incremental restore
1151+
* If page in backup has the same checksum and lsn as
1152+
* page in backup, then page can be skipped.
1153+
*/
1154+
if (page_crc == checksum_map[blknum].checksum &&
1155+
page_lsn == checksum_map[blknum].lsn)
11341156
{
11351157
if (map)
11361158
datapagemap_add(map, blknum);
@@ -1812,10 +1834,10 @@ check_file_pages(pgFile *file, const char *fullpath, XLogRecPtr stop_lsn,
18121834
}
18131835

18141836
/* read local data file and construct map with block checksums */
1815-
uint16 *get_checksum_map(const char *fullpath, uint32 checksum_version,
1837+
PageState *get_checksum_map(const char *fullpath, uint32 checksum_version,
18161838
int n_blocks, XLogRecPtr dest_stop_lsn, BlockNumber segmentno)
18171839
{
1818-
uint16 *checksum_map = NULL;
1840+
PageState *checksum_map = NULL;
18191841
FILE *in = NULL;
18201842
BlockNumber blknum = 0;
18211843
XLogRecPtr page_lsn = 0;
@@ -1834,8 +1856,8 @@ uint16 *get_checksum_map(const char *fullpath, uint32 checksum_version,
18341856
setvbuf(in, in_buf, _IOFBF, STDIO_BUFSIZE);
18351857

18361858
/* initialize array of checksums */
1837-
checksum_map = pgut_malloc(n_blocks * sizeof(uint16));
1838-
memset(checksum_map, 0, n_blocks * sizeof(uint16));
1859+
checksum_map = pgut_malloc(n_blocks * sizeof(PageState));
1860+
memset(checksum_map, 0, n_blocks * sizeof(PageState));
18391861

18401862
for (blknum = 0; blknum < n_blocks; blknum++)
18411863
{
@@ -1855,9 +1877,11 @@ uint16 *get_checksum_map(const char *fullpath, uint32 checksum_version,
18551877
if (rc == PAGE_IS_VALID)
18561878
{
18571879
if (checksum_version)
1858-
checksum_map[blknum] = ((PageHeader) read_buffer)->pd_checksum;
1880+
checksum_map[blknum].checksum = ((PageHeader) read_buffer)->pd_checksum;
18591881
else
1860-
checksum_map[blknum] = pg_checksum_page(read_buffer, segmentno + blknum);
1882+
checksum_map[blknum].checksum = pg_checksum_page(read_buffer, segmentno + blknum);
1883+
1884+
checksum_map[blknum].lsn = page_lsn;
18611885
}
18621886
}
18631887
else
@@ -1875,3 +1899,71 @@ uint16 *get_checksum_map(const char *fullpath, uint32 checksum_version,
18751899

18761900
return checksum_map;
18771901
}
1902+
1903+
/* return bitmap of valid blocks, bitmap is empty, then NULL is returned */
1904+
datapagemap_t *
1905+
get_lsn_map(const char *fullpath, uint32 checksum_version,
1906+
int n_blocks, XLogRecPtr horizonLsn, BlockNumber segmentno)
1907+
{
1908+
FILE *in = NULL;
1909+
BlockNumber blknum = 0;
1910+
XLogRecPtr page_lsn = 0;
1911+
char read_buffer[BLCKSZ];
1912+
char in_buf[STDIO_BUFSIZE];
1913+
datapagemap_t *lsn_map = NULL;
1914+
1915+
/* truncate up to blocks */
1916+
if (truncate(fullpath, n_blocks * BLCKSZ) != 0)
1917+
elog(ERROR, "Cannot truncate file to blknum %u \"%s\": %s",
1918+
n_blocks, fullpath, strerror(errno));
1919+
1920+
Assert(horizonLsn > 0);
1921+
1922+
/* open file */
1923+
in = fopen(fullpath, PG_BINARY_R);
1924+
if (!in)
1925+
elog(ERROR, "Cannot open source file \"%s\": %s", fullpath, strerror(errno));
1926+
setvbuf(in, in_buf, _IOFBF, STDIO_BUFSIZE);
1927+
1928+
lsn_map = pgut_malloc(sizeof(datapagemap_t));
1929+
memset(lsn_map, 0, sizeof(datapagemap_t));
1930+
1931+
for (blknum = 0; blknum < n_blocks; blknum++)
1932+
{
1933+
size_t read_len = fread(read_buffer, 1, BLCKSZ, in);
1934+
page_lsn = InvalidXLogRecPtr;
1935+
1936+
/* report error */
1937+
if (ferror(in))
1938+
elog(ERROR, "Cannot read block %u of \"%s\": %s",
1939+
blknum, fullpath, strerror(errno));
1940+
1941+
if (read_len == BLCKSZ)
1942+
{
1943+
int rc = validate_one_page(read_buffer, segmentno + blknum,
1944+
horizonLsn, &page_lsn, checksum_version);
1945+
1946+
if (rc == PAGE_IS_VALID)
1947+
datapagemap_add(lsn_map, blknum);
1948+
}
1949+
else
1950+
elog(ERROR, "Failed to read blknum %u from file \"%s\"", blknum, fullpath);
1951+
1952+
if (feof(in))
1953+
break;
1954+
1955+
if (interrupted)
1956+
elog(ERROR, "Interrupted during page reading");
1957+
}
1958+
1959+
if (in)
1960+
fclose(in);
1961+
1962+
if (lsn_map->bitmapsize == 0)
1963+
{
1964+
pg_free(lsn_map);
1965+
lsn_map = NULL;
1966+
}
1967+
1968+
return lsn_map;
1969+
}

src/merge.c

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1161,8 +1161,12 @@ 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, NULL);
1165-
fclose(out);
1164+
tmp_file->size = restore_data_file(parent_chain, dest_file, out, to_fullpath_tmp1,
1165+
use_bitmap, NULL, InvalidXLogRecPtr, NULL);
1166+
if (fclose(out) != 0)
1167+
elog(ERROR, "Cannot close file \"%s\": %s",
1168+
to_fullpath_tmp1, strerror(errno));
1169+
11661170
pg_free(buffer);
11671171

11681172
/* tmp_file->size is greedy, even if there is single 8KB block in file,

src/pg_probackup.c

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,8 @@ 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;
101+
bool incremental = false;
102+
bool incremental_lsn = false;
102103

103104
bool skip_block_validation = false;
104105
bool skip_external_dirs = false;
@@ -204,7 +205,8 @@ static ConfigOption cmd_options[] =
204205
{ 'b', 'R', "restore-as-replica", &restore_as_replica, SOURCE_CMD_STRICT },
205206
{ 's', 160, "primary-conninfo", &primary_conninfo, SOURCE_CMD_STRICT },
206207
{ 's', 'S', "primary-slot-name",&replication_slot, SOURCE_CMD_STRICT },
207-
{ 'b', 161, "incremental", &incremental_restore, SOURCE_CMD_STRICT },
208+
{ 'b', 161, "incremental", &incremental, SOURCE_CMD_STRICT },
209+
{ 'b', 167, "incremental-lsn", &incremental_lsn, SOURCE_CMD_STRICT },
208210
/* checkdb options */
209211
{ 'b', 195, "amcheck", &need_amcheck, SOURCE_CMD_STRICT },
210212
{ 'b', 196, "heapallindexed", &heapallindexed, SOURCE_CMD_STRICT },
@@ -702,6 +704,9 @@ main(int argc, char *argv[])
702704
if (replication_slot != NULL)
703705
restore_as_replica = true;
704706

707+
if (!incremental && incremental_lsn)
708+
incremental = true;
709+
705710
/* keep all params in one structure */
706711
restore_params = pgut_new(pgRestoreParams);
707712
restore_params->is_restore = (backup_subcmd == RESTORE_CMD);
@@ -714,7 +719,8 @@ main(int argc, char *argv[])
714719
restore_params->partial_db_list = NULL;
715720
restore_params->partial_restore_type = NONE;
716721
restore_params->primary_conninfo = primary_conninfo;
717-
restore_params->incremental = incremental_restore;
722+
restore_params->incremental = incremental;
723+
restore_params->incremental_lsn = incremental_lsn;
718724

719725
/* handle partial restore parameters */
720726
if (datname_exclude_list && datname_include_list)

src/pg_probackup.h

Lines changed: 29 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,18 @@ extern const char *PROGRAM_EMAIL;
101101
#define XRecOffIsNull(xlrp) \
102102
((xlrp) % XLOG_BLCKSZ == 0)
103103

104+
typedef struct RedoParams
105+
{
106+
TimeLineID tli;
107+
XLogRecPtr lsn;
108+
} RedoParams;
109+
110+
typedef struct PageState
111+
{
112+
uint16 checksum;
113+
XLogRecPtr lsn;
114+
} PageState;
115+
104116
typedef struct db_map_entry
105117
{
106118
Oid dbOid;
@@ -442,6 +454,8 @@ typedef struct pgRestoreParams
442454
bool skip_external_dirs;
443455
bool skip_block_validation; //Start using it
444456
bool incremental;
457+
bool incremental_lsn;
458+
XLogRecPtr horizonLsn;
445459
const char *restore_command;
446460
const char *primary_slot_name;
447461

@@ -938,10 +952,12 @@ extern void backup_non_data_file_internal(const char *from_fullpath,
938952
bool missing_ok);
939953

940954
extern size_t restore_data_file(parray *parent_chain, pgFile *dest_file, FILE *out,
941-
const char *to_fullpath, bool use_bitmap, uint16 *checksum_map);
955+
const char *to_fullpath, bool use_bitmap, PageState *checksum_map,
956+
XLogRecPtr horizonLsn, datapagemap_t *lsn_map);
942957
extern size_t restore_data_file_internal(FILE *in, FILE *out, pgFile *file, uint32 backup_version,
943958
const char *from_fullpath, const char *to_fullpath, int nblocks,
944-
datapagemap_t *map, uint16 *checksum_map, int checksum_version);
959+
datapagemap_t *map, PageState *checksum_map, int checksum_version,
960+
datapagemap_t *lsn_map);
945961
extern size_t restore_non_data_file(parray *parent_chain, pgBackup *dest_backup,
946962
pgFile *dest_file, FILE *out, const char *to_fullpath,
947963
bool already_exists);
@@ -950,8 +966,10 @@ extern void restore_non_data_file_internal(FILE *in, FILE *out, pgFile *file,
950966
extern bool create_empty_file(fio_location from_location, const char *to_root,
951967
fio_location to_location, pgFile *file);
952968

953-
extern uint16 *get_checksum_map(const char *fullpath, uint32 checksum_version,
969+
extern PageState *get_checksum_map(const char *fullpath, uint32 checksum_version,
954970
int n_blocks, XLogRecPtr dest_stop_lsn, BlockNumber segmentno);
971+
extern datapagemap_t *get_lsn_map(const char *fullpath, uint32 checksum_version,
972+
int n_blocks, XLogRecPtr horizonLsn, BlockNumber segmentno);
955973
extern pid_t check_postmaster(const char *pgdata);
956974

957975
extern bool check_file_pages(pgFile *file, const char *fullpath, XLogRecPtr stop_lsn,
@@ -989,6 +1007,7 @@ extern uint64 get_remote_system_identifier(PGconn *conn);
9891007
extern uint32 get_data_checksum_version(bool safe);
9901008
extern pg_crc32c get_pgcontrol_checksum(const char *pgdata_path);
9911009
extern uint32 get_xlog_seg_size(char *pgdata_path);
1010+
extern void get_redo(const char *pgdata_path, RedoParams *redo);
9921011
extern void set_min_recovery_point(pgFile *file, const char *backup_path,
9931012
XLogRecPtr stop_backup_lsn);
9941013
extern void copy_pgcontrol_file(const char *from_fullpath, fio_location from_location,
@@ -1048,9 +1067,13 @@ extern void fio_list_dir(parray *files, const char *root, bool exclude, bool fol
10481067

10491068
extern bool pgut_rmtree(const char *path, bool rmtopdir, bool strict);
10501069

1051-
extern uint16 *fio_get_checksum_map(const char *fullpath, uint32 checksum_version,
1052-
int n_blocks, XLogRecPtr dest_stop_lsn, BlockNumber segmentno);
1053-
extern pid_t fio_check_postmaster(const char *pgdata);
1070+
extern PageState *fio_get_checksum_map(const char *fullpath, uint32 checksum_version, int n_blocks,
1071+
XLogRecPtr dest_stop_lsn, BlockNumber segmentno, fio_location location);
1072+
1073+
extern datapagemap_t *fio_get_lsn_map(const char *fullpath, uint32 checksum_version,
1074+
int n_blocks, XLogRecPtr horizonLsn, BlockNumber segmentno,
1075+
fio_location location);
1076+
extern pid_t fio_check_postmaster(const char *pgdata, fio_location location);
10541077

10551078
extern int32 fio_decompress(void* dst, void const* src, size_t size, int compress_alg);
10561079

0 commit comments

Comments
 (0)