@@ -227,7 +227,10 @@ def test_external_dir_mapping(self):
227227 backup_dir , 'node' , node , backup_type = "delta" ,
228228 options = [
229229 "-j" , "4" , "--stream" ,
230- "-E" , "{0}:{1}" .format (external_dir1_old , external_dir2_old )])
230+ "-E" , "{0}{1}{2}" .format (
231+ external_dir1_old ,
232+ self .EXTERNAL_DIRECTORY_DELIMITER ,
233+ external_dir2_old )])
231234
232235 pgdata = self .pgdata_content (
233236 node .base_dir , exclude_dirs = ['logs' ])
@@ -383,8 +386,9 @@ def test_external_backward_compatibility(self):
383386 backup_dir , 'node' , node , backup_type = "delta" ,
384387 options = [
385388 "-j" , "4" , "--stream" ,
386- "-E" , "{0}:{1 }" .format (
389+ "-E" , "{0}{1}{2 }" .format (
387390 external_dir1_old ,
391+ self .EXTERNAL_DIRECTORY_DELIMITER ,
388392 external_dir2_old )])
389393
390394 pgdata = self .pgdata_content (
@@ -472,8 +476,9 @@ def test_external_backward_compatibility_merge_1(self):
472476 backup_dir , 'node' , node , backup_type = "delta" ,
473477 options = [
474478 "-j" , "4" , "--stream" ,
475- "-E" , "{0}:{1 }" .format (
479+ "-E" , "{0}{1}{2 }" .format (
476480 external_dir1_old ,
481+ self .EXTERNAL_DIRECTORY_DELIMITER ,
477482 external_dir2_old )])
478483
479484 pgdata = self .pgdata_content (
@@ -565,8 +570,9 @@ def test_external_backward_compatibility_merge_2(self):
565570 backup_type = "delta" ,
566571 options = [
567572 "-j" , "4" , "--stream" ,
568- "-E" , "{0}:{1 }" .format (
573+ "-E" , "{0}{1}{2 }" .format (
569574 external_dir1_old ,
575+ self .EXTERNAL_DIRECTORY_DELIMITER ,
570576 external_dir2_old )])
571577
572578 pgbench = node .pgbench (options = ['-T' , '30' , '-c' , '1' ])
@@ -592,8 +598,9 @@ def test_external_backward_compatibility_merge_2(self):
592598 backup_type = "delta" ,
593599 options = [
594600 "-j" , "4" , "--stream" ,
595- "-E" , "{0}:{1 }" .format (
601+ "-E" , "{0}{1}{2 }" .format (
596602 external_dir1_old ,
603+ self .EXTERNAL_DIRECTORY_DELIMITER ,
597604 external_dir2_old )])
598605
599606 pgdata = self .pgdata_content (
@@ -687,8 +694,9 @@ def test_external_merge(self):
687694 backup_dir , 'node' , node , backup_type = "delta" ,
688695 options = [
689696 "-j" , "4" , "--stream" ,
690- "-E" , "{0}:{1 }" .format (
697+ "-E" , "{0}{1}{2 }" .format (
691698 external_dir1_old ,
699+ self .EXTERNAL_DIRECTORY_DELIMITER ,
692700 external_dir2_old )])
693701
694702 self .merge_backup (backup_dir , 'node' , backup_id = backup_id )
@@ -767,8 +775,9 @@ def test_external_merge_1(self):
767775 backup_dir , 'node' , node ,
768776 options = [
769777 "-j" , "4" , "--stream" ,
770- "-E" , "{0}:{1 }" .format (
778+ "-E" , "{0}{1}{2 }" .format (
771779 external_dir1_old ,
780+ self .EXTERNAL_DIRECTORY_DELIMITER ,
772781 external_dir2_old )])
773782
774783 # drop old external data
@@ -791,8 +800,9 @@ def test_external_merge_1(self):
791800 backup_dir , 'node' , node , backup_type = "delta" ,
792801 options = [
793802 "-j" , "4" , "--stream" ,
794- "-E" , "{0}:{1 }" .format (
803+ "-E" , "{0}{1}{2 }" .format (
795804 external_dir1_old ,
805+ self .EXTERNAL_DIRECTORY_DELIMITER ,
796806 external_dir2_old )])
797807
798808 pgdata = self .pgdata_content (
@@ -877,8 +887,9 @@ def test_external_merge_single(self):
877887 backup_dir , 'node' , node , backup_type = "delta" ,
878888 options = [
879889 "-j" , "4" , "--stream" ,
880- "-E" , "{0}:{1 }" .format (
890+ "-E" , "{0}{1}{2 }" .format (
881891 external_dir1_old ,
892+ self .EXTERNAL_DIRECTORY_DELIMITER ,
882893 external_dir2_old )])
883894
884895 self .merge_backup (backup_dir , 'node' , backup_id = backup_id )
@@ -959,17 +970,19 @@ def test_external_merge_double(self):
959970 backup_dir , 'node' , node , backup_type = "delta" ,
960971 options = [
961972 "-j" , "4" , "--stream" ,
962- "-E" , "{0}:{1 }" .format (
973+ "-E" , "{0}{1}{2 }" .format (
963974 external_dir1_old ,
975+ self .EXTERNAL_DIRECTORY_DELIMITER ,
964976 external_dir2_old )])
965977
966978 # delta backup with external directories
967979 backup_id = self .backup_node (
968980 backup_dir , 'node' , node , backup_type = "delta" ,
969981 options = [
970982 "-j" , "4" , "--stream" ,
971- "-E" , "{0}:{1 }" .format (
983+ "-E" , "{0}{1}{2 }" .format (
972984 external_dir1_old ,
985+ self .EXTERNAL_DIRECTORY_DELIMITER ,
973986 external_dir2_old )])
974987
975988 pgdata = self .pgdata_content (
@@ -1049,8 +1062,9 @@ def test_restore_skip_external(self):
10491062 backup_dir , 'node' , node ,
10501063 options = [
10511064 "-j" , "4" , "--stream" ,
1052- "-E" , "{0}:{1 }" .format (
1065+ "-E" , "{0}{1}{2 }" .format (
10531066 external_dir1 ,
1067+ self .EXTERNAL_DIRECTORY_DELIMITER ,
10541068 external_dir2 )])
10551069
10561070 # delete first externals, so pgdata_compare
@@ -1476,9 +1490,95 @@ def test_restore_external_dir_not_empty(self):
14761490 def test_restore_external_dir_is_missing (self ):
14771491 """
14781492 take FULL backup with not empty external directory
1479- drop external directory
1480- take DELTA backup
1481- restore page backup, check that restored
1493+ delete external directory
1494+ take DELTA backup with external directory, which
1495+ should fail
1496+ """
1497+ fname = self .id ().split ('.' )[3 ]
1498+ backup_dir = os .path .join (self .tmp_path , module_name , fname , 'backup' )
1499+ core_dir = os .path .join (self .tmp_path , module_name , fname )
1500+ shutil .rmtree (core_dir , ignore_errors = True )
1501+ node = self .make_simple_node (
1502+ base_dir = os .path .join (module_name , fname , 'node' ),
1503+ set_replication = True ,
1504+ initdb_params = ['--data-checksums' ],
1505+ pg_options = {
1506+ 'max_wal_senders' : '2' ,
1507+ 'autovacuum' : 'off' })
1508+
1509+ self .init_pb (backup_dir )
1510+ self .add_instance (backup_dir , 'node' , node )
1511+ node .slow_start ()
1512+
1513+ external_dir = self .get_tblspace_path (node , 'external_dir' )
1514+
1515+ # create empty file in external directory
1516+ # open(os.path.join(external_dir, 'file'), 'a').close()
1517+ os .mkdir (external_dir )
1518+ with open (os .path .join (external_dir , 'file' ), 'w+' ) as f :
1519+ f .close ()
1520+
1521+ # FULL backup with external directory
1522+ self .backup_node (
1523+ backup_dir , 'node' , node ,
1524+ options = [
1525+ "-j" , "4" , "--stream" ,
1526+ "-E" , "{0}" .format (
1527+ external_dir )])
1528+
1529+ # drop external directory
1530+ shutil .rmtree (external_dir , ignore_errors = True )
1531+
1532+ try :
1533+ self .backup_node (
1534+ backup_dir , 'node' , node ,
1535+ backup_type = 'delta' ,
1536+ options = [
1537+ "-j" , "4" , "--stream" ,
1538+ "-E" , "{0}" .format (
1539+ external_dir )])
1540+ # we should die here because exception is what we expect to happen
1541+ self .assertEqual (
1542+ 1 , 0 ,
1543+ "Expecting Error because external dir is missing"
1544+ "\n Output: {0} \n CMD: {1}" .format (
1545+ repr (self .output ), self .cmd ))
1546+ except ProbackupException as e :
1547+ self .assertTrue (
1548+ 'ERROR: External directory is not found: "{0}"' .format (external_dir ) in e .message ,
1549+ '\n Unexpected Error Message: {0}\n CMD: {1}' .format (
1550+ repr (e .message ), self .cmd ))
1551+
1552+ sleep (1 )
1553+
1554+ # take DELTA without external directories
1555+ self .backup_node (
1556+ backup_dir , 'node' , node ,
1557+ backup_type = 'delta' ,
1558+ options = ["-j" , "4" , "--stream" ])
1559+
1560+ pgdata = self .pgdata_content (
1561+ node .base_dir , exclude_dirs = ['logs' ])
1562+
1563+ # Restore Delta backup
1564+ node .cleanup ()
1565+ shutil .rmtree (node .base_dir , ignore_errors = True )
1566+
1567+ self .restore_node (backup_dir , 'node' , node )
1568+
1569+ pgdata_restored = self .pgdata_content (
1570+ node .base_dir , exclude_dirs = ['logs' ])
1571+ self .compare_pgdata (pgdata , pgdata_restored )
1572+
1573+ # Clean after yourself
1574+ self .del_test_dir (module_name , fname )
1575+
1576+ def test_restore_external_dir_is_empty (self ):
1577+ """
1578+ take FULL backup with not empty external directory
1579+ drop external directory content
1580+ take DELTA backup with the same external directory
1581+ restore DELRA backup, check that restored
14821582 external directory is empty
14831583 """
14841584 fname = self .id ().split ('.' )[3 ]
@@ -1513,9 +1613,10 @@ def test_restore_external_dir_is_missing(self):
15131613 "-E" , "{0}" .format (
15141614 external_dir )])
15151615
1516- # drop external file only
1616+ # make external directory empty
15171617 os .remove (os .path .join (external_dir , 'file' ))
15181618
1619+ # take DELTA backup with empty external directory
15191620 self .backup_node (
15201621 backup_dir , 'node' , node ,
15211622 backup_type = 'delta' ,
@@ -1530,18 +1631,101 @@ def test_restore_external_dir_is_missing(self):
15301631 # Restore Delta backup
15311632 node_restored = self .make_simple_node (
15321633 base_dir = os .path .join (module_name , fname , 'node_restored' ))
1634+
15331635 node_restored .cleanup ()
15341636
1535- external_dir_new = self .get_tblspace_path (node_restored , 'external_dir_new' )
1637+ external_dir_new = self .get_tblspace_path (
1638+ node_restored , 'external_dir' )
15361639
15371640 self .restore_node (
15381641 backup_dir , 'node' , node_restored ,
1539- options = [" --external-mapping={0}={1}" .format (
1540- external_dir , external_dir_new )])
1642+ options = [' --external-mapping={0}={1}' .format (
1643+ external_dir , external_dir_new )])
15411644
15421645 pgdata_restored = self .pgdata_content (
15431646 node_restored .base_dir , exclude_dirs = ['logs' ])
15441647 self .compare_pgdata (pgdata , pgdata_restored )
15451648
15461649 # Clean after yourself
15471650 self .del_test_dir (module_name , fname )
1651+
1652+ def test_restore_external_dir_string_order (self ):
1653+ """
1654+ take FULL backup with not empty external directory
1655+ drop external directory content
1656+ take DELTA backup with the same external directory
1657+ restore DELRA backup, check that restored
1658+ external directory is empty
1659+ """
1660+ fname = self .id ().split ('.' )[3 ]
1661+ backup_dir = os .path .join (self .tmp_path , module_name , fname , 'backup' )
1662+ core_dir = os .path .join (self .tmp_path , module_name , fname )
1663+ shutil .rmtree (core_dir , ignore_errors = True )
1664+ node = self .make_simple_node (
1665+ base_dir = os .path .join (module_name , fname , 'node' ),
1666+ set_replication = True ,
1667+ initdb_params = ['--data-checksums' ],
1668+ pg_options = {
1669+ 'max_wal_senders' : '2' ,
1670+ 'autovacuum' : 'off' })
1671+
1672+ self .init_pb (backup_dir )
1673+ self .add_instance (backup_dir , 'node' , node )
1674+ node .slow_start ()
1675+
1676+ external_dir_1 = self .get_tblspace_path (node , 'external_dir_1' )
1677+ external_dir_2 = self .get_tblspace_path (node , 'external_dir_2' )
1678+
1679+ # create empty file in external directory
1680+ os .mkdir (external_dir_1 )
1681+ with open (os .path .join (external_dir_1 , 'fileA' ), 'w+' ) as f :
1682+ f .close ()
1683+
1684+ os .mkdir (external_dir_2 )
1685+ with open (os .path .join (external_dir_2 , 'fileZ' ), 'w+' ) as f :
1686+ f .close ()
1687+
1688+ # FULL backup with external directory
1689+ self .backup_node (
1690+ backup_dir , 'node' , node ,
1691+ options = [
1692+ "-j" , "4" , "--stream" ,
1693+ "-E" , "{0}{1}{2}" .format (
1694+ external_dir_1 ,
1695+ self .EXTERNAL_DIRECTORY_DELIMITER ,
1696+ external_dir_2 )])
1697+
1698+ with open (os .path .join (external_dir_1 , 'fileB' ), 'w+' ) as f :
1699+ f .close ()
1700+
1701+ with open (os .path .join (external_dir_2 , 'fileY' ), 'w+' ) as f :
1702+ f .close ()
1703+
1704+ # take DELTA backup and swap external_dir_2 and external_dir_1
1705+ # in external_dir_str
1706+ self .backup_node (
1707+ backup_dir , 'node' , node ,
1708+ backup_type = 'delta' ,
1709+ options = [
1710+ "-j" , "4" , "--stream" ,
1711+ "-E" , "{0}{1}{2}" .format (
1712+ external_dir_2 ,
1713+ self .EXTERNAL_DIRECTORY_DELIMITER ,
1714+ external_dir_1 )])
1715+
1716+ pgdata = self .pgdata_content (
1717+ node .base_dir , exclude_dirs = ['logs' ])
1718+
1719+ # Restore Delta backup
1720+ node .cleanup ()
1721+ shutil .rmtree (node .base_dir , ignore_errors = True )
1722+
1723+ self .restore_node (backup_dir , 'node' , node )
1724+
1725+ pgdata_restored = self .pgdata_content (
1726+ node .base_dir , exclude_dirs = ['logs' ])
1727+
1728+ self .compare_pgdata (pgdata , pgdata_restored )
1729+
1730+ # Clean after yourself
1731+ self .del_test_dir (module_name , fname )
0 commit comments