Skip to content

Commit 541195b

Browse files
committed
fix PGPRO-1507: null-sized files were not copied to backup
1 parent 1b61ca8 commit 541195b

File tree

7 files changed

+219
-64
lines changed

7 files changed

+219
-64
lines changed

src/backup.c

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -586,7 +586,7 @@ do_backup_instance(void)
586586
* For backup from master wait for previous segment.
587587
* For backup from replica wait for current segment.
588588
*/
589-
!from_replica);
589+
!from_replica, backup_files_list);
590590
}
591591

592592
if (current.backup_mode == BACKUP_MODE_DIFF_PTRACK)
@@ -1948,7 +1948,7 @@ backup_files(void *arg)
19481948
if (S_ISREG(buf.st_mode))
19491949
{
19501950
/* Check that file exist in previous backup */
1951-
if (current.backup_mode == BACKUP_MODE_DIFF_DELTA)
1951+
if (current.backup_mode != BACKUP_MODE_FULL)
19521952
{
19531953
int p;
19541954
char *relative;
@@ -1961,7 +1961,7 @@ backup_files(void *arg)
19611961
{
19621962
/* File exists in previous backup */
19631963
file->exists_in_prev = true;
1964-
elog(INFO, "File exists at the time of previous backup %s", relative);
1964+
// elog(VERBOSE, "File exists at the time of previous backup %s", relative);
19651965
break;
19661966
}
19671967
}
@@ -1981,7 +1981,17 @@ backup_files(void *arg)
19811981
continue;
19821982
}
19831983
}
1984-
else if (!copy_file(arguments->from_root,
1984+
else
1985+
/* TODO:
1986+
* Check if file exists in previous backup
1987+
* If exists:
1988+
* if mtime > start_backup_time of parent backup,
1989+
* copy file to backup
1990+
* if mtime < start_backup_time
1991+
* calculate crc, compare crc to old file
1992+
* if crc is the same -> skip file
1993+
*/
1994+
if (!copy_file(arguments->from_root,
19851995
arguments->to_root,
19861996
file))
19871997
{

src/data.c

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -422,15 +422,21 @@ backup_data_file(backup_files_args* arguments,
422422
int n_blocks_skipped = 0;
423423
int n_blocks_read = 0;
424424

425+
/*
426+
* Skip unchanged file only if it exists in previous backup.
427+
* This way we can correctly handle null-sized files which are
428+
* not tracked by pagemap and thus always marked as unchanged.
429+
*/
425430
if ((backup_mode == BACKUP_MODE_DIFF_PAGE ||
426431
backup_mode == BACKUP_MODE_DIFF_PTRACK) &&
427-
file->pagemap.bitmapsize == PageBitmapIsEmpty)
432+
file->pagemap.bitmapsize == PageBitmapIsEmpty &&
433+
file->exists_in_prev)
428434
{
429435
/*
430436
* There are no changed blocks since last backup. We want make
431437
* incremental backup, so we should exit.
432438
*/
433-
elog(VERBOSE, "Skipping the file because it didn`t changed: %s", file->path);
439+
elog(VERBOSE, "Skipping the unchanged file: %s", file->path);
434440
return false;
435441
}
436442

@@ -485,10 +491,12 @@ backup_data_file(backup_files_args* arguments,
485491

486492
/*
487493
* Read each page, verify checksum and write it to backup.
488-
* If page map is empty backup all pages of the relation.
494+
* If page map is empty or file is not present in previous backup
495+
* backup all pages of the relation.
489496
*/
490497
if (file->pagemap.bitmapsize == PageBitmapIsEmpty
491-
|| file->pagemap.bitmapsize == PageBitmapIsAbsent)
498+
|| file->pagemap.bitmapsize == PageBitmapIsAbsent
499+
|| !file->exists_in_prev)
492500
{
493501
for (blknum = 0; blknum < nblocks; blknum++)
494502
{

src/dir.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,7 @@ pgFileInit(const char *path)
156156
file->is_datafile = false;
157157
file->linked = NULL;
158158
file->pagemap.bitmap = NULL;
159-
file->pagemap.bitmapsize = (current.backup_mode == BACKUP_MODE_DIFF_PAGE) ? PageBitmapIsEmpty : PageBitmapIsAbsent;
159+
file->pagemap.bitmapsize = PageBitmapIsAbsent;
160160
file->tblspcOid = 0;
161161
file->dbOid = 0;
162162
file->relOid = 0;

src/parsexlog.c

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,8 +115,9 @@ static int SimpleXLogPageRead(XLogReaderState *xlogreader,
115115
*/
116116
void
117117
extractPageMap(const char *archivedir, XLogRecPtr startpoint, TimeLineID tli,
118-
XLogRecPtr endpoint, bool prev_segno)
118+
XLogRecPtr endpoint, bool prev_segno, parray *files)
119119
{
120+
size_t i;
120121
XLogRecord *record;
121122
XLogReaderState *xlogreader;
122123
char *errormsg;
@@ -187,6 +188,15 @@ extractPageMap(const char *archivedir, XLogRecPtr startpoint, TimeLineID tli,
187188
xlogreadfd = -1;
188189
xlogexists = false;
189190
}
191+
192+
/* Mark every datafile with empty pagemap as unchanged */
193+
for (i = 0; i < parray_num(files); i++)
194+
{
195+
pgFile *file = (pgFile *) parray_get(files, i);
196+
if (file->is_datafile && file->pagemap.bitmap == NULL)
197+
file->pagemap.bitmapsize = PageBitmapIsEmpty;
198+
}
199+
190200
elog(LOG, "Pagemap compiled");
191201
}
192202

src/pg_probackup.h

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -107,8 +107,8 @@ typedef struct pgFile
107107
} pgFile;
108108

109109
/* Special values of datapagemap_t bitmapsize */
110-
#define PageBitmapIsEmpty 0
111-
#define PageBitmapIsAbsent -1
110+
#define PageBitmapIsEmpty 0 /* Used to mark unchanged datafiles */
111+
#define PageBitmapIsAbsent -1 /* Used to mark files with unknown state of pagemap, i.e. datafiles without _ptrack */
112112

113113
/* Current state of backup */
114114
typedef enum BackupStatus
@@ -468,7 +468,8 @@ extern bool calc_file_checksum(pgFile *file);
468468
extern void extractPageMap(const char *datadir,
469469
XLogRecPtr startpoint,
470470
TimeLineID tli,
471-
XLogRecPtr endpoint, bool prev_segno);
471+
XLogRecPtr endpoint, bool prev_segno,
472+
parray *backup_files_list);
472473
extern void validate_wal(pgBackup *backup,
473474
const char *archivedir,
474475
time_t target_time,

tests/delta.py

Lines changed: 31 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,12 @@ class DeltaTest(ProbackupTest, unittest.TestCase):
1414

1515
# @unittest.skip("skip")
1616
def test_delta_vacuum_truncate_1(self):
17-
"""make node, create table, take full backup,
18-
delete last 3 pages, vacuum relation,
19-
take delta backup, take second delta backup,
20-
restore latest delta backup and check data correctness"""
17+
"""
18+
make node, create table, take full backup,
19+
delete last 3 pages, vacuum relation,
20+
take delta backup, take second delta backup,
21+
restore latest delta backup and check data correctness
22+
"""
2123
fname = self.id().split('.')[3]
2224
backup_dir = os.path.join(self.tmp_path, module_name, fname, 'backup')
2325
node = self.make_simple_node(
@@ -100,10 +102,12 @@ def test_delta_vacuum_truncate_1(self):
100102

101103
# @unittest.skip("skip")
102104
def test_delta_vacuum_truncate_2(self):
103-
"""make node, create table, take full backup,
104-
delete last 3 pages, vacuum relation,
105-
take delta backup, take second delta backup,
106-
restore latest delta backup and check data correctness"""
105+
"""
106+
make node, create table, take full backup,
107+
delete last 3 pages, vacuum relation,
108+
take delta backup, take second delta backup,
109+
restore latest delta backup and check data correctness
110+
"""
107111
fname = self.id().split('.')[3]
108112
backup_dir = os.path.join(self.tmp_path, module_name, fname, 'backup')
109113
node = self.make_simple_node(
@@ -191,10 +195,12 @@ def test_delta_vacuum_truncate_2(self):
191195

192196
# @unittest.skip("skip")
193197
def test_delta_vacuum_truncate_3(self):
194-
"""make node, create table, take full backup,
195-
delete last 3 pages, vacuum relation,
196-
take delta backup, take second delta backup,
197-
restore latest delta backup and check data correctness"""
198+
"""
199+
make node, create table, take full backup,
200+
delete last 3 pages, vacuum relation,
201+
take delta backup, take second delta backup,
202+
restore latest delta backup and check data correctness
203+
"""
198204
fname = self.id().split('.')[3]
199205
backup_dir = os.path.join(self.tmp_path, module_name, fname, 'backup')
200206
node = self.make_simple_node(
@@ -421,7 +427,7 @@ def test_delta_archive(self):
421427
# Clean after yourself
422428
self.del_test_dir(module_name, fname)
423429

424-
@unittest.skip("skip")
430+
# @unittest.skip("skip")
425431
def test_delta_multiple_segments(self):
426432
"""
427433
Make node, create table with multiple segments,
@@ -508,8 +514,10 @@ def test_delta_multiple_segments(self):
508514

509515
# @unittest.skip("skip")
510516
def test_delta_vacuum_full(self):
511-
"""make node, make full and delta stream backups,
512-
restore them and check data correctness"""
517+
"""
518+
make node, make full and delta stream backups,
519+
restore them and check data correctness
520+
"""
513521
fname = self.id().split('.')[3]
514522
backup_dir = os.path.join(self.tmp_path, module_name, fname, 'backup')
515523
node = self.make_simple_node(
@@ -836,8 +844,10 @@ def test_exists_in_previous_backup(self):
836844

837845
# @unittest.skip("skip")
838846
def test_alter_table_set_tablespace_delta(self):
839-
"""Make node, create tablespace with table, take full backup,
840-
alter tablespace location, take delta backup, restore database."""
847+
"""
848+
Make node, create tablespace with table, take full backup,
849+
alter tablespace location, take delta backup, restore database.
850+
"""
841851
fname = self.id().split('.')[3]
842852
backup_dir = os.path.join(self.tmp_path, module_name, fname, 'backup')
843853
node = self.make_simple_node(
@@ -1023,8 +1033,10 @@ def test_alter_database_set_tablespace_delta(self):
10231033

10241034
# @unittest.skip("skip")
10251035
def test_delta_delete(self):
1026-
"""Make node, create tablespace with table, take full backup,
1027-
alter tablespace location, take delta backup, restore database."""
1036+
"""
1037+
Make node, create tablespace with table, take full backup,
1038+
alter tablespace location, take delta backup, restore database.
1039+
"""
10281040
fname = self.id().split('.')[3]
10291041
backup_dir = os.path.join(self.tmp_path, module_name, fname, 'backup')
10301042
node = self.make_simple_node(

0 commit comments

Comments
 (0)