Skip to content

Commit 3496020

Browse files
author
Daniel Shelepanov
committed
[PBCKP-150] Reading buffer is flushed each time we verify the checksum.
The race condition is covered with a unit-test, the buffer is flushed now so each of 300 reads requests the data from the disc.
1 parent 0834e54 commit 3496020

File tree

3 files changed

+85
-1
lines changed

3 files changed

+85
-1
lines changed

src/data.c

+2
Original file line numberDiff line numberDiff line change
@@ -301,6 +301,8 @@ prepare_page(pgFile *file, XLogRecPtr prev_backup_start_lsn,
301301
{
302302
/* read the block */
303303
int read_len = fio_pread(in, page, blknum * BLCKSZ);
304+
/* avoid re-reading once buffered data, see PBCKP-150 */
305+
fflush(in);
304306

305307
/* The block could have been truncated. It is fine. */
306308
if (read_len == 0)

tests/__init__.py

+7-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
compression, page, ptrack, archive, exclude, cfs_backup, cfs_restore, \
88
cfs_validate_backup, auth_test, time_stamp, logging, \
99
locking, remote, external, config, checkdb, set_backup, incr_restore, \
10-
catchup, CVE_2018_1058
10+
catchup, CVE_2018_1058, pbckp150
1111

1212

1313
def load_tests(loader, tests, pattern):
@@ -21,6 +21,12 @@ def load_tests(loader, tests, pattern):
2121
if os.environ['PG_PROBACKUP_PTRACK'] == 'ON':
2222
suite.addTests(loader.loadTestsFromModule(ptrack))
2323

24+
# PG_PROBACKUP_LONG section for tests that are long
25+
# by design e.g. they contain loops, sleeps and so on
26+
if 'PG_PROBACKUP_LONG' in os.environ:
27+
if os.environ['PG_PROBACKUP_LONG'] == 'ON':
28+
suite.addTests(loader.loadTestsFromModule(pbckp150))
29+
2430
# suite.addTests(loader.loadTestsFromModule(auth_test))
2531
suite.addTests(loader.loadTestsFromModule(archive))
2632
suite.addTests(loader.loadTestsFromModule(backup))

tests/pbckp150.py

+76
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
import os
2+
import unittest
3+
from .helpers.ptrack_helpers import ProbackupTest
4+
import subprocess
5+
from time import sleep
6+
7+
module_name = 'pbckp150'
8+
9+
class TestPageCorruptionDueToRace(ProbackupTest, unittest.TestCase):
10+
def test_pbckp150(self):
11+
"""
12+
https://jira.postgrespro.ru/browse/PBCKP-150
13+
create a node filled with pgbench
14+
create FULL backup followed by PTRACK backup
15+
run pgbench, vacuum VERBOSE FULL and ptrack backups in parallel
16+
"""
17+
# init node
18+
fname = self.id().split('.')[3]
19+
node = self.make_simple_node(
20+
base_dir=os.path.join(module_name, fname, 'node'),
21+
set_replication=True,
22+
initdb_params=['--data-checksums'])
23+
node.append_conf('postgresql.conf',
24+
"""
25+
max_connections = 100
26+
wal_keep_size = 16000
27+
ptrack.map_size = 1
28+
shared_preload_libraries='ptrack'
29+
log_statement = 'none'
30+
fsync = off
31+
log_checkpoints = on
32+
autovacuum = off
33+
""")
34+
35+
# init probackup and add an instance
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+
40+
# run the node and init ptrack
41+
node.slow_start()
42+
node.safe_psql("postgres", "CREATE EXTENSION ptrack")
43+
# populate it with pgbench
44+
node.pgbench_init(scale=5)
45+
46+
# FULL backup followed by PTRACK backup
47+
self.backup_node(backup_dir, 'node', node, options=['--stream'])
48+
self.backup_node(backup_dir, 'node', node, backup_type='ptrack', options=['--stream'])
49+
50+
# run ordinary pgbench scenario to imitate some activity and another pgbench for vacuuming in parallel
51+
nBenchDuration = 30
52+
pgbench = node.pgbench(options=['-c', '20', '-j', '8', '-T', str(nBenchDuration)])
53+
with open('/tmp/pbckp150vacuum.sql', 'w') as f:
54+
f.write('VACUUM (FULL) pgbench_accounts, pgbench_tellers, pgbench_history; SELECT pg_sleep(1);\n')
55+
pgbenchval = node.pgbench(options=['-c', '1', '-f', '/tmp/pbckp150vacuum.sql', '-T', str(nBenchDuration)])
56+
57+
# several PTRACK backups
58+
for i in range(nBenchDuration):
59+
print("[{}] backing up PTRACK diff...".format(i+1))
60+
self.backup_node(backup_dir, 'node', node, backup_type='ptrack', options=['--stream', '--log-level-console', 'VERBOSE'])
61+
sleep(0.1)
62+
# if the activity pgbench has finished, stop backing up
63+
if pgbench.poll() is not None:
64+
break
65+
66+
pgbench.kill()
67+
pgbenchval.kill()
68+
pgbench.wait()
69+
pgbenchval.wait()
70+
71+
backups = self.show_pb(backup_dir, 'node')
72+
for b in backups:
73+
self.assertEqual("OK", b['status'])
74+
75+
# Clean after yourself
76+
self.del_test_dir(module_name, fname)

0 commit comments

Comments
 (0)