pg_probackup
1417 строк · 49.1 Кб
1import os
2import unittest
3from .helpers.ptrack_helpers import ProbackupTest, ProbackupException
4from testgres import QueryException
5from datetime import datetime, timedelta
6import subprocess
7import gzip
8import shutil
9import time
10
11class PageTest(ProbackupTest, unittest.TestCase):
12
13# @unittest.skip("skip")
14def test_basic_page_vacuum_truncate(self):
15"""
16make node, create table, take full backup,
17delete last 3 pages, vacuum relation,
18take page backup, take second page backup,
19restore last page backup and check data correctness
20"""
21backup_dir = os.path.join(self.tmp_path, self.module_name, self.fname, 'backup')
22node = self.make_simple_node(
23base_dir=os.path.join(self.module_name, self.fname, 'node'),
24set_replication=True,
25initdb_params=['--data-checksums'],
26pg_options={
27'checkpoint_timeout': '300s'})
28
29node_restored = self.make_simple_node(
30base_dir=os.path.join(self.module_name, self.fname, 'node_restored'))
31
32self.init_pb(backup_dir)
33self.add_instance(backup_dir, 'node', node)
34self.set_archiving(backup_dir, 'node', node)
35node_restored.cleanup()
36node.slow_start()
37self.create_tblspace_in_node(node, 'somedata')
38
39node.safe_psql(
40"postgres",
41"create sequence t_seq; "
42"create table t_heap tablespace somedata as select i as id, "
43"md5(i::text) as text, "
44"md5(repeat(i::text,10))::tsvector as tsvector "
45"from generate_series(0,1024) i;")
46
47node.safe_psql(
48"postgres",
49"vacuum t_heap")
50
51self.backup_node(backup_dir, 'node', node)
52
53# TODO: make it dynamic
54node.safe_psql(
55"postgres",
56"delete from t_heap where ctid >= '(11,0)'")
57node.safe_psql(
58"postgres",
59"vacuum t_heap")
60
61self.backup_node(
62backup_dir, 'node', node, backup_type='page')
63
64self.backup_node(
65backup_dir, 'node', node, backup_type='page')
66
67if self.paranoia:
68pgdata = self.pgdata_content(node.data_dir)
69
70old_tablespace = self.get_tblspace_path(node, 'somedata')
71new_tablespace = self.get_tblspace_path(node_restored, 'somedata_new')
72
73self.restore_node(
74backup_dir, 'node', node_restored,
75options=[
76"-j", "4",
77"-T", "{0}={1}".format(old_tablespace, new_tablespace)])
78
79# Physical comparison
80if self.paranoia:
81pgdata_restored = self.pgdata_content(node_restored.data_dir)
82self.compare_pgdata(pgdata, pgdata_restored)
83
84self.set_auto_conf(node_restored, {'port': node_restored.port})
85node_restored.slow_start()
86
87# Logical comparison
88result1 = node.table_checksum("t_heap")
89result2 = node_restored.table_checksum("t_heap")
90
91self.assertEqual(result1, result2)
92
93# @unittest.skip("skip")
94def test_page_vacuum_truncate_1(self):
95"""
96make node, create table, take full backup,
97delete all data, vacuum relation,
98take page backup, insert some data,
99take second page backup and check data correctness
100"""
101backup_dir = os.path.join(self.tmp_path, self.module_name, self.fname, 'backup')
102node = self.make_simple_node(
103base_dir=os.path.join(self.module_name, self.fname, 'node'),
104set_replication=True,
105initdb_params=['--data-checksums'])
106
107self.init_pb(backup_dir)
108self.add_instance(backup_dir, 'node', node)
109self.set_archiving(backup_dir, 'node', node)
110node.slow_start()
111
112node.safe_psql(
113"postgres",
114"create sequence t_seq; "
115"create table t_heap as select i as id, "
116"md5(i::text) as text, "
117"md5(repeat(i::text,10))::tsvector as tsvector "
118"from generate_series(0,1024) i")
119
120node.safe_psql(
121"postgres",
122"vacuum t_heap")
123
124self.backup_node(backup_dir, 'node', node)
125
126node.safe_psql(
127"postgres",
128"delete from t_heap")
129
130node.safe_psql(
131"postgres",
132"vacuum t_heap")
133
134self.backup_node(
135backup_dir, 'node', node, backup_type='page')
136
137node.safe_psql(
138"postgres",
139"insert into t_heap select i as id, "
140"md5(i::text) as text, "
141"md5(repeat(i::text,10))::tsvector as tsvector "
142"from generate_series(0,1) i")
143
144self.backup_node(
145backup_dir, 'node', node, backup_type='page')
146
147pgdata = self.pgdata_content(node.data_dir)
148
149node_restored = self.make_simple_node(
150base_dir=os.path.join(self.module_name, self.fname, 'node_restored'))
151node_restored.cleanup()
152
153self.restore_node(backup_dir, 'node', node_restored)
154
155# Physical comparison
156pgdata_restored = self.pgdata_content(node_restored.data_dir)
157self.compare_pgdata(pgdata, pgdata_restored)
158
159self.set_auto_conf(node_restored, {'port': node_restored.port})
160node_restored.slow_start()
161
162# @unittest.skip("skip")
163def test_page_stream(self):
164"""
165make archive node, take full and page stream backups,
166restore them and check data correctness
167"""
168self.maxDiff = None
169backup_dir = os.path.join(self.tmp_path, self.module_name, self.fname, 'backup')
170node = self.make_simple_node(
171base_dir=os.path.join(self.module_name, self.fname, 'node'),
172set_replication=True,
173initdb_params=['--data-checksums'],
174pg_options={
175'checkpoint_timeout': '30s'}
176)
177
178self.init_pb(backup_dir)
179self.add_instance(backup_dir, 'node', node)
180self.set_archiving(backup_dir, 'node', node)
181node.slow_start()
182
183# FULL BACKUP
184node.safe_psql(
185"postgres",
186"create table t_heap as select i as id, md5(i::text) as text, "
187"md5(i::text)::tsvector as tsvector "
188"from generate_series(0,100) i")
189
190full_result = node.table_checksum("t_heap")
191full_backup_id = self.backup_node(
192backup_dir, 'node', node,
193backup_type='full', options=['--stream'])
194
195# PAGE BACKUP
196node.safe_psql(
197"postgres",
198"insert into t_heap select i as id, md5(i::text) as text, "
199"md5(i::text)::tsvector as tsvector "
200"from generate_series(100,200) i")
201page_result = node.table_checksum("t_heap")
202page_backup_id = self.backup_node(
203backup_dir, 'node', node,
204backup_type='page', options=['--stream', '-j', '4'])
205
206if self.paranoia:
207pgdata = self.pgdata_content(node.data_dir)
208
209# Drop Node
210node.cleanup()
211
212# Check full backup
213self.assertIn(
214"INFO: Restore of backup {0} completed.".format(full_backup_id),
215self.restore_node(
216backup_dir, 'node', node,
217backup_id=full_backup_id, options=["-j", "4"]),
218'\n Unexpected Error Message: {0}\n'
219' CMD: {1}'.format(repr(self.output), self.cmd))
220
221node.slow_start()
222full_result_new = node.table_checksum("t_heap")
223self.assertEqual(full_result, full_result_new)
224node.cleanup()
225
226# Check page backup
227self.assertIn(
228"INFO: Restore of backup {0} completed.".format(page_backup_id),
229self.restore_node(
230backup_dir, 'node', node,
231backup_id=page_backup_id, options=["-j", "4"]),
232'\n Unexpected Error Message: {0}\n'
233' CMD: {1}'.format(repr(self.output), self.cmd))
234
235# GET RESTORED PGDATA AND COMPARE
236if self.paranoia:
237pgdata_restored = self.pgdata_content(node.data_dir)
238self.compare_pgdata(pgdata, pgdata_restored)
239
240node.slow_start()
241page_result_new = node.table_checksum("t_heap")
242self.assertEqual(page_result, page_result_new)
243node.cleanup()
244
245# @unittest.skip("skip")
246def test_page_archive(self):
247"""
248make archive node, take full and page archive backups,
249restore them and check data correctness
250"""
251self.maxDiff = None
252backup_dir = os.path.join(self.tmp_path, self.module_name, self.fname, 'backup')
253node = self.make_simple_node(
254base_dir=os.path.join(self.module_name, self.fname, 'node'),
255set_replication=True,
256initdb_params=['--data-checksums'],
257pg_options={
258'checkpoint_timeout': '30s'}
259)
260
261self.init_pb(backup_dir)
262self.add_instance(backup_dir, 'node', node)
263self.set_archiving(backup_dir, 'node', node)
264node.slow_start()
265
266# FULL BACKUP
267node.safe_psql(
268"postgres",
269"create table t_heap as select i as id, md5(i::text) as text, "
270"md5(i::text)::tsvector as tsvector from generate_series(0,100) i")
271full_result = node.table_checksum("t_heap")
272full_backup_id = self.backup_node(
273backup_dir, 'node', node, backup_type='full')
274
275# PAGE BACKUP
276node.safe_psql(
277"postgres",
278"insert into t_heap select i as id, "
279"md5(i::text) as text, md5(i::text)::tsvector as tsvector "
280"from generate_series(100, 200) i")
281page_result = node.table_checksum("t_heap")
282page_backup_id = self.backup_node(
283backup_dir, 'node', node,
284backup_type='page', options=["-j", "4"])
285
286if self.paranoia:
287pgdata = self.pgdata_content(node.data_dir)
288
289# Drop Node
290node.cleanup()
291
292# Restore and check full backup
293self.assertIn("INFO: Restore of backup {0} completed.".format(
294full_backup_id),
295self.restore_node(
296backup_dir, 'node', node,
297backup_id=full_backup_id,
298options=[
299"-j", "4",
300"--immediate",
301"--recovery-target-action=promote"]),
302'\n Unexpected Error Message: {0}\n CMD: {1}'.format(
303repr(self.output), self.cmd))
304
305node.slow_start()
306
307full_result_new = node.table_checksum("t_heap")
308self.assertEqual(full_result, full_result_new)
309node.cleanup()
310
311# Restore and check page backup
312self.assertIn(
313"INFO: Restore of backup {0} completed.".format(page_backup_id),
314self.restore_node(
315backup_dir, 'node', node,
316backup_id=page_backup_id,
317options=[
318"-j", "4",
319"--immediate",
320"--recovery-target-action=promote"]),
321'\n Unexpected Error Message: {0}\n CMD: {1}'.format(
322repr(self.output), self.cmd))
323
324# GET RESTORED PGDATA AND COMPARE
325if self.paranoia:
326pgdata_restored = self.pgdata_content(node.data_dir)
327self.compare_pgdata(pgdata, pgdata_restored)
328
329node.slow_start()
330
331page_result_new = node.table_checksum("t_heap")
332self.assertEqual(page_result, page_result_new)
333node.cleanup()
334
335# @unittest.skip("skip")
336def test_page_multiple_segments(self):
337"""
338Make node, create table with multiple segments,
339write some data to it, check page and data correctness
340"""
341backup_dir = os.path.join(self.tmp_path, self.module_name, self.fname, 'backup')
342node = self.make_simple_node(
343base_dir=os.path.join(self.module_name, self.fname, 'node'),
344set_replication=True,
345initdb_params=['--data-checksums'],
346pg_options={
347'fsync': 'off',
348'shared_buffers': '1GB',
349'maintenance_work_mem': '1GB',
350'full_page_writes': 'off'})
351
352self.init_pb(backup_dir)
353self.add_instance(backup_dir, 'node', node)
354self.set_archiving(backup_dir, 'node', node)
355node.slow_start()
356
357self.create_tblspace_in_node(node, 'somedata')
358
359# CREATE TABLE
360node.pgbench_init(scale=100, options=['--tablespace=somedata'])
361# FULL BACKUP
362self.backup_node(backup_dir, 'node', node)
363
364# PGBENCH STUFF
365pgbench = node.pgbench(options=['-T', '50', '-c', '1', '--no-vacuum'])
366pgbench.wait()
367
368# GET LOGICAL CONTENT FROM NODE
369result = node.table_checksum("pgbench_accounts")
370# PAGE BACKUP
371self.backup_node(backup_dir, 'node', node, backup_type='page')
372
373# GET PHYSICAL CONTENT FROM NODE
374pgdata = self.pgdata_content(node.data_dir)
375
376# RESTORE NODE
377restored_node = self.make_simple_node(
378base_dir=os.path.join(self.module_name, self.fname, 'restored_node'))
379restored_node.cleanup()
380tblspc_path = self.get_tblspace_path(node, 'somedata')
381tblspc_path_new = self.get_tblspace_path(
382restored_node, 'somedata_restored')
383
384self.restore_node(
385backup_dir, 'node', restored_node,
386options=[
387"-j", "4",
388"-T", "{0}={1}".format(tblspc_path, tblspc_path_new)])
389
390# GET PHYSICAL CONTENT FROM NODE_RESTORED
391pgdata_restored = self.pgdata_content(restored_node.data_dir)
392
393# START RESTORED NODE
394self.set_auto_conf(restored_node, {'port': restored_node.port})
395restored_node.slow_start()
396
397result_new = restored_node.table_checksum("pgbench_accounts")
398
399# COMPARE RESTORED FILES
400self.assertEqual(result, result_new, 'data is lost')
401
402if self.paranoia:
403self.compare_pgdata(pgdata, pgdata_restored)
404
405# @unittest.skip("skip")
406def test_page_delete(self):
407"""
408Make node, create tablespace with table, take full backup,
409delete everything from table, vacuum table, take page backup,
410restore page backup, compare .
411"""
412backup_dir = os.path.join(self.tmp_path, self.module_name, self.fname, 'backup')
413node = self.make_simple_node(
414base_dir=os.path.join(self.module_name, self.fname, 'node'),
415set_replication=True, initdb_params=['--data-checksums'],
416pg_options={
417'checkpoint_timeout': '30s',
418}
419)
420
421self.init_pb(backup_dir)
422self.add_instance(backup_dir, 'node', node)
423self.set_archiving(backup_dir, 'node', node)
424node.slow_start()
425
426self.create_tblspace_in_node(node, 'somedata')
427# FULL backup
428self.backup_node(backup_dir, 'node', node)
429node.safe_psql(
430"postgres",
431"create table t_heap tablespace somedata as select i as id,"
432" md5(i::text) as text, md5(i::text)::tsvector as tsvector"
433" from generate_series(0,100) i")
434
435node.safe_psql(
436"postgres",
437"delete from t_heap")
438
439node.safe_psql(
440"postgres",
441"vacuum t_heap")
442
443# PAGE BACKUP
444self.backup_node(
445backup_dir, 'node', node, backup_type='page')
446if self.paranoia:
447pgdata = self.pgdata_content(node.data_dir)
448
449# RESTORE
450node_restored = self.make_simple_node(
451base_dir=os.path.join(self.module_name, self.fname, 'node_restored'))
452node_restored.cleanup()
453
454self.restore_node(
455backup_dir, 'node', node_restored,
456options=[
457"-j", "4",
458"-T", "{0}={1}".format(
459self.get_tblspace_path(node, 'somedata'),
460self.get_tblspace_path(node_restored, 'somedata'))
461]
462)
463
464# GET RESTORED PGDATA AND COMPARE
465if self.paranoia:
466pgdata_restored = self.pgdata_content(node_restored.data_dir)
467self.compare_pgdata(pgdata, pgdata_restored)
468
469# START RESTORED NODE
470self.set_auto_conf(node_restored, {'port': node_restored.port})
471node_restored.slow_start()
472
473# @unittest.skip("skip")
474def test_page_delete_1(self):
475"""
476Make node, create tablespace with table, take full backup,
477delete everything from table, vacuum table, take page backup,
478restore page backup, compare .
479"""
480backup_dir = os.path.join(self.tmp_path, self.module_name, self.fname, 'backup')
481node = self.make_simple_node(
482base_dir=os.path.join(self.module_name, self.fname, 'node'),
483set_replication=True,
484initdb_params=['--data-checksums'],
485pg_options={
486'checkpoint_timeout': '30s',
487}
488)
489
490self.init_pb(backup_dir)
491self.add_instance(backup_dir, 'node', node)
492self.set_archiving(backup_dir, 'node', node)
493node.slow_start()
494
495self.create_tblspace_in_node(node, 'somedata')
496
497node.safe_psql(
498"postgres",
499"create table t_heap tablespace somedata as select i as id,"
500" md5(i::text) as text, md5(i::text)::tsvector as tsvector"
501" from generate_series(0,100) i"
502)
503# FULL backup
504self.backup_node(backup_dir, 'node', node)
505
506node.safe_psql(
507"postgres",
508"delete from t_heap"
509)
510
511node.safe_psql(
512"postgres",
513"vacuum t_heap"
514)
515
516# PAGE BACKUP
517self.backup_node(
518backup_dir, 'node', node, backup_type='page')
519if self.paranoia:
520pgdata = self.pgdata_content(node.data_dir)
521
522# RESTORE
523node_restored = self.make_simple_node(
524base_dir=os.path.join(self.module_name, self.fname, 'node_restored')
525)
526node_restored.cleanup()
527
528self.restore_node(
529backup_dir, 'node', node_restored,
530options=[
531"-j", "4",
532"-T", "{0}={1}".format(
533self.get_tblspace_path(node, 'somedata'),
534self.get_tblspace_path(node_restored, 'somedata'))
535]
536)
537
538# GET RESTORED PGDATA AND COMPARE
539if self.paranoia:
540pgdata_restored = self.pgdata_content(node_restored.data_dir)
541self.compare_pgdata(pgdata, pgdata_restored)
542
543# START RESTORED NODE
544self.set_auto_conf(node_restored, {'port': node_restored.port})
545node_restored.slow_start()
546
547def test_parallel_pagemap(self):
548"""
549Test for parallel WAL segments reading, during which pagemap is built
550"""
551backup_dir = os.path.join(self.tmp_path, self.module_name, self.fname, 'backup')
552
553# Initialize instance and backup directory
554node = self.make_simple_node(
555base_dir=os.path.join(self.module_name, self.fname, 'node'),
556initdb_params=['--data-checksums'],
557pg_options={
558"hot_standby": "on"
559}
560)
561node_restored = self.make_simple_node(
562base_dir=os.path.join(self.module_name, self.fname, 'node_restored'),
563)
564
565self.init_pb(backup_dir)
566self.add_instance(backup_dir, 'node', node)
567node_restored.cleanup()
568self.set_archiving(backup_dir, 'node', node)
569node.slow_start()
570
571# Do full backup
572self.backup_node(backup_dir, 'node', node)
573show_backup = self.show_pb(backup_dir, 'node')[0]
574
575self.assertEqual(show_backup['status'], "OK")
576self.assertEqual(show_backup['backup-mode'], "FULL")
577
578# Fill instance with data and make several WAL segments ...
579with node.connect() as conn:
580conn.execute("create table test (id int)")
581for x in range(0, 8):
582conn.execute(
583"insert into test select i from generate_series(1,100) s(i)")
584conn.commit()
585self.switch_wal_segment(conn)
586count1 = conn.execute("select count(*) from test")
587
588# ... and do page backup with parallel pagemap
589self.backup_node(
590backup_dir, 'node', node, backup_type="page", options=["-j", "4"])
591show_backup = self.show_pb(backup_dir, 'node')[1]
592
593self.assertEqual(show_backup['status'], "OK")
594self.assertEqual(show_backup['backup-mode'], "PAGE")
595
596if self.paranoia:
597pgdata = self.pgdata_content(node.data_dir)
598
599# Restore it
600self.restore_node(backup_dir, 'node', node_restored)
601
602# Physical comparison
603if self.paranoia:
604pgdata_restored = self.pgdata_content(node_restored.data_dir)
605self.compare_pgdata(pgdata, pgdata_restored)
606
607self.set_auto_conf(node_restored, {'port': node_restored.port})
608node_restored.slow_start()
609
610# Check restored node
611count2 = node_restored.execute("postgres", "select count(*) from test")
612
613self.assertEqual(count1, count2)
614
615# Clean after yourself
616node.cleanup()
617node_restored.cleanup()
618
619def test_parallel_pagemap_1(self):
620"""
621Test for parallel WAL segments reading, during which pagemap is built
622"""
623backup_dir = os.path.join(self.tmp_path, self.module_name, self.fname, 'backup')
624
625# Initialize instance and backup directory
626node = self.make_simple_node(
627base_dir=os.path.join(self.module_name, self.fname, 'node'),
628initdb_params=['--data-checksums'],
629pg_options={}
630)
631
632self.init_pb(backup_dir)
633self.add_instance(backup_dir, 'node', node)
634self.set_archiving(backup_dir, 'node', node)
635node.slow_start()
636
637# Do full backup
638self.backup_node(backup_dir, 'node', node)
639show_backup = self.show_pb(backup_dir, 'node')[0]
640
641self.assertEqual(show_backup['status'], "OK")
642self.assertEqual(show_backup['backup-mode'], "FULL")
643
644# Fill instance with data and make several WAL segments ...
645node.pgbench_init(scale=10)
646
647# do page backup in single thread
648page_id = self.backup_node(
649backup_dir, 'node', node, backup_type="page")
650
651self.delete_pb(backup_dir, 'node', page_id)
652
653# ... and do page backup with parallel pagemap
654self.backup_node(
655backup_dir, 'node', node, backup_type="page", options=["-j", "4"])
656show_backup = self.show_pb(backup_dir, 'node')[1]
657
658self.assertEqual(show_backup['status'], "OK")
659self.assertEqual(show_backup['backup-mode'], "PAGE")
660
661# Drop node and restore it
662node.cleanup()
663self.restore_node(backup_dir, 'node', node)
664node.slow_start()
665
666# Clean after yourself
667node.cleanup()
668
669# @unittest.skip("skip")
670def test_page_backup_with_lost_wal_segment(self):
671"""
672make node with archiving
673make archive backup, then generate some wals with pgbench,
674delete latest archived wal segment
675run page backup, expecting error because of missing wal segment
676make sure that backup status is 'ERROR'
677"""
678node = self.make_simple_node(
679base_dir=os.path.join(self.module_name, self.fname, 'node'),
680initdb_params=['--data-checksums'])
681
682backup_dir = os.path.join(self.tmp_path, self.module_name, self.fname, 'backup')
683self.init_pb(backup_dir)
684self.add_instance(backup_dir, 'node', node)
685self.set_archiving(backup_dir, 'node', node)
686node.slow_start()
687
688self.backup_node(backup_dir, 'node', node)
689
690# make some wals
691node.pgbench_init(scale=3)
692
693# delete last wal segment
694wals_dir = os.path.join(backup_dir, 'wal', 'node')
695wals = [f for f in os.listdir(wals_dir) if os.path.isfile(os.path.join(
696wals_dir, f)) and not f.endswith('.backup') and not f.endswith('.part')]
697wals = map(str, wals)
698file = os.path.join(wals_dir, max(wals))
699os.remove(file)
700if self.archive_compress:
701file = file[:-3]
702
703# Single-thread PAGE backup
704try:
705self.backup_node(
706backup_dir, 'node', node, backup_type='page')
707self.assertEqual(
7081, 0,
709"Expecting Error because of wal segment disappearance.\n "
710"Output: {0} \n CMD: {1}".format(
711self.output, self.cmd))
712except ProbackupException as e:
713self.assertTrue(
714'Could not read WAL record at' in e.message and
715'is absent' in e.message,
716'\n Unexpected Error Message: {0}\n CMD: {1}'.format(
717repr(e.message), self.cmd))
718
719self.assertEqual(
720'ERROR',
721self.show_pb(backup_dir, 'node')[1]['status'],
722'Backup {0} should have STATUS "ERROR"')
723
724# Multi-thread PAGE backup
725try:
726self.backup_node(
727backup_dir, 'node', node,
728backup_type='page',
729options=["-j", "4"])
730self.assertEqual(
7311, 0,
732"Expecting Error because of wal segment disappearance.\n "
733"Output: {0} \n CMD: {1}".format(
734self.output, self.cmd))
735except ProbackupException as e:
736self.assertTrue(
737'Could not read WAL record at' in e.message and
738'is absent' in e.message,
739'\n Unexpected Error Message: {0}\n CMD: {1}'.format(
740repr(e.message), self.cmd))
741
742self.assertEqual(
743'ERROR',
744self.show_pb(backup_dir, 'node')[2]['status'],
745'Backup {0} should have STATUS "ERROR"')
746
747# @unittest.skip("skip")
748def test_page_backup_with_corrupted_wal_segment(self):
749"""
750make node with archiving
751make archive backup, then generate some wals with pgbench,
752corrupt latest archived wal segment
753run page backup, expecting error because of missing wal segment
754make sure that backup status is 'ERROR'
755"""
756node = self.make_simple_node(
757base_dir=os.path.join(self.module_name, self.fname, 'node'),
758initdb_params=['--data-checksums'])
759
760backup_dir = os.path.join(self.tmp_path, self.module_name, self.fname, 'backup')
761self.init_pb(backup_dir)
762self.add_instance(backup_dir, 'node', node)
763self.set_archiving(backup_dir, 'node', node)
764node.slow_start()
765
766self.backup_node(backup_dir, 'node', node)
767
768# make some wals
769node.pgbench_init(scale=10)
770
771# delete last wal segment
772wals_dir = os.path.join(backup_dir, 'wal', 'node')
773wals = [f for f in os.listdir(wals_dir) if os.path.isfile(os.path.join(
774wals_dir, f)) and not f.endswith('.backup')]
775wals = map(str, wals)
776# file = os.path.join(wals_dir, max(wals))
777
778if self.archive_compress:
779original_file = os.path.join(wals_dir, '000000010000000000000004.gz')
780tmp_file = os.path.join(backup_dir, '000000010000000000000004')
781
782with gzip.open(original_file, 'rb') as f_in, open(tmp_file, 'wb') as f_out:
783shutil.copyfileobj(f_in, f_out)
784
785# drop healthy file
786os.remove(original_file)
787file = tmp_file
788
789else:
790file = os.path.join(wals_dir, '000000010000000000000004')
791
792# corrupt file
793print(file)
794with open(file, "rb+", 0) as f:
795f.seek(42)
796f.write(b"blah")
797f.flush()
798f.close
799
800if self.archive_compress:
801# compress corrupted file and replace with it old file
802with open(file, 'rb') as f_in, gzip.open(original_file, 'wb', compresslevel=1) as f_out:
803shutil.copyfileobj(f_in, f_out)
804
805file = os.path.join(wals_dir, '000000010000000000000004.gz')
806
807#if self.archive_compress:
808# file = file[:-3]
809
810# Single-thread PAGE backup
811try:
812self.backup_node(
813backup_dir, 'node', node, backup_type='page')
814self.assertEqual(
8151, 0,
816"Expecting Error because of wal segment disappearance.\n "
817"Output: {0} \n CMD: {1}".format(
818self.output, self.cmd))
819except ProbackupException as e:
820self.assertTrue(
821'Could not read WAL record at' in e.message and
822'Possible WAL corruption. Error has occured during reading WAL segment' in e.message,
823'\n Unexpected Error Message: {0}\n CMD: {1}'.format(
824repr(e.message), self.cmd))
825
826self.assertEqual(
827'ERROR',
828self.show_pb(backup_dir, 'node')[1]['status'],
829'Backup {0} should have STATUS "ERROR"')
830
831# Multi-thread PAGE backup
832try:
833self.backup_node(
834backup_dir, 'node', node,
835backup_type='page', options=["-j", "4"])
836self.assertEqual(
8371, 0,
838"Expecting Error because of wal segment disappearance.\n "
839"Output: {0} \n CMD: {1}".format(
840self.output, self.cmd))
841except ProbackupException as e:
842self.assertTrue(
843'Could not read WAL record at' in e.message and
844'Possible WAL corruption. Error has occured during reading WAL segment "{0}"'.format(
845file) in e.message,
846'\n Unexpected Error Message: {0}\n CMD: {1}'.format(
847repr(e.message), self.cmd))
848
849self.assertEqual(
850'ERROR',
851self.show_pb(backup_dir, 'node')[2]['status'],
852'Backup {0} should have STATUS "ERROR"')
853
854# @unittest.skip("skip")
855def test_page_backup_with_alien_wal_segment(self):
856"""
857make two nodes with archiving
858take archive full backup from both nodes,
859generate some wals with pgbench on both nodes,
860move latest archived wal segment from second node to first node`s archive
861run page backup on first node
862expecting error because of alien wal segment
863make sure that backup status is 'ERROR'
864"""
865node = self.make_simple_node(
866base_dir=os.path.join(self.module_name, self.fname, 'node'),
867set_replication=True,
868initdb_params=['--data-checksums'])
869
870alien_node = self.make_simple_node(
871base_dir=os.path.join(self.module_name, self.fname, 'alien_node'),
872set_replication=True,
873initdb_params=['--data-checksums'])
874
875backup_dir = os.path.join(self.tmp_path, self.module_name, self.fname, 'backup')
876self.init_pb(backup_dir)
877self.add_instance(backup_dir, 'node', node)
878self.set_archiving(backup_dir, 'node', node)
879node.slow_start()
880
881self.add_instance(backup_dir, 'alien_node', alien_node)
882self.set_archiving(backup_dir, 'alien_node', alien_node)
883alien_node.slow_start()
884
885self.backup_node(
886backup_dir, 'node', node, options=['--stream'])
887self.backup_node(
888backup_dir, 'alien_node', alien_node, options=['--stream'])
889
890# make some wals
891node.safe_psql(
892"postgres",
893"create sequence t_seq; "
894"create table t_heap as select i as id, "
895"md5(i::text) as text, "
896"md5(repeat(i::text,10))::tsvector as tsvector "
897"from generate_series(0,10000) i;")
898
899alien_node.safe_psql(
900"postgres",
901"create database alien")
902
903alien_node.safe_psql(
904"alien",
905"create sequence t_seq; "
906"create table t_heap_alien as select i as id, "
907"md5(i::text) as text, "
908"md5(repeat(i::text,10))::tsvector as tsvector "
909"from generate_series(0,10000) i;")
910
911# copy latest wal segment
912wals_dir = os.path.join(backup_dir, 'wal', 'alien_node')
913wals = [f for f in os.listdir(wals_dir) if os.path.isfile(os.path.join(
914wals_dir, f)) and not f.endswith('.backup')]
915wals = map(str, wals)
916filename = max(wals)
917file = os.path.join(wals_dir, filename)
918file_destination = os.path.join(
919os.path.join(backup_dir, 'wal', 'node'), filename)
920start = time.time()
921while not os.path.exists(file_destination) and time.time() - start < 20:
922time.sleep(0.1)
923os.remove(file_destination)
924os.rename(file, file_destination)
925
926# Single-thread PAGE backup
927try:
928self.backup_node(
929backup_dir, 'node', node,
930backup_type='page')
931self.assertEqual(
9321, 0,
933"Expecting Error because of alien wal segment.\n "
934"Output: {0} \n CMD: {1}".format(
935self.output, self.cmd))
936except ProbackupException as e:
937self.assertTrue(
938'Could not read WAL record at' in e.message and
939'Possible WAL corruption. Error has occured during reading WAL segment' in e.message,
940'\n Unexpected Error Message: {0}\n CMD: {1}'.format(
941repr(e.message), self.cmd))
942
943self.assertEqual(
944'ERROR',
945self.show_pb(backup_dir, 'node')[1]['status'],
946'Backup {0} should have STATUS "ERROR"')
947
948# Multi-thread PAGE backup
949try:
950self.backup_node(
951backup_dir, 'node', node,
952backup_type='page', options=["-j", "4"])
953self.assertEqual(
9541, 0,
955"Expecting Error because of alien wal segment.\n "
956"Output: {0} \n CMD: {1}".format(
957self.output, self.cmd))
958except ProbackupException as e:
959self.assertIn('Could not read WAL record at', e.message)
960self.assertIn('WAL file is from different database system: '
961'WAL file database system identifier is', e.message)
962self.assertIn('pg_control database system identifier is', e.message)
963self.assertIn('Possible WAL corruption. Error has occured '
964'during reading WAL segment', e.message)
965
966self.assertEqual(
967'ERROR',
968self.show_pb(backup_dir, 'node')[2]['status'],
969'Backup {0} should have STATUS "ERROR"')
970
971# @unittest.skip("skip")
972def test_multithread_page_backup_with_toast(self):
973"""
974make node, create toast, do multithread PAGE backup
975"""
976node = self.make_simple_node(
977base_dir=os.path.join(self.module_name, self.fname, 'node'),
978initdb_params=['--data-checksums'])
979
980backup_dir = os.path.join(self.tmp_path, self.module_name, self.fname, 'backup')
981self.init_pb(backup_dir)
982self.add_instance(backup_dir, 'node', node)
983self.set_archiving(backup_dir, 'node', node)
984node.slow_start()
985
986self.backup_node(backup_dir, 'node', node)
987
988# make some wals
989node.safe_psql(
990"postgres",
991"create table t3 as select i, "
992"repeat(md5(i::text),5006056) as fat_attr "
993"from generate_series(0,70) i")
994
995# Multi-thread PAGE backup
996self.backup_node(
997backup_dir, 'node', node,
998backup_type='page', options=["-j", "4"])
999
1000# @unittest.skip("skip")
1001def test_page_create_db(self):
1002"""
1003Make node, take full backup, create database db1, take page backup,
1004restore database and check it presense
1005"""
1006self.maxDiff = None
1007backup_dir = os.path.join(self.tmp_path, self.module_name, self.fname, 'backup')
1008node = self.make_simple_node(
1009base_dir=os.path.join(self.module_name, self.fname, 'node'),
1010set_replication=True,
1011initdb_params=['--data-checksums'],
1012pg_options={
1013'max_wal_size': '10GB',
1014'checkpoint_timeout': '5min',
1015}
1016)
1017
1018self.init_pb(backup_dir)
1019self.add_instance(backup_dir, 'node', node)
1020self.set_archiving(backup_dir, 'node', node)
1021node.slow_start()
1022
1023# FULL BACKUP
1024node.safe_psql(
1025"postgres",
1026"create table t_heap as select i as id, md5(i::text) as text, "
1027"md5(i::text)::tsvector as tsvector from generate_series(0,100) i")
1028
1029self.backup_node(
1030backup_dir, 'node', node)
1031
1032# CREATE DATABASE DB1
1033node.safe_psql("postgres", "create database db1")
1034node.safe_psql(
1035"db1",
1036"create table t_heap as select i as id, md5(i::text) as text, "
1037"md5(i::text)::tsvector as tsvector from generate_series(0,1000) i")
1038
1039# PAGE BACKUP
1040backup_id = self.backup_node(backup_dir, 'node', node, backup_type='page')
1041
1042if self.paranoia:
1043pgdata = self.pgdata_content(node.data_dir)
1044
1045# RESTORE
1046node_restored = self.make_simple_node(
1047base_dir=os.path.join(self.module_name, self.fname, 'node_restored'))
1048
1049node_restored.cleanup()
1050self.restore_node(
1051backup_dir, 'node', node_restored,
1052backup_id=backup_id, options=["-j", "4"])
1053
1054# COMPARE PHYSICAL CONTENT
1055if self.paranoia:
1056pgdata_restored = self.pgdata_content(node_restored.data_dir)
1057self.compare_pgdata(pgdata, pgdata_restored)
1058
1059# START RESTORED NODE
1060self.set_auto_conf(node_restored, {'port': node_restored.port})
1061node_restored.slow_start()
1062
1063node_restored.safe_psql('db1', 'select 1')
1064node_restored.cleanup()
1065
1066# DROP DATABASE DB1
1067node.safe_psql(
1068"postgres", "drop database db1")
1069# SECOND PAGE BACKUP
1070backup_id = self.backup_node(
1071backup_dir, 'node', node, backup_type='page')
1072
1073if self.paranoia:
1074pgdata = self.pgdata_content(node.data_dir)
1075
1076# RESTORE SECOND PAGE BACKUP
1077self.restore_node(
1078backup_dir, 'node', node_restored,
1079backup_id=backup_id, options=["-j", "4"]
1080)
1081
1082# COMPARE PHYSICAL CONTENT
1083if self.paranoia:
1084pgdata_restored = self.pgdata_content(
1085node_restored.data_dir, ignore_ptrack=False)
1086self.compare_pgdata(pgdata, pgdata_restored)
1087
1088# START RESTORED NODE
1089self.set_auto_conf(node_restored, {'port': node_restored.port})
1090node_restored.slow_start()
1091
1092try:
1093node_restored.safe_psql('db1', 'select 1')
1094# we should die here because exception is what we expect to happen
1095self.assertEqual(
10961, 0,
1097"Expecting Error because we are connecting to deleted database"
1098"\n Output: {0} \n CMD: {1}".format(
1099repr(self.output), self.cmd)
1100)
1101except QueryException as e:
1102self.assertTrue(
1103'FATAL: database "db1" does not exist' in e.message,
1104'\n Unexpected Error Message: {0}\n CMD: {1}'.format(
1105repr(e.message), self.cmd)
1106)
1107
1108# @unittest.skip("skip")
1109# @unittest.expectedFailure
1110def test_multi_timeline_page(self):
1111"""
1112Check that backup in PAGE mode choose
1113parent backup correctly:
1114t12 /---P-->
1115...
1116t3 /---->
1117t2 /---->
1118t1 -F-----D->
1119
1120P must have F as parent
1121"""
1122backup_dir = os.path.join(self.tmp_path, self.module_name, self.fname, 'backup')
1123node = self.make_simple_node(
1124base_dir=os.path.join(self.module_name, self.fname, 'node'),
1125set_replication=True,
1126initdb_params=['--data-checksums'])
1127
1128self.init_pb(backup_dir)
1129self.add_instance(backup_dir, 'node', node)
1130self.set_archiving(backup_dir, 'node', node)
1131node.slow_start()
1132
1133node.safe_psql("postgres", "create extension pageinspect")
1134
1135try:
1136node.safe_psql(
1137"postgres",
1138"create extension amcheck")
1139except QueryException as e:
1140node.safe_psql(
1141"postgres",
1142"create extension amcheck_next")
1143
1144node.pgbench_init(scale=20)
1145full_id = self.backup_node(backup_dir, 'node', node)
1146
1147pgbench = node.pgbench(options=['-T', '10', '-c', '1', '--no-vacuum'])
1148pgbench.wait()
1149
1150self.backup_node(backup_dir, 'node', node, backup_type='delta')
1151
1152node.cleanup()
1153self.restore_node(
1154backup_dir, 'node', node, backup_id=full_id,
1155options=[
1156'--recovery-target=immediate',
1157'--recovery-target-action=promote'])
1158
1159node.slow_start()
1160
1161pgbench = node.pgbench(options=['-T', '10', '-c', '1', '--no-vacuum'])
1162pgbench.wait()
1163
1164# create timelines
1165for i in range(2, 7):
1166node.cleanup()
1167self.restore_node(
1168backup_dir, 'node', node,
1169options=[
1170'--recovery-target=latest',
1171'--recovery-target-action=promote',
1172'--recovery-target-timeline={0}'.format(i)])
1173node.slow_start()
1174
1175# at this point there is i+1 timeline
1176pgbench = node.pgbench(options=['-T', '20', '-c', '1', '--no-vacuum'])
1177pgbench.wait()
1178
1179# create backup at 2, 4 and 6 timeline
1180if i % 2 == 0:
1181self.backup_node(backup_dir, 'node', node, backup_type='page')
1182
1183page_id = self.backup_node(
1184backup_dir, 'node', node, backup_type='page',
1185options=['--log-level-file=VERBOSE'])
1186
1187pgdata = self.pgdata_content(node.data_dir)
1188
1189result = node.table_checksum("pgbench_accounts")
1190
1191node_restored = self.make_simple_node(
1192base_dir=os.path.join(self.module_name, self.fname, 'node_restored'))
1193node_restored.cleanup()
1194
1195self.restore_node(backup_dir, 'node', node_restored)
1196pgdata_restored = self.pgdata_content(node_restored.data_dir)
1197
1198self.set_auto_conf(node_restored, {'port': node_restored.port})
1199node_restored.slow_start()
1200
1201result_new = node_restored.table_checksum("pgbench_accounts")
1202
1203self.assertEqual(result, result_new)
1204
1205self.compare_pgdata(pgdata, pgdata_restored)
1206
1207self.checkdb_node(
1208backup_dir,
1209'node',
1210options=[
1211'--amcheck',
1212'-d', 'postgres', '-p', str(node.port)])
1213
1214self.checkdb_node(
1215backup_dir,
1216'node',
1217options=[
1218'--amcheck',
1219'-d', 'postgres', '-p', str(node_restored.port)])
1220
1221backup_list = self.show_pb(backup_dir, 'node')
1222
1223self.assertEqual(
1224backup_list[2]['parent-backup-id'],
1225backup_list[0]['id'])
1226self.assertEqual(backup_list[2]['current-tli'], 3)
1227
1228self.assertEqual(
1229backup_list[3]['parent-backup-id'],
1230backup_list[2]['id'])
1231self.assertEqual(backup_list[3]['current-tli'], 5)
1232
1233self.assertEqual(
1234backup_list[4]['parent-backup-id'],
1235backup_list[3]['id'])
1236self.assertEqual(backup_list[4]['current-tli'], 7)
1237
1238self.assertEqual(
1239backup_list[5]['parent-backup-id'],
1240backup_list[4]['id'])
1241self.assertEqual(backup_list[5]['current-tli'], 7)
1242
1243# @unittest.skip("skip")
1244# @unittest.expectedFailure
1245def test_multitimeline_page_1(self):
1246"""
1247Check that backup in PAGE mode choose
1248parent backup correctly:
1249t2 /---->
1250t1 -F--P---D->
1251
1252P must have F as parent
1253"""
1254backup_dir = os.path.join(self.tmp_path, self.module_name, self.fname, 'backup')
1255node = self.make_simple_node(
1256base_dir=os.path.join(self.module_name, self.fname, 'node'),
1257set_replication=True,
1258initdb_params=['--data-checksums'],
1259pg_options={'wal_log_hints': 'on'})
1260
1261self.init_pb(backup_dir)
1262self.add_instance(backup_dir, 'node', node)
1263self.set_archiving(backup_dir, 'node', node)
1264node.slow_start()
1265
1266node.safe_psql("postgres", "create extension pageinspect")
1267
1268try:
1269node.safe_psql(
1270"postgres",
1271"create extension amcheck")
1272except QueryException as e:
1273node.safe_psql(
1274"postgres",
1275"create extension amcheck_next")
1276
1277node.pgbench_init(scale=20)
1278full_id = self.backup_node(backup_dir, 'node', node)
1279
1280pgbench = node.pgbench(options=['-T', '20', '-c', '1'])
1281pgbench.wait()
1282
1283page1 = self.backup_node(backup_dir, 'node', node, backup_type='page')
1284
1285pgbench = node.pgbench(options=['-T', '10', '-c', '1', '--no-vacuum'])
1286pgbench.wait()
1287
1288page1 = self.backup_node(backup_dir, 'node', node, backup_type='delta')
1289
1290node.cleanup()
1291self.restore_node(
1292backup_dir, 'node', node, backup_id=page1,
1293options=[
1294'--recovery-target=immediate',
1295'--recovery-target-action=promote'])
1296
1297node.slow_start()
1298
1299pgbench = node.pgbench(options=['-T', '20', '-c', '1', '--no-vacuum'])
1300pgbench.wait()
1301
1302print(self.backup_node(
1303backup_dir, 'node', node, backup_type='page',
1304options=['--log-level-console=LOG'], return_id=False))
1305
1306pgdata = self.pgdata_content(node.data_dir)
1307
1308node_restored = self.make_simple_node(
1309base_dir=os.path.join(self.module_name, self.fname, 'node_restored'))
1310node_restored.cleanup()
1311
1312self.restore_node(backup_dir, 'node', node_restored)
1313pgdata_restored = self.pgdata_content(node_restored.data_dir)
1314
1315self.set_auto_conf(node_restored, {'port': node_restored.port})
1316node_restored.slow_start()
1317
1318self.compare_pgdata(pgdata, pgdata_restored)
1319
1320@unittest.skip("skip")
1321# @unittest.expectedFailure
1322def test_page_pg_resetxlog(self):
1323node = self.make_simple_node(
1324base_dir=os.path.join(self.module_name, self.fname, 'node'),
1325set_replication=True,
1326initdb_params=['--data-checksums'],
1327pg_options={
1328'shared_buffers': '512MB',
1329'max_wal_size': '3GB'})
1330
1331backup_dir = os.path.join(self.tmp_path, self.module_name, self.fname, 'backup')
1332self.init_pb(backup_dir)
1333self.add_instance(backup_dir, 'node', node)
1334self.set_archiving(backup_dir, 'node', node)
1335node.slow_start()
1336
1337# Create table
1338node.safe_psql(
1339"postgres",
1340"create extension bloom; create sequence t_seq; "
1341"create table t_heap "
1342"as select nextval('t_seq')::int as id, md5(i::text) as text, "
1343"md5(repeat(i::text,10))::tsvector as tsvector "
1344# "from generate_series(0,25600) i")
1345"from generate_series(0,2560) i")
1346
1347self.backup_node(backup_dir, 'node', node)
1348
1349node.safe_psql(
1350'postgres',
1351"update t_heap set id = nextval('t_seq'), text = md5(text), "
1352"tsvector = md5(repeat(tsvector::text, 10))::tsvector")
1353
1354self.switch_wal_segment(node)
1355
1356# kill the bastard
1357if self.verbose:
1358print('Killing postmaster. Losing Ptrack changes')
1359node.stop(['-m', 'immediate', '-D', node.data_dir])
1360
1361# now smack it with sledgehammer
1362if node.major_version >= 10:
1363pg_resetxlog_path = self.get_bin_path('pg_resetwal')
1364wal_dir = 'pg_wal'
1365else:
1366pg_resetxlog_path = self.get_bin_path('pg_resetxlog')
1367wal_dir = 'pg_xlog'
1368
1369self.run_binary(
1370[
1371pg_resetxlog_path,
1372'-D',
1373node.data_dir,
1374'-o 42',
1375'-f'
1376],
1377asynchronous=False)
1378
1379if not node.status():
1380node.slow_start()
1381else:
1382print("Die! Die! Why won't you die?... Why won't you die?")
1383exit(1)
1384
1385# take ptrack backup
1386# self.backup_node(
1387# backup_dir, 'node', node,
1388# backup_type='page', options=['--stream'])
1389
1390try:
1391self.backup_node(
1392backup_dir, 'node', node, backup_type='page')
1393# we should die here because exception is what we expect to happen
1394self.assertEqual(
13951, 0,
1396"Expecting Error because instance was brutalized by pg_resetxlog"
1397"\n Output: {0} \n CMD: {1}".format(
1398repr(self.output), self.cmd)
1399)
1400except ProbackupException as e:
1401self.assertIn(
1402'Insert error message',
1403e.message,
1404'\n Unexpected Error Message: {0}\n'
1405' CMD: {1}'.format(repr(e.message), self.cmd))
1406
1407# pgdata = self.pgdata_content(node.data_dir)
1408#
1409# node_restored = self.make_simple_node(
1410# base_dir=os.path.join(self.module_name, self.fname, 'node_restored'))
1411# node_restored.cleanup()
1412#
1413# self.restore_node(
1414# backup_dir, 'node', node_restored)
1415#
1416# pgdata_restored = self.pgdata_content(node_restored.data_dir)
1417# self.compare_pgdata(pgdata, pgdata_restored)
1418