Skip to content

Commit ee5810c

Browse files
committed
Merge branch 'master' into stable
2 parents 85b3003 + 4ebcea7 commit ee5810c

File tree

6 files changed

+186
-90
lines changed

6 files changed

+186
-90
lines changed

src/restore.c

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@
1414
#include <sys/stat.h>
1515
#include <sys/types.h>
1616
#include <unistd.h>
17-
#include <pthread.h>
1817

1918
#include "catalog/pg_control.h"
2019
#include "utils/logger.h"

tests/false_positive.py

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,9 @@ class FalsePositive(ProbackupTest, unittest.TestCase):
1313
# @unittest.skip("skip")
1414
@unittest.expectedFailure
1515
def test_validate_wal_lost_segment(self):
16-
"""Loose segment located between backups. ExpectedFailure. This is BUG """
16+
"""
17+
Loose segment located between backups. ExpectedFailure. This is BUG
18+
"""
1719
fname = self.id().split('.')[3]
1820
node = self.make_simple_node(
1921
base_dir="{0}/{1}/node".format(module_name, fname),
@@ -31,14 +33,7 @@ def test_validate_wal_lost_segment(self):
3133
self.backup_node(backup_dir, 'node', node)
3234

3335
# make some wals
34-
node.pgbench_init(scale=2)
35-
pgbench = node.pgbench(
36-
stdout=subprocess.PIPE,
37-
stderr=subprocess.STDOUT,
38-
options=["-c", "4", "-T", "10"]
39-
)
40-
pgbench.wait()
41-
pgbench.stdout.close()
36+
node.pgbench_init(scale=5)
4237

4338
# delete last wal segment
4439
wals_dir = os.path.join(backup_dir, "wal", 'node')

tests/helpers/ptrack_helpers.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -614,15 +614,16 @@ def backup_node(
614614

615615
return self.run_pb(cmd_list + options, async, gdb)
616616

617-
def merge_backup(self, backup_dir, instance, backup_id):
617+
def merge_backup(
618+
self, backup_dir, instance, backup_id, async=False, gdb=False):
618619
cmd_list = [
619620
"merge",
620621
"-B", backup_dir,
621622
"--instance={0}".format(instance),
622623
"-i", backup_id
623624
]
624625

625-
return self.run_pb(cmd_list)
626+
return self.run_pb(cmd_list, async, gdb)
626627

627628
def restore_node(
628629
self, backup_dir, instance, node=False,

tests/merge.py

Lines changed: 73 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -458,7 +458,8 @@ def test_merge_ptrack_truncate(self):
458458
def test_merge_delta_delete(self):
459459
"""
460460
Make node, create tablespace with table, take full backup,
461-
alter tablespace location, take delta backup, restore database.
461+
alter tablespace location, take delta backup, merge full and delta,
462+
restore database.
462463
"""
463464
fname = self.id().split('.')[3]
464465
backup_dir = os.path.join(self.tmp_path, module_name, fname, 'backup')
@@ -542,3 +543,74 @@ def test_merge_delta_delete(self):
542543

543544
# Clean after yourself
544545
self.del_test_dir(module_name, fname)
546+
547+
# @unittest.skip("skip")
548+
def test_continue_failed_merge(self):
549+
"""
550+
Check that failed MERGE can be continued
551+
"""
552+
fname = self.id().split('.')[3]
553+
backup_dir = os.path.join(self.tmp_path, module_name, fname, 'backup')
554+
node = self.make_simple_node(
555+
base_dir="{0}/{1}/node".format(module_name, fname),
556+
set_replication=True, initdb_params=['--data-checksums'],
557+
pg_options={
558+
'wal_level': 'replica'
559+
}
560+
)
561+
562+
self.init_pb(backup_dir)
563+
self.add_instance(backup_dir, 'node', node)
564+
self.set_archiving(backup_dir, 'node', node)
565+
node.start()
566+
567+
# FULL backup
568+
self.backup_node(backup_dir, 'node', node)
569+
570+
node.safe_psql(
571+
"postgres",
572+
"create table t_heap as select i as id,"
573+
" md5(i::text) as text, md5(i::text)::tsvector as tsvector"
574+
" from generate_series(0,1000) i"
575+
)
576+
577+
# DELTA BACKUP
578+
self.backup_node(
579+
backup_dir, 'node', node, backup_type='delta'
580+
)
581+
582+
node.safe_psql(
583+
"postgres",
584+
"delete from t_heap"
585+
)
586+
587+
node.safe_psql(
588+
"postgres",
589+
"vacuum t_heap"
590+
)
591+
592+
# DELTA BACKUP
593+
self.backup_node(
594+
backup_dir, 'node', node, backup_type='delta'
595+
)
596+
597+
if self.paranoia:
598+
pgdata = self.pgdata_content(node.data_dir)
599+
600+
backup_id = self.show_pb(backup_dir, "node")[2]["id"]
601+
602+
gdb = self.merge_backup(backup_dir, "node", backup_id, gdb=True)
603+
604+
gdb.set_breakpoint('move_file')
605+
gdb.run_until_break()
606+
607+
if gdb.continue_execution_until_break(20) != 'breakpoint-hit':
608+
print('Failed to hit breakpoint')
609+
exit(1)
610+
611+
gdb._execute('signal SIGKILL')
612+
613+
print(self.show_pb(backup_dir, as_text=True, as_json=False))
614+
615+
# Try to continue failed MERGE
616+
self.merge_backup(backup_dir, "node", backup_id)

tests/page.py

Lines changed: 95 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -166,11 +166,6 @@ def test_page_stream(self):
166166
'\n Unexpected Error Message: {0}\n'
167167
' CMD: {1}'.format(repr(self.output), self.cmd))
168168

169-
# GET RESTORED PGDATA AND COMPARE
170-
if self.paranoia:
171-
pgdata_restored = self.pgdata_content(node.data_dir)
172-
self.compare_pgdata(pgdata, pgdata_restored)
173-
174169
node.slow_start()
175170
full_result_new = node.execute("postgres", "SELECT * FROM t_heap")
176171
self.assertEqual(full_result, full_result_new)
@@ -184,6 +179,12 @@ def test_page_stream(self):
184179
backup_id=page_backup_id, options=["-j", "4"]),
185180
'\n Unexpected Error Message: {0}\n'
186181
' CMD: {1}'.format(repr(self.output), self.cmd))
182+
183+
# GET RESTORED PGDATA AND COMPARE
184+
if self.paranoia:
185+
pgdata_restored = self.pgdata_content(node.data_dir)
186+
self.compare_pgdata(pgdata, pgdata_restored)
187+
187188
node.slow_start()
188189
page_result_new = node.execute("postgres", "SELECT * FROM t_heap")
189190
self.assertEqual(page_result, page_result_new)
@@ -659,3 +660,92 @@ def test_parallel_pagemap_1(self):
659660
# Clean after yourself
660661
node.cleanup()
661662
self.del_test_dir(module_name, fname)
663+
664+
# @unittest.skip("skip")
665+
def test_page_backup_with_lost_wal_segment(self):
666+
"""
667+
make node with archiving
668+
make archive backup, then generate some wals with pgbench,
669+
delete latest archived wal segment
670+
run page backup, expecting error because of missing wal segment
671+
make sure that backup status is 'ERROR'
672+
"""
673+
fname = self.id().split('.')[3]
674+
node = self.make_simple_node(
675+
base_dir="{0}/{1}/node".format(module_name, fname),
676+
initdb_params=['--data-checksums'],
677+
pg_options={'wal_level': 'replica'}
678+
)
679+
backup_dir = os.path.join(self.tmp_path, module_name, fname, 'backup')
680+
self.init_pb(backup_dir)
681+
self.add_instance(backup_dir, 'node', node)
682+
self.set_archiving(backup_dir, 'node', node)
683+
node.start()
684+
685+
self.backup_node(backup_dir, 'node', node)
686+
687+
# make some wals
688+
node.pgbench_init(scale=3)
689+
690+
# delete last wal segment
691+
wals_dir = os.path.join(backup_dir, 'wal', 'node')
692+
wals = [f for f in os.listdir(wals_dir) if os.path.isfile(os.path.join(
693+
wals_dir, f)) and not f.endswith('.backup')]
694+
wals = map(str, wals)
695+
file = os.path.join(wals_dir, max(wals))
696+
os.remove(file)
697+
if self.archive_compress:
698+
file = file[:-3]
699+
700+
# Single-thread PAGE backup
701+
try:
702+
self.backup_node(
703+
backup_dir, 'node', node,
704+
backup_type='page')
705+
self.assertEqual(
706+
1, 0,
707+
"Expecting Error because of wal segment disappearance.\n "
708+
"Output: {0} \n CMD: {1}".format(
709+
self.output, self.cmd))
710+
except ProbackupException as e:
711+
self.assertTrue(
712+
'INFO: Wait for LSN' in e.message and
713+
'in archived WAL segment' in e.message and
714+
'WARNING: could not read WAL record at' in e.message and
715+
'ERROR: WAL segment "{0}" is absent\n'.format(
716+
file) in e.message,
717+
'\n Unexpected Error Message: {0}\n CMD: {1}'.format(
718+
repr(e.message), self.cmd))
719+
720+
self.assertEqual(
721+
'ERROR',
722+
self.show_pb(backup_dir, 'node')[1]['status'],
723+
'Backup {0} should have STATUS "ERROR"')
724+
725+
# Multi-thread PAGE backup
726+
try:
727+
self.backup_node(
728+
backup_dir, 'node', node,
729+
backup_type='page', options=["-j", "4"])
730+
self.assertEqual(
731+
1, 0,
732+
"Expecting Error because of wal segment disappearance.\n "
733+
"Output: {0} \n CMD: {1}".format(
734+
self.output, self.cmd))
735+
except ProbackupException as e:
736+
self.assertTrue(
737+
'INFO: Wait for LSN' in e.message and
738+
'in archived WAL segment' in e.message and
739+
'WARNING: could not read WAL record at' in e.message and
740+
'ERROR: WAL segment "{0}" is absent\n'.format(
741+
file) in e.message,
742+
'\n Unexpected Error Message: {0}\n CMD: {1}'.format(
743+
repr(e.message), self.cmd))
744+
745+
self.assertEqual(
746+
'ERROR',
747+
self.show_pb(backup_dir, 'node')[2]['status'],
748+
'Backup {0} should have STATUS "ERROR"')
749+
750+
# Clean after yourself
751+
self.del_test_dir(module_name, fname)

