@@ -2954,6 +2954,154 @@ sub lsn
2954
2954
2955
2955
=pod
2956
2956
2957
+ =item $node->write_wal($tli, $lsn, $segment_size, $data)
2958
+
2959
+ Write some arbitrary data in WAL for the given segment at $lsn (in bytes).
2960
+ This should be called while the cluster is not running.
2961
+
2962
+ Returns the path of the WAL segment written to.
2963
+
2964
+ =cut
2965
+
2966
+ sub write_wal
2967
+ {
2968
+ my ($self , $tli , $lsn , $segment_size , $data ) = @_ ;
2969
+
2970
+ # Calculate segment number and offset position in segment based on the
2971
+ # input LSN.
2972
+ my $segment = $lsn / $segment_size ;
2973
+ my $offset = $lsn % $segment_size ;
2974
+ my $path =
2975
+ sprintf (" %s /pg_wal/%08X%08X%08X" , $self -> data_dir, $tli , 0, $segment );
2976
+
2977
+ open my $fh , " +<:raw" , $path or die " could not open WAL segment $path " ;
2978
+ seek ($fh , $offset , SEEK_SET) or die " could not seek WAL segment $path " ;
2979
+ print $fh $data ;
2980
+ close $fh ;
2981
+
2982
+ return $path ;
2983
+ }
2984
+
2985
+ =pod
2986
+
2987
+ =item $node->emit_wal($size)
2988
+
2989
+ Emit a WAL record of arbitrary size, using pg_logical_emit_message().
2990
+
2991
+ Returns the end LSN of the record inserted, in bytes.
2992
+
2993
+ =cut
2994
+
2995
+ sub emit_wal
2996
+ {
2997
+ my ($self , $size ) = @_ ;
2998
+
2999
+ return int (
3000
+ $self -> safe_psql(
3001
+ ' postgres' ,
3002
+ " SELECT pg_logical_emit_message(true, '', repeat('a', $size )) - '0/0'"
3003
+ ));
3004
+ }
3005
+
3006
+
3007
+ # Private routine returning the current insert LSN of a node, in bytes.
3008
+ # Used by the routines below in charge of advancing WAL to arbitrary
3009
+ # positions. The insert LSN is returned in bytes.
3010
+ sub _get_insert_lsn
3011
+ {
3012
+ my ($self ) = @_ ;
3013
+ return int (
3014
+ $self -> safe_psql(
3015
+ ' postgres' , " SELECT pg_current_wal_insert_lsn() - '0/0'" ));
3016
+ }
3017
+
3018
+ =pod
3019
+
3020
+ =item $node->advance_wal_out_of_record_splitting_zone($wal_block_size)
3021
+
3022
+ Advance WAL at the end of a page, making sure that we are far away enough
3023
+ from the end of a page that we could insert a couple of small records.
3024
+
3025
+ This inserts a few records of a fixed size, until the threshold gets close
3026
+ enough to the end of the WAL page inserting records to.
3027
+
3028
+ Returns the end LSN up to which WAL has advanced, in bytes.
3029
+
3030
+ =cut
3031
+
3032
+ sub advance_wal_out_of_record_splitting_zone
3033
+ {
3034
+ my ($self , $wal_block_size ) = @_ ;
3035
+
3036
+ my $page_threshold = $wal_block_size / 4;
3037
+ my $end_lsn = $self -> _get_insert_lsn();
3038
+ my $page_offset = $end_lsn % $wal_block_size ;
3039
+ while ($page_offset >= $wal_block_size - $page_threshold )
3040
+ {
3041
+ $self -> emit_wal($page_threshold );
3042
+ $end_lsn = $self -> _get_insert_lsn();
3043
+ $page_offset = $end_lsn % $wal_block_size ;
3044
+ }
3045
+ return $end_lsn ;
3046
+ }
3047
+
3048
+ =pod
3049
+
3050
+ =item $node->advance_wal_to_record_splitting_zone($wal_block_size)
3051
+
3052
+ Advance WAL so close to the end of a page that an XLogRecordHeader would not
3053
+ fit on it.
3054
+
3055
+ Returns the end LSN up to which WAL has advanced, in bytes.
3056
+
3057
+ =cut
3058
+
3059
+ sub advance_wal_to_record_splitting_zone
3060
+ {
3061
+ my ($self , $wal_block_size ) = @_ ;
3062
+
3063
+ # Size of record header.
3064
+ my $RECORD_HEADER_SIZE = 24;
3065
+
3066
+ my $end_lsn = $self -> _get_insert_lsn();
3067
+ my $page_offset = $end_lsn % $wal_block_size ;
3068
+
3069
+ # Get fairly close to the end of a page in big steps
3070
+ while ($page_offset <= $wal_block_size - 512)
3071
+ {
3072
+ $self -> emit_wal($wal_block_size - $page_offset - 256);
3073
+ $end_lsn = $self -> _get_insert_lsn();
3074
+ $page_offset = $end_lsn % $wal_block_size ;
3075
+ }
3076
+
3077
+ # Calibrate our message size so that we can get closer 8 bytes at
3078
+ # a time.
3079
+ my $message_size = $wal_block_size - 80;
3080
+ while ($page_offset <= $wal_block_size - $RECORD_HEADER_SIZE )
3081
+ {
3082
+ $self -> emit_wal($message_size );
3083
+ $end_lsn = $self -> _get_insert_lsn();
3084
+
3085
+ my $old_offset = $page_offset ;
3086
+ $page_offset = $end_lsn % $wal_block_size ;
3087
+
3088
+ # Adjust the message size until it causes 8 bytes changes in
3089
+ # offset, enough to be able to split a record header.
3090
+ my $delta = $page_offset - $old_offset ;
3091
+ if ($delta > 8)
3092
+ {
3093
+ $message_size -= 8;
3094
+ }
3095
+ elsif ($delta <= 0)
3096
+ {
3097
+ $message_size += 8;
3098
+ }
3099
+ }
3100
+ return $end_lsn ;
3101
+ }
3102
+
3103
+ =pod
3104
+
2957
3105
=item $node->check_extension(extension_name)
2958
3106
2959
3107
Scan pg_available_extensions to check that an extension is available in an
0 commit comments