From 1a87d755258cf96d82c67fca18b3db97980af237 Mon Sep 17 00:00:00 2001 From: "suyu.cmj" Date: Wed, 12 Jul 2023 04:05:48 +0000 Subject: [PATCH] Reproduce the error that the same 2pc may be added repeatedly --- src/backend/access/transam/xlog.c | 10 +++ src/backend/postmaster/checkpointer.c | 51 ++++++++++++++ src/include/catalog/pg_proc.dat | 8 +++ src/include/postmaster/bgwriter.h | 3 + .../037_add_duplicate_2pc_during_recovery.pl | 68 +++++++++++++++++++ 5 files changed, 140 insertions(+) create mode 100644 src/test/recovery/t/037_add_duplicate_2pc_during_recovery.pl diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c index 8b0710abe6..1e91f1e342 100644 --- a/src/backend/access/transam/xlog.c +++ b/src/backend/access/transam/xlog.c @@ -6624,6 +6624,11 @@ CreateCheckPoint(int flags) TRACE_POSTGRESQL_CHECKPOINT_START(flags); + /* Test inject fault */ + while (IsCheckpointDelayed()) + pg_usleep(1000000); + /* Test inject fault end */ + /* * Get the other info we need for the checkpoint record. * @@ -7039,6 +7044,11 @@ CheckPointGuts(XLogRecPtr checkPointRedo, int flags) /* We deliberately delay 2PC checkpointing as long as possible */ CheckPointTwoPhase(checkPointRedo); + + /* Test inject fault */ + if (IsCheckpointPanicInjected()) + elog(PANIC, "panic is injected during checkpoint"); + /* Test inject fault end */ } /* diff --git a/src/backend/postmaster/checkpointer.c b/src/backend/postmaster/checkpointer.c index ace9893d95..161f98a1a3 100644 --- a/src/backend/postmaster/checkpointer.c +++ b/src/backend/postmaster/checkpointer.c @@ -122,6 +122,9 @@ typedef struct int ckpt_flags; /* checkpoint flags, as defined in xlog.h */ + bool is_ckpt_delayed; /* add for test */ + bool is_ckpt_panic_injected; /* add for test */ + ConditionVariable start_cv; /* signaled when ckpt_started advances */ ConditionVariable done_cv; /* signaled when ckpt_done advances */ @@ -1351,3 +1354,51 @@ FirstCallSinceLastCheckpoint(void) return FirstCall; } + +bool +IsCheckpointDelayed(void) +{ + bool is_delayed = false; + + SpinLockAcquire(&CheckpointerShmem->ckpt_lck); + is_delayed = CheckpointerShmem->is_ckpt_delayed; + SpinLockRelease(&CheckpointerShmem->ckpt_lck); + + return is_delayed; +} + +bool +IsCheckpointPanicInjected(void) +{ + bool inject_panic = false; + + SpinLockAcquire(&CheckpointerShmem->ckpt_lck); + inject_panic = CheckpointerShmem->is_ckpt_panic_injected; + SpinLockRelease(&CheckpointerShmem->ckpt_lck); + + return inject_panic; +} + +Datum +pg_test_inject_checkpoint_delay(PG_FUNCTION_ARGS) +{ + bool set_delay = PG_GETARG_BOOL(0); + + SpinLockAcquire(&CheckpointerShmem->ckpt_lck); + CheckpointerShmem->is_ckpt_delayed = set_delay; + SpinLockRelease(&CheckpointerShmem->ckpt_lck); + + PG_RETURN_VOID(); +} + +Datum +pg_test_inject_checkpoint_panic(PG_FUNCTION_ARGS) +{ + bool inject_panic = PG_GETARG_BOOL(0); + + SpinLockAcquire(&CheckpointerShmem->ckpt_lck); + CheckpointerShmem->is_ckpt_panic_injected = inject_panic; + SpinLockRelease(&CheckpointerShmem->ckpt_lck); + + PG_RETURN_VOID(); +} \ No newline at end of file diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat index 6996073989..8e3d9c5ea3 100644 --- a/src/include/catalog/pg_proc.dat +++ b/src/include/catalog/pg_proc.dat @@ -12043,4 +12043,12 @@ proname => 'any_value_transfn', prorettype => 'anyelement', proargtypes => 'anyelement anyelement', prosrc => 'any_value_transfn' }, +{ oid => '6393', descr => 'inject delay during checkpoint', + proname => 'pg_test_inject_checkpoint_delay', provolatile => 'v', prorettype => 'void', + proargtypes => 'bool', prosrc => 'pg_test_inject_checkpoint_delay' }, + +{ oid => '6394', descr => 'inject panic during checkpoint', + proname => 'pg_test_inject_checkpoint_panic', provolatile => 'v', prorettype => 'void', + proargtypes => 'bool', prosrc => 'pg_test_inject_checkpoint_panic' }, + ] diff --git a/src/include/postmaster/bgwriter.h b/src/include/postmaster/bgwriter.h index a66722873f..3b845680ff 100644 --- a/src/include/postmaster/bgwriter.h +++ b/src/include/postmaster/bgwriter.h @@ -42,4 +42,7 @@ extern void CheckpointerShmemInit(void); extern bool FirstCallSinceLastCheckpoint(void); +extern bool IsCheckpointDelayed(void); +extern bool IsCheckpointPanicInjected(void); + #endif /* _BGWRITER_H */ diff --git a/src/test/recovery/t/037_add_duplicate_2pc_during_recovery.pl b/src/test/recovery/t/037_add_duplicate_2pc_during_recovery.pl new file mode 100644 index 0000000000..9896c65bf9 --- /dev/null +++ b/src/test/recovery/t/037_add_duplicate_2pc_during_recovery.pl @@ -0,0 +1,68 @@ + +# Copyright (c) 2021-2023, PostgreSQL Global Development Group + +# Tests add duplicate two-phase transaction during recovery +use strict; +use warnings; + +use PostgreSQL::Test::Cluster; +use PostgreSQL::Test::Utils; +use Test::More tests => 1; + +# Setup primary node +my $node_primary = PostgreSQL::Test::Cluster->new("primary"); +$node_primary->init; +$node_primary->append_conf( + 'postgresql.conf', qq( + max_prepared_transactions = 10 + log_checkpoints = true + checkpoint_timeout = 3000 +)); +$node_primary->start; + +$node_primary->psql('postgres', "create table t_test_2pc(id int, msg text)"); +$node_primary->psql('postgres', "select pg_test_inject_checkpoint_delay(true)"); + +# start do checkpoint1 +my $host = $node_primary->host; +my $port = $node_primary->port; +`nohup psql -h $host -p $port -c 'checkpoint' >tmp_check/test.file 2>&1 &`; + +# prepare transaction during checkpoint1 +$node_primary->psql( + 'postgres', " + BEGIN; + INSERT INTO t_test_2pc VALUES (1, 'test add duplicate 2pc'); + PREPARE TRANSACTION 'xact_test_1';"); + +# reset delay flag to finish checkpoint1 +$node_primary->psql('postgres', "select pg_test_inject_checkpoint_delay(false)"); + +my $logdir = $node_primary->logfile; +my $found = 0; +my @result; +while ($found == 0) +{ + @result = `grep -rn "checkpoint complete" $logdir`; + $found = @result; +} +print "checkpoint1 finished\n"; + +# inject panic during checkpoint2 +$node_primary->psql('postgres', "select pg_test_inject_checkpoint_panic(true)"); +$node_primary->psql('postgres', "checkpoint"); + +$found = 0; +while ($found == 0) +{ + @result = `grep -rn "terminating any other active server processes" $logdir`; + $found = @result; +} + +@result = `grep -rn "panic is injected during checkpoint" $logdir`; +$found = @result; +ok($found == 1, "inject panic success"); + +# start will fail due to error in lock_twophase_recover() +$node_primary->_update_pid(0); +$node_primary->start; \ No newline at end of file -- 2.19.1.6.gb485710b