@@ -982,6 +982,204 @@ def test_validate_specific_target_corrupted_intermediate_backups(self):
982982# Clean after yourself
983983self .del_test_dir (module_name ,fname )
984984
985+ # @unittest.skip("skip")
986+ def test_validate_instance_with_several_corrupt_backups (self ):
987+ """
988+ make archive node, take FULL1, PAGE1_1, FULL2, PAGE2_1 backups, FULL3
989+ corrupt file in FULL and FULL2 and run validate on instance,
990+ expect FULL1 to gain status CORRUPT, PAGE1_1 to gain status ORPHAN
991+ FULL2 to gain status CORRUPT, PAGE2_1 to gain status ORPHAN
992+ """
993+ fname = self .id ().split ('.' )[3 ]
994+ node = self .make_simple_node (
995+ base_dir = os .path .join (module_name ,fname ,'node' ),
996+ initdb_params = ['--data-checksums' ])
997+
998+ backup_dir = os .path .join (self .tmp_path ,module_name ,fname ,'backup' )
999+ self .init_pb (backup_dir )
1000+ self .add_instance (backup_dir ,'node' ,node )
1001+ self .set_archiving (backup_dir ,'node' ,node )
1002+ node .slow_start ()
1003+
1004+ node .safe_psql (
1005+ "postgres" ,
1006+ "create table t_heap as select generate_series(0,1) i" )
1007+ # FULL1
1008+ backup_id_1 = self .backup_node (
1009+ backup_dir ,'node' ,node ,options = ['--no-validate' ])
1010+
1011+ # FULL2
1012+ backup_id_2 = self .backup_node (backup_dir ,'node' ,node )
1013+ rel_path = node .safe_psql (
1014+ "postgres" ,
1015+ "select pg_relation_filepath('t_heap')" ).decode ('utf-8' ).rstrip ()
1016+
1017+ node .safe_psql (
1018+ "postgres" ,
1019+ "insert into t_heap values(2)" )
1020+
1021+ backup_id_3 = self .backup_node (
1022+ backup_dir ,'node' ,node ,backup_type = 'page' )
1023+
1024+ # FULL3
1025+ backup_id_4 = self .backup_node (backup_dir ,'node' ,node )
1026+
1027+ node .safe_psql (
1028+ "postgres" ,
1029+ "insert into t_heap values(3)" )
1030+
1031+ backup_id_5 = self .backup_node (
1032+ backup_dir ,'node' ,node ,backup_type = 'page' )
1033+
1034+ # FULL4
1035+ backup_id_6 = self .backup_node (
1036+ backup_dir ,'node' ,node ,options = ['--no-validate' ])
1037+
1038+ # Corrupt some files in FULL2 and FULL3 backup
1039+ os .remove (os .path .join (
1040+ backup_dir ,'backups' ,'node' ,backup_id_2 ,
1041+ 'database' ,rel_path ))
1042+ os .remove (os .path .join (
1043+ backup_dir ,'backups' ,'node' ,backup_id_4 ,
1044+ 'database' ,rel_path ))
1045+
1046+ # Validate Instance
1047+ try :
1048+ self .validate_pb (backup_dir ,'node' ,options = ["-j" ,"4" ,"--log-level-file=LOG" ])
1049+ self .assertEqual (
1050+ 1 ,0 ,
1051+ "Expecting Error because of data files corruption.\n "
1052+ "Output: {0}\n CMD: {1}" .format (
1053+ repr (self .output ),self .cmd ))
1054+ except ProbackupException as e :
1055+ self .assertTrue (
1056+ "INFO: Validate backups of the instance 'node'" in e .message ,
1057+ "\n Unexpected Error Message: {0}\n "
1058+ "CMD: {1}" .format (repr (e .message ),self .cmd ))
1059+ self .assertTrue (
1060+ 'WARNING: Some backups are not valid' in e .message ,
1061+ '\n Unexpected Error Message: {0}\n CMD: {1}' .format (
1062+ repr (e .message ),self .cmd ))
1063+
1064+ self .assertEqual (
1065+ 'OK' ,self .show_pb (backup_dir ,'node' ,backup_id_1 )['status' ],
1066+ 'Backup STATUS should be "OK"' )
1067+ self .assertEqual (
1068+ 'CORRUPT' ,self .show_pb (backup_dir ,'node' ,backup_id_2 )['status' ],
1069+ 'Backup STATUS should be "CORRUPT"' )
1070+ self .assertEqual (
1071+ 'ORPHAN' ,self .show_pb (backup_dir ,'node' ,backup_id_3 )['status' ],
1072+ 'Backup STATUS should be "ORPHAN"' )
1073+ self .assertEqual (
1074+ 'CORRUPT' ,self .show_pb (backup_dir ,'node' ,backup_id_4 )['status' ],
1075+ 'Backup STATUS should be "CORRUPT"' )
1076+ self .assertEqual (
1077+ 'ORPHAN' ,self .show_pb (backup_dir ,'node' ,backup_id_5 )['status' ],
1078+ 'Backup STATUS should be "ORPHAN"' )
1079+ self .assertEqual (
1080+ 'OK' ,self .show_pb (backup_dir ,'node' ,backup_id_6 )['status' ],
1081+ 'Backup STATUS should be "OK"' )
1082+
1083+ # Clean after yourself
1084+ self .del_test_dir (module_name ,fname )
1085+
1086+ # @unittest.skip("skip")
1087+ def test_validate_instance_with_several_corrupt_backups_interrupt (self ):
1088+ """
1089+ check that interrupt during validation is handled correctly
1090+ """
1091+ fname = self .id ().split ('.' )[3 ]q
1092+ node = self .make_simple_node (
1093+ base_dir = os .path .join (module_name ,fname ,'node' ),
1094+ initdb_params = ['--data-checksums' ])
1095+
1096+ backup_dir = os .path .join (self .tmp_path ,module_name ,fname ,'backup' )
1097+ self .init_pb (backup_dir )
1098+ self .add_instance (backup_dir ,'node' ,node )
1099+ self .set_archiving (backup_dir ,'node' ,node )
1100+ node .slow_start ()
1101+
1102+ node .safe_psql (
1103+ "postgres" ,
1104+ "create table t_heap as select generate_series(0,1) i" )
1105+ # FULL1
1106+ backup_id_1 = self .backup_node (
1107+ backup_dir ,'node' ,node ,options = ['--no-validate' ])
1108+
1109+ # FULL2
1110+ backup_id_2 = self .backup_node (backup_dir ,'node' ,node )
1111+ rel_path = node .safe_psql (
1112+ "postgres" ,
1113+ "select pg_relation_filepath('t_heap')" ).decode ('utf-8' ).rstrip ()
1114+
1115+ node .safe_psql (
1116+ "postgres" ,
1117+ "insert into t_heap values(2)" )
1118+
1119+ backup_id_3 = self .backup_node (
1120+ backup_dir ,'node' ,node ,backup_type = 'page' )
1121+
1122+ # FULL3
1123+ backup_id_4 = self .backup_node (backup_dir ,'node' ,node )
1124+
1125+ node .safe_psql (
1126+ "postgres" ,
1127+ "insert into t_heap values(3)" )
1128+
1129+ backup_id_5 = self .backup_node (
1130+ backup_dir ,'node' ,node ,backup_type = 'page' )
1131+
1132+ # FULL4
1133+ backup_id_6 = self .backup_node (
1134+ backup_dir ,'node' ,node ,options = ['--no-validate' ])
1135+
1136+ # Corrupt some files in FULL2 and FULL3 backup
1137+ os .remove (os .path .join (
1138+ backup_dir ,'backups' ,'node' ,backup_id_1 ,
1139+ 'database' ,rel_path ))
1140+ os .remove (os .path .join (
1141+ backup_dir ,'backups' ,'node' ,backup_id_3 ,
1142+ 'database' ,rel_path ))
1143+
1144+ # Validate Instance
1145+ gdb = self .validate_pb (
1146+ backup_dir ,'node' ,options = ["-j" ,"4" ,"--log-level-file=LOG" ],gdb = True )
1147+
1148+ gdb .set_breakpoint ('validate_file_pages' )
1149+ gdb .run_until_break ()
1150+ gdb .continue_execution_until_break ()
1151+ gdb .remove_all_breakpoints ()
1152+ gdb ._execute ('signal SIGINT' )
1153+ gdb .continue_execution_until_error ()
1154+
1155+ self .assertEqual (
1156+ 'DONE' ,self .show_pb (backup_dir ,'node' ,backup_id_1 )['status' ],
1157+ 'Backup STATUS should be "OK"' )
1158+ self .assertEqual (
1159+ 'OK' ,self .show_pb (backup_dir ,'node' ,backup_id_2 )['status' ],
1160+ 'Backup STATUS should be "OK"' )
1161+ self .assertEqual (
1162+ 'OK' ,self .show_pb (backup_dir ,'node' ,backup_id_3 )['status' ],
1163+ 'Backup STATUS should be "CORRUPT"' )
1164+ self .assertEqual (
1165+ 'OK' ,self .show_pb (backup_dir ,'node' ,backup_id_4 )['status' ],
1166+ 'Backup STATUS should be "ORPHAN"' )
1167+ self .assertEqual (
1168+ 'OK' ,self .show_pb (backup_dir ,'node' ,backup_id_5 )['status' ],
1169+ 'Backup STATUS should be "OK"' )
1170+ self .assertEqual (
1171+ 'DONE' ,self .show_pb (backup_dir ,'node' ,backup_id_6 )['status' ],
1172+ 'Backup STATUS should be "OK"' )
1173+
1174+ log_file = os .path .join (backup_dir ,'log' ,'pg_probackup.log' )
1175+ with open (log_file ,'r' )as f :
1176+ log_content = f .read ()
1177+ self .assertNotIn (
1178+ 'Interrupted while locking backup' ,log_content )
1179+
1180+ # Clean after yourself
1181+ self .del_test_dir (module_name ,fname )
1182+
9851183# @unittest.skip("skip")
9861184def test_validate_instance_with_corrupted_page (self ):
9871185"""