mirror of https://github.com/postgres/postgres
There's some ongoing performance work on this area, so let's make sure we don't break things. Extracted from a larger patch originally by Stas Kelvich. Authors: Stas Kelvich, Nikhil Sontakke, Michael Paquier Discussion: https://postgr.es/m/CAMGcDxfsuLLOg=h5cTg3g77Jjk-UGnt=RW7zK57zBSoFsapiWA@mail.gmail.compull/17/merge
parent
74321d87fb
commit
30820982b2
@ -0,0 +1,322 @@ |
||||
# Tests dedicated to two-phase commit in recovery |
||||
use strict; |
||||
use warnings; |
||||
|
||||
use PostgresNode; |
||||
use TestLib; |
||||
use Test::More tests => 13; |
||||
|
||||
# Setup master node |
||||
my $node_master = get_new_node("master"); |
||||
$node_master->init(allows_streaming => 1); |
||||
$node_master->append_conf('postgresql.conf', qq( |
||||
max_prepared_transactions = 10 |
||||
log_checkpoints = true |
||||
)); |
||||
$node_master->start; |
||||
$node_master->backup('master_backup'); |
||||
$node_master->psql('postgres', "CREATE TABLE t_009_tbl (id int)"); |
||||
|
||||
# Setup slave node |
||||
my $node_slave = get_new_node('slave'); |
||||
$node_slave->init_from_backup($node_master, 'master_backup', has_streaming => 1); |
||||
$node_slave->start; |
||||
|
||||
# Switch to synchronous replication |
||||
$node_master->append_conf('postgresql.conf', qq( |
||||
synchronous_standby_names = '*' |
||||
)); |
||||
$node_master->psql('postgres', "SELECT pg_reload_conf()"); |
||||
|
||||
my $psql_out = ''; |
||||
my $psql_rc = ''; |
||||
|
||||
############################################################################### |
||||
# Check that we can commit and abort transaction after soft restart. |
||||
# Here checkpoint happens before shutdown and no WAL replay will occur at next |
||||
# startup. In this case postgres re-creates shared-memory state from twophase |
||||
# files. |
||||
############################################################################### |
||||
|
||||
$node_master->psql('postgres', " |
||||
BEGIN; |
||||
INSERT INTO t_009_tbl VALUES (42); |
||||
SAVEPOINT s1; |
||||
INSERT INTO t_009_tbl VALUES (43); |
||||
PREPARE TRANSACTION 'xact_009_1'; |
||||
BEGIN; |
||||
INSERT INTO t_009_tbl VALUES (142); |
||||
SAVEPOINT s1; |
||||
INSERT INTO t_009_tbl VALUES (143); |
||||
PREPARE TRANSACTION 'xact_009_2';"); |
||||
$node_master->stop; |
||||
$node_master->start; |
||||
|
||||
$psql_rc = $node_master->psql('postgres', "COMMIT PREPARED 'xact_009_1'"); |
||||
is($psql_rc, '0', 'Commit prepared transaction after restart'); |
||||
|
||||
$psql_rc = $node_master->psql('postgres', "ROLLBACK PREPARED 'xact_009_2'"); |
||||
is($psql_rc, '0', 'Rollback prepared transaction after restart'); |
||||
|
||||
############################################################################### |
||||
# Check that we can commit and abort after a hard restart. |
||||
# At next startup, WAL replay will re-create shared memory state for prepared |
||||
# transaction using dedicated WAL records. |
||||
############################################################################### |
||||
|
||||
$node_master->psql('postgres', " |
||||
CHECKPOINT; |
||||
BEGIN; |
||||
INSERT INTO t_009_tbl VALUES (42); |
||||
SAVEPOINT s1; |
||||
INSERT INTO t_009_tbl VALUES (43); |
||||
PREPARE TRANSACTION 'xact_009_1'; |
||||
BEGIN; |
||||
INSERT INTO t_009_tbl VALUES (142); |
||||
SAVEPOINT s1; |
||||
INSERT INTO t_009_tbl VALUES (143); |
||||
PREPARE TRANSACTION 'xact_009_2';"); |
||||
$node_master->teardown_node; |
||||
$node_master->start; |
||||
|
||||
$psql_rc = $node_master->psql('postgres', "COMMIT PREPARED 'xact_009_1'"); |
||||
is($psql_rc, '0', 'Commit prepared transaction after teardown'); |
||||
|
||||
$psql_rc = $node_master->psql('postgres', "ROLLBACK PREPARED 'xact_009_2'"); |
||||
is($psql_rc, '0', 'Rollback prepared transaction after teardown'); |
||||
|
||||
############################################################################### |
||||
# Check that WAL replay can handle several transactions with same GID name. |
||||
############################################################################### |
||||
|
||||
$node_master->psql('postgres', " |
||||
CHECKPOINT; |
||||
BEGIN; |
||||
INSERT INTO t_009_tbl VALUES (42); |
||||
SAVEPOINT s1; |
||||
INSERT INTO t_009_tbl VALUES (43); |
||||
PREPARE TRANSACTION 'xact_009_1'; |
||||
COMMIT PREPARED 'xact_009_1'; |
||||
BEGIN; |
||||
INSERT INTO t_009_tbl VALUES (42); |
||||
SAVEPOINT s1; |
||||
INSERT INTO t_009_tbl VALUES (43); |
||||
PREPARE TRANSACTION 'xact_009_1';"); |
||||
$node_master->teardown_node; |
||||
$node_master->start; |
||||
|
||||
$psql_rc = $node_master->psql('postgres', "COMMIT PREPARED 'xact_009_1'"); |
||||
is($psql_rc, '0', 'Replay several transactions with same GID'); |
||||
|
||||
############################################################################### |
||||
# Check that WAL replay cleans up its shared memory state and releases locks |
||||
# while replaying transaction commits. |
||||
############################################################################### |
||||
|
||||
$node_master->psql('postgres', " |
||||
BEGIN; |
||||
INSERT INTO t_009_tbl VALUES (42); |
||||
SAVEPOINT s1; |
||||
INSERT INTO t_009_tbl VALUES (43); |
||||
PREPARE TRANSACTION 'xact_009_1'; |
||||
COMMIT PREPARED 'xact_009_1';"); |
||||
$node_master->teardown_node; |
||||
$node_master->start; |
||||
$psql_rc = $node_master->psql('postgres', " |
||||
BEGIN; |
||||
INSERT INTO t_009_tbl VALUES (42); |
||||
SAVEPOINT s1; |
||||
INSERT INTO t_009_tbl VALUES (43); |
||||
-- This prepare can fail due to conflicting GID or locks conflicts if |
||||
-- replay did not fully cleanup its state on previous commit. |
||||
PREPARE TRANSACTION 'xact_009_1';"); |
||||
is($psql_rc, '0', "Cleanup of shared memory state for 2PC commit"); |
||||
|
||||
$node_master->psql('postgres', "COMMIT PREPARED 'xact_009_1'"); |
||||
|
||||
############################################################################### |
||||
# Check that WAL replay will cleanup its shared memory state on running slave. |
||||
############################################################################### |
||||
|
||||
$node_master->psql('postgres', " |
||||
BEGIN; |
||||
INSERT INTO t_009_tbl VALUES (42); |
||||
SAVEPOINT s1; |
||||
INSERT INTO t_009_tbl VALUES (43); |
||||
PREPARE TRANSACTION 'xact_009_1'; |
||||
COMMIT PREPARED 'xact_009_1';"); |
||||
$node_slave->psql('postgres', "SELECT count(*) FROM pg_prepared_xacts", |
||||
stdout => \$psql_out); |
||||
is($psql_out, '0', |
||||
"Cleanup of shared memory state on running standby without checkpoint"); |
||||
|
||||
############################################################################### |
||||
# Same as in previous case, but let's force checkpoint on slave between |
||||
# prepare and commit to use on-disk twophase files. |
||||
############################################################################### |
||||
|
||||
$node_master->psql('postgres', " |
||||
BEGIN; |
||||
INSERT INTO t_009_tbl VALUES (42); |
||||
SAVEPOINT s1; |
||||
INSERT INTO t_009_tbl VALUES (43); |
||||
PREPARE TRANSACTION 'xact_009_1';"); |
||||
$node_slave->psql('postgres', "CHECKPOINT"); |
||||
$node_master->psql('postgres', "COMMIT PREPARED 'xact_009_1'"); |
||||
$node_slave->psql('postgres', "SELECT count(*) FROM pg_prepared_xacts", |
||||
stdout => \$psql_out); |
||||
is($psql_out, '0', |
||||
"Cleanup of shared memory state on running standby after checkpoint"); |
||||
|
||||
############################################################################### |
||||
# Check that prepared transactions can be committed on promoted slave. |
||||
############################################################################### |
||||
|
||||
$node_master->psql('postgres', " |
||||
BEGIN; |
||||
INSERT INTO t_009_tbl VALUES (42); |
||||
SAVEPOINT s1; |
||||
INSERT INTO t_009_tbl VALUES (43); |
||||
PREPARE TRANSACTION 'xact_009_1';"); |
||||
$node_master->teardown_node; |
||||
$node_slave->promote; |
||||
$node_slave->poll_query_until('postgres', |
||||
"SELECT NOT pg_is_in_recovery()") |
||||
or die "Timed out while waiting for promotion of standby"; |
||||
|
||||
$psql_rc = $node_slave->psql('postgres', "COMMIT PREPARED 'xact_009_1'"); |
||||
is($psql_rc, '0', "Restore of prepared transaction on promoted slave"); |
||||
|
||||
# change roles |
||||
($node_master, $node_slave) = ($node_slave, $node_master); |
||||
$node_slave->enable_streaming($node_master); |
||||
$node_slave->append_conf('recovery.conf', qq( |
||||
recovery_target_timeline='latest' |
||||
)); |
||||
$node_slave->start; |
||||
|
||||
############################################################################### |
||||
# Check that prepared transactions are replayed after soft restart of standby |
||||
# while master is down. Since standby knows that master is down it uses a |
||||
# different code path on startup to ensure that the status of transactions is |
||||
# consistent. |
||||
############################################################################### |
||||
|
||||
$node_master->psql('postgres', " |
||||
BEGIN; |
||||
INSERT INTO t_009_tbl VALUES (42); |
||||
SAVEPOINT s1; |
||||
INSERT INTO t_009_tbl VALUES (43); |
||||
PREPARE TRANSACTION 'xact_009_1';"); |
||||
$node_master->stop; |
||||
$node_slave->restart; |
||||
$node_slave->promote; |
||||
$node_slave->poll_query_until('postgres', |
||||
"SELECT NOT pg_is_in_recovery()") |
||||
or die "Timed out while waiting for promotion of standby"; |
||||
|
||||
$node_slave->psql('postgres', "SELECT count(*) FROM pg_prepared_xacts", |
||||
stdout => \$psql_out); |
||||
is($psql_out, '1', |
||||
"Restore prepared transactions from files with master down"); |
||||
|
||||
# restore state |
||||
($node_master, $node_slave) = ($node_slave, $node_master); |
||||
$node_slave->enable_streaming($node_master); |
||||
$node_slave->append_conf('recovery.conf', qq( |
||||
recovery_target_timeline='latest' |
||||
)); |
||||
$node_slave->start; |
||||
$node_master->psql('postgres', "COMMIT PREPARED 'xact_009_1'"); |
||||
|
||||
############################################################################### |
||||
# Check that prepared transactions are correctly replayed after slave hard |
||||
# restart while master is down. |
||||
############################################################################### |
||||
|
||||
$node_master->psql('postgres', " |
||||
BEGIN; |
||||
INSERT INTO t_009_tbl VALUES (242); |
||||
SAVEPOINT s1; |
||||
INSERT INTO t_009_tbl VALUES (243); |
||||
PREPARE TRANSACTION 'xact_009_1'; |
||||
"); |
||||
$node_master->stop; |
||||
$node_slave->teardown_node; |
||||
$node_slave->start; |
||||
$node_slave->promote; |
||||
$node_slave->poll_query_until('postgres', |
||||
"SELECT NOT pg_is_in_recovery()") |
||||
or die "Timed out while waiting for promotion of standby"; |
||||
|
||||
$node_slave->psql('postgres', "SELECT count(*) FROM pg_prepared_xacts", |
||||
stdout => \$psql_out); |
||||
is($psql_out, '1', |
||||
"Restore prepared transactions from records with master down"); |
||||
|
||||
# restore state |
||||
($node_master, $node_slave) = ($node_slave, $node_master); |
||||
$node_slave->enable_streaming($node_master); |
||||
$node_slave->append_conf('recovery.conf', qq( |
||||
recovery_target_timeline='latest' |
||||
)); |
||||
$node_slave->start; |
||||
$node_master->psql('postgres', "COMMIT PREPARED 'xact_009_1'"); |
||||
|
||||
|
||||
############################################################################### |
||||
# Check for a lock conflict between prepared transaction with DDL inside and replay of |
||||
# XLOG_STANDBY_LOCK wal record. |
||||
############################################################################### |
||||
|
||||
$node_master->psql('postgres', " |
||||
BEGIN; |
||||
CREATE TABLE t_009_tbl2 (id int); |
||||
SAVEPOINT s1; |
||||
INSERT INTO t_009_tbl2 VALUES (42); |
||||
PREPARE TRANSACTION 'xact_009_1'; |
||||
-- checkpoint will issue XLOG_STANDBY_LOCK that can conflict with lock |
||||
-- held by 'create table' statement |
||||
CHECKPOINT; |
||||
COMMIT PREPARED 'xact_009_1';"); |
||||
|
||||
$node_slave->psql('postgres', "SELECT count(*) FROM pg_prepared_xacts", |
||||
stdout => \$psql_out); |
||||
is($psql_out, '0', "Replay prepared transaction with DDL"); |
||||
|
||||
|
||||
############################################################################### |
||||
# Check that replay will correctly set SUBTRANS and properly advance nextXid |
||||
# so that it won't conflict with savepoint xids. |
||||
############################################################################### |
||||
|
||||
$node_master->psql('postgres', " |
||||
BEGIN; |
||||
DELETE FROM t_009_tbl; |
||||
INSERT INTO t_009_tbl VALUES (43); |
||||
SAVEPOINT s1; |
||||
INSERT INTO t_009_tbl VALUES (43); |
||||
SAVEPOINT s2; |
||||
INSERT INTO t_009_tbl VALUES (43); |
||||
SAVEPOINT s3; |
||||
INSERT INTO t_009_tbl VALUES (43); |
||||
SAVEPOINT s4; |
||||
INSERT INTO t_009_tbl VALUES (43); |
||||
SAVEPOINT s5; |
||||
INSERT INTO t_009_tbl VALUES (43); |
||||
PREPARE TRANSACTION 'xact_009_1'; |
||||
CHECKPOINT;"); |
||||
|
||||
$node_master->stop; |
||||
$node_master->start; |
||||
$node_master->psql('postgres', " |
||||
-- here we can get xid of previous savepoint if nextXid |
||||
-- wasn't properly advanced |
||||
BEGIN; |
||||
INSERT INTO t_009_tbl VALUES (142); |
||||
ROLLBACK; |
||||
COMMIT PREPARED 'xact_009_1';"); |
||||
|
||||
$node_master->psql('postgres', "SELECT count(*) FROM t_009_tbl", |
||||
stdout => \$psql_out); |
||||
is($psql_out, '6', "Check nextXid handling for prepared subtransactions"); |
||||
Loading…
Reference in new issue