Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ OBJS = src/utils/configuration.o src/utils/json.o src/utils/logger.o \
OBJS += src/archive.o src/backup.o src/catalog.o src/checkdb.o src/configure.o src/data.o \
src/delete.o src/dir.o src/fetch.o src/help.o src/init.o src/merge.o \
src/parsexlog.o src/ptrack.o src/pg_probackup.o src/restore.o src/show.o src/stream.o \
src/util.o src/validate.o src/datapagemap.o src/catchup.o
src/util.o src/validate.o src/datapagemap.o src/catchup.o src/walsummary.o

# borrowed files
OBJS += src/pg_crc.o src/receivelog.o src/streamutil.o \
Expand Down
44 changes: 42 additions & 2 deletions src/backup.c
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,8 @@ do_backup_pg(InstanceState *instanceState, PGconn *backup_conn,
*/
if (current.backup_mode == BACKUP_MODE_DIFF_PAGE ||
current.backup_mode == BACKUP_MODE_DIFF_PTRACK ||
current.backup_mode == BACKUP_MODE_DIFF_DELTA)
current.backup_mode == BACKUP_MODE_DIFF_DELTA ||
current.backup_mode == BACKUP_MODE_DIFF_SUMMARIZE)
{
/* get list of backups already taken */
backup_list = catalog_get_backup_list(instanceState, INVALID_BACKUP_ID);
Expand Down Expand Up @@ -231,6 +232,34 @@ do_backup_pg(InstanceState *instanceState, PGconn *backup_conn,
}
}

/*
* For SUMMARIZE backup mode, verify that WAL summarize is enabled
* and wait for the summarizer to catch up to the required LSN.
*
* We need to wait for WAL summary up to prev_backup->start_lsn to be
* available before starting the incremental backup. If the summarizer
* hasn't caught up within the timeout period, the backup will fail.
*/
if (current.backup_mode == BACKUP_MODE_DIFF_SUMMARIZE)
{
if (!pg_is_walsummary_enabled(backup_conn))
elog(ERROR, "WAL summarize backup mode requires summarize_wal to be enabled");

/*
* Wait for WAL summarizer to catch up to the previous backup start LSN.
* This ensures that all WAL summary files needed for this incremental
* backup are available before we start.
*
* If the summarizer hasn't caught up within 60 seconds, the backup
* will fail with an error, preventing a backup that would miss data.
*/
if (!wait_wal_summarization(backup_conn, prev_backup->start_lsn))
elog(ERROR, "WAL summarizer did not catch up to %X/%X within timeout period. "
"Incremental backup cannot proceed without complete WAL summaries.",
(uint32) (prev_backup->start_lsn >> 32),
(uint32) (prev_backup->start_lsn));
}

/* For incremental backup check that start_lsn is not from the past
* Though it will not save us if PostgreSQL instance is actually
* restored STREAM backup.
Expand Down Expand Up @@ -357,7 +386,8 @@ do_backup_pg(InstanceState *instanceState, PGconn *backup_conn,
*/

if (current.backup_mode == BACKUP_MODE_DIFF_PAGE ||
current.backup_mode == BACKUP_MODE_DIFF_PTRACK)
current.backup_mode == BACKUP_MODE_DIFF_PTRACK ||
current.backup_mode == BACKUP_MODE_DIFF_SUMMARIZE)
{
bool pagemap_isok = true;

Expand Down Expand Up @@ -386,6 +416,16 @@ do_backup_pg(InstanceState *instanceState, PGconn *backup_conn,
nodeInfo->ptrack_version_num,
prev_backup_start_lsn);
}
else if (current.backup_mode == BACKUP_MODE_DIFF_SUMMARIZE)
{
/*
* Build the page map from WAL summary information.
*/
make_pagemap_from_walsummary(backup_files_list, backup_conn,
prev_backup->start_lsn,
current.start_lsn,
current.tli);
}

time(&end_time);

Expand Down
6 changes: 5 additions & 1 deletion src/catalog.c
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@

