Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit 149ed87

Browse files
committed
Move routines to manipulate WAL into PostgreSQL::Test::Cluster
These facilities were originally in the recovery TAP test 039_end_of_wal.pl. A follow-up bug fix with a TAP test doing similar WAL manipulations requires them, and all these had better not be duplicated due to their complexity. The routine names are tweaked to use "wal" more consistently, similarly to the existing "advance_wal". In v14 and v13, the new routines are moved to PostgresNode.pm. 039_end_of_wal.pl is updated to use the refactored routines, without changing its coverage. Reviewed-by: Alexander Kukushkin Discussion: https://postgr.es/m/CAFh8B=mozC+e1wGJq0H=0O65goZju+6ab5AU7DEWCSUA2OtwDg@mail.gmail.com Backpatch-through: 13
1 parent a0dfeae commit 149ed87

File tree

2 files changed

+214
-191
lines changed

2 files changed

+214
-191
lines changed

src/test/perl/PostgreSQL/Test/Cluster.pm

Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2837,6 +2837,154 @@ sub lsn
28372837

28382838
=pod
28392839
2840+
=item $node->write_wal($tli, $lsn, $segment_size, $data)
2841+
2842+
Write some arbitrary data in WAL for the given segment at $lsn (in bytes).
2843+
This should be called while the cluster is not running.
2844+
2845+
Returns the path of the WAL segment written to.
2846+
2847+
=cut
2848+
2849+
sub write_wal
2850+
{
2851+
my ($self, $tli, $lsn, $segment_size, $data) = @_;
2852+
2853+
# Calculate segment number and offset position in segment based on the
2854+
# input LSN.
2855+
my $segment = $lsn / $segment_size;
2856+
my $offset = $lsn % $segment_size;
2857+
my $path =
2858+
sprintf("%s/pg_wal/%08X%08X%08X", $self->data_dir, $tli, 0, $segment);
2859+
2860+
open my $fh, "+<:raw", $path or die "could not open WAL segment $path";
2861+
seek($fh, $offset, SEEK_SET) or die "could not seek WAL segment $path";
2862+
print $fh $data;
2863+
close $fh;
2864+
2865+
return $path;
2866+
}
2867+
2868+
=pod
2869+
2870+
=item $node->emit_wal($size)
2871+
2872+
Emit a WAL record of arbitrary size, using pg_logical_emit_message().
2873+
2874+
Returns the end LSN of the record inserted, in bytes.
2875+
2876+
=cut
2877+
2878+
sub emit_wal
2879+
{
2880+
my ($self, $size) = @_;
2881+
2882+
return int(
2883+
$self->safe_psql(
2884+
'postgres',
2885+
"SELECT pg_logical_emit_message(true, '', repeat('a', $size)) - '0/0'"
2886+
));
2887+
}
2888+
2889+
2890+
# Private routine returning the current insert LSN of a node, in bytes.
2891+
# Used by the routines below in charge of advancing WAL to arbitrary
2892+
# positions. The insert LSN is returned in bytes.
2893+
sub _get_insert_lsn
2894+
{
2895+
my ($self) = @_;
2896+
return int(
2897+
$self->safe_psql(
2898+
'postgres', "SELECT pg_current_wal_insert_lsn() - '0/0'"));
2899+
}
2900+
2901+
=pod
2902+
2903+
=item $node->advance_wal_out_of_record_splitting_zone($wal_block_size)
2904+
2905+
Advance WAL at the end of a page, making sure that we are far away enough
2906+
from the end of a page that we could insert a couple of small records.
2907+
2908+
This inserts a few records of a fixed size, until the threshold gets close
2909+
enough to the end of the WAL page inserting records to.
2910+
2911+
Returns the end LSN up to which WAL has advanced, in bytes.
2912+
2913+
=cut
2914+
2915+
sub advance_wal_out_of_record_splitting_zone
2916+
{
2917+
my ($self, $wal_block_size) = @_;
2918+
2919+
my $page_threshold = $wal_block_size / 4;
2920+
my $end_lsn = $self->_get_insert_lsn();
2921+
my $page_offset = $end_lsn % $wal_block_size;
2922+
while ($page_offset >= $wal_block_size - $page_threshold)
2923+
{
2924+
$self->emit_wal($page_threshold);
2925+
$end_lsn = $self->_get_insert_lsn();
2926+
$page_offset = $end_lsn % $wal_block_size;
2927+
}
2928+
return $end_lsn;
2929+
}
2930+
2931+
=pod
2932+
2933+
=item $node->advance_wal_to_record_splitting_zone($wal_block_size)
2934+
2935+
Advance WAL so close to the end of a page that an XLogRecordHeader would not
2936+
fit on it.
2937+
2938+
Returns the end LSN up to which WAL has advanced, in bytes.
2939+
2940+
=cut
2941+
2942+
sub advance_wal_to_record_splitting_zone
2943+
{
2944+
my ($self, $wal_block_size) = @_;
2945+
2946+
# Size of record header.
2947+
my $RECORD_HEADER_SIZE = 24;
2948+
2949+
my $end_lsn = $self->_get_insert_lsn();
2950+
my $page_offset = $end_lsn % $wal_block_size;
2951+
2952+
# Get fairly close to the end of a page in big steps
2953+
while ($page_offset <= $wal_block_size - 512)
2954+
{
2955+
$self->emit_wal($wal_block_size - $page_offset - 256);
2956+
$end_lsn = $self->_get_insert_lsn();
2957+
$page_offset = $end_lsn % $wal_block_size;
2958+
}
2959+
2960+
# Calibrate our message size so that we can get closer 8 bytes at
2961+
# a time.
2962+
my $message_size = $wal_block_size - 80;
2963+
while ($page_offset <= $wal_block_size - $RECORD_HEADER_SIZE)
2964+
{
2965+
$self->emit_wal($message_size);
2966+
$end_lsn = $self->_get_insert_lsn();
2967+
2968+
my $old_offset = $page_offset;
2969+
$page_offset = $end_lsn % $wal_block_size;
2970+
2971+
# Adjust the message size until it causes 8 bytes changes in
2972+
# offset, enough to be able to split a record header.
2973+
my $delta = $page_offset - $old_offset;
2974+
if ($delta > 8)
2975+
{
2976+
$message_size -= 8;
2977+
}
2978+
elsif ($delta <= 0)
2979+
{
2980+
$message_size += 8;
2981+
}
2982+
}
2983+
return $end_lsn;
2984+
}
2985+
2986+
=pod
2987+
28402988
=item $node->wait_for_event(backend_type, wait_event_name)
28412989
28422990
Poll pg_stat_activity until backend_type reaches wait_event_name.

0 commit comments

Comments
 (0)