mirror of
https://github.com/timescale/timescaledb.git
synced 2025-05-16 02:23:49 +08:00
A new health check function _timescaledb_internal.health() returns the health and status of the database instance, including any configured data nodes (in case the instance is an access node). Since the function returns also the health of the data nodes, it tries hard to avoid throwing errors. An error will fail the whole function and therefore not return any node statuses, although some of the nodes might be healthy. The health check on the data nodes is a recursive (remote) call to the same function on those nodes. Unfortunately, the check will fail with an error if a connection cannot be established to a node (or an error occurs on the connection), which means the whole function call will fail. This will be addressed in a future change by returning the error in the function result instead.
281 lines
9.6 KiB
PL/PgSQL
281 lines
9.6 KiB
PL/PgSQL
-- This file and its contents are licensed under the Timescale License.
|
|
-- Please see the included NOTICE for copyright information and
|
|
-- LICENSE-TIMESCALE for a copy of the license.
|
|
|
|
\c :TEST_DBNAME :ROLE_CLUSTER_SUPERUSER
|
|
|
|
-- Support for execute_sql_and_filter_server_name_on_error()
|
|
\unset ECHO
|
|
\o /dev/null
|
|
\ir include/remote_exec.sql
|
|
\ir include/filter_exec.sql
|
|
\o
|
|
\set ECHO all
|
|
|
|
\set DATA_NODE_1 :TEST_DBNAME _1
|
|
\set DATA_NODE_2 :TEST_DBNAME _2
|
|
\set DATA_NODE_3 :TEST_DBNAME _3
|
|
|
|
SELECT node_name, database, node_created, database_created, extension_created
|
|
FROM (
|
|
SELECT (add_data_node(name, host => 'localhost', DATABASE => name)).*
|
|
FROM (VALUES (:'DATA_NODE_1'), (:'DATA_NODE_2'), (:'DATA_NODE_3')) v(name)
|
|
) a;
|
|
GRANT USAGE ON FOREIGN SERVER :DATA_NODE_1, :DATA_NODE_2, :DATA_NODE_3 TO PUBLIC;
|
|
|
|
\des
|
|
|
|
RESET ROLE;
|
|
CREATE FUNCTION _timescaledb_internal.invoke_distributed_commands()
|
|
RETURNS void
|
|
AS :TSL_MODULE_PATHNAME, 'ts_invoke_distributed_commands'
|
|
LANGUAGE C STRICT;
|
|
|
|
CREATE FUNCTION _timescaledb_internal.invoke_faulty_distributed_command()
|
|
RETURNS void
|
|
AS :TSL_MODULE_PATHNAME, 'ts_invoke_faulty_distributed_command'
|
|
LANGUAGE C STRICT;
|
|
SET ROLE :ROLE_1;
|
|
|
|
SELECT _timescaledb_internal.invoke_distributed_commands();
|
|
|
|
\c :DATA_NODE_1
|
|
\dt
|
|
SELECT * FROM disttable1;
|
|
\c :DATA_NODE_2
|
|
\dt
|
|
SELECT * FROM disttable1;
|
|
\c :DATA_NODE_3
|
|
\dt
|
|
SELECT * FROM disttable1;
|
|
\c :TEST_DBNAME :ROLE_SUPERUSER
|
|
SET ROLE :ROLE_1;
|
|
|
|
-- Verify failed insert command gets fully rolled back
|
|
\set ON_ERROR_STOP 0
|
|
SELECT _timescaledb_internal.invoke_faulty_distributed_command();
|
|
\set ON_ERROR_STOP 1
|
|
|
|
\c :DATA_NODE_1
|
|
SELECT * from disttable2;
|
|
\c :DATA_NODE_2
|
|
SELECT * from disttable2;
|
|
|
|
-- Test connection session identity
|
|
\c :TEST_DBNAME :ROLE_SUPERUSER
|
|
\unset ECHO
|
|
\o /dev/null
|
|
\ir include/remote_exec.sql
|
|
\o
|
|
\set ECHO all
|
|
|
|
-- Register_is_access_node_session_on_data_node() function and test that it returns false for
|
|
-- connections openned by test suite. This simualates behaviour expected
|
|
-- with a client connections.
|
|
CREATE OR REPLACE FUNCTION is_access_node_session_on_data_node()
|
|
RETURNS BOOL
|
|
AS :TSL_MODULE_PATHNAME, 'ts_test_dist_util_is_access_node_session_on_data_node' LANGUAGE C;
|
|
|
|
\c :DATA_NODE_1
|
|
CREATE OR REPLACE FUNCTION is_access_node_session_on_data_node()
|
|
RETURNS BOOL
|
|
AS :TSL_MODULE_PATHNAME, 'ts_test_dist_util_is_access_node_session_on_data_node' LANGUAGE C;
|
|
SELECT is_access_node_session_on_data_node();
|
|
|
|
\c :DATA_NODE_2
|
|
CREATE OR REPLACE FUNCTION is_access_node_session_on_data_node()
|
|
RETURNS BOOL
|
|
AS :TSL_MODULE_PATHNAME, 'ts_test_dist_util_is_access_node_session_on_data_node' LANGUAGE C;
|
|
SELECT is_access_node_session_on_data_node();
|
|
|
|
\c :DATA_NODE_3
|
|
CREATE OR REPLACE FUNCTION is_access_node_session_on_data_node()
|
|
RETURNS BOOL
|
|
AS :TSL_MODULE_PATHNAME, 'ts_test_dist_util_is_access_node_session_on_data_node' LANGUAGE C;
|
|
SELECT is_access_node_session_on_data_node();
|
|
|
|
\c :TEST_DBNAME :ROLE_SUPERUSER
|
|
SET ROLE :ROLE_1;
|
|
SELECT is_access_node_session_on_data_node();
|
|
|
|
-- Ensure peer dist id is already set and can be set only once
|
|
\set ON_ERROR_STOP 0
|
|
SELECT * FROM test.remote_exec(ARRAY[:'DATA_NODE_1'], $$ SELECT * FROM _timescaledb_internal.set_peer_dist_id('77348176-09da-4a80-bc78-e31bdf5e63ec'); $$);
|
|
\set ON_ERROR_STOP 1
|
|
|
|
-- Repeat is_access_node_session_on_data_node() test again, but this time using connections openned from
|
|
-- access node to data nodes. Must return true.
|
|
SELECT * FROM test.remote_exec(NULL, $$ SELECT is_access_node_session_on_data_node(); $$);
|
|
|
|
-- Test distributed_exec()
|
|
|
|
-- Test calling distributed exec via direct function call
|
|
RESET ROLE;
|
|
CREATE OR REPLACE PROCEDURE distributed_exec_direct_function_call(
|
|
query TEXT,
|
|
node_list name[] = NULL,
|
|
transactional BOOLEAN = TRUE)
|
|
AS :TSL_MODULE_PATHNAME, 'ts_test_direct_function_call' LANGUAGE C;
|
|
SET ROLE :ROLE_1;
|
|
|
|
-- Invalid input
|
|
\set ON_ERROR_STOP 0
|
|
\set VERBOSITY default
|
|
BEGIN;
|
|
-- Not allowed in transcation block if transactional=false
|
|
CALL distributed_exec_direct_function_call('CREATE TABLE dist_test(id int)', NULL, false);
|
|
ROLLBACK;
|
|
-- multi-dimensional array of data nodes
|
|
CALL distributed_exec('CREATE TABLE dist_test(id int)', '{{db_data_node_1}}');
|
|
-- Specified, but empty data node array
|
|
CALL distributed_exec('CREATE TABLE dist_test(id int)', '{}');
|
|
-- Specified, but contains null values
|
|
CALL distributed_exec('CREATE TABLE dist_test(id int)', '{db_data_node_1, NULL}');
|
|
-- Specified, but not a data node
|
|
CALL distributed_exec('CREATE TABLE dist_test(id int)', '{db_data_node_1928}');
|
|
\set VERBOSITY terse
|
|
\set ON_ERROR_STOP 1
|
|
|
|
-- Make sure dist session is properly set
|
|
CALL distributed_exec('DO $$ BEGIN ASSERT(SELECT is_access_node_session_on_data_node()) = true; END; $$;');
|
|
|
|
-- Test creating and dropping a table
|
|
CALL distributed_exec('CREATE TABLE dist_test (id int)');
|
|
CALL distributed_exec('INSERT INTO dist_test values (7)');
|
|
-- Test INSERTING data using empty array of data nodes (same behavior as not specifying).
|
|
SELECT * FROM test.remote_exec(NULL, $$ SELECT * from dist_test; $$);
|
|
CALL distributed_exec('DROP TABLE dist_test');
|
|
\set ON_ERROR_STOP 0
|
|
CALL distributed_exec('INSERT INTO dist_test VALUES (8)', ARRAY[:'DATA_NODE_1']);
|
|
\set ON_ERROR_STOP 1
|
|
|
|
-- Test creating and dropping a role
|
|
CREATE ROLE dist_test_role;
|
|
-- Expect this to be an error, since data nodes are created on the same instance
|
|
\set ON_ERROR_STOP 0
|
|
SELECT test.execute_sql_and_filter_data_node_name_on_error($$
|
|
CALL distributed_exec('CREATE ROLE dist_test_role');
|
|
$$);
|
|
\set ON_ERROR_STOP 1
|
|
SELECT * FROM test.remote_exec(NULL, $$ SELECT true from pg_catalog.pg_roles WHERE rolname = 'dist_test_role'; $$);
|
|
DROP ROLE DIST_TEST_ROLE;
|
|
\set ON_ERROR_STOP 0
|
|
SELECT test.execute_sql_and_filter_data_node_name_on_error($$
|
|
CALL distributed_exec('DROP ROLE dist_test_role');
|
|
$$);
|
|
\set ON_ERROR_STOP 1
|
|
|
|
-- Do not allow to run distributed_exec() on a data nodes
|
|
\c :DATA_NODE_1
|
|
\set ON_ERROR_STOP 0
|
|
CALL distributed_exec('SELECT 1');
|
|
\set ON_ERROR_STOP 1
|
|
|
|
\c :TEST_DBNAME :ROLE_SUPERUSER
|
|
|
|
-- Test health check function output on access node
|
|
SELECT * FROM _timescaledb_internal.health() ORDER BY 1 NULLS FIRST;
|
|
|
|
SELECT * FROM delete_data_node(:'DATA_NODE_1');
|
|
SELECT * FROM delete_data_node(:'DATA_NODE_2');
|
|
SELECT * FROM delete_data_node(:'DATA_NODE_3');
|
|
|
|
-- Test health check when no longer an access node (no data nodes)
|
|
SELECT * FROM _timescaledb_internal.health() ORDER BY 1 NULLS FIRST;
|
|
|
|
DROP DATABASE :DATA_NODE_1;
|
|
DROP DATABASE :DATA_NODE_2;
|
|
DROP DATABASE :DATA_NODE_3;
|
|
|
|
\set ON_ERROR_STOP 0
|
|
-- Calling distributed_exec without data nodes should fail
|
|
CALL distributed_exec('SELECT 1');
|
|
CALL distributed_exec('SELECT 1', '{data_node_1}');
|
|
\set ON_ERROR_STOP 1
|
|
|
|
-- Test TS execution on non-TSDB server
|
|
CREATE EXTENSION postgres_fdw;
|
|
CREATE SERVER myserver FOREIGN DATA WRAPPER postgres_fdw
|
|
OPTIONS (host 'foo', dbname 'foodb', port '5432');
|
|
\set ON_ERROR_STOP 0
|
|
SELECT * FROM test.remote_exec('{myserver}', $$ SELECT 1; $$);
|
|
\set ON_ERROR_STOP 1
|
|
DROP SERVER myserver;
|
|
DROP EXTENSION postgres_fdw;
|
|
|
|
-- Test that transactional behaviour is the default and that it can be
|
|
-- disabled.
|
|
--
|
|
-- In this case, we only execute it on one data node since we are
|
|
-- creating a database and multiple creations of the database would
|
|
-- clash when executed on the same instace.
|
|
--
|
|
-- We prefix the database names with the test file to be able to
|
|
-- parallelize the test. Not possible right now because there are
|
|
-- other databases above that prevents this.
|
|
\c :TEST_DBNAME :ROLE_SUPERUSER
|
|
SELECT node_name, database, node_created, database_created, extension_created FROM add_data_node('dist_commands_1', host => 'localhost', database => :'DATA_NODE_1');
|
|
SELECT node_name, database, node_created, database_created, extension_created FROM add_data_node('dist_commands_2', host => 'localhost', database => :'DATA_NODE_2');
|
|
GRANT USAGE ON FOREIGN SERVER dist_commands_1, dist_commands_2 TO PUBLIC;
|
|
|
|
\set ON_ERROR_STOP 0
|
|
CALL distributed_exec('CREATE DATABASE dist_commands_magic',
|
|
node_list => '{dist_commands_1}');
|
|
\set ON_ERROR_STOP 1
|
|
CALL distributed_exec('CREATE DATABASE dist_commands_magic',
|
|
node_list => '{dist_commands_1}', transactional => FALSE);
|
|
DROP DATABASE dist_commands_magic;
|
|
|
|
-- Test that distributed_exec honor the 2PC behaviour when starting a
|
|
-- transaction locally. It should also give an error if attempting to
|
|
-- execute non-transactionally inside a local transaction.
|
|
|
|
-- To test that distributed_exec honors transactions, we create a
|
|
-- table on both data nodes, and then tweak one of the tables so that
|
|
-- we get a duplicate key when updating the table on both data
|
|
-- nodes. This should then abort the transaction on all data nodes.
|
|
\c :TEST_DBNAME :ROLE_1
|
|
CALL distributed_exec($$
|
|
CREATE TABLE my_table (key INT, value TEXT, PRIMARY KEY (key));
|
|
$$);
|
|
|
|
\c :DATA_NODE_1
|
|
INSERT INTO my_table VALUES (1, 'foo');
|
|
|
|
\c :TEST_DBNAME :ROLE_1
|
|
\set ON_ERROR_STOP 0
|
|
BEGIN;
|
|
CALL distributed_exec($$ INSERT INTO my_table VALUES (1, 'bar') $$);
|
|
COMMIT;
|
|
\set ON_ERROR_STOP 1
|
|
|
|
-- No changes should be there
|
|
SELECT * FROM test.remote_exec(NULL, $$ SELECT * FROM my_table; $$);
|
|
|
|
-- This should work.
|
|
BEGIN;
|
|
CALL distributed_exec($$ INSERT INTO my_table VALUES (2, 'bar'); $$);
|
|
COMMIT;
|
|
|
|
-- We should see changes
|
|
SELECT * FROM test.remote_exec(NULL, $$ SELECT * FROM my_table; $$);
|
|
|
|
-- This should fail since we are inside a transaction and asking for
|
|
-- transactional execution on the remote nodes. Non-transactional
|
|
-- execution should be outside transactions.
|
|
\set ON_ERROR_STOP 0
|
|
BEGIN;
|
|
CALL distributed_exec(
|
|
$$ INSERT INTO my_table VALUES (3, 'baz') $$,
|
|
transactional => FALSE
|
|
);
|
|
COMMIT;
|
|
\set ON_ERROR_STOP 1
|
|
|
|
-- We should see no changes
|
|
SELECT * FROM test.remote_exec(NULL, $$ SELECT * FROM my_table; $$);
|
|
|
|
\c :TEST_DBNAME :ROLE_CLUSTER_SUPERUSER
|
|
DROP DATABASE :DATA_NODE_1;
|
|
DROP DATABASE :DATA_NODE_2;
|