mirror of
https://github.com/postgrespro/pg_probackup.git
synced 2024-11-28 09:33:54 +02:00
[Issue #66] Add lsn-based incremental restore
This commit is contained in:
parent
87dc977baa
commit
45995f0b0d
126
src/data.c
126
src/data.c
@ -862,14 +862,19 @@ backup_non_data_file(pgFile *file, pgFile *prev_file,
|
||||
*/
|
||||
size_t
|
||||
restore_data_file(parray *parent_chain, pgFile *dest_file, FILE *out,
|
||||
const char *to_fullpath, bool use_bitmap, uint16 *checksum_map)
|
||||
const char *to_fullpath, bool use_bitmap, PageState *checksum_map,
|
||||
XLogRecPtr horizonLsn, datapagemap_t *lsn_map)
|
||||
{
|
||||
size_t total_write_len = 0;
|
||||
char *in_buf = pgut_malloc(STDIO_BUFSIZE);
|
||||
int backup_seq = 0;
|
||||
|
||||
// FULL -> INCR -> DEST
|
||||
// 2 1 0
|
||||
/*
|
||||
* FULL -> INCR -> DEST
|
||||
* 2 1 0
|
||||
* Restore of backups of older versions cannot be optimized with bitmap
|
||||
* because of n_blocks
|
||||
*/
|
||||
if (use_bitmap)
|
||||
/* start with dest backup */
|
||||
backup_seq = 0;
|
||||
@ -942,7 +947,8 @@ restore_data_file(parray *parent_chain, pgFile *dest_file, FILE *out,
|
||||
parse_program_version(backup->program_version),
|
||||
from_fullpath, to_fullpath, dest_file->n_blocks,
|
||||
use_bitmap ? &(dest_file)->pagemap : NULL,
|
||||
checksum_map, backup->checksum_version);
|
||||
checksum_map, backup->checksum_version,
|
||||
backup->start_lsn <= horizonLsn ? lsn_map : NULL);
|
||||
|
||||
if (fclose(in) != 0)
|
||||
elog(ERROR, "Cannot close file \"%s\": %s", from_fullpath,
|
||||
@ -963,7 +969,8 @@ restore_data_file(parray *parent_chain, pgFile *dest_file, FILE *out,
|
||||
size_t
|
||||
restore_data_file_internal(FILE *in, FILE *out, pgFile *file, uint32 backup_version,
|
||||
const char *from_fullpath, const char *to_fullpath, int nblocks,
|
||||
datapagemap_t *map, uint16 *checksum_map, int checksum_version)
|
||||
datapagemap_t *map, PageState *checksum_map, int checksum_version,
|
||||
datapagemap_t *lsn_map)
|
||||
{
|
||||
BackupPageHeader header;
|
||||
BlockNumber blknum = 0;
|
||||
@ -1071,6 +1078,9 @@ restore_data_file_internal(FILE *in, FILE *out, pgFile *file, uint32 backup_vers
|
||||
if (compressed_size > BLCKSZ)
|
||||
elog(ERROR, "Size of a blknum %i exceed BLCKSZ", blknum);
|
||||
|
||||
if (lsn_map && datapagemap_is_set(lsn_map, blknum))
|
||||
datapagemap_add(map, blknum);
|
||||
|
||||
/* if this page is marked as already restored, then skip it */
|
||||
if (map && datapagemap_is_set(map, blknum))
|
||||
{
|
||||
@ -1101,10 +1111,14 @@ restore_data_file_internal(FILE *in, FILE *out, pgFile *file, uint32 backup_vers
|
||||
is_compressed = true;
|
||||
}
|
||||
|
||||
/* Incremental restore */
|
||||
if (checksum_map && checksum_map[blknum] != 0)
|
||||
/* Incremental restore
|
||||
* TODO: move to another function
|
||||
*/
|
||||
if (checksum_map && checksum_map[blknum].checksum != 0)
|
||||
{
|
||||
uint16 page_crc = 0;
|
||||
XLogRecPtr page_lsn = InvalidXLogRecPtr;
|
||||
PageHeader phdr;
|
||||
|
||||
if (is_compressed)
|
||||
{
|
||||
@ -1117,8 +1131,7 @@ restore_data_file_internal(FILE *in, FILE *out, pgFile *file, uint32 backup_vers
|
||||
else
|
||||
page_crc = pg_checksum_page(uncompressed_buf, file->segno * RELSEG_SIZE + blknum);
|
||||
|
||||
// page_crc_1 = pg_checksum_page(uncompressed_buf, file->segno * RELSEG_SIZE + blknum);
|
||||
// Assert(page_crc == page_crc_1);
|
||||
phdr = (PageHeader) uncompressed_buf;
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -1127,10 +1140,19 @@ restore_data_file_internal(FILE *in, FILE *out, pgFile *file, uint32 backup_vers
|
||||
page_crc = ((PageHeader) page.data)->pd_checksum;
|
||||
else
|
||||
page_crc = pg_checksum_page(page.data, file->segno + blknum);
|
||||
|
||||
phdr = (PageHeader) page.data;
|
||||
}
|
||||
|
||||
/* the heart of incremental restore */
|
||||
if (page_crc == checksum_map[blknum])
|
||||
page_lsn = PageXLogRecPtrGet(phdr->pd_lsn);
|
||||
|
||||
/*
|
||||
* The heart of incremental restore
|
||||
* If page in backup has the same checksum and lsn as
|
||||
* page in backup, then page can be skipped.
|
||||
*/
|
||||
if (page_crc == checksum_map[blknum].checksum &&
|
||||
page_lsn == checksum_map[blknum].lsn)
|
||||
{
|
||||
if (map)
|
||||
datapagemap_add(map, blknum);
|
||||
@ -1812,10 +1834,10 @@ check_file_pages(pgFile *file, const char *fullpath, XLogRecPtr stop_lsn,
|
||||
}
|
||||
|
||||
/* read local data file and construct map with block checksums */
|
||||
uint16 *get_checksum_map(const char *fullpath, uint32 checksum_version,
|
||||
PageState *get_checksum_map(const char *fullpath, uint32 checksum_version,
|
||||
int n_blocks, XLogRecPtr dest_stop_lsn, BlockNumber segmentno)
|
||||
{
|
||||
uint16 *checksum_map = NULL;
|
||||
PageState *checksum_map = NULL;
|
||||
FILE *in = NULL;
|
||||
BlockNumber blknum = 0;
|
||||
XLogRecPtr page_lsn = 0;
|
||||
@ -1834,8 +1856,8 @@ uint16 *get_checksum_map(const char *fullpath, uint32 checksum_version,
|
||||
setvbuf(in, in_buf, _IOFBF, STDIO_BUFSIZE);
|
||||
|
||||
/* initialize array of checksums */
|
||||
checksum_map = pgut_malloc(n_blocks * sizeof(uint16));
|
||||
memset(checksum_map, 0, n_blocks * sizeof(uint16));
|
||||
checksum_map = pgut_malloc(n_blocks * sizeof(PageState));
|
||||
memset(checksum_map, 0, n_blocks * sizeof(PageState));
|
||||
|
||||
for (blknum = 0; blknum < n_blocks; blknum++)
|
||||
{
|
||||
@ -1855,9 +1877,11 @@ uint16 *get_checksum_map(const char *fullpath, uint32 checksum_version,
|
||||
if (rc == PAGE_IS_VALID)
|
||||
{
|
||||
if (checksum_version)
|
||||
checksum_map[blknum] = ((PageHeader) read_buffer)->pd_checksum;
|
||||
checksum_map[blknum].checksum = ((PageHeader) read_buffer)->pd_checksum;
|
||||
else
|
||||
checksum_map[blknum] = pg_checksum_page(read_buffer, segmentno + blknum);
|
||||
checksum_map[blknum].checksum = pg_checksum_page(read_buffer, segmentno + blknum);
|
||||
|
||||
checksum_map[blknum].lsn = page_lsn;
|
||||
}
|
||||
}
|
||||
else
|
||||
@ -1875,3 +1899,71 @@ uint16 *get_checksum_map(const char *fullpath, uint32 checksum_version,
|
||||
|
||||
return checksum_map;
|
||||
}
|
||||
|
||||
/* return bitmap of valid blocks, bitmap is empty, then NULL is returned */
|
||||
datapagemap_t *
|
||||
get_lsn_map(const char *fullpath, uint32 checksum_version,
|
||||
int n_blocks, XLogRecPtr horizonLsn, BlockNumber segmentno)
|
||||
{
|
||||
FILE *in = NULL;
|
||||
BlockNumber blknum = 0;
|
||||
XLogRecPtr page_lsn = 0;
|
||||
char read_buffer[BLCKSZ];
|
||||
char in_buf[STDIO_BUFSIZE];
|
||||
datapagemap_t *lsn_map = NULL;
|
||||
|
||||
/* truncate up to blocks */
|
||||
if (truncate(fullpath, n_blocks * BLCKSZ) != 0)
|
||||
elog(ERROR, "Cannot truncate file to blknum %u \"%s\": %s",
|
||||
n_blocks, fullpath, strerror(errno));
|
||||
|
||||
Assert(horizonLsn > 0);
|
||||
|
||||
/* open file */
|
||||
in = fopen(fullpath, PG_BINARY_R);
|
||||
if (!in)
|
||||
elog(ERROR, "Cannot open source file \"%s\": %s", fullpath, strerror(errno));
|
||||
setvbuf(in, in_buf, _IOFBF, STDIO_BUFSIZE);
|
||||
|
||||
lsn_map = pgut_malloc(sizeof(datapagemap_t));
|
||||
memset(lsn_map, 0, sizeof(datapagemap_t));
|
||||
|
||||
for (blknum = 0; blknum < n_blocks; blknum++)
|
||||
{
|
||||
size_t read_len = fread(read_buffer, 1, BLCKSZ, in);
|
||||
page_lsn = InvalidXLogRecPtr;
|
||||
|
||||
/* report error */
|
||||
if (ferror(in))
|
||||
elog(ERROR, "Cannot read block %u of \"%s\": %s",
|
||||
blknum, fullpath, strerror(errno));
|
||||
|
||||
if (read_len == BLCKSZ)
|
||||
{
|
||||
int rc = validate_one_page(read_buffer, segmentno + blknum,
|
||||
horizonLsn, &page_lsn, checksum_version);
|
||||
|
||||
if (rc == PAGE_IS_VALID)
|
||||
datapagemap_add(lsn_map, blknum);
|
||||
}
|
||||
else
|
||||
elog(ERROR, "Failed to read blknum %u from file \"%s\"", blknum, fullpath);
|
||||
|
||||
if (feof(in))
|
||||
break;
|
||||
|
||||
if (interrupted)
|
||||
elog(ERROR, "Interrupted during page reading");
|
||||
}
|
||||
|
||||
if (in)
|
||||
fclose(in);
|
||||
|
||||
if (lsn_map->bitmapsize == 0)
|
||||
{
|
||||
pg_free(lsn_map);
|
||||
lsn_map = NULL;
|
||||
}
|
||||
|
||||
return lsn_map;
|
||||
}
|
||||
|
@ -1161,8 +1161,12 @@ merge_data_file(parray *parent_chain, pgBackup *full_backup,
|
||||
setvbuf(out, buffer, _IOFBF, STDIO_BUFSIZE);
|
||||
|
||||
/* restore file into temp file */
|
||||
tmp_file->size = restore_data_file(parent_chain, dest_file, out, to_fullpath_tmp1, use_bitmap, NULL);
|
||||
fclose(out);
|
||||
tmp_file->size = restore_data_file(parent_chain, dest_file, out, to_fullpath_tmp1,
|
||||
use_bitmap, NULL, InvalidXLogRecPtr, NULL);
|
||||
if (fclose(out) != 0)
|
||||
elog(ERROR, "Cannot close file \"%s\": %s",
|
||||
to_fullpath_tmp1, strerror(errno));
|
||||
|
||||
pg_free(buffer);
|
||||
|
||||
/* tmp_file->size is greedy, even if there is single 8KB block in file,
|
||||
|
@ -98,7 +98,8 @@ static pgRestoreParams *restore_params = NULL;
|
||||
time_t current_time = 0;
|
||||
bool restore_as_replica = false;
|
||||
bool no_validate = false;
|
||||
bool incremental_restore = false;
|
||||
bool incremental = false;
|
||||
bool incremental_lsn = false;
|
||||
|
||||
bool skip_block_validation = false;
|
||||
bool skip_external_dirs = false;
|
||||
@ -204,7 +205,8 @@ static ConfigOption cmd_options[] =
|
||||
{ 'b', 'R', "restore-as-replica", &restore_as_replica, SOURCE_CMD_STRICT },
|
||||
{ 's', 160, "primary-conninfo", &primary_conninfo, SOURCE_CMD_STRICT },
|
||||
{ 's', 'S', "primary-slot-name",&replication_slot, SOURCE_CMD_STRICT },
|
||||
{ 'b', 161, "incremental", &incremental_restore, SOURCE_CMD_STRICT },
|
||||
{ 'b', 161, "incremental", &incremental, SOURCE_CMD_STRICT },
|
||||
{ 'b', 167, "incremental-lsn", &incremental_lsn, SOURCE_CMD_STRICT },
|
||||
/* checkdb options */
|
||||
{ 'b', 195, "amcheck", &need_amcheck, SOURCE_CMD_STRICT },
|
||||
{ 'b', 196, "heapallindexed", &heapallindexed, SOURCE_CMD_STRICT },
|
||||
@ -702,6 +704,9 @@ main(int argc, char *argv[])
|
||||
if (replication_slot != NULL)
|
||||
restore_as_replica = true;
|
||||
|
||||
if (!incremental && incremental_lsn)
|
||||
incremental = true;
|
||||
|
||||
/* keep all params in one structure */
|
||||
restore_params = pgut_new(pgRestoreParams);
|
||||
restore_params->is_restore = (backup_subcmd == RESTORE_CMD);
|
||||
@ -714,7 +719,8 @@ main(int argc, char *argv[])
|
||||
restore_params->partial_db_list = NULL;
|
||||
restore_params->partial_restore_type = NONE;
|
||||
restore_params->primary_conninfo = primary_conninfo;
|
||||
restore_params->incremental = incremental_restore;
|
||||
restore_params->incremental = incremental;
|
||||
restore_params->incremental_lsn = incremental_lsn;
|
||||
|
||||
/* handle partial restore parameters */
|
||||
if (datname_exclude_list && datname_include_list)
|
||||
|
@ -101,6 +101,18 @@ extern const char *PROGRAM_EMAIL;
|
||||
#define XRecOffIsNull(xlrp) \
|
||||
((xlrp) % XLOG_BLCKSZ == 0)
|
||||
|
||||
typedef struct RedoParams
|
||||
{
|
||||
TimeLineID tli;
|
||||
XLogRecPtr lsn;
|
||||
} RedoParams;
|
||||
|
||||
typedef struct PageState
|
||||
{
|
||||
uint16 checksum;
|
||||
XLogRecPtr lsn;
|
||||
} PageState;
|
||||
|
||||
typedef struct db_map_entry
|
||||
{
|
||||
Oid dbOid;
|
||||
@ -442,6 +454,8 @@ typedef struct pgRestoreParams
|
||||
bool skip_external_dirs;
|
||||
bool skip_block_validation; //Start using it
|
||||
bool incremental;
|
||||
bool incremental_lsn;
|
||||
XLogRecPtr horizonLsn;
|
||||
const char *restore_command;
|
||||
const char *primary_slot_name;
|
||||
|
||||
@ -938,10 +952,12 @@ extern void backup_non_data_file_internal(const char *from_fullpath,
|
||||
bool missing_ok);
|
||||
|
||||
extern size_t restore_data_file(parray *parent_chain, pgFile *dest_file, FILE *out,
|
||||
const char *to_fullpath, bool use_bitmap, uint16 *checksum_map);
|
||||
const char *to_fullpath, bool use_bitmap, PageState *checksum_map,
|
||||
XLogRecPtr horizonLsn, datapagemap_t *lsn_map);
|
||||
extern size_t restore_data_file_internal(FILE *in, FILE *out, pgFile *file, uint32 backup_version,
|
||||
const char *from_fullpath, const char *to_fullpath, int nblocks,
|
||||
datapagemap_t *map, uint16 *checksum_map, int checksum_version);
|
||||
datapagemap_t *map, PageState *checksum_map, int checksum_version,
|
||||
datapagemap_t *lsn_map);
|
||||
extern size_t restore_non_data_file(parray *parent_chain, pgBackup *dest_backup,
|
||||
pgFile *dest_file, FILE *out, const char *to_fullpath,
|
||||
bool already_exists);
|
||||
@ -950,8 +966,10 @@ extern void restore_non_data_file_internal(FILE *in, FILE *out, pgFile *file,
|
||||
extern bool create_empty_file(fio_location from_location, const char *to_root,
|
||||
fio_location to_location, pgFile *file);
|
||||
|
||||
extern uint16 *get_checksum_map(const char *fullpath, uint32 checksum_version,
|
||||
extern PageState *get_checksum_map(const char *fullpath, uint32 checksum_version,
|
||||
int n_blocks, XLogRecPtr dest_stop_lsn, BlockNumber segmentno);
|
||||
extern datapagemap_t *get_lsn_map(const char *fullpath, uint32 checksum_version,
|
||||
int n_blocks, XLogRecPtr horizonLsn, BlockNumber segmentno);
|
||||
extern pid_t check_postmaster(const char *pgdata);
|
||||
|
||||
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);
|
||||
extern uint32 get_data_checksum_version(bool safe);
|
||||
extern pg_crc32c get_pgcontrol_checksum(const char *pgdata_path);
|
||||
extern uint32 get_xlog_seg_size(char *pgdata_path);
|
||||
extern void get_redo(const char *pgdata_path, RedoParams *redo);
|
||||
extern void set_min_recovery_point(pgFile *file, const char *backup_path,
|
||||
XLogRecPtr stop_backup_lsn);
|
||||
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
|
||||
|
||||
extern bool pgut_rmtree(const char *path, bool rmtopdir, bool strict);
|
||||
|
||||
extern uint16 *fio_get_checksum_map(const char *fullpath, uint32 checksum_version,
|
||||
int n_blocks, XLogRecPtr dest_stop_lsn, BlockNumber segmentno);
|
||||
extern pid_t fio_check_postmaster(const char *pgdata);
|
||||
extern PageState *fio_get_checksum_map(const char *fullpath, uint32 checksum_version, int n_blocks,
|
||||
XLogRecPtr dest_stop_lsn, BlockNumber segmentno, fio_location location);
|
||||
|
||||
extern datapagemap_t *fio_get_lsn_map(const char *fullpath, uint32 checksum_version,
|
||||
int n_blocks, XLogRecPtr horizonLsn, BlockNumber segmentno,
|
||||
fio_location location);
|
||||
extern pid_t fio_check_postmaster(const char *pgdata, fio_location location);
|
||||
|
||||
extern int32 fio_decompress(void* dst, void const* src, size_t size, int compress_alg);
|
||||
|
||||
|
157
src/restore.c
157
src/restore.c
@ -30,6 +30,8 @@ typedef struct
|
||||
size_t restored_bytes;
|
||||
bool use_bitmap;
|
||||
bool incremental;
|
||||
bool incremental_lsn;
|
||||
XLogRecPtr horizonLsn;
|
||||
|
||||
/*
|
||||
* Return value from the thread.
|
||||
@ -50,7 +52,7 @@ static void restore_chain(pgBackup *dest_backup, parray *parent_chain,
|
||||
parray *dbOid_exclude_list, pgRestoreParams *params,
|
||||
const char *pgdata_path, bool no_sync);
|
||||
static void check_incremental_compatibility(const char *pgdata, uint64 system_identifier,
|
||||
bool lsn_based);
|
||||
bool incremental_lsn);
|
||||
|
||||
/*
|
||||
* Iterate over backup list to find all ancestors of the broken parent_backup
|
||||
@ -115,6 +117,7 @@ do_restore_or_validate(time_t target_backup_id, pgRecoveryTarget *rt,
|
||||
parray *dbOid_exclude_list = NULL;
|
||||
bool pgdata_is_empty = true;
|
||||
bool tblspaces_are_empty = true;
|
||||
XLogRecPtr horizonLsn = InvalidXLogRecPtr;
|
||||
|
||||
if (params->is_restore)
|
||||
{
|
||||
@ -124,8 +127,7 @@ do_restore_or_validate(time_t target_backup_id, pgRecoveryTarget *rt,
|
||||
/* Check if restore destination empty */
|
||||
if (!dir_is_empty(instance_config.pgdata, FIO_DB_HOST))
|
||||
{
|
||||
// TODO: check that remote systemd id is the same as ours.
|
||||
// TODO: check that remote system is NOT running, check pg_control and pid.
|
||||
/* Check that remote system is NOT running and systemd id is the same as ours */
|
||||
if (params->incremental)
|
||||
{
|
||||
elog(INFO, "Running incremental restore into nonempty directory: \"%s\"",
|
||||
@ -133,14 +135,14 @@ do_restore_or_validate(time_t target_backup_id, pgRecoveryTarget *rt,
|
||||
|
||||
check_incremental_compatibility(instance_config.pgdata,
|
||||
instance_config.system_identifier,
|
||||
false);
|
||||
params->incremental_lsn);
|
||||
}
|
||||
else
|
||||
elog(ERROR, "Restore destination is not empty: \"%s\"",
|
||||
instance_config.pgdata);
|
||||
|
||||
/* if remote directory is empty then incremental restore may be disabled */
|
||||
pgdata_is_empty = true;
|
||||
/* if destination directory is empty, then incremental restore may be disabled */
|
||||
pgdata_is_empty = false;
|
||||
}
|
||||
}
|
||||
|
||||
@ -366,13 +368,72 @@ do_restore_or_validate(time_t target_backup_id, pgRecoveryTarget *rt,
|
||||
*/
|
||||
|
||||
tmp_backup = dest_backup;
|
||||
while(tmp_backup->parent_backup_link)
|
||||
while (tmp_backup)
|
||||
{
|
||||
parray_append(parent_chain, tmp_backup);
|
||||
tmp_backup = tmp_backup->parent_backup_link;
|
||||
}
|
||||
|
||||
parray_append(parent_chain, base_full_backup);
|
||||
/*
|
||||
* Determine horizon LSN
|
||||
* Consider the following example:
|
||||
*
|
||||
*
|
||||
* /----D----------F->
|
||||
* -A--B---C---*-------X----->
|
||||
*
|
||||
* [A,F] - incremental chain
|
||||
* X - the state of pgdata
|
||||
* F - destination backup
|
||||
*
|
||||
* When running incremental-lsn restore, we get a bitmap of pages,
|
||||
* whose LSN is less than C.
|
||||
* So when restoring file, we can skip restore of pages coming from
|
||||
* A, B and C.
|
||||
* Pages from D and F cannot be skipped due to incremental restore.
|
||||
*
|
||||
*/
|
||||
if (params->is_restore && params->incremental && params->incremental_lsn)
|
||||
{
|
||||
RedoParams redo;
|
||||
get_redo(instance_config.pgdata, &redo);
|
||||
|
||||
tmp_backup = dest_backup;
|
||||
|
||||
while (tmp_backup)
|
||||
{
|
||||
if (tmp_backup->start_lsn < redo.lsn &&
|
||||
redo.tli == tmp_backup->tli)
|
||||
{
|
||||
horizonLsn = tmp_backup->start_lsn;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!tmp_backup->parent_backup_link)
|
||||
break;
|
||||
|
||||
tmp_backup = tmp_backup->parent_backup_link;
|
||||
}
|
||||
|
||||
if (XLogRecPtrIsInvalid(horizonLsn))
|
||||
elog(ERROR, "Cannot perform lsn-based incremental restore of backup chain %s, "
|
||||
"because destination directory redo point %X/%X on tli %i is less than "
|
||||
"START LSN %X/%X of oldest backup in chain",
|
||||
base36enc(dest_backup->start_time),
|
||||
(uint32) (redo.lsn >> 32), (uint32) redo.lsn, redo.tli,
|
||||
(uint32) (tmp_backup->start_lsn >> 32), (uint32) tmp_backup->start_lsn);
|
||||
else
|
||||
elog(INFO, "Destination directory redo point %X/%X on tli %i is within reach of "
|
||||
"backup %s with START LSN %X/%X, lsn-based incremental restore is possible",
|
||||
(uint32) (redo.lsn >> 32), (uint32) redo.lsn, redo.tli,
|
||||
base36enc(tmp_backup->start_time),
|
||||
(uint32) (tmp_backup->start_lsn >> 32), (uint32) tmp_backup->start_lsn);
|
||||
|
||||
elog(INFO, "Horizon LSN: %X/%X",
|
||||
(uint32) (horizonLsn >> 32), (uint32) horizonLsn);
|
||||
|
||||
params->horizonLsn = horizonLsn;
|
||||
}
|
||||
|
||||
/* for validation or restore with enabled validation */
|
||||
if (!params->is_restore || !params->no_validate)
|
||||
@ -601,10 +662,16 @@ restore_chain(pgBackup *dest_backup, parray *parent_chain,
|
||||
elog(ERROR, "incremental restore is not possible for backups older than 2.3.0 version");
|
||||
}
|
||||
|
||||
/* There is no point in bitmap restore, when restoring a single FULL backup */
|
||||
// TODO: if there is lsn-based incremental restore, then bitmap is mandatory
|
||||
if (parray_num(parent_chain) == 1)
|
||||
use_bitmap = false;
|
||||
/* There is no point in bitmap restore, when restoring a single FULL backup,
|
||||
* unless we are running incremental-lsn restore, then bitmap is mandatory.
|
||||
*/
|
||||
if (use_bitmap && parray_num(parent_chain) == 1)
|
||||
{
|
||||
if (params->incremental && params->incremental_lsn)
|
||||
use_bitmap = true;
|
||||
else
|
||||
use_bitmap = false;
|
||||
}
|
||||
|
||||
/*
|
||||
* Restore dest_backup internal directories.
|
||||
@ -661,7 +728,7 @@ restore_chain(pgBackup *dest_backup, parray *parent_chain,
|
||||
pg_atomic_clear_flag(&file->lock);
|
||||
}
|
||||
|
||||
// Get list of files in destination directory and remove redundant files.
|
||||
/* Get list of files in destination directory and remove redundant files */
|
||||
if (params->incremental)
|
||||
{
|
||||
pgdata_files = parray_new();
|
||||
@ -698,7 +765,7 @@ restore_chain(pgBackup *dest_backup, parray *parent_chain,
|
||||
join_path_components(full_file_path, pgdata_path, file->rel_path);
|
||||
|
||||
fio_pgFileDelete(file, full_file_path);
|
||||
elog(WARNING, "Deleted remote file \"%s\"", full_file_path);
|
||||
elog(WARNING, "Deleted file \"%s\"", full_file_path);
|
||||
}
|
||||
}
|
||||
|
||||
@ -706,10 +773,8 @@ restore_chain(pgBackup *dest_backup, parray *parent_chain,
|
||||
pretty_time_interval(difftime(end_time, start_time),
|
||||
pretty_time, lengthof(pretty_time));
|
||||
|
||||
/* At this point PDATA do not contain files, that do not exists in dest backup file list */
|
||||
elog(INFO, "Redundant files are removed, time elapsed: %s", pretty_time);
|
||||
|
||||
// use_bitmap = true;
|
||||
/* At this point PDATA do not contain files, that also do not exists in backup filelist */
|
||||
}
|
||||
|
||||
/*
|
||||
@ -746,6 +811,8 @@ restore_chain(pgBackup *dest_backup, parray *parent_chain,
|
||||
arg->to_root = pgdata_path;
|
||||
arg->use_bitmap = use_bitmap;
|
||||
arg->incremental = params->incremental;
|
||||
arg->incremental_lsn = params->incremental_lsn;
|
||||
arg->horizonLsn = params->horizonLsn;
|
||||
threads_args[i].restored_bytes = 0;
|
||||
/* By default there are some error */
|
||||
threads_args[i].ret = 1;
|
||||
@ -776,7 +843,7 @@ restore_chain(pgBackup *dest_backup, parray *parent_chain,
|
||||
elog(INFO, "Backup files are restored. Transfered bytes: %s, time elapsed: %s",
|
||||
pretty_total_bytes, pretty_time);
|
||||
|
||||
elog(INFO, "Restore overwriting ratio (less is better): %.f%% (%s/%s)",
|
||||
elog(INFO, "Restore incremental ratio (less is better): %.f%% (%s/%s)",
|
||||
((float) total_bytes / dest_bytes) * 100,
|
||||
pretty_total_bytes, pretty_dest_bytes);
|
||||
}
|
||||
@ -871,7 +938,8 @@ restore_files(void *arg)
|
||||
for (i = 0; i < parray_num(arguments->dest_files); i++)
|
||||
{
|
||||
bool already_exists = false;
|
||||
uint16 *checksum_map = NULL; /* it should take 512kB at most */
|
||||
PageState *checksum_map = NULL; /* it should take ~1.5MB at most */
|
||||
datapagemap_t *lsn_map = NULL; /* it should take 16kB at most */
|
||||
pgFile *dest_file = (pgFile *) parray_get(arguments->dest_files, i);
|
||||
|
||||
/* Directories were created before */
|
||||
@ -955,16 +1023,19 @@ restore_files(void *arg)
|
||||
dest_file->is_datafile && !dest_file->is_cfs &&
|
||||
dest_file->n_blocks > 0)
|
||||
{
|
||||
/* remote mode */
|
||||
if (fio_is_remote(FIO_DB_HOST))
|
||||
if (arguments->incremental_lsn)
|
||||
{
|
||||
/* TODO: return lsn_map */
|
||||
lsn_map = fio_get_lsn_map(to_fullpath, arguments->dest_backup->checksum_version,
|
||||
dest_file->n_blocks, arguments->horizonLsn,
|
||||
dest_file->segno * RELSEG_SIZE, FIO_DB_HOST);
|
||||
}
|
||||
else
|
||||
{
|
||||
checksum_map = fio_get_checksum_map(to_fullpath, arguments->dest_backup->checksum_version,
|
||||
dest_file->n_blocks, arguments->dest_backup->stop_lsn,
|
||||
dest_file->segno * RELSEG_SIZE);
|
||||
/* local mode */
|
||||
else
|
||||
checksum_map = get_checksum_map(to_fullpath, arguments->dest_backup->checksum_version,
|
||||
dest_file->n_blocks, arguments->dest_backup->stop_lsn,
|
||||
dest_file->segno * RELSEG_SIZE);
|
||||
dest_file->segno * RELSEG_SIZE, FIO_DB_HOST);
|
||||
}
|
||||
}
|
||||
|
||||
/* open destination file */
|
||||
@ -999,7 +1070,8 @@ restore_files(void *arg)
|
||||
/* Destination file is data file */
|
||||
arguments->restored_bytes += restore_data_file(arguments->parent_chain,
|
||||
dest_file, out, to_fullpath,
|
||||
arguments->use_bitmap, checksum_map);
|
||||
arguments->use_bitmap, checksum_map,
|
||||
arguments->horizonLsn, lsn_map);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -1021,6 +1093,8 @@ done:
|
||||
elog(ERROR, "Cannot close file \"%s\": %s", to_fullpath,
|
||||
strerror(errno));
|
||||
|
||||
if (lsn_map)
|
||||
pg_free(lsn_map->bitmap);
|
||||
pg_free(checksum_map);
|
||||
}
|
||||
|
||||
@ -1765,9 +1839,11 @@ get_dbOid_exclude_list(pgBackup *backup, parray *datname_list,
|
||||
return dbOid_exclude_list;
|
||||
}
|
||||
|
||||
/* check that instance has the same SYSTEM_ID, */
|
||||
/* Check that instance is suitable for incremental restore
|
||||
* Depending on type of incremental restore requirements are differs.
|
||||
*/
|
||||
void
|
||||
check_incremental_compatibility(const char *pgdata, uint64 system_identifier, bool lsn_based)
|
||||
check_incremental_compatibility(const char *pgdata, uint64 system_identifier, bool incremental_lsn)
|
||||
{
|
||||
uint64 system_id_pgdata;
|
||||
bool success = true;
|
||||
@ -1783,7 +1859,6 @@ check_incremental_compatibility(const char *pgdata, uint64 system_identifier, bo
|
||||
* data files content, because based on pg_control information we will
|
||||
* choose a backup suitable for lsn based incremental restore.
|
||||
*/
|
||||
/* TODO: handle timeline discrepancies */
|
||||
|
||||
system_id_pgdata = get_system_identifier(pgdata);
|
||||
|
||||
@ -1796,10 +1871,7 @@ check_incremental_compatibility(const char *pgdata, uint64 system_identifier, bo
|
||||
}
|
||||
|
||||
/* check postmaster pid */
|
||||
if (fio_is_remote(FIO_DB_HOST))
|
||||
pid = fio_check_postmaster(pgdata);
|
||||
else
|
||||
pid = check_postmaster(pgdata);
|
||||
pid = fio_check_postmaster(pgdata, FIO_DB_HOST);
|
||||
|
||||
if (pid == 1) /* postmaster.pid is mangled */
|
||||
{
|
||||
@ -1810,22 +1882,27 @@ check_incremental_compatibility(const char *pgdata, uint64 system_identifier, bo
|
||||
pid_file);
|
||||
success = false;
|
||||
}
|
||||
else if (pid > 1)
|
||||
else if (pid > 1) /* postmaster is up */
|
||||
{
|
||||
elog(WARNING, "Postmaster with pid %u is running in destination directory \"%s\"",
|
||||
pid, pgdata);
|
||||
success = false;
|
||||
}
|
||||
|
||||
if (lsn_based)
|
||||
/*
|
||||
* TODO: maybe there should be some other signs, pointing to pg_control
|
||||
* desynchronization with cluster state.
|
||||
*/
|
||||
if (incremental_lsn)
|
||||
{
|
||||
snprintf(backup_label, MAXPGPATH, "%s/backup_label", pgdata);
|
||||
if (fio_access(backup_label, F_OK, FIO_DB_HOST) == 0)
|
||||
{
|
||||
elog(WARNING, "Destination directory contains \"backup_control\" file. "
|
||||
"It does not mean that you should delete this file, only that "
|
||||
"lsn-based incremental restore is dangerous to use in this case. "
|
||||
"Consider to use checksum-based incremental restore");
|
||||
"This does NOT mean that you should delete this file and retry, only that "
|
||||
"lsn-based incremental restore can corrupt data, when applied to instance "
|
||||
"with pg_control not synchronized with cluster state."
|
||||
"Consider to use checksum-based incremental restore.");
|
||||
success = false;
|
||||
}
|
||||
}
|
||||
|
36
src/util.c
36
src/util.c
@ -351,6 +351,37 @@ get_pgcontrol_checksum(const char *pgdata_path)
|
||||
return ControlFile.crc;
|
||||
}
|
||||
|
||||
void
|
||||
get_redo(const char *pgdata_path, RedoParams *redo)
|
||||
{
|
||||
ControlFileData ControlFile;
|
||||
char *buffer;
|
||||
size_t size;
|
||||
|
||||
/* First fetch file... */
|
||||
buffer = slurpFile(pgdata_path, XLOG_CONTROL_FILE, &size, false, FIO_DB_HOST);
|
||||
|
||||
digestControlFile(&ControlFile, buffer, size);
|
||||
pg_free(buffer);
|
||||
|
||||
redo->lsn = ControlFile.checkPointCopy.redo;
|
||||
redo->tli = ControlFile.checkPointCopy.ThisTimeLineID;
|
||||
|
||||
if (ControlFile.minRecoveryPoint > 0 &&
|
||||
ControlFile.minRecoveryPoint < redo->lsn)
|
||||
{
|
||||
redo->lsn = ControlFile.minRecoveryPoint;
|
||||
redo->tli = ControlFile.minRecoveryPointTLI;
|
||||
}
|
||||
|
||||
if (ControlFile.backupStartPoint > 0 &&
|
||||
ControlFile.backupStartPoint < redo->lsn)
|
||||
{
|
||||
redo->lsn = ControlFile.backupStartPoint;
|
||||
redo->tli = ControlFile.checkPointCopy.ThisTimeLineID;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Rewrite minRecoveryPoint of pg_control in backup directory. minRecoveryPoint
|
||||
* 'as-is' is not to be trusted.
|
||||
@ -551,8 +582,9 @@ datapagemap_print_debug(datapagemap_t *map)
|
||||
}
|
||||
|
||||
/*
|
||||
* return pid of postmaster process running in given pgdata.
|
||||
* return 0 if there is none.
|
||||
* Return pid of postmaster process running in given pgdata.
|
||||
* Return 0 if there is none.
|
||||
* Return 1 if postmaster.pid is mangled.
|
||||
*/
|
||||
pid_t
|
||||
check_postmaster(const char *pgdata)
|
||||
|
196
src/utils/file.c
196
src/utils/file.c
@ -72,6 +72,14 @@ typedef struct
|
||||
uint32 checksumVersion;
|
||||
} fio_checksum_map_request;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
BlockNumber n_blocks;
|
||||
BlockNumber segmentno;
|
||||
XLogRecPtr horizonLsn;
|
||||
uint32 checksumVersion;
|
||||
} fio_lsn_map_request;
|
||||
|
||||
|
||||
/* Convert FIO pseudo handle to index in file descriptor array */
|
||||
#define fio_fileno(f) (((size_t)f - 1) | FIO_PIPE_MARKER)
|
||||
@ -99,6 +107,7 @@ void fio_error(int rc, int size, char const* file, int line)
|
||||
else
|
||||
{
|
||||
char buf[PRINTF_BUF_SIZE+1];
|
||||
// Assert(false);
|
||||
int err_size = read(fio_stderr, buf, PRINTF_BUF_SIZE);
|
||||
if (err_size > 0)
|
||||
{
|
||||
@ -200,14 +209,16 @@ static ssize_t fio_read_all(int fd, void* buf, size_t size)
|
||||
while (offs < size)
|
||||
{
|
||||
ssize_t rc = read(fd, (char*)buf + offs, size - offs);
|
||||
if (rc < 0) {
|
||||
if (errno == EINTR) {
|
||||
if (rc < 0)
|
||||
{
|
||||
if (errno == EINTR)
|
||||
continue;
|
||||
}
|
||||
elog(WARNING, "fio_read_all error: %s", strerror(errno));
|
||||
return rc;
|
||||
} else if (rc == 0) {
|
||||
break;
|
||||
}
|
||||
else if (rc == 0)
|
||||
break;
|
||||
|
||||
offs += rc;
|
||||
}
|
||||
return offs;
|
||||
@ -2260,43 +2271,53 @@ static void fio_list_dir_impl(int out, char* buf)
|
||||
IO_CHECK(fio_write_all(out, &hdr, sizeof(hdr)), sizeof(hdr));
|
||||
}
|
||||
|
||||
uint16 *fio_get_checksum_map(const char *fullpath, uint32 checksum_version,
|
||||
int n_blocks, XLogRecPtr dest_stop_lsn, BlockNumber segmentno)
|
||||
PageState *
|
||||
fio_get_checksum_map(const char *fullpath, uint32 checksum_version, int n_blocks,
|
||||
XLogRecPtr dest_stop_lsn, BlockNumber segmentno, fio_location location)
|
||||
{
|
||||
fio_header hdr;
|
||||
fio_checksum_map_request req_hdr;
|
||||
uint16 *checksum_map = NULL;
|
||||
size_t path_len = strlen(fullpath) + 1;
|
||||
|
||||
req_hdr.n_blocks = n_blocks;
|
||||
req_hdr.segmentno = segmentno;
|
||||
req_hdr.stop_lsn = dest_stop_lsn;
|
||||
req_hdr.checksumVersion = checksum_version;
|
||||
|
||||
hdr.cop = FIO_GET_CHECKSUM_MAP;
|
||||
hdr.size = sizeof(req_hdr) + path_len;
|
||||
|
||||
IO_CHECK(fio_write_all(fio_stdout, &hdr, sizeof(hdr)), sizeof(hdr));
|
||||
IO_CHECK(fio_write_all(fio_stdout, &req_hdr, sizeof(req_hdr)), sizeof(req_hdr));
|
||||
IO_CHECK(fio_write_all(fio_stdout, fullpath, path_len), path_len);
|
||||
|
||||
/* receive data */
|
||||
IO_CHECK(fio_read_all(fio_stdin, &hdr, sizeof(hdr)), sizeof(hdr));
|
||||
|
||||
if (hdr.size > 0)
|
||||
if (fio_is_remote(location))
|
||||
{
|
||||
checksum_map = pgut_malloc(hdr.size * sizeof(uint16));
|
||||
IO_CHECK(fio_read_all(fio_stdin, checksum_map, hdr.size * sizeof(uint16)), hdr.size * sizeof(uint16));
|
||||
}
|
||||
fio_header hdr;
|
||||
fio_checksum_map_request req_hdr;
|
||||
PageState *checksum_map = NULL;
|
||||
size_t path_len = strlen(fullpath) + 1;
|
||||
|
||||
return checksum_map;
|
||||
req_hdr.n_blocks = n_blocks;
|
||||
req_hdr.segmentno = segmentno;
|
||||
req_hdr.stop_lsn = dest_stop_lsn;
|
||||
req_hdr.checksumVersion = checksum_version;
|
||||
|
||||
hdr.cop = FIO_GET_CHECKSUM_MAP;
|
||||
hdr.size = sizeof(req_hdr) + path_len;
|
||||
|
||||
IO_CHECK(fio_write_all(fio_stdout, &hdr, sizeof(hdr)), sizeof(hdr));
|
||||
IO_CHECK(fio_write_all(fio_stdout, &req_hdr, sizeof(req_hdr)), sizeof(req_hdr));
|
||||
IO_CHECK(fio_write_all(fio_stdout, fullpath, path_len), path_len);
|
||||
|
||||
/* receive data */
|
||||
IO_CHECK(fio_read_all(fio_stdin, &hdr, sizeof(hdr)), sizeof(hdr));
|
||||
|
||||
if (hdr.size > 0)
|
||||
{
|
||||
checksum_map = pgut_malloc(n_blocks * sizeof(PageState));
|
||||
memset(checksum_map, 0, n_blocks * sizeof(PageState));
|
||||
IO_CHECK(fio_read_all(fio_stdin, checksum_map, hdr.size * sizeof(PageState)), hdr.size * sizeof(PageState));
|
||||
}
|
||||
|
||||
return checksum_map;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
return get_checksum_map(fullpath, checksum_version,
|
||||
n_blocks, dest_stop_lsn, segmentno);
|
||||
}
|
||||
}
|
||||
|
||||
/* TODO: sent n_blocks to truncate file before reading */
|
||||
static void fio_get_checksum_map_impl(int out, char *buf)
|
||||
{
|
||||
fio_header hdr;
|
||||
uint16 *checksum_map = NULL;
|
||||
PageState *checksum_map = NULL;
|
||||
char *fullpath = (char*) buf + sizeof(fio_checksum_map_request);
|
||||
fio_checksum_map_request *req = (fio_checksum_map_request*) buf;
|
||||
|
||||
@ -2307,24 +2328,107 @@ static void fio_get_checksum_map_impl(int out, char *buf)
|
||||
/* send arrays of checksums to main process */
|
||||
IO_CHECK(fio_write_all(out, &hdr, sizeof(hdr)), sizeof(hdr));
|
||||
if (hdr.size > 0)
|
||||
IO_CHECK(fio_write_all(out, checksum_map, hdr.size * sizeof(uint16)), hdr.size * sizeof(uint16));
|
||||
IO_CHECK(fio_write_all(out, checksum_map, hdr.size * sizeof(PageState)), hdr.size * sizeof(PageState));
|
||||
|
||||
pg_free(checksum_map);
|
||||
}
|
||||
|
||||
pid_t fio_check_postmaster(const char *pgdata)
|
||||
datapagemap_t *
|
||||
fio_get_lsn_map(const char *fullpath, uint32 checksum_version,
|
||||
int n_blocks, XLogRecPtr horizonLsn, BlockNumber segmentno,
|
||||
fio_location location)
|
||||
{
|
||||
fio_header hdr;
|
||||
datapagemap_t* lsn_map = NULL;
|
||||
|
||||
hdr.cop = FIO_CHECK_POSTMASTER;
|
||||
hdr.size = strlen(pgdata) + 1;
|
||||
if (fio_is_remote(location))
|
||||
{
|
||||
fio_header hdr;
|
||||
fio_lsn_map_request req_hdr;
|
||||
size_t path_len = strlen(fullpath) + 1;
|
||||
|
||||
IO_CHECK(fio_write_all(fio_stdout, &hdr, sizeof(hdr)), sizeof(hdr));
|
||||
IO_CHECK(fio_write_all(fio_stdout, pgdata, hdr.size), hdr.size);
|
||||
req_hdr.n_blocks = n_blocks;
|
||||
req_hdr.segmentno = segmentno;
|
||||
req_hdr.horizonLsn = horizonLsn;
|
||||
req_hdr.checksumVersion = checksum_version;
|
||||
|
||||
/* receive result */
|
||||
IO_CHECK(fio_read_all(fio_stdin, &hdr, sizeof(hdr)), sizeof(hdr));
|
||||
return hdr.arg;
|
||||
hdr.cop = FIO_GET_LSN_MAP;
|
||||
hdr.size = sizeof(req_hdr) + path_len;
|
||||
|
||||
IO_CHECK(fio_write_all(fio_stdout, &hdr, sizeof(hdr)), sizeof(hdr));
|
||||
IO_CHECK(fio_write_all(fio_stdout, &req_hdr, sizeof(req_hdr)), sizeof(req_hdr));
|
||||
IO_CHECK(fio_write_all(fio_stdout, fullpath, path_len), path_len);
|
||||
|
||||
/* receive data */
|
||||
IO_CHECK(fio_read_all(fio_stdin, &hdr, sizeof(hdr)), sizeof(hdr));
|
||||
|
||||
if (hdr.size > 0)
|
||||
{
|
||||
lsn_map = pgut_malloc(sizeof(datapagemap_t));
|
||||
memset(lsn_map, 0, sizeof(datapagemap_t));
|
||||
|
||||
lsn_map->bitmap = pgut_malloc(hdr.size);
|
||||
lsn_map->bitmapsize = hdr.size;
|
||||
|
||||
IO_CHECK(fio_read_all(fio_stdin, lsn_map->bitmap, hdr.size), hdr.size);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
lsn_map = get_lsn_map(fullpath, checksum_version, n_blocks,
|
||||
horizonLsn, segmentno);
|
||||
}
|
||||
|
||||
return lsn_map;
|
||||
}
|
||||
|
||||
static void fio_get_lsn_map_impl(int out, char *buf)
|
||||
{
|
||||
fio_header hdr;
|
||||
datapagemap_t *lsn_map = NULL;
|
||||
char *fullpath = (char*) buf + sizeof(fio_lsn_map_request);
|
||||
fio_lsn_map_request *req = (fio_lsn_map_request*) buf;
|
||||
|
||||
lsn_map = get_lsn_map(fullpath, req->checksumVersion, req->n_blocks,
|
||||
req->horizonLsn, req->segmentno);
|
||||
if (lsn_map)
|
||||
hdr.size = lsn_map->bitmapsize;
|
||||
else
|
||||
hdr.size = 0;
|
||||
|
||||
/* send arrays of checksums to main process */
|
||||
IO_CHECK(fio_write_all(out, &hdr, sizeof(hdr)), sizeof(hdr));
|
||||
if (hdr.size > 0)
|
||||
IO_CHECK(fio_write_all(out, lsn_map->bitmap, hdr.size), hdr.size);
|
||||
|
||||
if (lsn_map)
|
||||
{
|
||||
pg_free(lsn_map->bitmap);
|
||||
pg_free(lsn_map);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Go to the remote host and get postmaster pid from file postmaster.pid
|
||||
* and check that process is running, if process is running, return its pid number.
|
||||
*/
|
||||
pid_t fio_check_postmaster(const char *pgdata, fio_location location)
|
||||
{
|
||||
if (fio_is_remote(location))
|
||||
{
|
||||
fio_header hdr;
|
||||
|
||||
hdr.cop = FIO_CHECK_POSTMASTER;
|
||||
hdr.size = strlen(pgdata) + 1;
|
||||
|
||||
IO_CHECK(fio_write_all(fio_stdout, &hdr, sizeof(hdr)), sizeof(hdr));
|
||||
IO_CHECK(fio_write_all(fio_stdout, pgdata, hdr.size), hdr.size);
|
||||
|
||||
/* receive result */
|
||||
IO_CHECK(fio_read_all(fio_stdin, &hdr, sizeof(hdr)), sizeof(hdr));
|
||||
return hdr.arg;
|
||||
}
|
||||
else
|
||||
return check_postmaster(pgdata);
|
||||
}
|
||||
|
||||
static void fio_check_postmaster_impl(int out, char *buf)
|
||||
@ -2515,6 +2619,10 @@ void fio_communicate(int in, int out)
|
||||
/* calculate crc32 for a file */
|
||||
fio_get_checksum_map_impl(out, buf);
|
||||
break;
|
||||
case FIO_GET_LSN_MAP:
|
||||
/* calculate crc32 for a file */
|
||||
fio_get_lsn_map_impl(out, buf);
|
||||
break;
|
||||
case FIO_CHECK_POSTMASTER:
|
||||
/* calculate crc32 for a file */
|
||||
fio_check_postmaster_impl(out, buf);
|
||||
|
@ -35,7 +35,9 @@ typedef enum
|
||||
FIO_PAGE,
|
||||
FIO_WRITE_COMPRESSED,
|
||||
FIO_GET_CRC32,
|
||||
/* used for incremental restore */
|
||||
FIO_GET_CHECKSUM_MAP,
|
||||
FIO_GET_LSN_MAP,
|
||||
/* used in fio_send_pages */
|
||||
FIO_SEND_PAGES,
|
||||
FIO_ERROR,
|
||||
|
Loading…
Reference in New Issue
Block a user