@@ -1666,6 +1666,152 @@ def test_losing_file_after_failed_merge(self):
16661666
16671667self .del_test_dir (module_name ,fname )
16681668
1669+ def test_failed_merge_after_delete (self ):
1670+ """
1671+ """
1672+ fname = self .id ().split ('.' )[3 ]
1673+ backup_dir = os .path .join (self .tmp_path ,module_name ,fname ,'backup' )
1674+ node = self .make_simple_node (
1675+ base_dir = os .path .join (module_name ,fname ,'node' ),
1676+ set_replication = True ,
1677+ initdb_params = ['--data-checksums' ],
1678+ pg_options = {'autovacuum' :'off' })
1679+
1680+ self .init_pb (backup_dir )
1681+ self .add_instance (backup_dir ,'node' ,node )
1682+ self .set_archiving (backup_dir ,'node' ,node )
1683+ node .slow_start ()
1684+
1685+ # add database
1686+ node .safe_psql (
1687+ 'postgres' ,
1688+ 'CREATE DATABASE testdb' )
1689+
1690+ dboid = node .safe_psql (
1691+ "postgres" ,
1692+ "select oid from pg_database where datname = 'testdb'" ).rstrip ()
1693+
1694+ # take FULL backup
1695+ full_id = self .backup_node (
1696+ backup_dir ,'node' ,node ,options = ['--stream' ])
1697+
1698+ # drop database
1699+ node .safe_psql (
1700+ 'postgres' ,
1701+ 'DROP DATABASE testdb' )
1702+
1703+ # take PAGE backup
1704+ page_id = self .backup_node (
1705+ backup_dir ,'node' ,node ,backup_type = 'page' )
1706+
1707+ gdb = self .merge_backup (
1708+ backup_dir ,'node' ,page_id ,
1709+ gdb = True ,options = ['--log-level-console=verbose' ])
1710+ gdb .set_breakpoint ('delete_backup_files' )
1711+ gdb .run_until_break ()
1712+
1713+ gdb .set_breakpoint ('parray_bsearch' )
1714+ gdb .continue_execution_until_break ()
1715+
1716+ gdb .set_breakpoint ('pgFileDelete' )
1717+ gdb .continue_execution_until_break (30 )
1718+
1719+ gdb ._execute ('signal SIGKILL' )
1720+
1721+ # backup half-merged
1722+ self .assertEqual (
1723+ 'OK' ,self .show_pb (backup_dir ,'node' )[0 ]['status' ])
1724+
1725+ self .assertEqual (
1726+ full_id ,self .show_pb (backup_dir ,'node' )[0 ]['id' ])
1727+
1728+ db_path = os .path .join (
1729+ backup_dir ,'backups' ,'node' ,
1730+ full_id ,'database' ,'base' ,dboid )
1731+
1732+ self .assertNotTrue (
1733+ os .path .isdir (db_path ))
1734+
1735+ exit (1 )
1736+
1737+ # try to continue failed MERGE
1738+ self .merge_backup (backup_dir ,"node" ,backup_id )
1739+
1740+ self .assertEqual (
1741+ 'OK' ,self .show_pb (backup_dir ,'node' )[0 ]['status' ])
1742+
1743+ node .cleanup ()
1744+
1745+ self .restore_node (backup_dir ,'node' ,node )
1746+
1747+ pgdata_restored = self .pgdata_content (node .data_dir )
1748+ self .compare_pgdata (pgdata ,pgdata_restored )
1749+
1750+ self .del_test_dir (module_name ,fname )
1751+
1752+ def test_failed_merge_after_delete_1 (self ):
1753+ """
1754+ """
1755+ fname = self .id ().split ('.' )[3 ]
1756+ backup_dir = os .path .join (self .tmp_path ,module_name ,fname ,'backup' )
1757+ node = self .make_simple_node (
1758+ base_dir = os .path .join (module_name ,fname ,'node' ),
1759+ set_replication = True ,
1760+ initdb_params = ['--data-checksums' ],
1761+ pg_options = {'autovacuum' :'off' })
1762+
1763+ self .init_pb (backup_dir )
1764+ self .add_instance (backup_dir ,'node' ,node )
1765+ self .set_archiving (backup_dir ,'node' ,node )
1766+ node .slow_start ()
1767+
1768+ # add database
1769+ node .pgbench_init (scale = 1 )
1770+
1771+ # take FULL backup
1772+ full_id = self .backup_node (
1773+ backup_dir ,'node' ,node ,options = ['--stream' ])
1774+
1775+ pgdata = self .pgdata_content (node .data_dir )
1776+
1777+ # drop database
1778+ pgbench = node .pgbench (options = ['-T' ,'10' ,'-c' ,'2' ,'--no-vacuum' ])
1779+ pgbench .wait ()
1780+
1781+ # take PAGE backup
1782+ page_id = self .backup_node (
1783+ backup_dir ,'node' ,node ,backup_type = 'page' )
1784+
1785+ gdb = self .merge_backup (
1786+ backup_dir ,'node' ,page_id ,
1787+ gdb = True ,options = ['--log-level-console=verbose' ])
1788+ gdb .set_breakpoint ('delete_backup_files' )
1789+ gdb .run_until_break ()
1790+
1791+ gdb .set_breakpoint ('parray_bsearch' )
1792+ gdb .continue_execution_until_break ()
1793+
1794+ gdb .set_breakpoint ('pgFileDelete' )
1795+ gdb .continue_execution_until_break (30 )
1796+
1797+ gdb ._execute ('signal SIGKILL' )
1798+
1799+ # backup half-merged
1800+ self .assertEqual (
1801+ 'OK' ,self .show_pb (backup_dir ,'node' )[0 ]['status' ])
1802+
1803+ self .assertEqual (
1804+ full_id ,self .show_pb (backup_dir ,'node' )[0 ]['id' ])
1805+
1806+ # restore
1807+ node .cleanup ()
1808+ self .restore_node (backup_dir ,'node' ,node )
1809+
1810+ pgdata_restored = self .pgdata_content (node .data_dir )
1811+ self .compare_pgdata (pgdata ,pgdata_restored )
1812+
1813+ self .del_test_dir (module_name ,fname )
1814+
16691815# @unittest.skip("skip")
16701816def test_merge_backup_from_future (self ):
16711817"""
@@ -1906,6 +2052,66 @@ def test_merge_multiple_descendants(self):
19062052# Clean after yourself
19072053self .del_test_dir (module_name ,fname )
19082054
2055+ # @unittest.skip("skip")
2056+ def test_smart_merge (self ):
2057+ """
2058+ make node, create database, take full backup, drop database,
2059+ take PAGE backup and merge it into FULL,
2060+ make sure that files from dropped database are not
2061+ copied during restore
2062+ https://github.com/postgrespro/pg_probackup/issues/63
2063+ """
2064+ fname = self .id ().split ('.' )[3 ]
2065+ node = self .make_simple_node (
2066+ base_dir = os .path .join (module_name ,fname ,'node' ),
2067+ set_replication = True ,
2068+ initdb_params = ['--data-checksums' ])
2069+
2070+ backup_dir = os .path .join (self .tmp_path ,module_name ,fname ,'backup' )
2071+ self .init_pb (backup_dir )
2072+ self .add_instance (backup_dir ,'node' ,node )
2073+ self .set_archiving (backup_dir ,'node' ,node )
2074+ node .slow_start ()
2075+
2076+ # create database
2077+ node .safe_psql (
2078+ "postgres" ,
2079+ "CREATE DATABASE testdb" )
2080+
2081+ # take FULL backup
2082+ full_id = self .backup_node (backup_dir ,'node' ,node )
2083+
2084+ # drop database
2085+ node .safe_psql (
2086+ "postgres" ,
2087+ "DROP DATABASE testdb" )
2088+
2089+ # take PAGE backup
2090+ page_id = self .backup_node (
2091+ backup_dir ,'node' ,node ,backup_type = 'page' )
2092+
2093+ # get delta between FULL and PAGE filelists
2094+ filelist_full = self .get_backup_filelist (
2095+ backup_dir ,'node' ,full_id )
2096+
2097+ filelist_page = self .get_backup_filelist (
2098+ backup_dir ,'node' ,page_id )
2099+
2100+ filelist_diff = self .get_backup_filelist_diff (
2101+ filelist_full ,filelist_page )
2102+
2103+ # merge PAGE backup
2104+ self .merge_backup (
2105+ backup_dir ,'node' ,page_id ,
2106+ options = ['--log-level-file=VERBOSE' ])
2107+
2108+ logfile = os .path .join (backup_dir ,'log' ,'pg_probackup.log' )
2109+ with open (logfile ,'r' )as f :
2110+ logfile_content = f .read ()
2111+
2112+ # Clean after yourself
2113+ self .del_test_dir (module_name ,fname )
2114+
19092115
19102116# 1. always use parent link when merging (intermediates may be from different chain)
19112117# 2. page backup we are merging with may disappear after failed merge,