pg_probackup
628 строк · 21.8 Кб
1import unittest2import os3from time import sleep4from .helpers.ptrack_helpers import ProbackupTest, ProbackupException5
6
7class LockingTest(ProbackupTest, unittest.TestCase):8
9# @unittest.skip("skip")10# @unittest.expectedFailure11def test_locking_running_validate_1(self):12"""13make node, take full backup, stop it in the middle
14run validate, expect it to successfully executed,
15concurrent RUNNING backup with pid file and active process is legal
16"""
17self._check_gdb_flag_or_skip_test()18
19node = self.make_simple_node(20base_dir=os.path.join(self.module_name, self.fname, 'node'),21initdb_params=['--data-checksums'])22
23backup_dir = os.path.join(self.tmp_path, self.module_name, self.fname, 'backup')24self.init_pb(backup_dir)25self.add_instance(backup_dir, 'node', node)26self.set_archiving(backup_dir, 'node', node)27node.slow_start()28
29self.backup_node(backup_dir, 'node', node)30
31gdb = self.backup_node(32backup_dir, 'node', node, gdb=True)33
34gdb.set_breakpoint('backup_non_data_file')35gdb.run_until_break()36
37gdb.continue_execution_until_break(20)38
39self.assertEqual(40'OK', self.show_pb(backup_dir, 'node')[0]['status'])41
42self.assertEqual(43'RUNNING', self.show_pb(backup_dir, 'node')[1]['status'])44
45validate_output = self.validate_pb(46backup_dir, options=['--log-level-console=LOG'])47
48backup_id = self.show_pb(backup_dir, 'node')[1]['id']49
50self.assertIn(51"is using backup {0}, and is still running".format(backup_id),52validate_output,53'\n Unexpected Validate Output: {0}\n'.format(repr(validate_output)))54
55self.assertEqual(56'OK', self.show_pb(backup_dir, 'node')[0]['status'])57
58self.assertEqual(59'RUNNING', self.show_pb(backup_dir, 'node')[1]['status'])60
61# Clean after yourself62gdb.kill()63
64def test_locking_running_validate_2(self):65"""66make node, take full backup, stop it in the middle,
67kill process so no cleanup is done - pid file is in place,
68run validate, expect it to not successfully executed,
69RUNNING backup with pid file AND without active pid is legal,
70but his status must be changed to ERROR and pid file is deleted
71"""
72self._check_gdb_flag_or_skip_test()73
74node = self.make_simple_node(75base_dir=os.path.join(self.module_name, self.fname, 'node'),76initdb_params=['--data-checksums'])77
78backup_dir = os.path.join(self.tmp_path, self.module_name, self.fname, 'backup')79self.init_pb(backup_dir)80self.add_instance(backup_dir, 'node', node)81self.set_archiving(backup_dir, 'node', node)82node.slow_start()83
84self.backup_node(backup_dir, 'node', node)85
86gdb = self.backup_node(87backup_dir, 'node', node, gdb=True)88
89gdb.set_breakpoint('backup_non_data_file')90gdb.run_until_break()91
92gdb.continue_execution_until_break(20)93
94gdb._execute('signal SIGKILL')95gdb.continue_execution_until_error()96
97self.assertEqual(98'OK', self.show_pb(backup_dir, 'node')[0]['status'])99
100self.assertEqual(101'RUNNING', self.show_pb(backup_dir, 'node')[1]['status'])102
103backup_id = self.show_pb(backup_dir, 'node')[1]['id']104
105try:106self.validate_pb(backup_dir)107self.assertEqual(1081, 0,109"Expecting Error because RUNNING backup is no longer active.\n "110"Output: {0} \n CMD: {1}".format(111repr(self.output), self.cmd))112except ProbackupException as e:113self.assertTrue(114"which used backup {0} no longer exists".format(115backup_id) in e.message and116"Backup {0} has status RUNNING, change it "117"to ERROR and skip validation".format(118backup_id) in e.message and119"WARNING: Some backups are not valid" in120e.message,121'\n Unexpected Error Message: {0}\n CMD: {1}'.format(122repr(e.message), self.cmd))123
124self.assertEqual(125'OK', self.show_pb(backup_dir, 'node')[0]['status'])126
127self.assertEqual(128'ERROR', self.show_pb(backup_dir, 'node')[1]['status'])129
130# Clean after yourself131gdb.kill()132
133def test_locking_running_validate_2_specific_id(self):134"""135make node, take full backup, stop it in the middle,
136kill process so no cleanup is done - pid file is in place,
137run validate on this specific backup,
138expect it to not successfully executed,
139RUNNING backup with pid file AND without active pid is legal,
140but his status must be changed to ERROR and pid file is deleted
141"""
142self._check_gdb_flag_or_skip_test()143
144node = self.make_simple_node(145base_dir=os.path.join(self.module_name, self.fname, 'node'),146initdb_params=['--data-checksums'])147
148backup_dir = os.path.join(self.tmp_path, self.module_name, self.fname, 'backup')149self.init_pb(backup_dir)150self.add_instance(backup_dir, 'node', node)151self.set_archiving(backup_dir, 'node', node)152node.slow_start()153
154self.backup_node(backup_dir, 'node', node)155
156gdb = self.backup_node(157backup_dir, 'node', node, gdb=True)158
159gdb.set_breakpoint('backup_non_data_file')160gdb.run_until_break()161
162gdb.continue_execution_until_break(20)163
164gdb._execute('signal SIGKILL')165gdb.continue_execution_until_error()166
167self.assertEqual(168'OK', self.show_pb(backup_dir, 'node')[0]['status'])169
170self.assertEqual(171'RUNNING', self.show_pb(backup_dir, 'node')[1]['status'])172
173backup_id = self.show_pb(backup_dir, 'node')[1]['id']174
175try:176self.validate_pb(backup_dir, 'node', backup_id)177self.assertEqual(1781, 0,179"Expecting Error because RUNNING backup is no longer active.\n "180"Output: {0} \n CMD: {1}".format(181repr(self.output), self.cmd))182except ProbackupException as e:183self.assertTrue(184"which used backup {0} no longer exists".format(185backup_id) in e.message and186"Backup {0} has status RUNNING, change it "187"to ERROR and skip validation".format(188backup_id) in e.message and189"ERROR: Backup {0} has status: ERROR".format(backup_id) in190e.message,191'\n Unexpected Error Message: {0}\n CMD: {1}'.format(192repr(e.message), self.cmd))193
194self.assertEqual(195'OK', self.show_pb(backup_dir, 'node')[0]['status'])196
197self.assertEqual(198'ERROR', self.show_pb(backup_dir, 'node')[1]['status'])199
200try:201self.validate_pb(backup_dir, 'node', backup_id)202self.assertEqual(2031, 0,204"Expecting Error because backup has status ERROR.\n "205"Output: {0} \n CMD: {1}".format(206repr(self.output), self.cmd))207except ProbackupException as e:208self.assertIn(209"ERROR: Backup {0} has status: ERROR".format(backup_id),210e.message,211'\n Unexpected Error Message: {0}\n CMD: {1}'.format(212repr(e.message), self.cmd))213
214try:215self.validate_pb(backup_dir)216self.assertEqual(2171, 0,218"Expecting Error because backup has status ERROR.\n "219"Output: {0} \n CMD: {1}".format(220repr(self.output), self.cmd))221except ProbackupException as e:222self.assertTrue(223"WARNING: Backup {0} has status ERROR. Skip validation".format(224backup_id) in e.message and225"WARNING: Some backups are not valid" in e.message,226'\n Unexpected Error Message: {0}\n CMD: {1}'.format(227repr(e.message), self.cmd))228
229# Clean after yourself230gdb.kill()231
232def test_locking_running_3(self):233"""234make node, take full backup, stop it in the middle,
235terminate process, delete pid file,
236run validate, expect it to not successfully executed,
237RUNNING backup without pid file AND without active pid is legal,
238his status must be changed to ERROR
239"""
240self._check_gdb_flag_or_skip_test()241
242node = self.make_simple_node(243base_dir=os.path.join(self.module_name, self.fname, 'node'),244initdb_params=['--data-checksums'])245
246backup_dir = os.path.join(self.tmp_path, self.module_name, self.fname, 'backup')247self.init_pb(backup_dir)248self.add_instance(backup_dir, 'node', node)249self.set_archiving(backup_dir, 'node', node)250node.slow_start()251
252self.backup_node(backup_dir, 'node', node)253
254gdb = self.backup_node(255backup_dir, 'node', node, gdb=True)256
257gdb.set_breakpoint('backup_non_data_file')258gdb.run_until_break()259
260gdb.continue_execution_until_break(20)261
262gdb._execute('signal SIGKILL')263gdb.continue_execution_until_error()264
265self.assertEqual(266'OK', self.show_pb(backup_dir, 'node')[0]['status'])267
268self.assertEqual(269'RUNNING', self.show_pb(backup_dir, 'node')[1]['status'])270
271backup_id = self.show_pb(backup_dir, 'node')[1]['id']272
273os.remove(274os.path.join(backup_dir, 'backups', 'node', backup_id, 'backup.pid'))275
276try:277self.validate_pb(backup_dir)278self.assertEqual(2791, 0,280"Expecting Error because RUNNING backup is no longer active.\n "281"Output: {0} \n CMD: {1}".format(282repr(self.output), self.cmd))283except ProbackupException as e:284self.assertTrue(285"Backup {0} has status RUNNING, change it "286"to ERROR and skip validation".format(287backup_id) in e.message and288"WARNING: Some backups are not valid" in289e.message,290'\n Unexpected Error Message: {0}\n CMD: {1}'.format(291repr(e.message), self.cmd))292
293self.assertEqual(294'OK', self.show_pb(backup_dir, 'node')[0]['status'])295
296self.assertEqual(297'ERROR', self.show_pb(backup_dir, 'node')[1]['status'])298
299# Clean after yourself300gdb.kill()301
302def test_locking_restore_locked(self):303"""304make node, take full backup, take two page backups,
305launch validate on PAGE1 and stop it in the middle,
306launch restore of PAGE2.
307Expect restore to sucseed because read-only locks
308do not conflict
309"""
310self._check_gdb_flag_or_skip_test()311
312node = self.make_simple_node(313base_dir=os.path.join(self.module_name, self.fname, 'node'),314initdb_params=['--data-checksums'])315
316backup_dir = os.path.join(self.tmp_path, self.module_name, self.fname, 'backup')317self.init_pb(backup_dir)318self.add_instance(backup_dir, 'node', node)319self.set_archiving(backup_dir, 'node', node)320node.slow_start()321
322# FULL323full_id = self.backup_node(backup_dir, 'node', node)324
325# PAGE1326backup_id = self.backup_node(backup_dir, 'node', node, backup_type='page')327
328# PAGE2329self.backup_node(backup_dir, 'node', node, backup_type='page')330
331gdb = self.validate_pb(332backup_dir, 'node', backup_id=backup_id, gdb=True)333
334gdb.set_breakpoint('pgBackupValidate')335gdb.run_until_break()336
337node.cleanup()338
339self.restore_node(backup_dir, 'node', node)340
341# Clean after yourself342gdb.kill()343
344def test_concurrent_delete_and_restore(self):345"""346make node, take full backup, take page backup,
347launch validate on FULL and stop it in the middle,
348launch restore of PAGE.
349Expect restore to fail because validation of
350intermediate backup is impossible
351"""
352self._check_gdb_flag_or_skip_test()353
354node = self.make_simple_node(355base_dir=os.path.join(self.module_name, self.fname, 'node'),356initdb_params=['--data-checksums'])357
358backup_dir = os.path.join(self.tmp_path, self.module_name, self.fname, 'backup')359self.init_pb(backup_dir)360self.add_instance(backup_dir, 'node', node)361self.set_archiving(backup_dir, 'node', node)362node.slow_start()363
364# FULL365backup_id = self.backup_node(backup_dir, 'node', node)366
367# PAGE1368restore_id = self.backup_node(backup_dir, 'node', node, backup_type='page')369
370gdb = self.delete_pb(371backup_dir, 'node', backup_id=backup_id, gdb=True)372
373# gdb.set_breakpoint('pgFileDelete')374gdb.set_breakpoint('delete_backup_files')375gdb.run_until_break()376
377node.cleanup()378
379try:380self.restore_node(381backup_dir, 'node', node, options=['--no-validate'])382self.assertEqual(3831, 0,384"Expecting Error because restore without whole chain validation "385"is prohibited unless --no-validate provided.\n "386"Output: {0} \n CMD: {1}".format(387repr(self.output), self.cmd))388except ProbackupException as e:389self.assertTrue(390"Backup {0} is used without validation".format(391restore_id) in e.message and392'is using backup {0}, and is still running'.format(393backup_id) in e.message and394'ERROR: Cannot lock backup' in e.message,395'\n Unexpected Error Message: {0}\n CMD: {1}'.format(396repr(e.message), self.cmd))397
398# Clean after yourself399gdb.kill()400
401def test_locking_concurrent_validate_and_backup(self):402"""403make node, take full backup, launch validate
404and stop it in the middle, take page backup.
405Expect PAGE backup to be successfully executed
406"""
407self._check_gdb_flag_or_skip_test()408
409node = self.make_simple_node(410base_dir=os.path.join(self.module_name, self.fname, 'node'),411initdb_params=['--data-checksums'])412
413backup_dir = os.path.join(self.tmp_path, self.module_name, self.fname, 'backup')414self.init_pb(backup_dir)415self.add_instance(backup_dir, 'node', node)416self.set_archiving(backup_dir, 'node', node)417node.slow_start()418
419# FULL420self.backup_node(backup_dir, 'node', node)421
422# PAGE2423backup_id = self.backup_node(backup_dir, 'node', node, backup_type='page')424
425gdb = self.validate_pb(426backup_dir, 'node', backup_id=backup_id, gdb=True)427
428gdb.set_breakpoint('pgBackupValidate')429gdb.run_until_break()430
431# This PAGE backup is expected to be successfull432self.backup_node(backup_dir, 'node', node, backup_type='page')433
434# Clean after yourself435gdb.kill()436
437def test_locking_concurren_restore_and_delete(self):438"""439make node, take full backup, launch restore
440and stop it in the middle, delete full backup.
441Expect it to fail.
442"""
443self._check_gdb_flag_or_skip_test()444
445node = self.make_simple_node(446base_dir=os.path.join(self.module_name, self.fname, 'node'),447initdb_params=['--data-checksums'])448
449backup_dir = os.path.join(self.tmp_path, self.module_name, self.fname, 'backup')450self.init_pb(backup_dir)451self.add_instance(backup_dir, 'node', node)452self.set_archiving(backup_dir, 'node', node)453node.slow_start()454
455# FULL456full_id = self.backup_node(backup_dir, 'node', node)457
458node.cleanup()459gdb = self.restore_node(backup_dir, 'node', node, gdb=True)460
461gdb.set_breakpoint('create_data_directories')462gdb.run_until_break()463
464try:465self.delete_pb(backup_dir, 'node', full_id)466self.assertEqual(4671, 0,468"Expecting Error because backup is locked\n "469"Output: {0} \n CMD: {1}".format(470repr(self.output), self.cmd))471except ProbackupException as e:472self.assertIn(473"ERROR: Cannot lock backup {0} directory".format(full_id),474e.message,475'\n Unexpected Error Message: {0}\n CMD: {1}'.format(476repr(e.message), self.cmd))477
478# Clean after yourself479gdb.kill()480
481def test_backup_directory_name(self):482"""483"""
484node = self.make_simple_node(485base_dir=os.path.join(self.module_name, self.fname, 'node'),486initdb_params=['--data-checksums'])487
488backup_dir = os.path.join(self.tmp_path, self.module_name, self.fname, 'backup')489self.init_pb(backup_dir)490self.add_instance(backup_dir, 'node', node)491self.set_archiving(backup_dir, 'node', node)492node.slow_start()493
494# FULL495full_id_1 = self.backup_node(backup_dir, 'node', node)496page_id_1 = self.backup_node(backup_dir, 'node', node, backup_type='page')497
498full_id_2 = self.backup_node(backup_dir, 'node', node)499page_id_2 = self.backup_node(backup_dir, 'node', node, backup_type='page')500
501node.cleanup()502
503old_path = os.path.join(backup_dir, 'backups', 'node', full_id_1)504new_path = os.path.join(backup_dir, 'backups', 'node', 'hello_kitty')505
506os.rename(old_path, new_path)507
508# This PAGE backup is expected to be successfull509self.show_pb(backup_dir, 'node', full_id_1)510
511self.validate_pb(backup_dir)512self.validate_pb(backup_dir, 'node')513self.validate_pb(backup_dir, 'node', full_id_1)514
515self.restore_node(backup_dir, 'node', node, backup_id=full_id_1)516
517self.delete_pb(backup_dir, 'node', full_id_1)518
519old_path = os.path.join(backup_dir, 'backups', 'node', full_id_2)520new_path = os.path.join(backup_dir, 'backups', 'node', 'hello_kitty')521
522self.set_backup(523backup_dir, 'node', full_id_2, options=['--note=hello'])524
525self.merge_backup(backup_dir, 'node', page_id_2, options=["-j", "4"])526
527self.assertNotIn(528'note',529self.show_pb(backup_dir, 'node', page_id_2))530
531# Clean after yourself532
533def test_empty_lock_file(self):534"""535https://github.com/postgrespro/pg_probackup/issues/308
536"""
537node = self.make_simple_node(538base_dir=os.path.join(self.module_name, self.fname, 'node'),539initdb_params=['--data-checksums'])540
541backup_dir = os.path.join(self.tmp_path, self.module_name, self.fname, 'backup')542self.init_pb(backup_dir)543self.add_instance(backup_dir, 'node', node)544self.set_archiving(backup_dir, 'node', node)545node.slow_start()546
547# Fill with data548node.pgbench_init(scale=100)549
550# FULL551backup_id = self.backup_node(backup_dir, 'node', node)552
553lockfile = os.path.join(backup_dir, 'backups', 'node', backup_id, 'backup.pid')554with open(lockfile, "w+") as f:555f.truncate()556
557out = self.validate_pb(backup_dir, 'node', backup_id)558
559self.assertIn(560"Waiting 30 seconds on empty exclusive lock for backup", out)561
562# lockfile = os.path.join(backup_dir, 'backups', 'node', backup_id, 'backup.pid')
563# with open(lockfile, "w+") as f:
564# f.truncate()
565#
566# p1 = self.validate_pb(backup_dir, 'node', backup_id, asynchronous=True,
567# options=['--log-level-file=LOG', '--log-filename=validate.log'])
568# sleep(3)
569# p2 = self.delete_pb(backup_dir, 'node', backup_id, asynchronous=True,
570# options=['--log-level-file=LOG', '--log-filename=delete.log'])
571#
572# p1.wait()
573# p2.wait()
574
575def test_shared_lock(self):576"""577Make sure that shared lock leaves no files with pids
578"""
579self._check_gdb_flag_or_skip_test()580
581node = self.make_simple_node(582base_dir=os.path.join(self.module_name, self.fname, 'node'),583initdb_params=['--data-checksums'])584
585backup_dir = os.path.join(self.tmp_path, self.module_name, self.fname, 'backup')586self.init_pb(backup_dir)587self.add_instance(backup_dir, 'node', node)588self.set_archiving(backup_dir, 'node', node)589node.slow_start()590
591# Fill with data592node.pgbench_init(scale=1)593
594# FULL595backup_id = self.backup_node(backup_dir, 'node', node)596
597lockfile_excl = os.path.join(backup_dir, 'backups', 'node', backup_id, 'backup.pid')598lockfile_shr = os.path.join(backup_dir, 'backups', 'node', backup_id, 'backup_ro.pid')599
600self.validate_pb(backup_dir, 'node', backup_id)601
602self.assertFalse(603os.path.exists(lockfile_excl),604"File should not exist: {0}".format(lockfile_excl))605
606self.assertFalse(607os.path.exists(lockfile_shr),608"File should not exist: {0}".format(lockfile_shr))609
610gdb = self.validate_pb(backup_dir, 'node', backup_id, gdb=True)611
612gdb.set_breakpoint('validate_one_page')613gdb.run_until_break()614gdb.kill()615
616self.assertTrue(617os.path.exists(lockfile_shr),618"File should exist: {0}".format(lockfile_shr))619
620self.validate_pb(backup_dir, 'node', backup_id)621
622self.assertFalse(623os.path.exists(lockfile_excl),624"File should not exist: {0}".format(lockfile_excl))625
626self.assertFalse(627os.path.exists(lockfile_shr),628"File should not exist: {0}".format(lockfile_shr))629
630