Skip to content

Commit 841e608

Browse files
committed
PGPRO-2068: tests for block from future
1 parent 687677f commit 841e608

File tree

2 files changed

+187
-2
lines changed

2 files changed

+187
-2
lines changed

tests/helpers/ptrack_helpers.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1321,12 +1321,16 @@ def __init__(self, cmd, verbose, attach=False):
13211321
break
13221322

13231323
def set_breakpoint(self, location):
1324+
13241325
result = self._execute('break ' + location)
13251326
for line in result:
13261327
if line.startswith('~"Breakpoint'):
13271328
return
13281329

1329-
elif line.startswith('^error') or line.startswith('(gdb)'):
1330+
elif line.startswith('=breakpoint-created'):
1331+
return
1332+
1333+
elif line.startswith('^error'): #or line.startswith('(gdb)'):
13301334
break
13311335

13321336
elif line.startswith('&"break'):
@@ -1430,7 +1434,7 @@ def _execute(self, cmd, running=True):
14301434
output += [line]
14311435
if self.verbose:
14321436
print(repr(line))
1433-
if line == '^done\n' or line.startswith('*stopped'):
1437+
if line.startswith('^done') or line.startswith('*stopped'):
14341438
break
14351439
if running and line.startswith('*running'):
14361440
break

tests/pgpro2068.py

