1616#include <unistd.h>
1717#include <dirent.h>
1818#include <time.h>
19+ #include <pthread.h>
1920
2021#include "libpq/pqsignal.h"
2122#include "pgut/pgut-port.h"
@@ -33,12 +34,22 @@ static bool in_backup = false;/* TODO: more robust logic */
3334/* list of files contained in backup */
3435parray * backup_files_list ;
3536
37+ typedef struct
38+ {
39+ const char * from_root ;
40+ const char * to_root ;
41+ parray * files ;
42+ parray * prev_files ;
43+ const XLogRecPtr * lsn ;
44+ unsignedint start_file_idx ;
45+ unsignedint end_file_idx ;
46+ }backup_files_args ;
47+
3648/*
3749 * Backup routines
3850 */
3951static void backup_cleanup (bool fatal ,void * userdata );
40- static void backup_files (const char * from_root ,const char * to_root ,
41- parray * files ,parray * prev_files ,const XLogRecPtr * lsn ,const char * prefix );
52+ static void backup_files (void * arg );
4253static parray * do_backup_database (parray * backup_list ,pgBackupOption bkupopt );
4354static void confirm_block_size (const char * name ,int blcksz );
4455static void pg_start_backup (const char * label ,bool smooth ,pgBackup * backup );
@@ -72,6 +83,8 @@ do_backup_database(parray *backup_list, pgBackupOption bkupopt)
7283char prev_file_txt [MAXPGPATH ];/* path of the previous backup
7384 * list file */
7485bool has_backup_label = true;/* flag if backup_label is there */
86+ pthread_t backup_threads [num_threads ];
87+ backup_files_args * backup_threads_args [num_threads ];
7588
7689/* repack the options */
7790bool smooth_checkpoint = bkupopt .smooth_checkpoint ;
@@ -221,7 +234,77 @@ do_backup_database(parray *backup_list, pgBackupOption bkupopt)
221234make_pagemap_from_ptrack (backup_files_list );
222235}
223236
224- backup_files (pgdata ,path ,backup_files_list ,prev_files ,lsn ,NULL );
237+ /* sort pathname ascending */
238+ parray_qsort (backup_files_list ,pgFileComparePath );
239+
240+ /* make dirs before backup */
241+ for (i = 0 ;i < parray_num (backup_files_list );i ++ )
242+ {
243+ int ret ;
244+ struct stat buf ;
245+ pgFile * file = (pgFile * )parray_get (backup_files_list ,i );
246+
247+ ret = stat (file -> path ,& buf );
248+ if (ret == -1 )
249+ {
250+ if (errno == ENOENT )
251+ {
252+ /* record as skipped file in file_xxx.txt */
253+ file -> write_size = BYTES_INVALID ;
254+ elog (LOG ,"skip" );
255+ continue ;
256+ }
257+ else
258+ {
259+ elog (ERROR ,
260+ "can't stat backup mode. \"%s\": %s" ,
261+ file -> path ,strerror (errno ));
262+ }
263+ }
264+ /* if the entry was a directory, create it in the backup */
265+ if (S_ISDIR (buf .st_mode ))
266+ {
267+ char dirpath [MAXPGPATH ];
268+ if (verbose )
269+ elog (LOG ,"Make dir %s" ,file -> path + strlen (pgdata )+ 1 );
270+ join_path_components (dirpath ,path ,JoinPathEnd (file -> path ,pgdata ));
271+ if (!check )
272+ dir_create_dir (dirpath ,DIR_PERMISSION );
273+ }
274+ }
275+
276+ if (num_threads < 1 )
277+ num_threads = 1 ;
278+
279+ for (i = 0 ;i < num_threads ;i ++ )
280+ {
281+ backup_files_args * arg = pg_malloc (sizeof (backup_files_args ));
282+ arg -> from_root = pgdata ;
283+ arg -> to_root = path ;
284+ arg -> files = backup_files_list ;
285+ arg -> prev_files = prev_files ;
286+ arg -> lsn = lsn ;
287+ arg -> start_file_idx = i * (parray_num (backup_files_list )/num_threads );
288+ if (i == num_threads - 1 )
289+ arg -> end_file_idx = parray_num (backup_files_list );
290+ else
291+ arg -> end_file_idx = (i + 1 )* (parray_num (backup_files_list )/num_threads );
292+
293+ if (verbose )
294+ elog (WARNING ,"Start thread for start_file_idx:%i end_file_idx:%i num:%li" ,
295+ arg -> start_file_idx ,
296+ arg -> end_file_idx ,
297+ parray_num (backup_files_list ));
298+ backup_threads_args [i ]= arg ;
299+ pthread_create (& backup_threads [i ],NULL , (void * (* )(void * ))backup_files ,arg );
300+ }
301+
302+ /* Wait theads */
303+ for (i = 0 ;i < num_threads ;i ++ )
304+ {
305+ pthread_join (backup_threads [i ],NULL );
306+ pg_free (backup_threads_args [i ]);
307+ }
225308
226309/* Clear ptrack files after backup */
227310if (current .backup_mode == BACKUP_MODE_DIFF_PTRACK )
@@ -266,8 +349,8 @@ do_backup(pgBackupOption bkupopt)
266349int ret ;
267350
268351/* repack the necessary options */
269- int keep_data_generations = bkupopt .keep_data_generations ;
270- int keep_data_days = bkupopt .keep_data_days ;
352+ int keep_data_generations = bkupopt .keep_data_generations ;
353+ int keep_data_days = bkupopt .keep_data_days ;
271354
272355/* PGDATA and BACKUP_MODE are always required */
273356if (pgdata == NULL )
@@ -656,28 +739,22 @@ backup_cleanup(bool fatal, void *userdata)
656739 * Take differential backup at page level.
657740 */
658741static void
659- backup_files (const char * from_root ,
660- const char * to_root ,
661- parray * files ,
662- parray * prev_files ,
663- const XLogRecPtr * lsn ,
664- const char * prefix )
742+ backup_files (void * arg )
665743{
666744int i ;
667745struct timeval tv ;
668746
669- /* sort pathname ascending */
670- parray_qsort (files ,pgFileComparePath );
747+ backup_files_args * arguments = (backup_files_args * )arg ;
671748
672749gettimeofday (& tv ,NULL );
673750
674751/* backup a file or create a directory */
675- for (i = 0 ;i < parray_num ( files ) ;i ++ )
752+ for (i = arguments -> start_file_idx ;i < arguments -> end_file_idx ;i ++ )
676753{
677754int ret ;
678755struct stat buf ;
679756
680- pgFile * file = (pgFile * )parray_get (files ,i );
757+ pgFile * file = (pgFile * )parray_get (arguments -> files ,i );
681758
682759/* If current time is rewinded, abort this backup. */
683760if (tv .tv_sec < file -> mtime )
@@ -690,18 +767,8 @@ backup_files(const char *from_root,
690767
691768/* print progress in verbose mode */
692769if (verbose )
693- {
694- if (prefix )
695- {
696- char path [MAXPGPATH ];
697- join_path_components (path ,prefix ,file -> path + strlen (from_root )+ 1 );
698- elog (LOG ,"(%d/%lu) %s" ,i + 1 ,
699- (unsigned long )parray_num (files ),path );
700- }
701- else
702- elog (LOG ,"(%d/%lu) %s" ,i + 1 , (unsigned long )parray_num (files ),
703- file -> path + strlen (from_root )+ 1 );
704- }
770+ elog (LOG ,"(%d/%lu) %s" ,i + 1 , (unsigned long )parray_num (arguments -> files ),
771+ file -> path + strlen (arguments -> from_root )+ 1 );
705772
706773/* stat file to get file type, size and modify timestamp */
707774ret = stat (file -> path ,& buf );
@@ -722,52 +789,20 @@ backup_files(const char *from_root,
722789}
723790}
724791
725- /*if the entry was a directory, create it in the backup */
792+ /*skip dir because make before */
726793if (S_ISDIR (buf .st_mode ))
727794{
728- char dirpath [MAXPGPATH ];
729-
730- join_path_components (dirpath ,to_root ,JoinPathEnd (file -> path ,from_root ));
731- if (!check )
732- dir_create_dir (dirpath ,DIR_PERMISSION );
733- elog (LOG ,"directory" );
795+ continue ;
734796}
735797else if (S_ISREG (buf .st_mode ))
736798{
737799/* skip files which have not been modified since last backup */
738- if (prev_files )
800+ if (arguments -> prev_files )
739801{
740802pgFile * prev_file = NULL ;
741-
742- /*
743- * If prefix is not NULL, the table space is backup from the snapshot.
744- * Therefore, adjust file name to correspond to the file list.
745- */
746- if (prefix )
747- {
748- int j ;
749-
750- for (j = 0 ;j < parray_num (prev_files );j ++ )
751- {
752- pgFile * p = (pgFile * )parray_get (prev_files ,j );
753- char * prev_path ;
754- char curr_path [MAXPGPATH ];
755-
756- prev_path = p -> path + strlen (from_root )+ 1 ;
757- join_path_components (curr_path ,prefix ,file -> path + strlen (from_root )+ 1 );
758- if (strcmp (curr_path ,prev_path )== 0 )
759- {
760- prev_file = p ;
761- break ;
762- }
763- }
764- }
765- else
766- {
767- pgFile * * p = (pgFile * * )parray_bsearch (prev_files ,file ,pgFileComparePath );
768- if (p )
769- prev_file = * p ;
770- }
803+ pgFile * * p = (pgFile * * )parray_bsearch (arguments -> prev_files ,file ,pgFileComparePath );
804+ if (p )
805+ prev_file = * p ;
771806
772807if (prev_file && prev_file -> mtime == file -> mtime )
773808{
@@ -797,8 +832,8 @@ backup_files(const char *from_root,
797832
798833/* copy the file into backup */
799834if (!(file -> is_datafile
800- ?backup_data_file (from_root ,to_root ,file ,lsn )
801- :copy_file (from_root ,to_root ,file )))
835+ ?backup_data_file (arguments -> from_root ,arguments -> to_root ,file ,arguments -> lsn )
836+ :copy_file (arguments -> from_root ,arguments -> to_root ,file )))
802837{
803838/* record as skipped file in file_xxx.txt */
804839file -> write_size = BYTES_INVALID ;