From e188d6dc6c5bc0dea5ff33692b0f44a607f8e855 Mon Sep 17 00:00:00 2001 From: Andres Freund Date: Sat, 21 Jan 2023 13:27:40 -0800 Subject: [PATCH v4 6/6] wip: test that postgres_fdw can be interrupted --- contrib/postgres_fdw/Makefile | 1 + contrib/postgres_fdw/expected/interrupt.out | 95 +++++++++++++++++++++ contrib/postgres_fdw/meson.build | 5 ++ contrib/postgres_fdw/specs/interrupt.spec | 68 +++++++++++++++ 4 files changed, 169 insertions(+) create mode 100644 contrib/postgres_fdw/expected/interrupt.out create mode 100644 contrib/postgres_fdw/specs/interrupt.spec diff --git a/contrib/postgres_fdw/Makefile b/contrib/postgres_fdw/Makefile index c1b0cad453f..eb141442dee 100644 --- a/contrib/postgres_fdw/Makefile +++ b/contrib/postgres_fdw/Makefile @@ -17,6 +17,7 @@ EXTENSION = postgres_fdw DATA = postgres_fdw--1.0.sql postgres_fdw--1.0--1.1.sql REGRESS = postgres_fdw +ISOLATION = interrupt ifdef USE_PGXS PG_CONFIG = pg_config diff --git a/contrib/postgres_fdw/expected/interrupt.out b/contrib/postgres_fdw/expected/interrupt.out new file mode 100644 index 00000000000..fe32e8ea27b --- /dev/null +++ b/contrib/postgres_fdw/expected/interrupt.out @@ -0,0 +1,95 @@ +Parsed test spec with 3 sessions + +starting permutation: s2_begin s2_hang_logins s1_select_remote s3_cancel_s1 s2_commit +step s2_begin: BEGIN; +step s2_hang_logins: LOCK pg_db_role_setting; +step s1_select_remote: SELECT count(*) FROM remote_t1; +step s3_cancel_s1: + SELECT wait_for_s1(); + SELECT pg_cancel_backend(pid) FROM pg_stat_activity WHERE datname = current_database() AND application_name = 'isolation/interrupt/s1'; + +wait_for_s1 +----------- + +(1 row) + +pg_cancel_backend +----------------- +t +(1 row) + +step s1_select_remote: <... completed> +ERROR: canceling statement due to user request +step s2_commit: COMMIT; + +starting permutation: s2_begin s2_hang_logins s1_select_remote s2_commit +step s2_begin: BEGIN; +step s2_hang_logins: LOCK pg_db_role_setting; +step s1_select_remote: SELECT count(*) FROM remote_t1; +step s2_commit: COMMIT; +step s1_select_remote: <... completed> +count +----- + 0 +(1 row) + + +starting permutation: s2_begin s2_hang_select s1_select_remote s3_cancel_s1 s2_commit +step s2_begin: BEGIN; +step s2_hang_select: LOCK local_t1; +step s1_select_remote: SELECT count(*) FROM remote_t1; +step s3_cancel_s1: + SELECT wait_for_s1(); + SELECT pg_cancel_backend(pid) FROM pg_stat_activity WHERE datname = current_database() AND application_name = 'isolation/interrupt/s1'; + +wait_for_s1 +----------- + +(1 row) + +pg_cancel_backend +----------------- +t +(1 row) + +step s1_select_remote: <... completed> +ERROR: canceling statement due to user request +step s2_commit: COMMIT; + +starting permutation: s2_begin s2_hang_select s1_select_remote s2_commit +step s2_begin: BEGIN; +step s2_hang_select: LOCK local_t1; +step s1_select_remote: SELECT count(*) FROM remote_t1; +step s2_commit: COMMIT; +step s1_select_remote: <... completed> +count +----- + 0 +(1 row) + + +starting permutation: s2_begin s2_hang_logins s1_select_remote s3_terminate_s1 s2_commit +step s2_begin: BEGIN; +step s2_hang_logins: LOCK pg_db_role_setting; +step s1_select_remote: SELECT count(*) FROM remote_t1; +step s3_terminate_s1: + SELECT wait_for_s1(); + SELECT pg_terminate_backend(pid) FROM pg_stat_activity WHERE datname = current_database() AND application_name = 'isolation/interrupt/s1'; + +wait_for_s1 +----------- + +(1 row) + +pg_terminate_backend +-------------------- +t +(1 row) + +step s1_select_remote: <... completed> +FATAL: terminating connection due to administrator command +server closed the connection unexpectedly + This probably means the server terminated abnormally + before or while processing the request. + +step s2_commit: COMMIT; diff --git a/contrib/postgres_fdw/meson.build b/contrib/postgres_fdw/meson.build index 2b451f165e1..4ae5e0e2f9b 100644 --- a/contrib/postgres_fdw/meson.build +++ b/contrib/postgres_fdw/meson.build @@ -39,4 +39,9 @@ tests += { ], 'regress_args': ['--dlpath', meson.build_root() / 'src/test/regress'], }, + 'isolation': { + 'specs': [ + 'interrupt', + ], + }, } diff --git a/contrib/postgres_fdw/specs/interrupt.spec b/contrib/postgres_fdw/specs/interrupt.spec new file mode 100644 index 00000000000..5b4d77450d7 --- /dev/null +++ b/contrib/postgres_fdw/specs/interrupt.spec @@ -0,0 +1,68 @@ +setup { + CREATE EXTENSION postgres_fdw; + + DO $d$ + BEGIN + EXECUTE $$CREATE SERVER loopback FOREIGN DATA WRAPPER postgres_fdw + OPTIONS (dbname '$$||current_database()||$$', + port '$$||current_setting('port')||$$' + )$$; + END; + $d$; + + CREATE USER MAPPING FOR CURRENT_USER SERVER loopback; + + CREATE TABLE local_t1 (id int); + CREATE FOREIGN TABLE remote_t1 (id int) SERVER loopback OPTIONS (table_name 'local_t1'); + + -- wait until postgres_fdw is waiting, isolation tester can't see that wait edge + CREATE FUNCTION wait_for_s1() RETURNS void LANGUAGE plpgsql AS $f$ + BEGIN + WHILE NOT EXISTS( + SELECT * FROM pg_stat_activity WHERE datname = current_database() AND application_name = 'isolation/interrupt/s1' AND wait_event = 'Extension') + LOOP + -- don't use too much CPU + PERFORM pg_sleep(0.05); + END LOOP; + END; + $f$ +} + +teardown { + DROP EXTENSION postgres_fdw CASCADE; + DROP TABLE local_t1; + DROP FUNCTION wait_for_s1(); +} + +session "s1" +step s1_select_remote {SELECT count(*) FROM remote_t1;} + +session "s2" +step s2_begin { BEGIN; } +step s2_hang_logins { LOCK pg_db_role_setting; } +step s2_hang_select { LOCK local_t1; } +step s2_commit { COMMIT; } + +session "s3" +step s3_cancel_s1 { + SELECT wait_for_s1(); + SELECT pg_cancel_backend(pid) FROM pg_stat_activity WHERE datname = current_database() AND application_name = 'isolation/interrupt/s1'; +} + +step s3_terminate_s1 { + SELECT wait_for_s1(); + SELECT pg_terminate_backend(pid) FROM pg_stat_activity WHERE datname = current_database() AND application_name = 'isolation/interrupt/s1'; +} + +# test that postgres_fdw can be interrupted during connection establishment +permutation s2_begin s2_hang_logins s1_select_remote(*) s3_cancel_s1 s2_commit + +# for completeness: verify that committing allows to continue too +permutation s2_begin s2_hang_logins s1_select_remote(*) s2_commit + +# test that statements can be interrupted +permutation s2_begin s2_hang_select s1_select_remote(*) s3_cancel_s1 s2_commit +permutation s2_begin s2_hang_select s1_select_remote(*) s2_commit + +# test that terminations work - needs to be last, as the connection is gone afterwards +permutation s2_begin s2_hang_logins s1_select_remote(*) s3_terminate_s1 s2_commit -- 2.38.0