@@ -2510,6 +2510,154 @@ sub lsn
2510
2510
2511
2511
=pod
2512
2512
2513
+ =item $node->write_wal($tli, $lsn, $segment_size, $data)
2514
+
2515
+ Write some arbitrary data in WAL for the given segment at $lsn (in bytes).
2516
+ This should be called while the cluster is not running.
2517
+
2518
+ Returns the path of the WAL segment written to.
2519
+
2520
+ =cut
2521
+
2522
+ sub write_wal
2523
+ {
2524
+ my ($self ,$tli ,$lsn ,$segment_size ,$data ) =@_ ;
2525
+
2526
+ # Calculate segment number and offset position in segment based on the
2527
+ # input LSN.
2528
+ my $segment =$lsn /$segment_size ;
2529
+ my $offset =$lsn %$segment_size ;
2530
+ my $path =
2531
+ sprintf (" %s /pg_wal/%08X%08X%08X" ,$self -> data_dir,$tli , 0,$segment );
2532
+
2533
+ open my $fh ," +<:raw" ,$path or die " could not open WAL segment$path " ;
2534
+ seek ($fh ,$offset , SEEK_SET)or die " could not seek WAL segment$path " ;
2535
+ print $fh $data ;
2536
+ close $fh ;
2537
+
2538
+ return $path ;
2539
+ }
2540
+
2541
+ =pod
2542
+
2543
+ =item $node->emit_wal($size)
2544
+
2545
+ Emit a WAL record of arbitrary size, using pg_logical_emit_message().
2546
+
2547
+ Returns the end LSN of the record inserted, in bytes.
2548
+
2549
+ =cut
2550
+
2551
+ sub emit_wal
2552
+ {
2553
+ my ($self ,$size ) =@_ ;
2554
+
2555
+ return int (
2556
+ $self -> safe_psql(
2557
+ ' postgres' ,
2558
+ " SELECT pg_logical_emit_message(true, '', repeat('a',$size )) - '0/0'"
2559
+ ));
2560
+ }
2561
+
2562
+
2563
+ # Private routine returning the current insert LSN of a node, in bytes.
2564
+ # Used by the routines below in charge of advancing WAL to arbitrary
2565
+ # positions. The insert LSN is returned in bytes.
2566
+ sub _get_insert_lsn
2567
+ {
2568
+ my ($self ) =@_ ;
2569
+ return int (
2570
+ $self -> safe_psql(
2571
+ ' postgres' ," SELECT pg_current_wal_insert_lsn() - '0/0'" ));
2572
+ }
2573
+
2574
+ =pod
2575
+
2576
+ =item $node->advance_wal_out_of_record_splitting_zone($wal_block_size)
2577
+
2578
+ Advance WAL at the end of a page, making sure that we are far away enough
2579
+ from the end of a page that we could insert a couple of small records.
2580
+
2581
+ This inserts a few records of a fixed size, until the threshold gets close
2582
+ enough to the end of the WAL page inserting records to.
2583
+
2584
+ Returns the end LSN up to which WAL has advanced, in bytes.
2585
+
2586
+ =cut
2587
+
2588
+ sub advance_wal_out_of_record_splitting_zone
2589
+ {
2590
+ my ($self ,$wal_block_size ) =@_ ;
2591
+
2592
+ my $page_threshold =$wal_block_size / 4;
2593
+ my $end_lsn =$self -> _get_insert_lsn();
2594
+ my $page_offset =$end_lsn %$wal_block_size ;
2595
+ while ($page_offset >=$wal_block_size -$page_threshold )
2596
+ {
2597
+ $self -> emit_wal($page_threshold );
2598
+ $end_lsn =$self -> _get_insert_lsn();
2599
+ $page_offset =$end_lsn %$wal_block_size ;
2600
+ }
2601
+ return $end_lsn ;
2602
+ }
2603
+
2604
+ =pod
2605
+
2606
+ =item $node->advance_wal_to_record_splitting_zone($wal_block_size)
2607
+
2608
+ Advance WAL so close to the end of a page that an XLogRecordHeader would not
2609
+ fit on it.
2610
+
2611
+ Returns the end LSN up to which WAL has advanced, in bytes.
2612
+
2613
+ =cut
2614
+
2615
+ sub advance_wal_to_record_splitting_zone
2616
+ {
2617
+ my ($self ,$wal_block_size ) =@_ ;
2618
+
2619
+ # Size of record header.
2620
+ my $RECORD_HEADER_SIZE = 24;
2621
+
2622
+ my $end_lsn =$self -> _get_insert_lsn();
2623
+ my $page_offset =$end_lsn %$wal_block_size ;
2624
+
2625
+ # Get fairly close to the end of a page in big steps
2626
+ while ($page_offset <=$wal_block_size - 512)
2627
+ {
2628
+ $self -> emit_wal($wal_block_size -$page_offset - 256);
2629
+ $end_lsn =$self -> _get_insert_lsn();
2630
+ $page_offset =$end_lsn %$wal_block_size ;
2631
+ }
2632
+
2633
+ # Calibrate our message size so that we can get closer 8 bytes at
2634
+ # a time.
2635
+ my $message_size =$wal_block_size - 80;
2636
+ while ($page_offset <=$wal_block_size -$RECORD_HEADER_SIZE )
2637
+ {
2638
+ $self -> emit_wal($message_size );
2639
+ $end_lsn =$self -> _get_insert_lsn();
2640
+
2641
+ my $old_offset =$page_offset ;
2642
+ $page_offset =$end_lsn %$wal_block_size ;
2643
+
2644
+ # Adjust the message size until it causes 8 bytes changes in
2645
+ # offset, enough to be able to split a record header.
2646
+ my $delta =$page_offset -$old_offset ;
2647
+ if ($delta > 8)
2648
+ {
2649
+ $message_size -= 8;
2650
+ }
2651
+ elsif ($delta <= 0)
2652
+ {
2653
+ $message_size += 8;
2654
+ }
2655
+ }
2656
+ return $end_lsn ;
2657
+ }
2658
+
2659
+ =pod
2660
+
2513
2661
=item $node->wait_for_catchup(standby_name, mode, target_lsn)
2514
2662
2515
2663
Wait for the node with application_name standby_name (usually from node->name,