static pgBackup* get_closest_backup(timelineInfo *tlinfo);
static pgBackup* get_oldest_backup(timelineInfo *tlinfo);
static const char *backupModes[] = {"", "PAGE", "PTRACK", "DELTA", "FULL"};
static const char *backupModes[] = {"", "PAGE", "PTRACK", "DELTA", "SUMMARIZE", "FULL"};
static pgBackup *readBackupControlFile(const char *path);
static int create_backup_dir(pgBackup *backup, const char *backup_instance_path);

Expand Down Expand Up @@ -2839,6 +2839,8 @@ parse_backup_mode(const char *value)
return BACKUP_MODE_DIFF_PTRACK;
else if (len > 0 && pg_strncasecmp("delta", v, len) == 0)
return BACKUP_MODE_DIFF_DELTA;
else if (len > 0 && pg_strncasecmp("summarize", v, len) == 0)
return BACKUP_MODE_DIFF_SUMMARIZE;

/* Backup mode is invalid, so leave with an error */
elog(ERROR, "Invalid backup-mode \"%s\"", value);
Expand All @@ -2858,6 +2860,8 @@ deparse_backup_mode(BackupMode mode)
return "ptrack";
case BACKUP_MODE_DIFF_DELTA:
return "delta";
case BACKUP_MODE_DIFF_SUMMARIZE:
return "summarize";
case BACKUP_MODE_INVALID:
return "invalid";
}
Expand Down
43 changes: 42 additions & 1 deletion src/catchup.c
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,8 @@ catchup_preflight_checks(PGNodeInfo *source_node_info, PGconn *source_conn,
if (dir_is_empty(dest_pgdata, FIO_LOCAL_HOST))
{
if (current.backup_mode == BACKUP_MODE_DIFF_PTRACK ||
current.backup_mode == BACKUP_MODE_DIFF_DELTA)
current.backup_mode == BACKUP_MODE_DIFF_DELTA ||
current.backup_mode == BACKUP_MODE_DIFF_SUMMARIZE)
elog(ERROR, "\"%s\" is empty, but incremental catchup mode requested.",
dest_pgdata);
}
Expand Down Expand Up @@ -193,6 +194,14 @@ catchup_preflight_checks(PGNodeInfo *source_node_info, PGconn *source_conn,
elog(ERROR, "Ptrack is disabled");
}

/* check WAL summarize support */
if (current.backup_mode == BACKUP_MODE_DIFF_SUMMARIZE)
{
PGconn *conn = pgdata_basic_setup(instance_config.conn_opt, source_node_info);
if (!pg_is_walsummary_enabled(conn))
elog(ERROR, "WAL summarize backup mode requires summarize_wal to be enabled in PostgreSQL 17+");
}

if (current.from_replica && exclusive_backup)
elog(ERROR, "Catchup from standby is only available for PostgreSQL >= 9.6");

Expand Down Expand Up @@ -693,6 +702,22 @@ do_catchup(const char *source_pgdata, const char *dest_pgdata, int num_threads,
(uint32) (dest_redo.lsn));
}

/*
* Make sure that sync point is within WAL summarize tracking range
*/
if (current.backup_mode == BACKUP_MODE_DIFF_SUMMARIZE)
{
XLogRecPtr summarized_lsn = get_walsummary_summarized_lsn(source_conn);

if (summarized_lsn == InvalidXLogRecPtr)
elog(ERROR, "WAL summarizer state is not available");
if (summarized_lsn < dest_redo.lsn)
elog(WARNING, "WAL summarizer has only reached %X/%X, which is before destination checkpoint LSN %X/%X. "
"Some changes may be missed.",
(uint32) (summarized_lsn >> 32), (uint32) (summarized_lsn),
(uint32) (dest_redo.lsn >> 32), (uint32) (dest_redo.lsn));
}

{
char label[1024];
/* notify start of backup to PostgreSQL server */
Expand Down Expand Up @@ -801,6 +826,22 @@ do_catchup(const char *source_pgdata, const char *dest_pgdata, int num_threads,
difftime(end_time, start_time));
}