Lines changed: 181 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,181 @@
1+
import os
2+
import unittest
3+
from .helpers.ptrack_helpers import ProbackupTest, ProbackupException, idx_ptrack
4+
from datetime import datetime, timedelta
5+
import subprocess
6+
from time import sleep
7+
import shutil
8+
import signal
9+
10+
11+
module_name = '2068'
12+
13+
14+
class BugTest(ProbackupTest, unittest.TestCase):
15+
16+
def test_minrecpoint_on_replica(self):
17+
"""
18+
https://jira.postgrespro.ru/browse/PGPRO-2068
19+
make node without archive support, make backup which should fail
20+
check that backup status equal to ERROR
21+
check that no files where copied to backup catalogue
22+
"""
23+
fname = self.id().split('.')[3]
24+
node = self.make_simple_node(
25+
base_dir=os.path.join(module_name, fname, 'node'),
26+
set_replication=True,
27+
initdb_params=['--data-checksums'],
28+
pg_options={
29+
'checkpoint_timeout': '60min',
30+
'checkpoint_completion_target': '0.9',
31+
'bgwriter_delay': '10ms',
32+
'bgwriter_lru_maxpages': '2000',
33+
'bgwriter_lru_multiplier': '4.0',
34+
'max_wal_size': '100GB'})
35+
36+
backup_dir = os.path.join(self.tmp_path, module_name, fname, 'backup')
37+
self.init_pb(backup_dir)
38+
self.add_instance(backup_dir, 'node', node)
39+
self.set_archiving(backup_dir, 'node', node)
40+
node.slow_start()
41+
42+
# take full backup and restore it as replica
43+
self.backup_node(
44+
backup_dir, 'node', node, options=['--stream'])
45+
46+
replica = self.make_simple_node(
47+
base_dir=os.path.join(module_name, fname, 'replica'))
48+
replica.cleanup()
49+
50+
self.restore_node(backup_dir, 'node', replica, options=['-R'])
51+
self.set_replica(node, replica)
52+
self.add_instance(backup_dir, 'replica', replica)
53+
self.set_archiving(backup_dir, 'replica', replica, replica=True)
54+
55+
replica.append_conf(
56+
'postgresql.auto.conf', 'port = {0}'.format(replica.port))
57+
replica.append_conf(
58+
'postgresql.auto.conf', 'restart_after_crash = off')
59+
60+
node.safe_psql(
61+
"postgres",
62+
"CREATE EXTENSION plpythonu")
63+
64+
node.safe_psql(
65+
"postgres",
66+
"CREATE EXTENSION pageinspect")
67+
68+
69+
# pg_last_wal_replay_lsn
70+
replica.slow_start(replica=True)
71+
72+
73+
node.pgbench_init(scale=10)
74+
75+
pgbench = node.pgbench(
76+
stdout=subprocess.PIPE,
77+
stderr=subprocess.STDOUT,
78+
options=["-c", "4", "-T", "20"]
79+
)
80+
pgbench.wait()
81+
pgbench.stdout.close()
82+
83+
84+
pgbench = node.pgbench(
85+
stdout=subprocess.PIPE,
86+
stderr=subprocess.STDOUT,
87+
options=["-c", "4", "-T", "30"]
88+
)
89+
90+
# select pid from pg_stat_activity where backend_type in ('walreceiver', 'checkpointer', 'background writer', 'startup') ;
91+
startup_pid = replica.safe_psql(
92+
'postgres',
93+
"select pid from pg_stat_activity where backend_type = 'startup'").rstrip()
94+
95+
checkpointer_pid = replica.safe_psql(
96+
'postgres',
97+
"select pid from pg_stat_activity where backend_type = 'checkpointer'").rstrip()
98+
99+
bgwriter_pid = replica.safe_psql(
100+
'postgres',
101+
"select pid from pg_stat_activity where backend_type = 'background writer'").rstrip()
102+
103+
sleep(5)
104+
105+
# startup process
106+
# checkpointer
107+
# writer process
108+
109+
# block checkpointer on UpdateLastRemovedPtr
110+
gdb_checkpointer = self.gdb_attach(checkpointer_pid)
111+
gdb_checkpointer.set_breakpoint('UpdateLastRemovedPtr')
112+
gdb_checkpointer.continue_execution_until_break()
113+
114+
# block recovery in on UpdateMinRecoveryPoint
115+
gdb_recovery = self.gdb_attach(startup_pid)
116+
gdb_recovery.set_breakpoint('UpdateMinRecoveryPoint')
117+
gdb_recovery.continue_execution_until_break()
118+
gdb_recovery.set_breakpoint('UpdateControlFile')
119+
gdb_recovery.continue_execution_until_break()
120+
121+
# stop bgwriter
122+
# gdb_bgwriter = self.gdb_attach(bgwriter_pid)
123+
124+
pgbench.wait()
125+
pgbench.stdout.close()
126+
127+
os.kill(int(bgwriter_pid), 9)
128+
gdb_recovery._execute('detach')
129+
gdb_checkpointer._execute('detach')
130+
131+
try:
132+
replica.stop(['-m', 'immediate', '-D', replica.data_dir])
133+
except:
134+
pass
135+
136+
# Promote replica with 'immediate' target action
137+
replica.append_conf(
138+
'recovery.conf', "recovery_target = 'immediate'")
139+
140+
replica.append_conf(
141+
'recovery.conf', "recovery_target_action = 'promote'")
142+
143+
#os.remove(os.path.join(replica.data_dir, 'postmaster.pid'))
144+
145+
# sleep(5)
146+
replica.slow_start()
147+
148+
script = '''
149+
DO
150+
$$
151+
relations = plpy.execute("select class.oid from pg_class class WHERE class.relkind IN ('r', 'i', 't', 'm') and class.relpersistence = 'p'")
152+
current_xlog_lsn = plpy.execute("select pg_last_wal_replay_lsn() as lsn")[0]['lsn']
153+
plpy.notice('CURRENT LSN: {0}'.format(current_xlog_lsn))
154+
found_corruption = False
155+
for relation in relations:
156+
pages_from_future = plpy.execute("with number_of_blocks as (select blknum from generate_series(0, pg_relation_size({0}) / 8192 -1) as blknum) select blknum, lsn, checksum, flags, lower, upper, special, pagesize, version, prune_xid from number_of_blocks, page_header(get_raw_page('{0}'::oid::regclass::text, number_of_blocks.blknum::int)) where lsn > '{1}'::pg_lsn".format(relation['oid'], current_xlog_lsn))
157+
158+
if pages_from_future.nrows() == 0:
159+
continue
160+
161+
for page in pages_from_future:
162+
plpy.notice('Found page from future. OID: {0}, BLKNUM: {1}, LSN: {2}'.format(relation['oid'], page['blknum'], page['lsn']))
163+
found_corruption = True
164+
if found_corruption:
165+
plpy.error('Found Corruption')
166+
$$ LANGUAGE plpythonu;
167+
'''
168+
169+
replica.safe_psql(
170+
'postgres',
171+
script)
172+
173+
# error is expected if version < 10.6
174+
# gdb_backup.continue_execution_until_exit()
175+
176+
# do basebackup
177+
178+
# do pg_probackup, expect error
179+
180+
# Clean after yourself
181+
self.del_test_dir(module_name, fname)

0 commit comments

Comments
 (0)