@@ -2837,6 +2837,154 @@ sub lsn
2837
2837
2838
2838
=pod
2839
2839
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
+
2840
2988
=item $node->wait_for_event(backend_type, wait_event_name)
2841
2989
2842
2990
Poll pg_stat_activity until backend_type reaches wait_event_name.
0 commit comments