Skip to content

Commit 2ec6bc6

Browse files
committed
Implement incremental backup of compressed files, both page and ptrack do the same. Adds two columns to the list of files.
1 parent 976694f commit 2ec6bc6

File tree

5 files changed

+507
-23
lines changed

5 files changed

+507
-23
lines changed

backup.c

Lines changed: 176 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1102,6 +1102,75 @@ backup_cleanup(bool fatal, void *userdata)
11021102
}
11031103
}
11041104

1105+
/* Count bytes in file */
1106+
static long
1107+
file_size(const char *file)
1108+
{
1109+
long r;
1110+
FILE *f = fopen(file, "r");
1111+
1112+
if (!f)
1113+
{
1114+
elog(ERROR, "pg_probackup: could not open file \"%s\" for reading: %s\n",
1115+
file, strerror(errno));
1116+
return -1;
1117+
}
1118+
fseek(f, 0, SEEK_END);
1119+
r = ftell(f);
1120+
fclose(f);
1121+
return r;
1122+
}
1123+
1124+
/*
1125+
* Find corresponding file in previous backup.
1126+
* Compare generations and return true if we don't need full copy
1127+
* of the file, but just part of it.
1128+
*
1129+
* skip_size - size of the file in previous backup. We can skip it
1130+
* and copy just remaining part of the file.
1131+
*/
1132+
bool
1133+
backup_compressed_file_partially(pgFile *file, void *arg, size_t *skip_size)
1134+
{
1135+
bool result = false;
1136+
pgFile *prev_file = NULL;
1137+
size_t current_file_size;
1138+
backup_files_args *arguments = (backup_files_args *) arg;
1139+
1140+
if (arguments->prev_files)
1141+
{
1142+
pgFile **p = (pgFile **) parray_bsearch(arguments->prev_files,
1143+
file, pgFileComparePath);
1144+
if (p)
1145+
prev_file = *p;
1146+
1147+
/* If file's gc generation has changed since last backup, just copy it*/
1148+
if (prev_file && prev_file->generation == file->generation)
1149+
{
1150+
current_file_size = file_size(file->path);
1151+
1152+
if (prev_file->write_size == BYTES_INVALID)
1153+
return false;
1154+
1155+
*skip_size = prev_file->write_size;
1156+
1157+
if (current_file_size >= prev_file->write_size)
1158+
{
1159+
elog(LOG, "Backup file %s partially: prev_size %lu, current_size %lu",
1160+
file->path, prev_file->write_size, current_file_size);
1161+
result = true;
1162+
}
1163+
else
1164+
elog(ERROR, "Something is wrong with %s. current_file_size %lu, prev %lu",
1165+
file->path, current_file_size, prev_file->write_size);
1166+
}
1167+
else
1168+
elog(LOG, "Copy full %s.", file->path);
1169+
}
1170+
1171+
return result;
1172+
}
1173+
11051174
/*
11061175
* Take differential backup at page level.
11071176
*/
@@ -1200,9 +1269,47 @@ backup_files(void *arg)
12001269
}
12011270

