|
| 1 | +# Copyright (c) 2024, PostgreSQL Global Development Group |
| 2 | + |
| 3 | +# This test verifies edge case of reading a multixact: |
| 4 | +# when we have multixact that is followed by exactly one another multixact, |
| 5 | +# and another multixact have no offset yet, we must wait until this offset |
| 6 | +# becomes observable. Previously we used to wait for 1ms in a loop in this |
| 7 | +# case, but now we use CV for this. This test is exercising such a sleep. |
| 8 | + |
| 9 | +use strict; |
| 10 | +use warnings FATAL => 'all'; |
| 11 | + |
| 12 | +use PostgreSQL::Test::Cluster; |
| 13 | +use PostgreSQL::Test::Utils; |
| 14 | + |
| 15 | +use Test::More; |
| 16 | + |
| 17 | +if ($ENV{enable_injection_points} ne 'yes') |
| 18 | +{ |
| 19 | + plan skip_all => 'Injection points not supported by this build'; |
| 20 | +} |
| 21 | + |
| 22 | +my ($node, $result); |
| 23 | + |
| 24 | +$node = PostgreSQL::Test::Cluster->new('mike'); |
| 25 | +$node->init; |
| 26 | +$node->append_conf('postgresql.conf', |
| 27 | + "shared_preload_libraries = 'test_slru'"); |
| 28 | +$node->start; |
| 29 | +$node->safe_psql('postgres', q(CREATE EXTENSION injection_points)); |
| 30 | +$node->safe_psql('postgres', q(CREATE EXTENSION test_slru)); |
| 31 | + |
| 32 | +# Test for Multixact generation edge case |
| 33 | +$node->safe_psql('postgres', |
| 34 | + q{select injection_points_attach('test-multixact-read','wait')}); |
| 35 | +$node->safe_psql('postgres', |
| 36 | + q{select injection_points_attach('multixact-get-members-cv-sleep','wait')} |
| 37 | +); |
| 38 | + |
| 39 | +# This session must observe sleep on the condition variable while generating a |
| 40 | +# multixact. To achieve this it first will create a multixact, then pause |
| 41 | +# before reading it. |
| 42 | +my $observer = $node->background_psql('postgres'); |
| 43 | + |
| 44 | +# This query will create a multixact, and hang just before reading it. |
| 45 | +$observer->query_until( |
| 46 | + qr/start/, |
| 47 | + q{ |
| 48 | + \echo start |
| 49 | + SELECT test_read_multixact(test_create_multixact()); |
| 50 | +}); |
| 51 | +$node->wait_for_event('client backend', 'test-multixact-read'); |
| 52 | + |
| 53 | +# This session will create the next Multixact. This is necessary to avoid |
| 54 | +# multixact.c's non-sleeping edge case 1. |
| 55 | +my $creator = $node->background_psql('postgres'); |
| 56 | +$node->safe_psql('postgres', |
| 57 | + q{SELECT injection_points_attach('multixact-create-from-members','wait');} |
| 58 | +); |
| 59 | + |
| 60 | +# We expect this query to hang in the critical section after generating new |
| 61 | +# multixact, but before filling it's offset into SLRU. |
| 62 | +# Running an injection point inside a critical section requires it to be |
| 63 | +# loaded beforehand. |
| 64 | +$creator->query_until( |
| 65 | + qr/start/, q{ |
| 66 | + \echo start |
| 67 | + SELECT injection_points_load('multixact-create-from-members'); |
| 68 | + SELECT test_create_multixact(); |
| 69 | +}); |
| 70 | + |
| 71 | +$node->wait_for_event('client backend', 'multixact-create-from-members'); |
| 72 | + |
| 73 | +# Ensure we have the backends waiting that we expect |
| 74 | +is( $node->safe_psql( |
| 75 | + 'postgres', |
| 76 | + q{SELECT string_agg(wait_event, ', ' ORDER BY wait_event) |
| 77 | + FROM pg_stat_activity WHERE wait_event_type = 'InjectionPoint'} |
| 78 | + ), |
| 79 | + 'multixact-create-from-members, test-multixact-read', |
| 80 | + "matching injection point waits"); |
| 81 | + |
| 82 | +# Now wake observer to get it to read the initial multixact. A subsequent |
| 83 | +# multixact already exists, but that one doesn't have an offset assigned, so |
| 84 | +# this will hit multixact.c's edge case 2. |
| 85 | +$node->safe_psql('postgres', |
| 86 | + q{SELECT injection_points_wakeup('test-multixact-read')}); |
| 87 | +$node->wait_for_event('client backend', 'multixact-get-members-cv-sleep'); |
| 88 | + |
| 89 | +# Ensure we have the backends waiting that we expect |
| 90 | +is( $node->safe_psql( |
| 91 | + 'postgres', |
| 92 | + q{SELECT string_agg(wait_event, ', ' ORDER BY wait_event) |
| 93 | + FROM pg_stat_activity WHERE wait_event_type = 'InjectionPoint'} |
| 94 | + ), |
| 95 | + 'multixact-create-from-members, multixact-get-members-cv-sleep', |
| 96 | + "matching injection point waits"); |
| 97 | + |
| 98 | +# Now we have two backends waiting in multixact-create-from-members and |
| 99 | +# multixact-get-members-cv-sleep. Also we have 3 injections points set to wait. |
| 100 | +# If we wakeup multixact-get-members-cv-sleep it will happen again, so we must |
| 101 | +# detach it first. So let's detach all injection points, then wake up all |
| 102 | +# backends. |
| 103 | + |
| 104 | +$node->safe_psql('postgres', |
| 105 | + q{SELECT injection_points_detach('test-multixact-read')}); |
| 106 | +$node->safe_psql('postgres', |
| 107 | + q{SELECT injection_points_detach('multixact-create-from-members')}); |
| 108 | +$node->safe_psql('postgres', |
| 109 | + q{SELECT injection_points_detach('multixact-get-members-cv-sleep')}); |
| 110 | + |
| 111 | +$node->safe_psql('postgres', |
| 112 | + q{SELECT injection_points_wakeup('multixact-create-from-members')}); |
| 113 | +$node->safe_psql('postgres', |
| 114 | + q{SELECT injection_points_wakeup('multixact-get-members-cv-sleep')}); |
| 115 | + |
| 116 | +# Background psql will now be able to read the result and disconnect. |
| 117 | +$observer->quit; |
| 118 | +$creator->quit; |
| 119 | + |
| 120 | +$node->stop; |
| 121 | + |
| 122 | +# If we reached this point - everything is OK. |
| 123 | +ok(1); |
| 124 | +done_testing(); |
0 commit comments