Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit43a33ef

Browse files
committed
Support RBM_ZERO_AND_CLEANUP_LOCK in ExtendBufferedRelTo(), add tests
For some reason I had not implemented RBM_ZERO_AND_CLEANUP_LOCK support inExtendBufferedRelTo(), likely thinking it not being reachable. But it isreachable, e.g. when replaying a WAL record for a page in a relation thatsubsequently is truncated (likely only reachable when doing crash recovery orPITR, not during ongoing streaming replication).As now all of the RBM_* modes are supported, remove assertions checking mode.As we had no test coverage for this scenario, add a new TAP test. There'splenty more that ought to be tested in this area...Reported-by: Tom Lane <tgl@sss.pgh.pa.us>Reported-by: Alexander Lakhin <exclusion@gmail.com>Reviewed-by: Alexander Lakhin <exclusion@gmail.com>Discussion:https://postgr.es/m/392271.1681238924%40sss.pgh.pa.usDiscussion:https://postgr.es/m/0b5eb82b-cb99-e0a4-b932-3dc60e2e3926@gmail.com
1 parente4d905f commit43a33ef

File tree

3 files changed

+154
-11
lines changed

3 files changed

+154
-11
lines changed

‎src/backend/storage/buffer/bufmgr.c

Lines changed: 17 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -888,8 +888,6 @@ ExtendBufferedRelTo(ExtendBufferedWhat eb,
888888
Assert((eb.rel!=NULL)!= (eb.smgr!=NULL));
889889
Assert(eb.smgr==NULL||eb.relpersistence!=0);
890890
Assert(extend_to!=InvalidBlockNumber&&extend_to>0);
891-
Assert(mode==RBM_NORMAL||mode==RBM_ZERO_ON_ERROR||
892-
mode==RBM_ZERO_AND_LOCK);
893891

894892
if (eb.smgr==NULL)
895893
{
@@ -933,7 +931,13 @@ ExtendBufferedRelTo(ExtendBufferedWhat eb,
933931
*/
934932
current_size=smgrnblocks(eb.smgr,fork);
935933

936-
if (mode==RBM_ZERO_AND_LOCK)
934+
/*
935+
* Since no-one else can be looking at the page contents yet, there is no
936+
* difference between an exclusive lock and a cleanup-strength lock. Note
937+
* that we pass the original mode to ReadBuffer_common() below, when
938+
* falling back to reading the buffer to a concurrent relation extension.
939+
*/
940+
if (mode==RBM_ZERO_AND_LOCK||mode==RBM_ZERO_AND_CLEANUP_LOCK)
937941
flags |=EB_LOCK_TARGET;
938942

939943
while (current_size<extend_to)
@@ -1008,11 +1012,12 @@ ReadBuffer_common(SMgrRelation smgr, char relpersistence, ForkNumber forkNum,
10081012
{
10091013
uint32flags=EB_SKIP_EXTENSION_LOCK;
10101014

1011-
Assert(mode==RBM_NORMAL||
1012-
mode==RBM_ZERO_AND_LOCK||
1013-
mode==RBM_ZERO_ON_ERROR);
1014-
1015-
if (mode==RBM_ZERO_AND_LOCK)
1015+
/*
1016+
* Since no-one else can be looking at the page contents yet, there is
1017+
* no difference between an exclusive lock and a cleanup-strength
1018+
* lock.
1019+
*/
1020+
if (mode==RBM_ZERO_AND_LOCK||mode==RBM_ZERO_AND_CLEANUP_LOCK)
10161021
flags |=EB_LOCK_FIRST;
10171022

10181023
returnExtendBufferedRel(EB_SMGR(smgr,relpersistence),
@@ -1145,9 +1150,10 @@ ReadBuffer_common(SMgrRelation smgr, char relpersistence, ForkNumber forkNum,
11451150
}
11461151

11471152
/*
1148-
* In RBM_ZERO_AND_LOCK mode, grab the buffer content lock before marking
1149-
* the page as valid, to make sure that no other backend sees the zeroed
1150-
* page before the caller has had a chance to initialize it.
1153+
* In RBM_ZERO_AND_LOCK / RBM_ZERO_AND_CLEANUP_LOCK mode, grab the buffer
1154+
* content lock before marking the page as valid, to make sure that no
1155+
* other backend sees the zeroed page before the caller has had a chance
1156+
* to initialize it.
11511157
*
11521158
* Since no-one else can be looking at the page contents yet, there is no
11531159
* difference between an exclusive lock and a cleanup-strength lock. (Note

‎src/test/recovery/meson.build

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ tests += {
4141
't/033_replay_tsp_drops.pl',
4242
't/034_create_database.pl',
4343
't/035_standby_logical_decoding.pl',
44+
't/036_truncated_dropped.pl',
4445
],
4546
},
4647
}
Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
# Copyright (c) 2021-2023, PostgreSQL Global Development Group
2+
3+
# Tests recovery scenarios where the files are shorter than in the common
4+
# cases, e.g. due to replaying WAL records of a relation that was subsequently
5+
# truncated or dropped.
6+
7+
use strict;
8+
use warnings;
9+
use PostgreSQL::Test::Cluster;
10+
use Test::More;
11+
12+
my$node = PostgreSQL::Test::Cluster->new('n1');
13+
14+
$node->init();
15+
16+
# Disable autovacuum to guarantee VACUUM can remove rows / truncate relations
17+
$node->append_conf(
18+
'postgresql.conf',qq[
19+
wal_level = 'replica'
20+
autovacuum = off
21+
]);
22+
23+
$node->start();
24+
25+
26+
# Test: Replay replay of PRUNE records for a pre-existing, then dropped,
27+
# relation
28+
29+
$node->safe_psql(
30+
'postgres',qq[
31+
CREATE TABLE truncme(i int) WITH (fillfactor = 50);
32+
INSERT INTO truncme SELECT generate_series(1, 1000);
33+
UPDATE truncme SET i = 1;
34+
CHECKPOINT; -- ensure relation exists at start of recovery
35+
VACUUM truncme; -- generate prune records
36+
DROP TABLE truncme;
37+
]);
38+
39+
$node->stop('immediate');
40+
41+
ok($node->start(),
42+
'replay of PRUNE records for a pre-existing, then dropped, relation');
43+
44+
45+
# Test: Replay of PRUNE records for a newly created, then dropped, relation
46+
47+
$node->safe_psql(
48+
'postgres',qq[
49+
CREATE TABLE truncme(i int) WITH (fillfactor = 50);
50+
INSERT INTO truncme SELECT generate_series(1, 1000);
51+
UPDATE truncme SET i = 1;
52+
VACUUM truncme; -- generate prune records
53+
DROP TABLE truncme;
54+
]);
55+
56+
$node->stop('immediate');
57+
58+
ok($node->start(),
59+
'replay of PRUNE records for a newly created, then dropped, relation');
60+
61+
62+
# Test: Replay of PRUNE records affecting truncated block. With FPIs used for
63+
# PRUNE.
64+
65+
$node->safe_psql(
66+
'postgres',qq[
67+
CREATE TABLE truncme(i int) WITH (fillfactor = 50);
68+
INSERT INTO truncme SELECT generate_series(1, 1000);
69+
UPDATE truncme SET i = 1;
70+
CHECKPOINT; -- generate FPIs
71+
VACUUM truncme; -- generate prune records
72+
TRUNCATE truncme; -- make blocks non-existing
73+
INSERT INTO truncme SELECT generate_series(1, 10);
74+
]);
75+
76+
$node->stop('immediate');
77+
78+
ok($node->start(),
79+
'replay of PRUNE records affecting truncated block (FPIs)');
80+
81+
is($node->safe_psql('postgres','select count(*), sum(i) FROM truncme'),
82+
'10|55','table contents as expected after recovery');
83+
$node->safe_psql('postgres','DROP TABLE truncme');
84+
85+
86+
# Test replay of PRUNE records for blocks that are later truncated. Without
87+
# FPIs used for PRUNE.
88+
89+
$node->safe_psql(
90+
'postgres',qq[
91+
CREATE TABLE truncme(i int) WITH (fillfactor = 50);
92+
INSERT INTO truncme SELECT generate_series(1, 1000);
93+
UPDATE truncme SET i = 1;
94+
VACUUM truncme; -- generate prune records
95+
TRUNCATE truncme; -- make blocks non-existing
96+
INSERT INTO truncme SELECT generate_series(1, 10);
97+
]);
98+
99+
$node->stop('immediate');
100+
101+
ok($node->start(),
102+
'replay of PRUNE records affecting truncated block (no FPIs)');
103+
104+
is($node->safe_psql('postgres','select count(*), sum(i) FROM truncme'),
105+
'10|55','table contents as expected after recovery');
106+
$node->safe_psql('postgres','DROP TABLE truncme');
107+
108+
109+
# Test: Replay of partial truncation via VACUUM
110+
111+
$node->safe_psql(
112+
'postgres',qq[
113+
CREATE TABLE truncme(i int) WITH (fillfactor = 50);
114+
INSERT INTO truncme SELECT generate_series(1, 1000);
115+
UPDATE truncme SET i = i + 1;
116+
-- ensure a mix of pre/post truncation rows
117+
DELETE FROM truncme WHERE i > 500;
118+
119+
VACUUM truncme; -- should truncate relation
120+
121+
-- rows at TIDs that previously existed
122+
INSERT INTO truncme SELECT generate_series(1000, 1010);
123+
]);
124+
125+
$node->stop('immediate');
126+
127+
ok($node->start(),'replay of partial truncation via VACUUM');
128+
129+
is($node->safe_psql(
130+
'postgres','select count(*), sum(i), min(i), max(i) FROM truncme'),
131+
'510|136304|2|1010',
132+
'table contents as expected after recovery');
133+
$node->safe_psql('postgres','DROP TABLE truncme');
134+
135+
136+
done_testing();

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp