@@ -1666,6 +1666,141 @@ 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+
1711+ gdb .set_breakpoint ('delete_backup_files' )
1712+ gdb .run_until_break ()
1713+
1714+ gdb .set_breakpoint ('parray_bsearch' )
1715+ gdb .continue_execution_until_break ()
1716+
1717+ gdb .set_breakpoint ('pgFileDelete' )
1718+ gdb .continue_execution_until_break (20 )
1719+
1720+ gdb ._execute ('signal SIGKILL' )
1721+
1722+ # backup half-merged
1723+ self .assertEqual (
1724+ 'OK' ,self .show_pb (backup_dir ,'node' )[0 ]['status' ])
1725+
1726+ self .assertEqual (
1727+ full_id ,self .show_pb (backup_dir ,'node' )[0 ]['id' ])
1728+
1729+ db_path = os .path .join (
1730+ backup_dir ,'backups' ,'node' ,
1731+ full_id ,'database' ,'base' ,dboid )
1732+
1733+ self .assertFalse (
1734+ os .path .isdir (db_path ),
1735+ 'Directory {0} should not exist' .format (
1736+ db_path ,full_id ))
1737+
1738+ self .del_test_dir (module_name ,fname )
1739+
1740+ def test_failed_merge_after_delete_1 (self ):
1741+ """
1742+ """
1743+ fname = self .id ().split ('.' )[3 ]
1744+ backup_dir = os .path .join (self .tmp_path ,module_name ,fname ,'backup' )
1745+ node = self .make_simple_node (
1746+ base_dir = os .path .join (module_name ,fname ,'node' ),
1747+ set_replication = True ,
1748+ initdb_params = ['--data-checksums' ],
1749+ pg_options = {'autovacuum' :'off' })
1750+
1751+ self .init_pb (backup_dir )
1752+ self .add_instance (backup_dir ,'node' ,node )
1753+ self .set_archiving (backup_dir ,'node' ,node )
1754+ node .slow_start ()
1755+
1756+ # add database
1757+ node .pgbench_init (scale = 1 )
1758+
1759+ # take FULL backup
1760+ full_id = self .backup_node (
1761+ backup_dir ,'node' ,node ,options = ['--stream' ])
1762+
1763+ pgdata = self .pgdata_content (node .data_dir )
1764+
1765+ # drop database
1766+ pgbench = node .pgbench (options = ['-T' ,'10' ,'-c' ,'2' ,'--no-vacuum' ])
1767+ pgbench .wait ()
1768+
1769+ # take PAGE backup
1770+ page_id = self .backup_node (
1771+ backup_dir ,'node' ,node ,backup_type = 'page' )
1772+
1773+ gdb = self .merge_backup (
1774+ backup_dir ,'node' ,page_id ,
1775+ gdb = True ,options = ['--log-level-console=verbose' ])
1776+
1777+ gdb .set_breakpoint ('delete_backup_files' )
1778+ gdb .run_until_break ()
1779+
1780+ gdb .set_breakpoint ('parray_bsearch' )
1781+ gdb .continue_execution_until_break ()
1782+
1783+ gdb .set_breakpoint ('pgFileDelete' )
1784+ gdb .continue_execution_until_break (30 )
1785+
1786+ gdb ._execute ('signal SIGKILL' )
1787+
1788+ # backup half-merged
1789+ self .assertEqual (
1790+ 'OK' ,self .show_pb (backup_dir ,'node' )[0 ]['status' ])
1791+
1792+ self .assertEqual (
1793+ full_id ,self .show_pb (backup_dir ,'node' )[0 ]['id' ])
1794+
1795+ # restore
1796+ node .cleanup ()
1797+ self .restore_node (backup_dir ,'node' ,node )
1798+
1799+ pgdata_restored = self .pgdata_content (node .data_dir )
1800+ self .compare_pgdata (pgdata ,pgdata_restored )
1801+
1802+ self .del_test_dir (module_name ,fname )
1803+
16691804# @unittest.skip("skip")
16701805def test_merge_backup_from_future (self ):
16711806"""
@@ -1906,6 +2041,66 @@ def test_merge_multiple_descendants(self):
19062041# Clean after yourself
19072042self .del_test_dir (module_name ,fname )
19082043
2044+ # @unittest.skip("skip")
2045+ def test_smart_merge (self ):
2046+ """
2047+ make node, create database, take full backup, drop database,
2048+ take PAGE backup and merge it into FULL,
2049+ make sure that files from dropped database are not
2050+ copied during restore
2051+ https://github.com/postgrespro/pg_probackup/issues/63
2052+ """
2053+ fname = self .id ().split ('.' )[3 ]
2054+ node = self .make_simple_node (
2055+ base_dir = os .path .join (module_name ,fname ,'node' ),
2056+ set_replication = True ,
2057+ initdb_params = ['--data-checksums' ])
2058+
2059+ backup_dir = os .path .join (self .tmp_path ,module_name ,fname ,'backup' )
2060+ self .init_pb (backup_dir )
2061+ self .add_instance (backup_dir ,'node' ,node )
2062+ self .set_archiving (backup_dir ,'node' ,node )
2063+ node .slow_start ()
2064+
2065+ # create database
2066+ node .safe_psql (
2067+ "postgres" ,
2068+ "CREATE DATABASE testdb" )
2069+
2070+ # take FULL backup
2071+ full_id = self .backup_node (backup_dir ,'node' ,node )
2072+
2073+ # drop database
2074+ node .safe_psql (
2075+ "postgres" ,
2076+ "DROP DATABASE testdb" )
2077+
2078+ # take PAGE backup
2079+ page_id = self .backup_node (
2080+ backup_dir ,'node' ,node ,backup_type = 'page' )
2081+
2082+ # get delta between FULL and PAGE filelists
2083+ filelist_full = self .get_backup_filelist (
2084+ backup_dir ,'node' ,full_id )
2085+
2086+ filelist_page = self .get_backup_filelist (
2087+ backup_dir ,'node' ,page_id )
2088+
2089+ filelist_diff = self .get_backup_filelist_diff (
2090+ filelist_full ,filelist_page )
2091+
2092+ # merge PAGE backup
2093+ self .merge_backup (
2094+ backup_dir ,'node' ,page_id ,
2095+ options = ['--log-level-file=VERBOSE' ])
2096+
2097+ logfile = os .path .join (backup_dir ,'log' ,'pg_probackup.log' )
2098+ with open (logfile ,'r' )as f :
2099+ logfile_content = f .read ()
2100+
2101+ # Clean after yourself
2102+ self .del_test_dir (module_name ,fname )
2103+
19092104
19102105# 1. always use parent link when merging (intermediates may be from different chain)
19112106# 2. page backup we are merging with may disappear after failed merge,