33 * restore.c: restore DB cluster and archived WAL.
44 *
55 * Portions Copyright (c) 2009-2013, NIPPON TELEGRAPH AND TELEPHONE CORPORATION
6- * Portions Copyright (c) 2015-2019 , Postgres Professional
6+ * Portions Copyright (c) 2015-2021 , Postgres Professional
77 *
88 *-------------------------------------------------------------------------
99 */
@@ -61,6 +61,8 @@ static void create_recovery_conf(InstanceState *instanceState, time_t backup_id,
6161pgBackup * backup ,
6262pgRestoreParams * params );
6363static void * restore_files (void * arg );
64+ static size_t restore_file (pgFile * dest_file ,const char * to_fullpath ,bool already_exists ,char * out_buf ,
65+ pgBackup * dest_backup ,parray * parent_chain ,bool use_bitmap ,IncrRestoreMode incremental_mode ,XLogRecPtr shift_lsn );
6466static void set_orphan_status (parray * backups ,pgBackup * parent_backup );
6567
6668static void restore_chain (pgBackup * dest_backup ,parray * parent_chain ,
@@ -710,6 +712,8 @@ restore_chain(pgBackup *dest_backup, parray *parent_chain,
710712parray * pgdata_files = NULL ;
711713parray * dest_files = NULL ;
712714parray * external_dirs = NULL ;
715+ pgFile * dest_pg_control_file = NULL ;
716+ char dest_pg_control_fullpath [MAXPGPATH ];
713717/* arrays with meta info for multi threaded backup */
714718pthread_t * threads ;
715719restore_files_arg * threads_args ;
@@ -965,37 +969,61 @@ restore_chain(pgBackup *dest_backup, parray *parent_chain,
965969dest_bytes = dest_backup -> pgdata_bytes ;
966970
967971pretty_size (dest_bytes ,pretty_dest_bytes ,lengthof (pretty_dest_bytes ));
972+
973+ /*
974+ * [Issue #313]
975+ * find pg_control file (in already sorted earlier dest_files, see parray_qsort(backup->files...))
976+ * and exclude it from list for future special processing
977+ */
978+ {
979+ int control_file_elem_index ;
980+ pgFile search_key ;
981+ MemSet (& search_key ,0 ,sizeof (pgFile ));
982+ /* pgFileCompareRelPathWithExternal uses only .rel_path and .external_dir_num for comparision */
983+ search_key .rel_path = XLOG_CONTROL_FILE ;
984+ search_key .external_dir_num = 0 ;
985+ control_file_elem_index = parray_bsearch_index (dest_files ,& search_key ,pgFileCompareRelPathWithExternal );
986+ if (control_file_elem_index < 0 )
987+ elog (ERROR ,"\"%s\" not found in backup %s" ,XLOG_CONTROL_FILE ,base36enc (dest_backup -> start_time ));
988+ dest_pg_control_file = parray_remove (dest_files ,control_file_elem_index );
989+
990+ join_path_components (dest_pg_control_fullpath ,pgdata_path ,dest_pg_control_file -> rel_path );
991+ /* remove dest control file before restoring */
992+ if (params -> incremental_mode != INCR_NONE )
993+ fio_unlink (dest_pg_control_fullpath ,FIO_DB_HOST );
994+ }
995+
968996elog (INFO ,"Start restoring backup files. PGDATA size: %s" ,pretty_dest_bytes );
969997time (& start_time );
970998thread_interrupted = false;
971999
9721000/* Restore files into target directory */
9731001for (i = 0 ;i < num_threads ;i ++ )
9741002{
975- restore_files_arg * arg = & ( threads_args [i ]);
976-
977- arg -> dest_files = dest_files ;
978- arg -> pgdata_files = pgdata_files ;
979- arg -> dest_backup = dest_backup ;
980- arg -> dest_external_dirs = external_dirs ;
981- arg -> parent_chain = parent_chain ;
982- arg -> dbOid_exclude_list = dbOid_exclude_list ;
983- arg -> skip_external_dirs = params -> skip_external_dirs ;
984- arg -> to_root = pgdata_path ;
985- arg -> use_bitmap = use_bitmap ;
986- arg -> incremental_mode = params -> incremental_mode ;
987- arg -> shift_lsn = params -> shift_lsn ;
988- threads_args [ i ]. restored_bytes = 0 ;
989- /* By default there are some error */
990- threads_args [ i ]. ret = 1 ;
1003+ threads_args [i ]= ( restore_files_arg ){
1004+ . dest_files = dest_files ,
1005+ . pgdata_files = pgdata_files ,
1006+ . dest_backup = dest_backup ,
1007+ . dest_external_dirs = external_dirs ,
1008+ . parent_chain = parent_chain ,
1009+ . dbOid_exclude_list = dbOid_exclude_list ,
1010+ . skip_external_dirs = params -> skip_external_dirs ,
1011+ . to_root = pgdata_path ,
1012+ . use_bitmap = use_bitmap ,
1013+ . incremental_mode = params -> incremental_mode ,
1014+ . shift_lsn = params -> shift_lsn ,
1015+ . restored_bytes = 0 ,
1016+ /* By default there are some error */
1017+ . ret = 1 ,
1018+ } ;
9911019
9921020/* Useless message TODO: rewrite */
9931021elog (LOG ,"Start thread %i" ,i + 1 );
9941022
995- pthread_create (& threads [i ],NULL ,restore_files ,arg );
1023+ pthread_create (& threads [i ],NULL ,restore_files ,& ( threads_args [ i ]) );
9961024}
9971025
998- /* Waittheads */
1026+ /* Waitthreads */
9991027for (i = 0 ;i < num_threads ;i ++ )
10001028{
10011029pthread_join (threads [i ],NULL );
@@ -1005,6 +1033,15 @@ restore_chain(pgBackup *dest_backup, parray *parent_chain,
10051033total_bytes += threads_args [i ].restored_bytes ;
10061034}
10071035
1036+ /* [Issue #313] copy pg_control at very end */
1037+ if (restore_isok )
1038+ {
1039+ fio_is_remote (FIO_DB_HOST );/* reopen already closed ssh connection */
1040+ total_bytes += restore_file (dest_pg_control_file ,dest_pg_control_fullpath , false,NULL ,
1041+ dest_backup ,parent_chain ,use_bitmap ,params -> incremental_mode ,params -> shift_lsn );
1042+ fio_disconnect ();
1043+ }
1044+
10081045time (& end_time );
10091046pretty_time_interval (difftime (end_time ,start_time ),
10101047pretty_time ,lengthof (pretty_time ));
@@ -1066,10 +1103,15 @@ restore_chain(pgBackup *dest_backup, parray *parent_chain,
10661103}
10671104
10681105/* TODO: write test for case: file to be synced is missing */
1106+ /* MKulagin question: where is fio connection reopened? */
10691107if (fio_sync (to_fullpath ,FIO_DB_HOST )!= 0 )
10701108elog (ERROR ,"Failed to sync file \"%s\": %s" ,to_fullpath ,strerror (errno ));
10711109}
10721110
1111+ /* sync control file */
1112+ if (fio_sync (dest_pg_control_fullpath ,FIO_DB_HOST )!= 0 )
1113+ elog (ERROR ,"Failed to sync file \"%s\": %s" ,dest_pg_control_fullpath ,strerror (errno ));
1114+
10731115time (& end_time );
10741116pretty_time_interval (difftime (end_time ,start_time ),
10751117pretty_time ,lengthof (pretty_time ));
@@ -1089,6 +1131,8 @@ restore_chain(pgBackup *dest_backup, parray *parent_chain,
10891131parray_free (pgdata_files );
10901132}
10911133
1134+ pgFileFree (dest_pg_control_file );
1135+
10921136for (i = parray_num (parent_chain )- 1 ;i >=0 ;i -- )
10931137{
10941138pgBackup * backup = (pgBackup * )parray_get (parent_chain ,i );
@@ -1107,7 +1151,6 @@ restore_files(void *arg)
11071151int i ;
11081152uint64 n_files ;
11091153char to_fullpath [MAXPGPATH ];
1110- FILE * out = NULL ;
11111154char * out_buf = pgut_malloc (STDIO_BUFSIZE );
11121155
11131156restore_files_arg * arguments = (restore_files_arg * )arg ;
@@ -1117,9 +1160,6 @@ restore_files(void *arg)
11171160for (i = 0 ;i < parray_num (arguments -> dest_files );i ++ )
11181161{
11191162bool already_exists = false;
1120- PageState * checksum_map = NULL ;/* it should take ~1.5MB at most */
1121- datapagemap_t * lsn_map = NULL ;/* it should take 16kB at most */
1122- char * errmsg = NULL ;/* remote agent error message */
11231163pgFile * dest_file = (pgFile * )parray_get (arguments -> dest_files ,i );
11241164
11251165/* Directories were created before */
@@ -1193,114 +1233,134 @@ restore_files(void *arg)
11931233already_exists = true;
11941234}
11951235
1196- /*
1197- * Handle incremental restore case for data files.
1198- * If file is already exists in pgdata, then
1199- * we scan it block by block and get
1200- * array of checksums for every page.
1201- */
1202- if (already_exists &&
1203- dest_file -> is_datafile && !dest_file -> is_cfs &&
1204- dest_file -> n_blocks > 0 )
1236+ arguments -> restored_bytes += restore_file (dest_file ,to_fullpath ,already_exists ,out_buf ,
1237+ arguments -> dest_backup ,arguments -> parent_chain ,arguments -> use_bitmap ,
1238+ arguments -> incremental_mode ,arguments -> shift_lsn );
1239+ }
1240+
1241+ free (out_buf );
1242+
1243+ /* ssh connection to longer needed */
1244+ fio_disconnect ();
1245+
1246+ /* Data files restoring is successful */
1247+ arguments -> ret = 0 ;
1248+
1249+ return NULL ;
1250+ }
1251+
1252+ /*
1253+ * Restore one file into $PGDATA.
1254+ */
1255+ static size_t
1256+ restore_file (pgFile * dest_file ,const char * to_fullpath ,bool already_exists ,char * out_buf ,
1257+ pgBackup * dest_backup ,parray * parent_chain ,bool use_bitmap ,IncrRestoreMode incremental_mode ,XLogRecPtr shift_lsn )
1258+ {
1259+ FILE * out = NULL ;
1260+ size_t restored_bytes = 0 ;
1261+ PageState * checksum_map = NULL ;/* it should take ~1.5MB at most */
1262+ datapagemap_t * lsn_map = NULL ;/* it should take 16kB at most */
1263+ char * errmsg = NULL ;/* remote agent error message */
1264+
1265+ /*
1266+ * Handle incremental restore case for data files.
1267+ * If file is already exists in pgdata, then
1268+ * we scan it block by block and get
1269+ * array of checksums for every page.
1270+ */
1271+ if (already_exists &&
1272+ dest_file -> is_datafile && !dest_file -> is_cfs &&
1273+ dest_file -> n_blocks > 0 )
1274+ {
1275+ if (incremental_mode == INCR_LSN )
12051276{
1206- if (arguments -> incremental_mode == INCR_LSN )
1207- {
1208- lsn_map = fio_get_lsn_map (to_fullpath ,arguments -> dest_backup -> checksum_version ,
1209- dest_file -> n_blocks ,arguments -> shift_lsn ,
1210- dest_file -> segno * RELSEG_SIZE ,FIO_DB_HOST );
1211- }
1212- else if (arguments -> incremental_mode == INCR_CHECKSUM )
1213- {
1214- checksum_map = fio_get_checksum_map (to_fullpath ,arguments -> dest_backup -> checksum_version ,
1215- dest_file -> n_blocks ,arguments -> dest_backup -> stop_lsn ,
1216- dest_file -> segno * RELSEG_SIZE ,FIO_DB_HOST );
1217- }
1277+ lsn_map = fio_get_lsn_map (to_fullpath ,dest_backup -> checksum_version ,
1278+ dest_file -> n_blocks ,shift_lsn ,
1279+ dest_file -> segno * RELSEG_SIZE ,FIO_DB_HOST );
12181280}
1281+ else if (incremental_mode == INCR_CHECKSUM )
1282+ {
1283+ checksum_map = fio_get_checksum_map (to_fullpath ,dest_backup -> checksum_version ,
1284+ dest_file -> n_blocks ,dest_backup -> stop_lsn ,
1285+ dest_file -> segno * RELSEG_SIZE ,FIO_DB_HOST );
1286+ }
1287+ }
12191288
1220- /*
1221- * Open dest file and truncate it to zero, if destination
1222- * file already exists and dest file size is zero, or
1223- * if file do not exist
1224- */
1225- if ((already_exists && dest_file -> write_size == 0 )|| !already_exists )
1226- out = fio_fopen (to_fullpath ,PG_BINARY_W ,FIO_DB_HOST );
1227- /*
1228- * If file already exists and dest size is not zero,
1229- * then open it for reading and writing.
1230- */
1231- else
1232- out = fio_fopen (to_fullpath ,PG_BINARY_R "+" ,FIO_DB_HOST );
1233-
1234- if (out == NULL )
1235- elog (ERROR ,"Cannot open restore target file \"%s\": %s" ,
1236- to_fullpath ,strerror (errno ));
1289+ /*
1290+ * Open dest file and truncate it to zero, if destination
1291+ * file already exists and dest file size is zero, or
1292+ * if file do not exist
1293+ */
1294+ if ((already_exists && dest_file -> write_size == 0 )|| !already_exists )
1295+ out = fio_fopen (to_fullpath ,PG_BINARY_W ,FIO_DB_HOST );
1296+ /*
1297+ * If file already exists and dest size is not zero,
1298+ * then open it for reading and writing.
1299+ */
1300+ else
1301+ out = fio_fopen (to_fullpath ,PG_BINARY_R "+" ,FIO_DB_HOST );
12371302
1238- /* update file permission */
1239- if (fio_chmod (to_fullpath ,dest_file -> mode ,FIO_DB_HOST )== -1 )
1240- elog (ERROR ,"Cannot change mode of \"%s\": %s" ,to_fullpath ,
1241- strerror (errno ));
1303+ if (out == NULL )
1304+ elog (ERROR ,"Cannot open restore target file \"%s\": %s" ,
1305+ to_fullpath ,strerror (errno ));
12421306
1243- if (! dest_file -> is_datafile || dest_file -> is_cfs )
1244- elog ( VERBOSE , "Restoring nonedata file: \"%s\"" , to_fullpath );
1245- else
1246- elog ( VERBOSE , "Restoring data file: \"%s\"" , to_fullpath );
1307+ /* update file permission */
1308+ if ( fio_chmod ( to_fullpath , dest_file -> mode , FIO_DB_HOST ) == -1 )
1309+ elog ( ERROR , "Cannot change mode of \"%s\": %s" , to_fullpath ,
1310+ strerror ( errno ) );
12471311
1248- // If destination file is 0 sized, then just close it and go for the next
1249- if (dest_file -> write_size == 0 )
1250- gotodone ;
1312+ if (!dest_file -> is_datafile || dest_file -> is_cfs )
1313+ elog (VERBOSE ,"Restoring nonedata file: \"%s\"" ,to_fullpath );
1314+ else
1315+ elog (VERBOSE ,"Restoring data file: \"%s\"" ,to_fullpath );
12511316
1317+ // If destination file is 0 sized, then just close it and go for the next
1318+ if (dest_file -> write_size != 0 )
1319+ {
12521320/* Restore destination file */
12531321if (dest_file -> is_datafile && !dest_file -> is_cfs )
12541322{
12551323/* enable stdio buffering for local destination data file */
1256- if (!fio_is_remote_file (out ))
1324+ if (!fio_is_remote_file (out )&& out_buf != NULL )
12571325setvbuf (out ,out_buf ,_IOFBF ,STDIO_BUFSIZE );
12581326/* Destination file is data file */
1259- arguments -> restored_bytes += restore_data_file (arguments -> parent_chain ,
1327+ restored_bytes += restore_data_file (parent_chain ,
12601328dest_file ,out ,to_fullpath ,
1261- arguments -> use_bitmap ,checksum_map ,
1262- arguments -> shift_lsn ,lsn_map , true);
1329+ use_bitmap ,checksum_map ,
1330+ shift_lsn ,lsn_map , true);
12631331}
12641332else
12651333{
12661334/* disable stdio buffering for local destination nonedata file */
12671335if (!fio_is_remote_file (out ))
12681336setvbuf (out ,NULL ,_IONBF ,BUFSIZ );
12691337/* Destination file is nonedata file */
1270- arguments -> restored_bytes += restore_non_data_file (arguments -> parent_chain ,
1271- arguments -> dest_backup ,dest_file ,out ,to_fullpath ,
1338+ restored_bytes += restore_non_data_file (parent_chain ,
1339+ dest_backup ,dest_file ,out ,to_fullpath ,
12721340already_exists );
12731341}
1342+ }
12741343
1275- done :
1276- /* Writing is asynchronous in case of restore in remote mode, so check the agent status */
1277- if (fio_check_error_file (out ,& errmsg ))
1278- elog (ERROR ,"Cannot write to the remote file \"%s\": %s" ,to_fullpath ,errmsg );
1279-
1280- /* close file */
1281- if (fio_fclose (out )!= 0 )
1282- elog (ERROR ,"Cannot close file \"%s\": %s" ,to_fullpath ,
1283- strerror (errno ));
1284-
1285- /* free pagemap used for restore optimization */
1286- pg_free (dest_file -> pagemap .bitmap );
1287-
1288- if (lsn_map )
1289- pg_free (lsn_map -> bitmap );
1344+ /* Writing is asynchronous in case of restore in remote mode, so check the agent status */
1345+ if (fio_check_error_file (out ,& errmsg ))
1346+ elog (ERROR ,"Cannot write to the remote file \"%s\": %s" ,to_fullpath ,errmsg );
12901347
1291- pg_free (lsn_map );
1292- pg_free (checksum_map );
1293- }
1348+ /* close file */
1349+ if (fio_fclose (out )!= 0 )
1350+ elog (ERROR ,"Cannot close file \"%s\": %s" ,to_fullpath ,
1351+ strerror (errno ));
12941352
1295- free (out_buf );
1353+ if (lsn_map )
1354+ pg_free (lsn_map -> bitmap );
12961355
1297- /* ssh connection to longer needed */
1298- fio_disconnect ( );
1356+ pg_free ( lsn_map );
1357+ pg_free ( checksum_map );
12991358
1300- /* Data files restoring is successful */
1301- arguments -> ret = 0 ;
1359+ /* free pagemap used for restore optimization */
1360+ pg_free (dest_file -> pagemap .bitmap );
1361+ dest_file -> pagemap .bitmap = NULL ;
13021362
1303- return NULL ;
1363+ return restored_bytes ;
13041364}
13051365
13061366/*