@@ -65,8 +65,7 @@ static bool pg_is_in_recovery(PGconn *conn);
6565static bool pg_is_superuser (PGconn * conn );
6666static void check_server_version (PGconn * conn ,PGNodeInfo * nodeInfo );
6767static void confirm_block_size (PGconn * conn ,const char * name ,int blcksz );
68- static size_t rewind_and_mark_cfs_datafiles (parray * files ,const char * root ,char * relative ,size_t i );
69- static void group_cfs_segments (parray * files ,size_t first ,size_t last );
68+ static void rewind_and_mark_cfs_datafiles (parray * files ,const char * root ,char * relative ,size_t i );
7069static bool remove_excluded_files_criterion (void * value ,void * exclude_args );
7170static void backup_cfs_segment (int i ,pgFile * file ,backup_files_arg * arguments );
7271static void process_file (int i ,pgFile * file ,backup_files_arg * arguments );
@@ -2228,7 +2227,11 @@ backup_cfs_segment(int i, pgFile *file, backup_files_arg *arguments) {
22282227cfm_bck_file = data_file ;
22292228}
22302229data_file = file ;
2231- Assert (cfm_file );/* ensure we always have cfm exist */
2230+ if (data_file -> relOid >=FirstNormalObjectId && cfm_file == NULL )
2231+ {
2232+ elog (ERROR ,"'CFS' file '%s' have to have '%s.cfm' companion file" ,
2233+ data_file -> rel_path ,data_file -> name );
2234+ }
22322235
22332236elog (LOG ,"backup CFS segment %s, data_file=%s, cfm_file=%s, data_bck_file=%s, cfm_bck_file=%s" ,
22342237data_file -> name ,data_file -> name ,cfm_file -> name ,data_bck_file == NULL ?"NULL" :data_bck_file -> name ,cfm_bck_file == NULL ?"NULL" :cfm_bck_file -> name );
@@ -2251,7 +2254,10 @@ backup_cfs_segment(int i, pgFile *file, backup_files_arg *arguments) {
22512254process_file (i ,cfm_bck_file ,arguments );
22522255
22532256/* storing cfs segment in order cfm_file -> datafile to guarantee their consistency */
2254- process_file (i ,cfm_file ,arguments );
2257+ /* cfm_file could be NULL for system tables. But we don't clear is_cfs flag
2258+ * for compatibility with older pg_probackup. */
2259+ if (cfm_file )
2260+ process_file (i ,cfm_file ,arguments );
22552261process_file (i ,data_file ,arguments );
22562262elog (LOG ,"Backup CFS segment %s done" ,data_file -> name );
22572263}
@@ -2299,9 +2305,7 @@ parse_filelist_filenames(parray *files, const char *root)
22992305strncmp (tmp_rel_path ,TABLESPACE_VERSION_DIRECTORY ,
23002306strlen (TABLESPACE_VERSION_DIRECTORY ))== 0 ) {
23012307/* rewind index to the beginning of cfs tablespace */
2302- size_t start = rewind_and_mark_cfs_datafiles (files ,root ,file -> rel_path ,i );
2303- /* group every to cfs segments chains */
2304- group_cfs_segments (files ,start ,i );
2308+ rewind_and_mark_cfs_datafiles (files ,root ,file -> rel_path ,i );
23052309}
23062310}
23072311}
@@ -2349,57 +2353,11 @@ remove_excluded_files_criterion(void *value, void *exclude_args) {
23492353return file -> remove_from_list ;
23502354}
23512355
2352- /*
2353- * For every cfs segment do group its files to linked list, datafile on the head.
2354- * All non data files of segment moved to linked list and marked to skip in backup processing threads.
2355- * @param first - first index of cfs tablespace files
2356- * @param last - last index of cfs tablespace files
2357- */
2358- void group_cfs_segments (parray * files ,size_t first ,size_t last ) {/* grouping cfs files by relOid.segno, removing leafs of group */
2359-
2360- for (;first <=last ;first ++ )
2361- {
2362- pgFile * file = parray_get (files ,first );
2363-
2364- if (file -> is_cfs )
2365- {
2366- pgFile * cfs_file = file ;
2367- size_t counter = first + 1 ;
2368- pgFile * chain_file = parray_get (files ,counter );
2369-
2370- bool has_cfm = false;/* flag for later assertion the cfm file also exist */
2371-
2372- elog (LOG ,"Preprocessing cfs file %s, %u.%d" ,cfs_file -> name ,cfs_file -> relOid ,cfs_file -> segno );
2373-
2374- elog (LOG ,"Checking file %s, %u.%d as cfs chain" ,chain_file -> name ,chain_file -> relOid ,chain_file -> segno );
2375-
2376- /* scanning cfs segment files */
2377- while (cfs_file -> relOid == chain_file -> relOid &&
2378- cfs_file -> segno == chain_file -> segno )
2379- {
2380- elog (LOG ,"Grouping cfs chain file %s, %d.%d" ,chain_file -> name ,chain_file -> relOid ,chain_file -> segno );
2381- chain_file -> skip_cfs_nested = true;
2382- cfs_file -> cfs_chain = chain_file ;/* adding to cfs group */
2383- cfs_file = chain_file ;
2384-
2385- /* next file */
2386- counter ++ ;
2387- chain_file = parray_get (files ,counter );
2388- elog (LOG ,"Checking file %s, %u.%d as cfs chain" ,chain_file -> name ,chain_file -> relOid ,chain_file -> segno );
2389- }
2390-
2391- /* assertion - we always have cfs data + cfs map files */
2392- cfs_file = file ;
2393- for (;cfs_file ;cfs_file = cfs_file -> cfs_chain ) {
2394- elog (LOG ,"searching cfm in %s, chain is %s" ,cfs_file -> name ,cfs_file -> cfs_chain == NULL ?"NULL" :cfs_file -> cfs_chain -> name );
2395- has_cfm = cfs_file -> forkName == cfm ;
2396- }
2397- Assert (has_cfm );
2398-
2399- /* shifting to last cfs segment file */
2400- first = counter - 1 ;
2401- }
2402- }
2356+ static uint32_t
2357+ hash_rel_seg (pgFile * file )
2358+ {
2359+ uint32 hash = hash_mix32_2 (file -> relOid ,file -> segno );
2360+ return hash_mix32_2 (hash ,0xcf5 );
24032361}
24042362
24052363/* If file is equal to pg_compression, then we consider this tablespace as
@@ -2416,13 +2374,24 @@ void group_cfs_segments(parray *files, size_t first, size_t last) {/* grouping c
24162374 *
24172375 * @returns index of first tablespace entry, i.e tblspcOid/TABLESPACE_VERSION_DIRECTORY
24182376 */
2419- static size_t
2377+ static void
24202378rewind_and_mark_cfs_datafiles (parray * files ,const char * root ,char * relative ,size_t i )
24212379{
24222380int len ;
24232381int p ;
2382+ int j ;
24242383pgFile * prev_file ;
2384+ pgFile * tmp_file ;
24252385char * cfs_tblspc_path ;
2386+ uint32_t h ;
2387+
2388+ /* hash table for cfm files */
2389+ #define HASHN 128
2390+ parray * hashtab [HASHN ]= {NULL };
2391+ parray * bucket ;
2392+ for (p = 0 ;p < HASHN ;p ++ )
2393+ hashtab [p ]= parray_new ();
2394+
24262395
24272396cfs_tblspc_path = strdup (relative );
24282397if (!cfs_tblspc_path )
@@ -2437,23 +2406,60 @@ rewind_and_mark_cfs_datafiles(parray *files, const char *root, char *relative, s
24372406
24382407elog (LOG ,"Checking file in cfs tablespace %s" ,prev_file -> rel_path );
24392408
2440- if (strstr (prev_file -> rel_path ,cfs_tblspc_path )!= NULL )
2409+ if (strstr (prev_file -> rel_path ,cfs_tblspc_path )== NULL )
2410+ {
2411+ elog (LOG ,"Breaking on %s" ,prev_file -> rel_path );
2412+ break ;
2413+ }
2414+
2415+ if (!S_ISREG (prev_file -> mode ))
2416+ continue ;
2417+
2418+ h = hash_rel_seg (prev_file );
2419+ bucket = hashtab [h %HASHN ];
2420+
2421+ if (prev_file -> forkName == cfm || prev_file -> forkName == cfm_bck ||
2422+ prev_file -> forkName == cfs_bck )
24412423{
2442- if (S_ISREG (prev_file -> mode )&& prev_file -> is_datafile )
2424+ parray_append (bucket ,prev_file );
2425+ }
2426+ else if (prev_file -> is_datafile && prev_file -> forkName == none )
2427+ {
2428+ elog (LOG ,"Processing 'cfs' file %s" ,prev_file -> rel_path );
2429+ /* have to mark as is_cfs even for system-tables for compatibility
2430+ * with older pg_probackup */
2431+ prev_file -> is_cfs = true;
2432+ prev_file -> cfs_chain = NULL ;
2433+ for (j = 0 ;j < parray_num (bucket );j ++ )
24432434{
2444- elog (LOG ,"Setting 'is_cfs' on file %s, name %s" ,
2445- prev_file -> rel_path ,prev_file -> name );
2446- prev_file -> is_cfs = true;
2435+ tmp_file = parray_get (bucket ,j );
2436+ elog (LOG ,"Linking 'cfs' file '%s' to '%s'" ,
2437+ tmp_file -> rel_path ,prev_file -> rel_path );
2438+ if (tmp_file -> relOid == prev_file -> relOid &&
2439+ tmp_file -> segno == prev_file -> segno )
2440+ {
2441+ tmp_file -> cfs_chain = prev_file -> cfs_chain ;
2442+ prev_file -> cfs_chain = tmp_file ;
2443+ parray_remove (bucket ,j );
2444+ j -- ;
2445+ }
24472446}
24482447}
2449- else
2448+ }
2449+
2450+ for (p = 0 ;p < HASHN ;p ++ )
2451+ {
2452+ bucket = hashtab [p ];
2453+ for (j = 0 ;j < parray_num (bucket );j ++ )
24502454{
2451- elog ( LOG , "Breaking on %s" , prev_file -> rel_path );
2452- break ;
2455+ tmp_file = parray_get ( bucket , j );
2456+ elog ( WARNING , "Orphaned cfs related file '%s'" , tmp_file -> rel_path ) ;
24532457}
2458+ parray_free (bucket );
2459+ hashtab [p ]= NULL ;
24542460}
2461+ #undef HASHN
24552462free (cfs_tblspc_path );
2456- return p + 1 ;
24572463}
24582464
24592465/*