Skip to content

Commit 70d7e46

Browse files
committed
PGPRO-1457: backup mode DELTA
1 parent 0ef622f commit 70d7e46

File tree

8 files changed

+394
-10
lines changed

8 files changed

+394
-10
lines changed

src/backup.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -479,7 +479,8 @@ do_backup_instance(void)
479479
* backup on current timeline exists and get its filelist.
480480
*/
481481
if (current.backup_mode == BACKUP_MODE_DIFF_PAGE ||
482-
current.backup_mode == BACKUP_MODE_DIFF_PTRACK)
482+
current.backup_mode == BACKUP_MODE_DIFF_PTRACK ||
483+
current.backup_mode == BACKUP_MODE_DIFF_DELTA)
483484
{
484485
parray *backup_list;
485486
/* get list of backups already taken */

src/catalog.c

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
#include <time.h>
2222
#include <unistd.h>
2323

24-
static const char *backupModes[] = {"", "PAGE", "PTRACK", "FULL"};
24+
static const char *backupModes[] = {"", "PAGE", "PTRACK", "DELTA", "FULL"};
2525
static pgBackup *readBackupControlFile(const char *path);
2626

2727
static bool exit_hook_registered = false;
@@ -593,6 +593,8 @@ parse_backup_mode(const char *value)
593593
return BACKUP_MODE_DIFF_PAGE;
594594
else if (len > 0 && pg_strncasecmp("ptrack", v, len) == 0)
595595
return BACKUP_MODE_DIFF_PTRACK;
596+
else if (len > 0 && pg_strncasecmp("delta", v, len) == 0)
597+
return BACKUP_MODE_DIFF_DELTA;
596598

597599
/* Backup mode is invalid, so leave with an error */
598600
elog(ERROR, "invalid backup-mode \"%s\"", value);
@@ -610,6 +612,8 @@ deparse_backup_mode(BackupMode mode)
610612
return "page";
611613
case BACKUP_MODE_DIFF_PTRACK:
612614
return "ptrack";
615+
case BACKUP_MODE_DIFF_DELTA:
616+
return "delta";
613617
case BACKUP_MODE_INVALID:
614618
return "invalid";
615619
}

src/data.c

Lines changed: 46 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -132,11 +132,10 @@ parse_page(Page page, XLogRecPtr *lsn)
132132
*/
133133
static int
134134
read_page_from_file(pgFile *file, BlockNumber blknum,
135-
FILE *in, Page page)
135+
FILE *in, Page page, XLogRecPtr *page_lsn)
136136
{
137137
off_t offset = blknum*BLCKSZ;
138138
size_t read_len = 0;
139-
XLogRecPtr page_lsn;
140139

141140
/* read the block */
142141
if (fseek(in, offset, SEEK_SET) != 0)
@@ -167,7 +166,7 @@ read_page_from_file(pgFile *file, BlockNumber blknum,
167166
* If after several attempts page header is still invalid, throw an error.
168167
* The same idea is applied to checksum verification.
169168
*/
170-
if (!parse_page(page, &page_lsn))
169+
if (!parse_page(page, page_lsn))
171170
{
172171
int i;
173172
/* Check if the page is zeroed. */
@@ -232,6 +231,7 @@ backup_data_page(backup_files_args *arguments,
232231
BackupPageHeader header;
233232
Page page = malloc(BLCKSZ);
234233
Page compressed_page = NULL;
234+
XLogRecPtr page_lsn = 0;
235235
size_t write_buffer_size;
236236
char write_buffer[BLCKSZ+sizeof(header)];
237237

@@ -252,7 +252,7 @@ backup_data_page(backup_files_args *arguments,
252252
while(!page_is_valid && try_again)
253253
{
254254
int result = read_page_from_file(file, blknum,
255-
in, page);
255+
in, page, &page_lsn);
256256

257257
try_again--;
258258
if (result == 0)
@@ -316,6 +316,20 @@ backup_data_page(backup_files_args *arguments,
316316
if (is_checksum_enabled)
317317
((PageHeader) page)->pd_checksum = pg_checksum_page(page, absolute_blknum);
318318
}
319+
/* get lsn from page */
320+
if (!parse_page(page, &page_lsn))
321+
elog(ERROR, "Cannot parse page after pg_ptrack_get_block. "
322+
"Possible risk of a memory corruption");
323+
324+
}
325+
326+
if (backup_mode == BACKUP_MODE_DIFF_DELTA &&
327+
header.compressed_size != PageIsTruncated &&
328+
page_lsn < prev_backup_start_lsn)
329+
{
330+
elog(VERBOSE, "Skipping blknum: %u in file: %s", blknum, file->path);
331+
free(page);
332+
return;
319333
}
320334

321335
if (header.compressed_size != PageIsTruncated)
@@ -478,6 +492,8 @@ backup_data_file(backup_files_args* arguments,
478492
&n_blocks_skipped, backup_mode);
479493
n_blocks_read++;
480494
}
495+
if (backup_mode == BACKUP_MODE_DIFF_DELTA)
496+
file->n_blocks = n_blocks_read;
481497
}
482498
/* If page map is not empty we scan only changed blocks, */
483499
else
@@ -545,6 +561,7 @@ restore_data_file(const char *from_root,
545561
FILE *out;
546562
BackupPageHeader header;
547563
BlockNumber blknum;
564+
size_t file_size;
548565

549566
/* open backup mode file for read */
550567
in = fopen(file->path, "r");
@@ -652,6 +669,31 @@ restore_data_file(const char *from_root,
652669
}
653670
}
654671

672+
/*
673+
* DELTA backup has no knowledge about truncated blocks as PAGE or PTRACK do
674+
* but knows file size at the time of backup.
675+
* So when restoring file from delta backup we, knowning it`s size at
676+
* a time of a backup, can truncate file to this size.
677+
*/
678+
679+
if (backup->backup_mode == BACKUP_MODE_DIFF_DELTA)
680+
{
681+
/* get file current size */
682+
fseek(out, 0, SEEK_END);
683+
file_size = ftell(out);
684+
685+
if (file_size > file->n_blocks * BLCKSZ)
686+
{
687+
/*
688+
* Truncate file to this length.
689+
*/
690+
if (ftruncate(fileno(out), file->n_blocks * BLCKSZ) != 0)
691+
elog(ERROR, "cannot truncate \"%s\": %s",
692+
file->path, strerror(errno));
693+
elog(INFO, "Delta truncate file %s to block %u", file->path, file->n_blocks);
694+
}
695+
}
696+
655697
/* update file permission */
656698
if (chmod(to_path, file->mode) == -1)
657699
{

src/dir.c

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,7 @@ pgFileInit(const char *path)
166166
file->path = pgut_malloc(strlen(path) + 1);
167167
strcpy(file->path, path); /* enough buffer size guaranteed */
168168
file->is_cfs = false;
169+
file->n_blocks = -1; /* can change only in DELTA backups */
169170
file->compress_alg = NOT_DEFINED_COMPRESS;
170171
return file;
171172
}
@@ -721,6 +722,9 @@ print_file_list(FILE *out, const parray *files, const char *root)
721722
if (S_ISLNK(file->mode))
722723
fprintf(out, ",\"linked\":\"%s\"", file->linked);
723724

725+
if (file->n_blocks != -1)
726+
fprintf(out, ",\"n_blocks\":\"%i\"", file->n_blocks);
727+
724728
fprintf(out, "}\n");
725729
}
726730
}
@@ -888,7 +892,8 @@ dir_read_file_list(const char *root, const char *file_txt)
888892
is_datafile,
889893
is_cfs,
890894
crc,
891-
segno;
895+
segno,
896+
n_blocks;
892897
pgFile *file;
893898

