@@ -733,14 +733,126 @@ sub wait_until_vacuum_can_remove
733
733
734
734
# #################################################
735
735
# Recovery conflict: Invalidate conflicting slots, including in-use slots
736
- # Scenario 6: incorrect wal_level on primary.
736
+ # Scenario 6: Race condition between slot invalidation and active process
737
+ # termination.
738
+ # #################################################
739
+ SKIP:
740
+ {
741
+ skip " Injection points not supported by this build" , 1
742
+ if ($ENV {enable_injection_points } ne ' yes' );
743
+
744
+ # Get the position to search from in the standby logfile
745
+ $logstart = -s $node_standby -> logfile;
746
+
747
+ # Drop the slots, re-create them, change hot_standby_feedback,
748
+ # check xmin and catalog_xmin values, make slot active and reset stat.
749
+ reactive_slots_change_hfs_and_wait_for_xmins(' pruning_' , ' injection_' , 0,
750
+ 1);
751
+
752
+ # Create the injection_points extension.
753
+ $node_primary -> safe_psql(' testdb' , ' CREATE EXTENSION injection_points;' );
754
+
755
+ # Wait until the extension has been created on the standby.
756
+ $node_primary -> wait_for_replay_catchup($node_standby );
757
+
758
+ # Attach the injection point.
759
+ $node_standby -> safe_psql(' testdb' ,
760
+ " SELECT injection_points_attach('terminate-process-holding-slot', 'wait');"
761
+ );
762
+
763
+ # Trigger a conflict and insert an XLOG_RUNNING_XACTS before triggering
764
+ # the vacuum.
765
+ $node_primary -> safe_psql(
766
+ ' testdb' , qq[ CREATE TABLE injection_test(x integer);
767
+ DROP TABLE injection_test;
768
+ SELECT pg_log_standby_snapshot();] );
769
+
770
+ # Now launch the vacuum.
771
+ wait_until_vacuum_can_remove(' ' ,
772
+ ' CREATE TABLE injection_test2(x integer);' , ' pg_class' );
773
+
774
+ # Wait until the startup process hits the injection point by looking at
775
+ # pg_stat_activity; the termination LOG message has been emitted and
776
+ # the process has been killed once we wait at the injection point.
777
+ $node_standby -> wait_for_event(' startup' ,
778
+ ' terminate-process-holding-slot' );
779
+
780
+ # Note: $node_primary->wait_for_replay_catchup($node_standby) would be
781
+ # hanging here due to the injection point, so check the log instead.
782
+
783
+ ok( $node_standby -> log_contains(
784
+ " terminating process .* to release replication slot \" injection_activeslot\" " ,
785
+ $logstart ),
786
+ " terminating process holding the active slot is logged with injection point"
787
+ );
788
+
789
+ # Extract xid_horizon from the logfile.
790
+ my $log_contents = slurp_file($node_standby -> logfile, $logstart );
791
+ (my $xid_horizon ) =
792
+ $log_contents =~ m / The slot conflicted with xid horizon ([0-9]*)./
793
+ or die " could not get xid horizon" ;
794
+
795
+ # Ensure the slot is not active.
796
+ $node_standby -> poll_query_until(' testdb' ,
797
+ " SELECT active_pid is NULL from pg_catalog.pg_replication_slots where slot_name = 'injection_activeslot'"
798
+ ) or die " injection_activeslot is still active" ;
799
+
800
+ # Decode changes from the slot to reach
801
+ # LogicalConfirmReceivedLocation().
802
+ $node_standby -> safe_psql(' testdb' ,
803
+ qq[ SELECT pg_logical_slot_get_changes('injection_activeslot', NULL, NULL);]
804
+ );
805
+
806
+ # Wait until catalog_xmin advances after the xid_horizon. A conflict
807
+ # reason has to be reported.
808
+ $node_standby -> poll_query_until(' testdb' ,
809
+ " SELECT (SELECT catalog_xmin::text::int - $xid_horizon from pg_catalog.pg_replication_slots where slot_name = 'injection_activeslot') > 0"
810
+ ) or die " catalog_xmin did not advance" ;
811
+
812
+ # Get the position to search from in the standby logfile.
813
+ $logstart = -s $node_standby -> logfile;
814
+
815
+ # Wakeup the injection point.
816
+ $node_standby -> safe_psql(' testdb' ,
817
+ " SELECT injection_points_wakeup('terminate-process-holding-slot');" );
818
+
819
+ # Wait for the standby to catchup.
820
+ $node_primary -> wait_for_replay_catchup($node_standby );
821
+
822
+ # Check invalidation in the logfile for the active slot.
823
+ ok( $node_standby -> log_contains(
824
+ " invalidating obsolete replication slot \" injection_activeslot\" " ,
825
+ $logstart ),
826
+ " activeslot slot invalidation is logged with injection point" );
827
+
828
+ # Verify conflict_reason is 'rows_removed' in pg_replication_slots.
829
+ check_slots_conflict_reason(' injection_' , ' rows_removed' );
830
+
831
+ # Detach from the injection point
832
+ $node_standby -> safe_psql(' testdb' ,
833
+ " SELECT injection_points_detach('terminate-process-holding-slot');" );
834
+
835
+ # Turn hot_standby_feedback back on
836
+ change_hot_standby_feedback_and_wait_for_xmins(1, 1);
837
+ }
838
+
839
+ # #################################################
840
+ # Recovery conflict: Invalidate conflicting slots, including in-use slots
841
+ # Scenario 7: incorrect wal_level on primary.
737
842
# #################################################
738
843
739
844
# get the position to search from in the standby logfile
740
845
$logstart = -s $node_standby -> logfile;
741
846
742
847
# drop the logical slots
743
- drop_logical_slots(' pruning_' );
848
+ if ($ENV {enable_injection_points } ne ' yes' )
849
+ {
850
+ drop_logical_slots(' pruning_' );
851
+ }
852
+ else
853
+ {
854
+ drop_logical_slots(' injection_' );
855
+ }
744
856
745
857
# create the logical slots
746
858
create_logical_slots($node_standby , ' wal_level_' );
0 commit comments