ALTER SYSTEM SET citus.recover_2pc_interval TO -1; SELECT pg_reload_conf(); pg_reload_conf --------------------------------------------------------------------- t (1 row) SELECT $definition$ CREATE OR REPLACE FUNCTION test.maintenance_worker() RETURNS pg_stat_activity LANGUAGE plpgsql AS $$ DECLARE activity record; BEGIN DO 'BEGIN END'; -- Force maintenance daemon to start -- we don't want to wait forever; loop will exit after 20 seconds FOR i IN 1 .. 200 LOOP PERFORM pg_stat_clear_snapshot(); SELECT * INTO activity FROM pg_stat_activity WHERE application_name = 'Citus Maintenance Daemon' AND datname = current_database(); IF activity.pid IS NOT NULL THEN RETURN activity; ELSE PERFORM pg_sleep(0.1); END IF ; END LOOP; -- fail if we reach the end of this loop raise 'Waited too long for maintenance daemon to start'; END; $$; $definition$ create_function_test_maintenance_worker \gset CREATE DATABASE db1; NOTICE: Citus partially supports CREATE DATABASE for distributed databases DETAIL: Citus does not propagate CREATE DATABASE command to other nodes HINT: You can manually create a database and its extensions on other nodes. SELECT oid AS db1_oid FROM pg_database WHERE datname = 'db1' \gset \c - - - :worker_1_port CREATE DATABASE db1; NOTICE: Citus partially supports CREATE DATABASE for distributed databases DETAIL: Citus does not propagate CREATE DATABASE command to other nodes HINT: You can manually create a database and its extensions on other nodes. \c - - - :worker_2_port CREATE DATABASE db1; NOTICE: Citus partially supports CREATE DATABASE for distributed databases DETAIL: Citus does not propagate CREATE DATABASE command to other nodes HINT: You can manually create a database and its extensions on other nodes. \c db1 - - :worker_1_port CREATE EXTENSION citus; \c db1 - - :worker_2_port CREATE EXTENSION citus; \c db1 - - :master_port CREATE EXTENSION citus; SELECT citus_add_node('localhost', :worker_1_port); citus_add_node --------------------------------------------------------------------- 1 (1 row) SELECT citus_add_node('localhost', :worker_2_port); citus_add_node --------------------------------------------------------------------- 2 (1 row) SELECT current_database(); current_database --------------------------------------------------------------------- db1 (1 row) CREATE SCHEMA test; :create_function_test_maintenance_worker -- check maintenance daemon is started SELECT datname, current_database(), usename, (SELECT extowner::regrole::text FROM pg_extension WHERE extname = 'citus') FROM test.maintenance_worker(); datname | current_database | usename | extowner --------------------------------------------------------------------- db1 | db1 | postgres | postgres (1 row) SELECT * FROM pg_dist_node; nodeid | groupid | nodename | nodeport | noderack | hasmetadata | isactive | noderole | nodecluster | metadatasynced | shouldhaveshards --------------------------------------------------------------------- 1 | 1 | localhost | 57637 | default | t | t | primary | default | t | t 2 | 2 | localhost | 57638 | default | t | t | primary | default | t | t (2 rows) CREATE DATABASE db2; NOTICE: Citus partially supports CREATE DATABASE for distributed databases DETAIL: Citus does not propagate CREATE DATABASE command to other nodes HINT: You can manually create a database and its extensions on other nodes. SELECT oid AS db2_oid FROM pg_database WHERE datname = 'db2' \gset \c - - - :worker_1_port CREATE DATABASE db2; NOTICE: Citus partially supports CREATE DATABASE for distributed databases DETAIL: Citus does not propagate CREATE DATABASE command to other nodes HINT: You can manually create a database and its extensions on other nodes. \c - - - :worker_2_port CREATE DATABASE db2; NOTICE: Citus partially supports CREATE DATABASE for distributed databases DETAIL: Citus does not propagate CREATE DATABASE command to other nodes HINT: You can manually create a database and its extensions on other nodes. \c db2 - - :worker_1_port CREATE EXTENSION citus; \c db2 - - :worker_2_port CREATE EXTENSION citus; \c db2 - - :master_port CREATE EXTENSION citus; SELECT citus_add_node('localhost', :worker_1_port); citus_add_node --------------------------------------------------------------------- 1 (1 row) SELECT citus_add_node('localhost', :worker_2_port); citus_add_node --------------------------------------------------------------------- 2 (1 row) SELECT current_database(); current_database --------------------------------------------------------------------- db2 (1 row) CREATE SCHEMA test; :create_function_test_maintenance_worker -- check maintenance daemon is started SELECT datname, current_database(), usename, (SELECT extowner::regrole::text FROM pg_extension WHERE extname = 'citus') FROM test.maintenance_worker(); datname | current_database | usename | extowner --------------------------------------------------------------------- db2 | db2 | postgres | postgres (1 row) SELECT * FROM pg_dist_node; nodeid | groupid | nodename | nodeport | noderack | hasmetadata | isactive | noderole | nodecluster | metadatasynced | shouldhaveshards --------------------------------------------------------------------- 1 | 1 | localhost | 57637 | default | t | t | primary | default | t | t 2 | 2 | localhost | 57638 | default | t | t | primary | default | t | t (2 rows) SELECT groupid AS worker_1_group_id FROM pg_dist_node WHERE nodeport = :worker_1_port; worker_1_group_id --------------------------------------------------------------------- 1 (1 row) \gset SELECT groupid AS worker_2_group_id FROM pg_dist_node WHERE nodeport = :worker_2_port; worker_2_group_id --------------------------------------------------------------------- 2 (1 row) \gset -- Prepare transactions on first database \c db1 - - :worker_1_port BEGIN; CREATE TABLE should_abort ( value int ); SELECT 'citus_0_1234_0_0_' || :'db1_oid' AS transaction_1_worker_1_db_1_name \gset PREPARE TRANSACTION :'transaction_1_worker_1_db_1_name'; BEGIN; CREATE TABLE should_commit ( value int ); SELECT 'citus_0_1234_1_0_' || :'db1_oid' AS transaction_2_worker_1_db_1_name \gset PREPARE TRANSACTION :'transaction_2_worker_1_db_1_name'; \c db1 - - :worker_2_port BEGIN; CREATE TABLE should_abort ( value int ); SELECT 'citus_0_1234_0_0_' || :'db1_oid' AS transaction_1_worker_2_db_1_name \gset PREPARE TRANSACTION :'transaction_1_worker_2_db_1_name'; BEGIN; CREATE TABLE should_commit ( value int ); SELECT 'citus_0_1234_1_0_' || :'db1_oid' AS transaction_2_worker_2_db_1_name \gset PREPARE TRANSACTION :'transaction_2_worker_2_db_1_name'; -- Prepare transactions on second database \c db2 - - :worker_1_port BEGIN; CREATE TABLE should_abort ( value int ); SELECT 'citus_0_1234_3_0_' || :'db2_oid' AS transaction_1_worker_1_db_2_name \gset PREPARE TRANSACTION :'transaction_1_worker_1_db_2_name'; BEGIN; CREATE TABLE should_commit ( value int ); SELECT 'citus_0_1234_4_0_' || :'db2_oid' AS transaction_2_worker_1_db_2_name \gset PREPARE TRANSACTION :'transaction_2_worker_1_db_2_name'; \c db2 - - :worker_2_port BEGIN; CREATE TABLE should_abort ( value int ); SELECT 'citus_0_1234_3_0_' || :'db2_oid' AS transaction_1_worker_2_db_2_name \gset PREPARE TRANSACTION :'transaction_1_worker_2_db_2_name'; BEGIN; CREATE TABLE should_commit ( value int ); SELECT 'citus_0_1234_4_0_' || :'db2_oid' AS transaction_2_worker_2_db_2_name \gset PREPARE TRANSACTION :'transaction_2_worker_2_db_2_name'; \c db1 - - :master_port INSERT INTO pg_dist_transaction VALUES (:worker_1_group_id, :'transaction_2_worker_1_db_1_name'), (:worker_2_group_id, :'transaction_2_worker_2_db_1_name'); INSERT INTO pg_dist_transaction VALUES (:worker_1_group_id, 'citus_0_should_be_forgotten_' || :'db1_oid'), (:worker_2_group_id, 'citus_0_should_be_forgotten_' || :'db1_oid'); \c db2 - - :master_port INSERT INTO pg_dist_transaction VALUES (:worker_1_group_id, :'transaction_2_worker_1_db_2_name'), (:worker_2_group_id, :'transaction_2_worker_2_db_2_name'); INSERT INTO pg_dist_transaction VALUES (:worker_1_group_id, 'citus_0_should_be_forgotten_' || :'db2_oid'), (:worker_2_group_id, 'citus_0_should_be_forgotten_' || :'db2_oid'); \c db1 - - :master_port SELECT count(*) != 0 FROM pg_dist_transaction; ?column? --------------------------------------------------------------------- t (1 row) SELECT recover_prepared_transactions() > 0; ?column? --------------------------------------------------------------------- t (1 row) SELECT count(*) = 0 FROM pg_dist_transaction; ?column? --------------------------------------------------------------------- t (1 row) \c db2 - - :master_port SELECT count(*) != 0 FROM pg_dist_transaction; ?column? --------------------------------------------------------------------- t (1 row) SELECT recover_prepared_transactions() > 0; ?column? --------------------------------------------------------------------- t (1 row) SELECT count(*) = 0 FROM pg_dist_transaction; ?column? --------------------------------------------------------------------- t (1 row) \c regression - - :master_port SELECT count(pg_terminate_backend(pid)) > 0 FROM pg_stat_activity WHERE pid <> pg_backend_pid() AND datname = 'db1' ; ?column? --------------------------------------------------------------------- t (1 row) DROP DATABASE db1; SELECT count(pg_terminate_backend(pid)) > 0 FROM pg_stat_activity WHERE pid <> pg_backend_pid() AND datname = 'db2' ; ?column? --------------------------------------------------------------------- t (1 row) DROP DATABASE db2; \c - - - :worker_1_port SELECT count(pg_terminate_backend(pid)) > 0 FROM pg_stat_activity WHERE pid <> pg_backend_pid() AND datname = 'db1' ; ?column? --------------------------------------------------------------------- t (1 row) DROP DATABASE db1; SELECT count(pg_terminate_backend(pid)) > 0 FROM pg_stat_activity WHERE pid <> pg_backend_pid() AND datname = 'db2' ; ?column? --------------------------------------------------------------------- t (1 row) DROP DATABASE db2; \c - - - :worker_2_port -- Count of terminated sessions is not important for the test, -- it is just to make output predictable SELECT count(pg_terminate_backend(pid)) >= 0 FROM pg_stat_activity WHERE pid <> pg_backend_pid() AND datname = 'db1' ; ?column? --------------------------------------------------------------------- t (1 row) DROP DATABASE db1; SELECT count(pg_terminate_backend(pid)) >= 0 FROM pg_stat_activity WHERE pid <> pg_backend_pid() AND datname = 'db2' ; ?column? --------------------------------------------------------------------- t (1 row) DROP DATABASE db2;