894899
get_control_value(buf, "path", path, NULL, true);
@@ -902,6 +907,7 @@ dir_read_file_list(const char *root, const char *file_txt)
902907
get_control_value(buf, "linked", linked, NULL, false);
903908
get_control_value(buf, "segno", NULL, &segno, false);
904909
get_control_value(buf, "compress_alg", compress_alg_string, NULL, false);
910+
get_control_value(buf, "n_blocks", NULL, &n_blocks, false);
905911

906912
if (root)
907913
join_path_components(filepath, root, path);
@@ -919,6 +925,7 @@ dir_read_file_list(const char *root, const char *file_txt)
919925
if (linked[0])
920926
file->linked = pgut_strdup(linked);
921927
file->segno = (int) segno;
928+
file->n_blocks = (int) n_blocks;
922929

923930
parray_append(files, file);
924931
}

src/help.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -190,7 +190,7 @@ help_backup(void)
190190
printf(_(" [--replica-timeout=timeout]\n\n"));
191191

192192
printf(_(" -B, --backup-path=backup-path location of the backup storage area\n"));
193-
printf(_(" -b, --backup-mode=backup-mode backup mode=FULL|PAGE|PTRACK\n"));
193+
printf(_(" -b, --backup-mode=backup-mode backup mode=FULL|PAGE|DELTA|PTRACK\n"));
194194
printf(_(" --instance=instance_name name of the instance\n"));
195195
printf(_(" -C, --smooth-checkpoint do smooth checkpoint before backup\n"));
196196
printf(_(" --stream stream the transaction log and include it in the backup\n"));

src/pg_probackup.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@ typedef struct pgFile
8383
char *name; /* file or directory name */
8484
mode_t mode; /* protection (file type and permission) */
8585
size_t size; /* size of the file */
86+
int n_blocks; /* size of the file in blocks, readed during DELTA backup */
8687
size_t read_size; /* size of the portion read (if only some pages are
8788
backed up, it's different from size) */
8889
size_t write_size; /* size of the backed-up file. BYTES_INVALID means
@@ -126,7 +127,8 @@ typedef enum BackupMode
126127
{
127128
BACKUP_MODE_INVALID = 0,
128129
BACKUP_MODE_DIFF_PAGE, /* incremental page backup */
129-
BACKUP_MODE_DIFF_PTRACK, /* incremental page backup with ptrack system*/
130+
BACKUP_MODE_DIFF_PTRACK, /* incremental page backup with ptrack system */
131+
BACKUP_MODE_DIFF_DELTA, /* incremental page backup with lsn comparison */
130132
BACKUP_MODE_FULL /* full backup */
131133
} BackupMode;
132134

src/validate.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,8 @@ pgBackupValidate(pgBackup *backup)
5151

5252
if (backup->backup_mode != BACKUP_MODE_FULL &&
5353
backup->backup_mode != BACKUP_MODE_DIFF_PAGE &&
54-
backup->backup_mode != BACKUP_MODE_DIFF_PTRACK)
54+
backup->backup_mode != BACKUP_MODE_DIFF_PTRACK &&
55+
backup->backup_mode != BACKUP_MODE_DIFF_DELTA)
5556
elog(INFO, "Invalid backup_mode of backup %s", base36enc(backup->start_time));
5657

5758
pgBackupGetPath(backup, base_path, lengthof(base_path), DATABASE_DIR);

0 commit comments

Comments
 (0)