/* Build page mapping in SUMMARIZE mode */
if (current.backup_mode == BACKUP_MODE_DIFF_SUMMARIZE)
{
time(&start_time);
elog(INFO, "Extracting pagemap of changed blocks from WAL summary");

/* Build the page map from WAL summary information */
make_pagemap_from_walsummary(source_filelist, source_conn,
dest_redo.lsn,
current.start_lsn,
current.tli);
time(&end_time);
elog(INFO, "Pagemap successfully extracted, time elapsed: %.0f sec",
difftime(end_time, start_time));
}

/*
* Make directories before catchup
*/
Expand Down
32 changes: 23 additions & 9 deletions src/data.c
Original file line number Diff line number Diff line change
Expand Up @@ -329,8 +329,10 @@ prepare_page(pgFile *file, XLogRecPtr prev_backup_start_lsn,
return PageIsOk;

case PAGE_IS_VALID:
/* in DELTA or PTRACK modes we must compare lsn */
if (backup_mode == BACKUP_MODE_DIFF_DELTA || backup_mode == BACKUP_MODE_DIFF_PTRACK)
/* in DELTA, PTRACK or SUMMARIZE modes we must compare lsn */
if (backup_mode == BACKUP_MODE_DIFF_DELTA ||
backup_mode == BACKUP_MODE_DIFF_PTRACK ||
backup_mode == BACKUP_MODE_DIFF_SUMMARIZE)
page_is_valid = true;
else
return PageIsOk;
Expand Down Expand Up @@ -394,7 +396,9 @@ prepare_page(pgFile *file, XLogRecPtr prev_backup_start_lsn,
* Skip page if page lsn is less than START_LSN of parent backup.
* Nullified pages must be copied by DELTA backup, just to be safe.
*/
if ((backup_mode == BACKUP_MODE_DIFF_DELTA || backup_mode == BACKUP_MODE_DIFF_PTRACK) &&
if ((backup_mode == BACKUP_MODE_DIFF_DELTA ||
backup_mode == BACKUP_MODE_DIFF_PTRACK ||
backup_mode == BACKUP_MODE_DIFF_SUMMARIZE) &&
file->exists_in_prev &&
page_st->lsn > 0 &&
page_st->lsn < prev_backup_start_lsn)
Expand Down Expand Up @@ -513,7 +517,8 @@ backup_data_file(pgFile *file, const char *from_fullpath, const char *to_fullpat
* not tracked by pagemap and thus always marked as unchanged.
*/
if ((backup_mode == BACKUP_MODE_DIFF_PAGE ||
backup_mode == BACKUP_MODE_DIFF_PTRACK) &&
backup_mode == BACKUP_MODE_DIFF_PTRACK ||
backup_mode == BACKUP_MODE_DIFF_SUMMARIZE) &&
file->pagemap.bitmapsize == PageBitmapIsEmpty &&
file->exists_in_prev && !file->pagemap_isabsent)
{
Expand Down Expand Up @@ -553,7 +558,9 @@ backup_data_file(pgFile *file, const char *from_fullpath, const char *to_fullpat
{
rc = fio_send_pages(to_fullpath, from_fullpath, file,
/* send prev backup START_LSN */
(backup_mode == BACKUP_MODE_DIFF_DELTA || backup_mode == BACKUP_MODE_DIFF_PTRACK) &&
(backup_mode == BACKUP_MODE_DIFF_DELTA ||
backup_mode == BACKUP_MODE_DIFF_PTRACK ||
backup_mode == BACKUP_MODE_DIFF_SUMMARIZE) &&
file->exists_in_prev ? prev_backup_start_lsn : InvalidXLogRecPtr,
calg, clevel, checksum_version,
/* send pagemap if any */
Expand All @@ -566,7 +573,9 @@ backup_data_file(pgFile *file, const char *from_fullpath, const char *to_fullpat
/* TODO: stop handling errors internally */
rc = send_pages(to_fullpath, from_fullpath, file,
/* send prev backup START_LSN */
(backup_mode == BACKUP_MODE_DIFF_DELTA || backup_mode == BACKUP_MODE_DIFF_PTRACK) &&
(backup_mode == BACKUP_MODE_DIFF_DELTA ||
backup_mode == BACKUP_MODE_DIFF_PTRACK ||
backup_mode == BACKUP_MODE_DIFF_SUMMARIZE) &&
file->exists_in_prev ? prev_backup_start_lsn : InvalidXLogRecPtr,
calg, clevel, checksum_version, use_pagemap,
&headers, backup_mode);
Expand Down Expand Up @@ -667,7 +676,8 @@ catchup_data_file(pgFile *file, const char *from_fullpath, const char *to_fullpa
* This way we can correctly handle null-sized files which are
* not tracked by pagemap and thus always marked as unchanged.
*/
if (backup_mode == BACKUP_MODE_DIFF_PTRACK &&
if ((backup_mode == BACKUP_MODE_DIFF_PTRACK ||
backup_mode == BACKUP_MODE_DIFF_SUMMARIZE) &&
file->pagemap.bitmapsize == PageBitmapIsEmpty &&
file->exists_in_prev && file->size == prev_size && !file->pagemap_isabsent)
{
Expand Down Expand Up @@ -703,7 +713,9 @@ catchup_data_file(pgFile *file, const char *from_fullpath, const char *to_fullpa
{
rc = fio_copy_pages(to_fullpath, from_fullpath, file,
/* send prev backup START_LSN */
((backup_mode == BACKUP_MODE_DIFF_DELTA || backup_mode == BACKUP_MODE_DIFF_PTRACK) &&
((backup_mode == BACKUP_MODE_DIFF_DELTA ||
backup_mode == BACKUP_MODE_DIFF_PTRACK ||
backup_mode == BACKUP_MODE_DIFF_SUMMARIZE) &&
file->exists_in_prev) ? sync_lsn : InvalidXLogRecPtr,
NONE_COMPRESS, 1, checksum_version,
/* send pagemap if any */
Expand All @@ -716,7 +728,9 @@ catchup_data_file(pgFile *file, const char *from_fullpath, const char *to_fullpa
/* TODO: stop handling errors internally */
rc = copy_pages(to_fullpath, from_fullpath, file,
/* send prev backup START_LSN */
((backup_mode == BACKUP_MODE_DIFF_DELTA || backup_mode == BACKUP_MODE_DIFF_PTRACK) &&
((backup_mode == BACKUP_MODE_DIFF_DELTA ||
backup_mode == BACKUP_MODE_DIFF_PTRACK ||
backup_mode == BACKUP_MODE_DIFF_SUMMARIZE) &&
file->exists_in_prev) ? sync_lsn : InvalidXLogRecPtr,
checksum_version, use_pagemap, backup_mode);
}
Expand Down
4 changes: 2 additions & 2 deletions src/help.c
Original file line number Diff line number Diff line change
Expand Up @@ -334,7 +334,7 @@ help_backup(void)
printf(_(" [--ttl=interval] [--expire-time=timestamp] [--note=text]\n\n"));

printf(_(" -B, --backup-path=backup-dir location of the backup storage area\n"));
printf(_(" -b, --backup-mode=backup-mode backup mode=FULL|PAGE|DELTA|PTRACK\n"));
printf(_(" -b, --backup-mode=backup-mode backup mode=FULL|PAGE|DELTA|PTRACK|SUMMARIZE\n"));
printf(_(" --instance=instance-name name of the instance\n"));
printf(_(" -D, --pgdata=pgdata-path location of the database storage area\n"));
printf(_(" -C, --smooth-checkpoint do smooth checkpoint before backup\n"));
Expand Down Expand Up @@ -1183,7 +1183,7 @@ help_catchup(void)
printf(_(" [--dry-run]\n"));
printf(_(" [--help]\n\n"));

printf(_(" -b, --backup-mode=catchup-mode catchup mode=FULL|DELTA|PTRACK\n"));
printf(_(" -b, --backup-mode=catchup-mode catchup mode=FULL|DELTA|PTRACK|SUMMARIZE\n"));
printf(_(" --stream stream the transaction log (only supported mode)\n"));
printf(_(" -S, --slot=SLOTNAME replication slot to use\n"));
printf(_(" --temp-slot use temporary replication slot\n"));
Expand Down
5 changes: 3 additions & 2 deletions src/pg_probackup.c
Original file line number Diff line number Diff line change
Expand Up @@ -843,8 +843,9 @@ main(int argc, char *argv[])
elog(ERROR, "No backup mode specified.\n"
"Please specify it either using environment variable BACKUP_MODE or\n"
"command line option --backup-mode (-b)");
if (current.backup_mode != BACKUP_MODE_FULL && current.backup_mode != BACKUP_MODE_DIFF_PTRACK && current.backup_mode != BACKUP_MODE_DIFF_DELTA)
elog(ERROR, "Only \"FULL\", \"PTRACK\" and \"DELTA\" modes are supported with the \"%s\" command", get_subcmd_name(backup_subcmd));
if (current.backup_mode != BACKUP_MODE_FULL && current.backup_mode != BACKUP_MODE_DIFF_PTRACK &&
current.backup_mode != BACKUP_MODE_DIFF_DELTA && current.backup_mode != BACKUP_MODE_DIFF_SUMMARIZE)
elog(ERROR, "Only \"FULL\", \"PTRACK\", \"DELTA\" and \"SUMMARIZE\" modes are supported with the \"%s\" command", get_subcmd_name(backup_subcmd));
if (!stream_wal)
elog(INFO, "--stream is required, forcing stream mode");
current.stream = stream_wal = true;
Expand Down
9 changes: 9 additions & 0 deletions src/pg_probackup.h
Original file line number Diff line number Diff line change
Expand Up @@ -341,6 +341,7 @@ typedef enum BackupMode
BACKUP_MODE_DIFF_PAGE, /* incremental page backup */
BACKUP_MODE_DIFF_PTRACK, /* incremental page backup with ptrack system */
BACKUP_MODE_DIFF_DELTA, /* incremental page backup with lsn comparison */
BACKUP_MODE_DIFF_SUMMARIZE, /* incremental page backup using PostgreSQL native WAL summarize */
BACKUP_MODE_FULL /* full backup */
} BackupMode;

Expand Down Expand Up @@ -1270,6 +1271,14 @@ extern XLogRecPtr get_last_ptrack_lsn(PGconn *backup_conn, PGNodeInfo *nodeInfo)
extern parray * pg_ptrack_get_pagemapset(PGconn *backup_conn, const char *ptrack_schema,
int ptrack_version_num, XLogRecPtr lsn);

/* in walsummary.c */
extern void make_pagemap_from_walsummary(parray* files, PGconn* backup_conn,
XLogRecPtr start_lsn, XLogRecPtr end_lsn,
TimeLineID tli);
extern bool pg_is_walsummary_enabled(PGconn *backup_conn);
extern XLogRecPtr get_walsummary_summarized_lsn(PGconn *backup_conn);
extern bool wait_wal_summarization(PGconn *backup_conn, XLogRecPtr target_lsn);

/* open local file to writing */
extern FILE* open_local_file_rw(const char *to_fullpath, char **out_buf, uint32 buf_size);

Expand Down
3 changes: 2 additions & 1 deletion src/validate.c
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,8 @@ pgBackupValidate(pgBackup *backup, pgRestoreParams *params)
if (backup->backup_mode != BACKUP_MODE_FULL &&
backup->backup_mode != BACKUP_MODE_DIFF_PAGE &&
backup->backup_mode != BACKUP_MODE_DIFF_PTRACK &&
backup->backup_mode != BACKUP_MODE_DIFF_DELTA)
backup->backup_mode != BACKUP_MODE_DIFF_DELTA &&
backup->backup_mode != BACKUP_MODE_DIFF_SUMMARIZE)
elog(WARNING, "Invalid backup_mode of backup %s", backup_id_of(backup));

join_path_components(external_prefix, backup->root_dir, EXTERNAL_DIR);
Expand Down
Loading