@@ -1109,6 +1109,27 @@ backup_cleanup(bool fatal, void *userdata)
1109
1109
}
1110
1110
}
1111
1111
1112
+ /*
1113
+ * Count bytes in file
1114
+ */
1115
+ static long
1116
+ file_size (const char * file )
1117
+ {
1118
+ long r ;
1119
+ FILE * f = fopen (file , "r" );
1120
+
1121
+ if (!f )
1122
+ {
1123
+ fprintf (stderr , _ ("%s: could not open file \"%s\" for reading: %s\n" ),
1124
+ progname , file , strerror (errno ));
1125
+ return -1 ;
1126
+ }
1127
+ fseek (f , 0 , SEEK_END );
1128
+ r = ftell (f );
1129
+ fclose (f );
1130
+ return r ;
1131
+ }
1132
+
1112
1133
/*
1113
1134
* Take differential backup at page level.
1114
1135
*/
@@ -1117,7 +1138,6 @@ backup_files(void *arg)
1117
1138
{
1118
1139
int i ;
1119
1140
struct timeval tv ;
1120
-
1121
1141
backup_files_args * arguments = (backup_files_args * ) arg ;
1122
1142
1123
1143
gettimeofday (& tv , NULL );
@@ -1221,39 +1241,56 @@ backup_files(void *arg)
1221
1241
}
1222
1242
else
1223
1243
{
1224
- /* Check if the file is a cfs relation's segment */
1244
+ /* Check first if the file is a cfs relation's segment */
1225
1245
bool is_cfs_relation_segment = false;
1226
1246
pgFile tmp_file ;
1227
1247
pgFile * * pre_search_file ;
1248
+ pgFile * prev_file = NULL ;
1249
+
1228
1250
tmp_file .path = psprintf ("%s.cfm" , file -> path );
1229
1251
pre_search_file = (pgFile * * ) parray_bsearch (arguments -> files , & tmp_file , pgFileComparePath );
1230
1252
if (pre_search_file != NULL )
1231
1253
{
1232
- is_cfs_relation_segment = true;
1233
- /* TODO If we don't have ptrack simply copy the file */
1234
- if (file -> pagemap .bitmapsize == 0 )
1254
+ /* Now check if it's generation has changed since last backup */
1255
+ if (arguments -> prev_files )
1235
1256
{
1236
- is_cfs_relation_segment = false;
1237
- elog (NOTICE , "1 file '%s' is a cfs relation's segment, bitmapsize == 0 \n" , file -> path );
1257
+ pgFile * * p = (pgFile * * ) parray_bsearch (arguments -> prev_files , file , pgFileComparePath );
1258
+ if (p )
1259
+ prev_file = * p ;
1260
+ elog (NOTICE , "file '%s' is a cfs relation's segment generation prev %d, now %d" ,
1261
+ file -> path , prev_file -> generation , file -> generation );
1262
+
1263
+ if (prev_file && prev_file -> generation == file -> generation )
1264
+ {
1265
+ elog (NOTICE , "prev->write_size %lu, file_size %lu" , prev_file -> write_size , file_size (file -> path ));
1266
+ if (prev_file -> write_size == file_size (file -> path ))
1267
+ {
1268
+ elog (NOTICE , "File hasn't changed since last backup. Don't copy at all" );
1269
+ is_cfs_relation_segment = true;
1270
+ }
1271
+ else
1272
+ {
1273
+ elog (NOTICE , "Backup part of the file. %s" , file -> path );
1274
+ is_cfs_relation_segment = true;
1275
+ }
1276
+ }
1238
1277
}
1239
1278
}
1240
1279
pg_free (tmp_file .path );
1241
1280
1242
1281
1243
1282
if (is_cfs_relation_segment )
1244
1283
{
1245
- /*
1246
- * TODO backup cfs segment
1247
- * see backup_data_file()
1248
- */
1249
- elog (NOTICE , "2 file '%s' is a cfs relation's segment \n" , file -> path );
1250
- elog (NOTICE , "2 file->pagemap.bitmapsize = %d" , file -> pagemap .bitmapsize );
1251
- datapagemap_iterator_t * iter ;
1252
- BlockNumber blknum = 0 ;
1253
-
1254
- iter = datapagemap_iterate (& file -> pagemap );
1255
- while (datapagemap_next (iter , & blknum ))
1256
- elog (NOTICE , "2 blknum %u" , blknum );
1284
+ /* backup cfs segment partly */
1285
+ if (!copy_file_partly (arguments -> from_root ,
1286
+ arguments -> to_root ,
1287
+ file , prev_file -> write_size ))
1288
+ {
1289
+ /* record as skipped file in file_xxx.txt */
1290
+ file -> write_size = BYTES_INVALID ;
1291
+ elog (LOG , "skip" );
1292
+ continue ;
1293
+ }
1257
1294
}
1258
1295
else if (!copy_file (arguments -> from_root ,
1259
1296
arguments -> to_root ,
@@ -1338,16 +1375,32 @@ add_files(parray *files, const char *root, bool add_root, bool is_pgdata)
1338
1375
sprintf (tmp_file .path + path_len - 7 , ".%d" , segno );
1339
1376
else
1340
1377
tmp_file .path [path_len - 7 ] = '\0' ;
1378
+
1341
1379
pre_search_file = (pgFile * * ) parray_bsearch (list_file , & tmp_file , pgFileComparePath );
1342
- if (pre_search_file != NULL )
1380
+ /* Use another scheme for compressed files */
1381
+ pgFile map_file ;
1382
+ pgFile * * map_search_file ;
1383
+ map_file .path = pg_strdup (file -> path );
1384
+ if (segno > 0 )
1385
+ sprintf (map_file .path + path_len - 7 , ".%d.cfm" , segno );
1386
+ else
1387
+ sprintf (map_file .path + path_len - 7 , ".cfm" );
1388
+ map_search_file = (pgFile * * ) parray_bsearch (list_file , & map_file , pgFileComparePath );
1389
+
1390
+ if (pre_search_file != NULL
1391
+ && map_search_file == NULL )
1343
1392
{
1344
1393
search_file = * pre_search_file ;
1345
1394
search_file -> ptrack_path = pg_strdup (file -> path );
1346
1395
search_file -> segno = segno ;
1347
- } else {
1396
+ }
1397
+ else
1398
+ {
1348
1399
pg_free (tmp_file .path );
1400
+ pg_free (map_file .path );
1349
1401
break ;
1350
1402
}
1403
+ pg_free (map_file .path );
1351
1404
pg_free (tmp_file .path );
1352
1405
segno ++ ;
1353
1406
}
@@ -1361,12 +1414,9 @@ add_files(parray *files, const char *root, bool add_root, bool is_pgdata)
1361
1414
/* compress map file it is not data file */
1362
1415
if (path_len > 4 && strncmp (file -> path + (path_len - 4 ), ".cfm" , 4 ) == 0 )
1363
1416
{
1364
- if (current .backup_mode == BACKUP_MODE_DIFF_PTRACK ||
1365
- current .backup_mode == BACKUP_MODE_DIFF_PAGE )
1366
- {
1367
- elog (NOTICE , "You can't use incremental backup with compress tablespace" );
1368
- /* TODO Add here incremental backup for compressed tablespaces */
1369
- }
1417
+ if (current .backup_mode == BACKUP_MODE_DIFF_PAGE )
1418
+ elog (ERROR , "You can't use PAGE mode backup with compressed tablespace.\n"
1419
+ "Try PTRACK mode instead." );
1370
1420
continue ;
1371
1421
}
1372
1422
@@ -1420,8 +1470,34 @@ add_files(parray *files, const char *root, bool add_root, bool is_pgdata)
1420
1470
pre_search_file = (pgFile * * ) parray_bsearch (list_file , & tmp_file , pgFileComparePath );
1421
1471
if (pre_search_file != NULL )
1422
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 )-> last_backup_write_size = map -> last_backup_write_size ;
1487
+ (* pre_search_file )-> generation = map -> generation ;
1423
1488
(* pre_search_file )-> is_datafile = false;
1489
+ map -> last_backup_write_size = map -> physSize ;
1490
+
1491
+ if (cfs_msync (map ) < 0 )
1492
+ elog (LOG , "add_files(). CFS failed to sync map %s: %m" , file -> path );
1493
+ if (cfs_munmap (map ) < 0 )
1494
+ elog (LOG , "add_files(). CFS failed to unmap file %s: %m" , file -> path );
1495
+ if (close (md ) < 0 )
1496
+ elog (LOG , "add_files(). CFS failed to close file %s: %m" , file -> path );
1424
1497
}
1498
+ else
1499
+ elog (ERROR , "corresponding segment '%s' is not found" , tmp_file .path );
1500
+
1425
1501
pg_free (tmp_file .path );
1426
1502
}
1427
1503
}
@@ -1693,3 +1769,42 @@ StreamLog(void *arg)
1693
1769
PQfinish (conn );
1694
1770
conn = NULL ;
1695
1771
}
1772
+
1773
+
1774
+ FileMap * cfs_mmap (int md )
1775
+ {
1776
+ FileMap * map ;
1777
+ #ifdef WIN32
1778
+ HANDLE mh = CreateFileMapping (_get_osfhandle (md ), NULL , PAGE_READWRITE ,
1779
+ 0 , (DWORD )sizeof (FileMap ), NULL );
1780
+ if (mh == NULL ) {
1781
+ return (FileMap * )MAP_FAILED ;
1782
+ }
1783
+ map = (FileMap * )MapViewOfFile (mh , FILE_MAP_ALL_ACCESS , 0 , 0 , 0 );
1784
+ CloseHandle (mh );
1785
+ if (map == NULL ) {
1786
+ return (FileMap * )MAP_FAILED ;
1787
+ }
1788
+ #else
1789
+ map = (FileMap * )mmap (NULL , sizeof (FileMap ), PROT_WRITE | PROT_READ , MAP_SHARED , md , 0 );
1790
+ #endif
1791
+ return map ;
1792
+ }
1793
+
1794
+ int cfs_munmap (FileMap * map )
1795
+ {
1796
+ #ifdef WIN32
1797
+ return UnmapViewOfFile (map ) ? 0 : -1 ;
1798
+ #else
1799
+ return munmap (map , sizeof (FileMap ));
1800
+ #endif
1801
+ }
1802
+
1803
+ int cfs_msync (FileMap * map )
1804
+ {
1805
+ #ifdef WIN32
1806
+ return FlushViewOfFile (map , sizeof (FileMap )) ? 0 : -1 ;
1807
+ #else
1808
+ return msync (map , sizeof (FileMap ), MS_SYNC );
1809
+ #endif
1810
+ }
0 commit comments