12021271
/* copy the file into backup */
1203-
if (!(file->is_datafile
1204-
? backup_data_file(arguments->from_root, arguments->to_root, file, arguments->lsn)
1205-
: copy_file(arguments->from_root, arguments->to_root, file)))
1272+
if (file->is_datafile)
1273+
{
1274+
if (!backup_data_file(arguments->from_root,
1275+
arguments->to_root, file,
1276+
arguments->lsn))
1277+
{
1278+
/* record as skipped file in file_xxx.txt */
1279+
file->write_size = BYTES_INVALID;
1280+
elog(LOG, "skip");
1281+
continue;
1282+
}
1283+
}
1284+
else if (is_compressed_data_file(file))
1285+
{
1286+
size_t skip_size = 0;
1287+
if (backup_compressed_file_partially(file, arguments, &skip_size))
1288+
{
1289+
/* backup cfs segment partly */
1290+
if (!copy_file_partly(arguments->from_root,
1291+
arguments->to_root,
1292+
file, skip_size))
1293+
{
1294+
/* record as skipped file in file_xxx.txt */
1295+
file->write_size = BYTES_INVALID;
1296+
elog(LOG, "skip");
1297+
continue;
1298+
}
1299+
}
1300+
else if (!copy_file(arguments->from_root,
1301+
arguments->to_root,
1302+
file))
1303+
{
1304+
/* record as skipped file in file_xxx.txt */
1305+
file->write_size = BYTES_INVALID;
1306+
elog(LOG, "skip");
1307+
continue;
1308+
}
1309+
}
1310+
else if (!copy_file(arguments->from_root,
1311+
arguments->to_root,
1312+
file))
12061313
{
12071314
/* record as skipped file in file_xxx.txt */
12081315
file->write_size = BYTES_INVALID;
@@ -1251,14 +1358,14 @@ add_files(parray *files, const char *root, bool add_root, bool is_pgdata)
12511358
relative = file->path + strlen(root) + 1;
12521359
if (is_pgdata &&
12531360
!path_is_prefix_of_path("base", relative) &&
1254-
/*!path_is_prefix_of_path("global", relative) &&*/
1361+
/*!path_is_prefix_of_path("global", relative) &&*/ //TODO What's wrong with this line?
12551362
!path_is_prefix_of_path("pg_tblspc", relative))
12561363
continue;
12571364

12581365
/* Get file name from path */
12591366
fname = last_dir_separator(relative);
12601367

1261-
/* Remove temp tables */
1368+
/* Remove temp tables from the list */
12621369
if (fname[0] == 't' && isdigit(fname[1]))
12631370
{
12641371
pgFileFree(file);
@@ -1268,7 +1375,7 @@ add_files(parray *files, const char *root, bool add_root, bool is_pgdata)
12681375
}
12691376

12701377
path_len = strlen(file->path);
1271-
/* Get link ptrack file to realations files */
1378+
/* Get link ptrack file to relations files */
12721379
if (path_len > 6 && strncmp(file->path+(path_len-6), "ptrack", 6) == 0)
12731380
{
12741381
pgFile *search_file;
@@ -1277,12 +1384,15 @@ add_files(parray *files, const char *root, bool add_root, bool is_pgdata)
12771384
while(true) {
12781385
pgFile tmp_file;
12791386
tmp_file.path = pg_strdup(file->path);
1280-
/* I hope segno not more than 999999 */
1387+
1388+
/* Segno fits into 6 digits since it is not more than 4000 */
12811389
if (segno > 0)
12821390
sprintf(tmp_file.path+path_len-7, ".%d", segno);
12831391
else
12841392
tmp_file.path[path_len-7] = '\0';
1393+
12851394
pre_search_file = (pgFile **) parray_bsearch(list_file, &tmp_file, pgFileComparePath);
1395+
12861396
if (pre_search_file != NULL)
12871397
{
12881398
search_file = *pre_search_file;
@@ -1296,6 +1406,7 @@ add_files(parray *files, const char *root, bool add_root, bool is_pgdata)
12961406
segno++;
12971407
}
12981408

1409+
/* Remove ptrack file itself from backup list */
12991410
pgFileFree(file);
13001411
parray_remove(list_file, i);
13011412
i--;
@@ -1305,9 +1416,9 @@ add_files(parray *files, const char *root, bool add_root, bool is_pgdata)
13051416
/* compress map file it is not data file */
13061417
if (path_len > 4 && strncmp(file->path+(path_len-4), ".cfm", 4) == 0)
13071418
{
1308-
if (current.backup_mode == BACKUP_MODE_DIFF_PTRACK ||
1309-
current.backup_mode == BACKUP_MODE_DIFF_PAGE)
1310-
elog(ERROR, "You can't use incremental backup with compress tablespace");
1419+
if (current.backup_mode == BACKUP_MODE_DIFF_PAGE)
1420+
elog(ERROR, "You can't use PAGE mode backup with compressed tablespace.\n"
1421+
"Try FULL or PTRACK mode instead.");
13111422
continue;
13121423
}
13131424

@@ -1355,11 +1466,34 @@ add_files(parray *files, const char *root, bool add_root, bool is_pgdata)
13551466
pgFile tmp_file;
13561467
tmp_file.path = pg_strdup(file->path);
13571468
tmp_file.path[path_len-4] = '\0';
1358-
pre_search_file = (pgFile **) parray_bsearch(list_file, &tmp_file, pgFileComparePath);
1469+
pre_search_file = (pgFile **) parray_bsearch(list_file,
1470+
&tmp_file, pgFileComparePath);
13591471
if (pre_search_file != NULL)
13601472
{
1473+
FileMap* map;
1474+
int md = open(file->path, O_RDWR|PG_BINARY, 0);
1475+
if (md < 0)
1476+
elog(ERROR, "add_files(). cannot open cfm file '%s'", file->path);
1477+
1478+
map = cfs_mmap(md);
1479+
if (map == MAP_FAILED)
1480+
{
1481+
elog(LOG, "add_files(). cfs_compression_ration failed to map file %s: %m", file->path);
1482+
close(md);
1483+
break;
1484+
}
1485+
1486+
(*pre_search_file)->generation = map->generation;
13611487
(*pre_search_file)->is_datafile = false;
1488+
1489+
if (cfs_munmap(map) < 0)
1490+
elog(LOG, "add_files(). CFS failed to unmap file %s: %m", file->path);
1491+
if (close(md) < 0)
1492+
elog(LOG, "add_files(). CFS failed to close file %s: %m", file->path);
13621493
}
1494+
else
1495+
elog(ERROR, "corresponding segment '%s' is not found", tmp_file.path);
1496+
13631497
pg_free(tmp_file.path);
13641498
}
13651499
}
@@ -1633,3 +1767,34 @@ StreamLog(void *arg)
16331767
PQfinish(conn);
16341768
conn = NULL;
16351769
}
1770+
1771+
1772+
FileMap* cfs_mmap(int md)
1773+
{
1774+
FileMap* map;
1775+
#ifdef WIN32
1776+
HANDLE mh = CreateFileMapping(_get_osfhandle(md), NULL, PAGE_READWRITE,
1777+
0, (DWORD)sizeof(FileMap), NULL);
1778+
if (mh == NULL)
1779+
return (FileMap*)MAP_FAILED;
1780+
1781+
map = (FileMap*)MapViewOfFile(mh, FILE_MAP_ALL_ACCESS, 0, 0, 0);
1782+
CloseHandle(mh);
1783+
if (map == NULL)
1784+
return (FileMap*)MAP_FAILED;
1785+
1786+
#else
1787+
map = (FileMap*)mmap(NULL, sizeof(FileMap),
1788+
PROT_WRITE | PROT_READ, MAP_SHARED, md, 0);
1789+
#endif
1790+
return map;
1791+
}
1792+
1793+
int cfs_munmap(FileMap* map)
1794+
{
1795+
#ifdef WIN32
1796+
return UnmapViewOfFile(map) ? 0 : -1;
1797+
#else
1798+
return munmap(map, sizeof(FileMap));
1799+
#endif
1800+
}

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy