summaryrefslogtreecommitdiffstats
path: root/src/test/recovery/t/008_fsm_truncation.pl
blob: 251f43a16185fbbf5e43174f74a2bd1bae165512 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
# Copyright (c) 2021-2022, PostgreSQL Global Development Group

# Test FSM-driven INSERT just after truncation clears FSM slots indicating
# free space in removed blocks.
# The FSM mustn't return a page that doesn't exist (anymore).
use strict;
use warnings;

use PostgreSQL::Test::Cluster;
use PostgreSQL::Test::Utils;
use Test::More;

my $node_primary = PostgreSQL::Test::Cluster->new('primary');
$node_primary->init(allows_streaming => 1);

$node_primary->append_conf(
	'postgresql.conf', qq{
wal_log_hints = on
max_prepared_transactions = 5
autovacuum = off
});

# Create a primary node and its standby, initializing both with some data
# at the same time.
$node_primary->start;

$node_primary->backup('primary_backup');
my $node_standby = PostgreSQL::Test::Cluster->new('standby');
$node_standby->init_from_backup($node_primary, 'primary_backup',
	has_streaming => 1);
$node_standby->start;

$node_primary->psql(
	'postgres', qq{
create table testtab (a int, b char(100));
insert into testtab select generate_series(1,1000), 'foo';
insert into testtab select generate_series(1,1000), 'foo';
delete from testtab where ctid > '(8,0)';
});

# Take a lock on the table to prevent following vacuum from truncating it
$node_primary->psql(
	'postgres', qq{
begin;
lock table testtab in row share mode;
prepare transaction 'p1';
});

# Vacuum, update FSM without truncation
$node_primary->psql('postgres', 'vacuum verbose testtab');

# Force a checkpoint
$node_primary->psql('postgres', 'checkpoint');

# Now do some more insert/deletes, another vacuum to ensure full-page writes
# are done
$node_primary->psql(
	'postgres', qq{
insert into testtab select generate_series(1,1000), 'foo';
delete from testtab where ctid > '(8,0)';
vacuum verbose testtab;
});

# Ensure all buffers are now clean on the standby
$node_standby->psql('postgres', 'checkpoint');

# Release the lock, vacuum again which should lead to truncation
$node_primary->psql(
	'postgres', qq{
rollback prepared 'p1';
vacuum verbose testtab;
});

$node_primary->psql('postgres', 'checkpoint');
my $until_lsn =
  $node_primary->safe_psql('postgres', "SELECT pg_current_wal_lsn();");

# Wait long enough for standby to receive and apply all WAL
my $caughtup_query =
  "SELECT '$until_lsn'::pg_lsn <= pg_last_wal_replay_lsn()";
$node_standby->poll_query_until('postgres', $caughtup_query)
  or die "Timed out while waiting for standby to catch up";

# Promote the standby
$node_standby->promote;
$node_standby->psql('postgres', 'checkpoint');

# Restart to discard in-memory copy of FSM
$node_standby->restart;

# Insert should work on standby
is( $node_standby->psql(
		'postgres',
		qq{insert into testtab select generate_series(1,1000), 'foo';}),
	0,
	'INSERT succeeds with truncated relation FSM');

done_testing();