tests/validate_test.py

Lines changed: 11 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -1237,78 +1237,12 @@ def test_validate_corrupt_wal_between_backups(self):
12371237
self.del_test_dir(module_name, fname)
12381238

12391239
# @unittest.skip("skip")
1240-
def test_validate_wal_lost_segment_2(self):
1240+
def test_pgpro702_688(self):
12411241
"""
1242-
make node with archiving
1243-
make archive backup
1244-
delete from archive wal segment which DO NOT belong to this backup
1245-
run validate, expecting error because of missing wal segment
1246-
make sure that backup status is 'ERROR'
1242+
make node without archiving, make stream backup,
1243+
get Recovery Time, validate to Recovery Time
12471244
"""
12481245
fname = self.id().split('.')[3]
1249-
node = self.make_simple_node(base_dir="{0}/{1}/node".format(module_name, fname),
1250-
initdb_params=['--data-checksums'],
1251-
pg_options={'wal_level': 'replica'}
1252-
)
1253-
backup_dir = os.path.join(self.tmp_path, module_name, fname, 'backup')
1254-
self.init_pb(backup_dir)
1255-
self.add_instance(backup_dir, 'node', node)
1256-
self.set_archiving(backup_dir, 'node', node)
1257-
node.start()
1258-
1259-
self.backup_node(backup_dir, 'node', node)
1260-
1261-
# make some wals
1262-
node.pgbench_init(scale=2)
1263-
pgbench = node.pgbench(
1264-
stdout=subprocess.PIPE,
1265-
stderr=subprocess.STDOUT,
1266-
options=["-c", "4", "-T", "10"]
1267-
)
1268-
pgbench.wait()
1269-
pgbench.stdout.close()
1270-
1271-
# delete last wal segment
1272-
wals_dir = os.path.join(backup_dir, 'wal', 'node')
1273-
wals = [f for f in os.listdir(wals_dir) if os.path.isfile(os.path.join(
1274-
wals_dir, f)) and not f.endswith('.backup')]
1275-
wals = map(str, wals)
1276-
file = os.path.join(wals_dir, max(wals))
1277-
os.remove(file)
1278-
if self.archive_compress:
1279-
file = file[:-3]
1280-
1281-
# Try to restore
1282-
try:
1283-
backup_id = self.backup_node(
1284-
backup_dir, 'node', node, backup_type='page')
1285-
self.assertEqual(
1286-
1, 0,
1287-
"Expecting Error because of wal segment disappearance.\n "
1288-
"Output: {0} \n CMD: {1}".format(
1289-
self.output, self.cmd))
1290-
except ProbackupException as e:
1291-
self.assertTrue(
1292-
'INFO: Wait for LSN' in e.message and
1293-
'in archived WAL segment' in e.message and
1294-
'WARNING: could not read WAL record at' in e.message and
1295-
'ERROR: WAL segment "{0}" is absent\n'.format(
1296-
file) in e.message,
1297-
'\n Unexpected Error Message: {0}\n CMD: {1}'.format(
1298-
repr(e.message), self.cmd))
1299-
1300-
self.assertEqual(
1301-
'ERROR',
1302-
self.show_pb(backup_dir, 'node')[1]['status'],
1303-
'Backup {0} should have STATUS "ERROR"')
1304-
1305-
# Clean after yourself
1306-
self.del_test_dir(module_name, fname)
1307-
1308-
# @unittest.skip("skip")
1309-
def test_pgpro702_688(self):
1310-
"""make node without archiving, make stream backup, get Recovery Time, validate to Recovery Time"""
1311-
fname = self.id().split('.')[3]
13121246
node = self.make_simple_node(
13131247
base_dir="{0}/{1}/node".format(module_name, fname),
13141248
set_replication=True,
@@ -1346,7 +1280,10 @@ def test_pgpro702_688(self):
13461280

13471281
# @unittest.skip("skip")
13481282
def test_pgpro688(self):
1349-
"""make node with archiving, make backup, get Recovery Time, validate to Recovery Time. Waiting PGPRO-688. RESOLVED"""
1283+
"""
1284+
make node with archiving, make backup, get Recovery Time,
1285+
validate to Recovery Time. Waiting PGPRO-688. RESOLVED
1286+
"""
13501287
fname = self.id().split('.')[3]
13511288
node = self.make_simple_node(
13521289
base_dir="{0}/{1}/node".format(module_name, fname),
@@ -1361,9 +1298,11 @@ def test_pgpro688(self):
13611298
node.start()
13621299

13631300
backup_id = self.backup_node(backup_dir, 'node', node)
1364-
recovery_time = self.show_pb(backup_dir, 'node', backup_id)['recovery-time']
1301+
recovery_time = self.show_pb(
1302+
backup_dir, 'node', backup_id)['recovery-time']
13651303

1366-
self.validate_pb(backup_dir, 'node', options=["--time={0}".format(recovery_time)])
1304+
self.validate_pb(
1305+
backup_dir, 'node', options=["--time={0}".format(recovery_time)])
13671306

13681307
# Clean after yourself
13691308
self.del_test_dir(module_name, fname)

0 commit comments

Comments
 (0)