@@ -1102,6 +1102,75 @@ backup_cleanup(bool fatal, void *userdata)
1102
1102
}
1103
1103
}
1104
1104
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
+
1105
1174
/*
1106
1175
* Take differential backup at page level.
1107
1176
*/
@@ -1200,9 +1269,47 @@ backup_files(void *arg)
1200
1269
}
1201
1270
1202
1271
/* 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 ))
1206
1313
{
1207
1314
/* record as skipped file in file_xxx.txt */
1208
1315
file -> write_size = BYTES_INVALID ;
@@ -1251,14 +1358,14 @@ add_files(parray *files, const char *root, bool add_root, bool is_pgdata)
1251
1358
relative = file -> path + strlen (root ) + 1 ;
1252
1359
if (is_pgdata &&
1253
1360
!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?
1255
1362
!path_is_prefix_of_path ("pg_tblspc" , relative ))
1256
1363
continue ;
1257
1364
1258
1365
/* Get file name from path */
1259
1366
fname = last_dir_separator (relative );
1260
1367
1261
- /* Remove temp tables */
1368
+ /* Remove temp tables from the list */
1262
1369
if (fname [0 ] == 't' && isdigit (fname [1 ]))
1263
1370
{
1264
1371
pgFileFree (file );
@@ -1268,7 +1375,7 @@ add_files(parray *files, const char *root, bool add_root, bool is_pgdata)
1268
1375
}
1269
1376
1270
1377
path_len = strlen (file -> path );
1271
- /* Get link ptrack file to realations files */
1378
+ /* Get link ptrack file to relations files */
1272
1379
if (path_len > 6 && strncmp (file -> path + (path_len - 6 ), "ptrack" , 6 ) == 0 )
1273
1380
{
1274
1381
pgFile * search_file ;
@@ -1277,12 +1384,15 @@ add_files(parray *files, const char *root, bool add_root, bool is_pgdata)
1277
1384
while (true) {
1278
1385
pgFile tmp_file ;
1279
1386
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 */
1281
1389
if (segno > 0 )
1282
1390
sprintf (tmp_file .path + path_len - 7 , ".%d" , segno );
1283
1391
else
1284
1392
tmp_file .path [path_len - 7 ] = '\0' ;
1393
+
1285
1394
pre_search_file = (pgFile * * ) parray_bsearch (list_file , & tmp_file , pgFileComparePath );
1395
+
1286
1396
if (pre_search_file != NULL )
1287
1397
{
1288
1398
search_file = * pre_search_file ;
@@ -1296,6 +1406,7 @@ add_files(parray *files, const char *root, bool add_root, bool is_pgdata)
1296
1406
segno ++ ;
1297
1407
}
1298
1408
1409
+ /* Remove ptrack file itself from backup list */
1299
1410
pgFileFree (file );
1300
1411
parray_remove (list_file , i );
1301
1412
i -- ;
@@ -1305,9 +1416,9 @@ add_files(parray *files, const char *root, bool add_root, bool is_pgdata)
1305
1416
/* compress map file it is not data file */
1306
1417
if (path_len > 4 && strncmp (file -> path + (path_len - 4 ), ".cfm" , 4 ) == 0 )
1307
1418
{
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. " );
1311
1422
continue ;
1312
1423
}
1313
1424
@@ -1355,11 +1466,34 @@ add_files(parray *files, const char *root, bool add_root, bool is_pgdata)
1355
1466
pgFile tmp_file ;
1356
1467
tmp_file .path = pg_strdup (file -> path );
1357
1468
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 );
1359
1471
if (pre_search_file != NULL )
1360
1472
{
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 ;
1361
1487
(* 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 );
1362
1493
}
1494
+ else
1495
+ elog (ERROR , "corresponding segment '%s' is not found" , tmp_file .path );
1496
+
1363
1497
pg_free (tmp_file .path );
1364
1498
}
1365
1499
}
@@ -1633,3 +1767,34 @@ StreamLog(void *arg)
1633
1767
PQfinish (conn );
1634
1768
conn = NULL ;
1635
1769
}
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