Skip to content

Commit cdf1f5a

Browse files
akorotkovHUUTFJ
andcommitted
Reintroduce test 046_checkpoint_logical_slot
This commit is only for HEAD and v18, where the test has been removed. It also incorporates improvements below to stability and coverage of the original test, which were already backpatched to v17. - Add one pg_logical_emit_message() call to force the creation of a record that spawns across two pages. - Make the logic wait for the checkpoint completion. Author: Alexander Korotkov <akorotkov@postgresql.org> Co-authored-by: Hayato Kuroda <kuroda.hayato@fujitsu.com> Reviewed-by: Michael Paquier <michael@paquier.xyz> Backpatch-through: 18
1 parent ccd9451 commit cdf1f5a

File tree

2 files changed

+143
-0
lines changed

2 files changed

+143
-0
lines changed

src/test/recovery/meson.build

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ tests += {
5454
't/043_no_contrecord_switch.pl',
5555
't/044_invalidate_inactive_slots.pl',
5656
't/045_archive_restartpoint.pl',
57+
't/046_checkpoint_logical_slot.pl',
5758
't/047_checkpoint_physical_slot.pl',
5859
't/048_vacuum_horizon_floor.pl'
5960
],
Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
# Copyright (c) 2025, PostgreSQL Global Development Group
2+
#
3+
# This test verifies the case when the logical slot is advanced during
4+
# checkpoint. The test checks that the logical slot's restart_lsn still refers
5+
# to an existed WAL segment after immediate restart.
6+
#
7+
use strict;
8+
use warnings FATAL => 'all';
9+
10+
use PostgreSQL::Test::Cluster;
11+
use PostgreSQL::Test::Utils;
12+
13+
use Test::More;
14+
15+
if ($ENV{enable_injection_points} ne 'yes')
16+
{
17+
plan skip_all => 'Injection points not supported by this build';
18+
}
19+
20+
my ($node, $result);
21+
22+
$node = PostgreSQL::Test::Cluster->new('mike');
23+
$node->init;
24+
$node->append_conf('postgresql.conf', "wal_level = 'logical'");
25+
$node->start;
26+
27+
# Check if the extension injection_points is available, as it may be
28+
# possible that this script is run with installcheck, where the module
29+
# would not be installed by default.
30+
if (!$node->check_extension('injection_points'))
31+
{
32+
plan skip_all => 'Extension injection_points not installed';
33+
}
34+
35+
$node->safe_psql('postgres', q(CREATE EXTENSION injection_points));
36+
37+
# Create the two slots we'll need.
38+
$node->safe_psql('postgres',
39+
q{select pg_create_logical_replication_slot('slot_logical', 'test_decoding')}
40+
);
41+
$node->safe_psql('postgres',
42+
q{select pg_create_physical_replication_slot('slot_physical', true)});
43+
44+
# Advance both slots to the current position just to have everything "valid".
45+
$node->safe_psql('postgres',
46+
q{select count(*) from pg_logical_slot_get_changes('slot_logical', null, null)}
47+
);
48+
$node->safe_psql('postgres',
49+
q{select pg_replication_slot_advance('slot_physical', pg_current_wal_lsn())}
50+
);
51+
52+
# Run checkpoint to flush current state to disk and set a baseline.
53+
$node->safe_psql('postgres', q{checkpoint});
54+
55+
# Generate some transactions to get RUNNING_XACTS.
56+
my $xacts = $node->background_psql('postgres');
57+
$xacts->query_until(
58+
qr/run_xacts/,
59+
q(\echo run_xacts
60+
SELECT 1 \watch 0.1
61+
\q
62+
));
63+
64+
$node->advance_wal(20);
65+
66+
# Run another checkpoint to set a new restore LSN.
67+
$node->safe_psql('postgres', q{checkpoint});
68+
69+
$node->advance_wal(20);
70+
71+
# Run another checkpoint, this time in the background, and make it wait
72+
# on the injection point) so that the checkpoint stops right before
73+
# removing old WAL segments.
74+
note('starting checkpoint');
75+
76+
my $checkpoint = $node->background_psql('postgres');
77+
$checkpoint->query_safe(
78+
q(select injection_points_attach('checkpoint-before-old-wal-removal','wait'))
79+
);
80+
$checkpoint->query_until(
81+
qr/starting_checkpoint/,
82+
q(\echo starting_checkpoint
83+
checkpoint;
84+
\q
85+
));
86+
87+
# Wait until the checkpoint stops right before removing WAL segments.
88+
note('waiting for injection_point');
89+
$node->wait_for_event('checkpointer', 'checkpoint-before-old-wal-removal');
90+
note('injection_point is reached');
91+
92+
# Try to advance the logical slot, but make it stop when it moves to the next
93+
# WAL segment (this has to happen in the background, too).
94+
my $logical = $node->background_psql('postgres');
95+
$logical->query_safe(
96+
q{select injection_points_attach('logical-replication-slot-advance-segment','wait');}
97+
);
98+
$logical->query_until(
99+
qr/get_changes/,
100+
q(
101+
\echo get_changes
102+
select count(*) from pg_logical_slot_get_changes('slot_logical', null, null) \watch 1
103+
\q
104+
));
105+
106+
# Wait until the slot's restart_lsn points to the next WAL segment.
107+
note('waiting for injection_point');
108+
$node->wait_for_event('client backend',
109+
'logical-replication-slot-advance-segment');
110+
note('injection_point is reached');
111+
112+
# OK, we're in the right situation: time to advance the physical slot, which
113+
# recalculates the required LSN, and then unblock the checkpoint, which
114+
# removes the WAL still needed by the logical slot.
115+
$node->safe_psql('postgres',
116+
q{select pg_replication_slot_advance('slot_physical', pg_current_wal_lsn())}
117+
);
118+
119+
# Generate a long WAL record, spawning at least two pages for the follow-up
120+
# post-recovery check.
121+
$node->safe_psql('postgres',
122+
q{select pg_logical_emit_message(false, '', repeat('123456789', 1000))});
123+
124+
# Continue the checkpoint and wait for its completion.
125+
my $log_offset = -s $node->logfile;
126+
$node->safe_psql('postgres',
127+
q{select injection_points_wakeup('checkpoint-before-old-wal-removal')});
128+
$node->wait_for_log(qr/checkpoint complete/, $log_offset);
129+
130+
# Abruptly stop the server.
131+
$node->stop('immediate');
132+
133+
$node->start;
134+
135+
eval {
136+
$node->safe_psql('postgres',
137+
q{select count(*) from pg_logical_slot_get_changes('slot_logical', null, null);}
138+
);
139+
};
140+
is($@, '', "Logical slot still valid");
141+
142+
done_testing();

0 commit comments

Comments
 (0)