@@ -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
28422990Poll pg_stat_activity until backend_type reaches wait_event_name.
0 commit comments