Commit cbeab05a authored by Yuchen Pei's avatar Yuchen Pei

MDEV-22979 MDEV-27233 MDEV-28218 Fixing spider init bugs

We introduce a new set of synchronisation primitives ("query ready")
that are signaled when the server is ready to take queries, and make
spider initialisation use them instead of the primitives associated
with server start.

During spider initialisation, if the server is query ready, proceed
with the initialisation synchronously (requiring the sql
service). Otherwise, leave the init to the first sts bg thread as
usual.

When creating a spider handler before the spider init is done, if the
server is not query ready, just give up and return NULL. If the server
is query ready, then wait for a specific amount of time (5s) for the
spider init to be done, and on timeout give up and return NULL.

How this fixes / prevents various init bugs:

MDEV-27233 (spider installed and used in init file). Before -
spider_create_handler() called by read_init_file() waits for bg thread
to finish init, whereas the bg thread is waiting for server
start. After - spider init is run synchronously because the server is
query ready when read_init_file(). No funny business with concurrency.

MDEV-29904 (spider loaded with plugin-load-add). Before (before the
original patch to MDEV-27233 was reverted) - fully synchronous spider
init fails due to dependencies on Aria / acl init. After - spider init
is run async, and as soon as the server is query ready, the init
proceeds and succeeds.

MDEV-22979 (mysqld --bootstrap --plugin-load-add=ha_spider). Before -
bootstrap queries causes call to spider_create_handler(), which waits
for bg thread to finish init, whereas bg thread waits for server
start. After - spider init is run async, waiting for server to be
query ready. spider_create_hander() notices bg thread still trying to
init spider, and server is not query ready so it gives up.

MDEV-28218 (spider is loaded in a query after server start,
immediately before another query dropping a spider table). Before - bg
thread is created to init spider async, while the server proceeds to
the next query that tries to drop a spider system table. race
condition causes the situation where the server holds the mdl lock to
the table and waits for the bg thread in spider_create_handler(), and
the bg thread waiting for the mdl lock to create the table. After -
since server has already started, spider init is run synchronously,
thus no concurrency issues. Same applies to the variant of MDEV-28218
represented by mdev_28218_init_file where the queries are in an init
file.

Another variant of MDEV-28218 represented by mdev_28218_mixed loads
spider with plugin-load-add and executes the drop table query in an
init_file. Spider init is async because the server was not query
ready. As soon as the the server becomes query ready the server tries
to execute the drop table query. It waits on the init in
spider_create_handler(), but because of the deadlock it times out
after 5 seconds. The drop table query did not find any table, but
spider init then proceeds as it finally acquires the mdl lock, and
creates the table. The moral of the story is to advise users not to
mix plugin-load-add=ha_spider with init_file containing queries
related to spider system tables. Queries using spider engine to create
tables are still ok because there's no deadlock.

MDEV-30370 (mysqld --wsrep-recover --plugin-load-add=spider). The
timed wait in the patch for MDEV-30370 still applies, so it still
passes.

More on the design:

Compared to the server start primitives, the query ready primitives
are signaled earlier ("query ready point") to avoid unnecessary wait
and deadlocks.

The spider initialisation does not only require other plugins like
Aria to be initialised first, but also acl init (due to its creation
of udfs) as well as the creation of the `mysql` database (due to its
system tables being in this database). Thus the point where it can go
ahead with the initialisation should be after the bootstrap.

By the time the server can accept queries by reading from init files,
it can definitely accept all spider init queries. So the point of
spider initialisation can be before this point.

Looking at the lines between boostrap and reading init file, the most
natural position is just before the call to read_init_file(), where we
can also give the primitives a most meaningful name "query ready".
parent cb0e0c91
......@@ -743,8 +743,12 @@ pthread_t signal_thread;
pthread_attr_t connection_attrib;
mysql_mutex_t LOCK_server_started;
mysql_cond_t COND_server_started;
mysql_mutex_t LOCK_server_query_ready;
mysql_cond_t COND_server_query_ready;
int mysqld_server_started=0, mysqld_server_initialized= 0;
/* Whether the server is ready to take queries. */
int mysqld_server_query_ready= 0;
File_parser_dummy_hook file_parser_dummy_hook;
/* replication parameters, if master_host is not NULL, we are a slave */
......@@ -907,7 +911,7 @@ PSI_mutex_key key_BINLOG_LOCK_index, key_BINLOG_LOCK_xid_list,
key_LOCK_gdl, key_LOCK_global_system_variables,
key_LOCK_manager, key_LOCK_backup_log,
key_LOCK_prepared_stmt_count,
key_LOCK_rpl_status, key_LOCK_server_started,
key_LOCK_rpl_status, key_LOCK_server_started, key_LOCK_server_query_ready,
key_LOCK_status, key_LOCK_temp_pool,
key_LOCK_system_variables_hash, key_LOCK_thd_data, key_LOCK_thd_kill,
key_LOCK_user_conn, key_LOCK_uuid_short_generator, key_LOG_LOCK_log,
......@@ -2138,6 +2142,8 @@ static void clean_up_mutexes()
mysql_cond_destroy(&COND_start_thread);
mysql_mutex_destroy(&LOCK_server_started);
mysql_cond_destroy(&COND_server_started);
mysql_mutex_destroy(&LOCK_server_query_ready);
mysql_cond_destroy(&COND_server_query_ready);
mysql_mutex_destroy(&LOCK_prepare_ordered);
mysql_cond_destroy(&COND_prepare_ordered);
mysql_mutex_destroy(&LOCK_after_binlog_sync);
......@@ -4465,6 +4471,9 @@ static int init_thread_environment()
mysql_mutex_init(key_LOCK_server_started,
&LOCK_server_started, MY_MUTEX_INIT_FAST);
mysql_cond_init(key_COND_server_started, &COND_server_started, NULL);
mysql_mutex_init(key_LOCK_server_query_ready,
&LOCK_server_query_ready, MY_MUTEX_INIT_FAST);
mysql_cond_init(key_COND_server_started, &COND_server_started, NULL);
sp_cache_init();
#ifdef HAVE_EVENT_SCHEDULER
Events::init_mutexes();
......@@ -5534,7 +5543,7 @@ int mysqld_main(int argc, char **argv)
*/
my_progname= argv[0];
sf_leaking_memory= 1; // no safemalloc memory leak reports if we exit early
mysqld_server_started= mysqld_server_initialized= 0;
mysqld_server_started= mysqld_server_initialized= mysqld_server_query_ready= 0;
if (init_early_variables())
exit(1);
......@@ -5870,6 +5879,12 @@ int mysqld_main(int argc, char **argv)
unireg_abort(1);
}
/* Signal threads waiting for server to be ready to take queries */
mysql_mutex_lock(&LOCK_server_query_ready);
mysqld_server_query_ready= 1;
mysql_cond_broadcast(&COND_server_query_ready);
mysql_mutex_unlock(&LOCK_server_query_ready);
if (opt_init_file && *opt_init_file)
{
if (read_init_file(opt_init_file))
......
......@@ -285,6 +285,7 @@ extern struct my_option my_long_options[];
int handle_early_options();
extern int MYSQL_PLUGIN_IMPORT mysqld_server_started;
extern int mysqld_server_initialized;
extern int MYSQL_PLUGIN_IMPORT mysqld_server_query_ready;
extern "C" MYSQL_PLUGIN_IMPORT int orig_argc;
extern "C" MYSQL_PLUGIN_IMPORT char **orig_argv;
extern pthread_attr_t connection_attrib;
......@@ -767,6 +768,8 @@ extern mysql_mutex_t LOCK_des_key_file;
#endif
extern MYSQL_PLUGIN_IMPORT mysql_mutex_t LOCK_server_started;
extern MYSQL_PLUGIN_IMPORT mysql_cond_t COND_server_started;
extern MYSQL_PLUGIN_IMPORT mysql_mutex_t LOCK_server_query_ready;
extern MYSQL_PLUGIN_IMPORT mysql_cond_t COND_server_query_ready;
extern mysql_rwlock_t LOCK_grant, LOCK_sys_init_connect, LOCK_sys_init_slave;
extern mysql_rwlock_t LOCK_ssl_refresh;
extern mysql_prlock_t LOCK_system_variables_hash;
......
MDEV-22979 "mysqld --bootstrap" / mysql_install_db hangs when Spider is installed
# Kill the server
# restart
#
# MDEV-27233 Server hangs when using --init-file which loads Spider and creates a Spider table
#
show create table t;
Table Create Table
t CREATE TABLE `t` (
`c` int(11) DEFAULT NULL
) ENGINE=SPIDER DEFAULT CHARSET=latin1 COLLATE=latin1_swedish_ci
Warnings:
Error 1429 Unable to connect to foreign data source: localhost
Error 1429 Unable to connect to foreign data source: localhost
#
# MDEV-28218 Spider: thread hang/deadlock as result of INSTALL PLUGIN and DROP TABLE
#
INSTALL SONAME 'ha_spider.so';
DROP TABLE IF EXISTS mysql.spider_tables;
show create table mysql.spider_tables;
ERROR 42S02: Table 'mysql.spider_tables' doesn't exist
DROP FUNCTION spider_flush_table_mon_cache;
DROP FUNCTION spider_copy_tables;
DROP FUNCTION spider_ping_table;
DROP FUNCTION spider_bg_direct_sql;
DROP FUNCTION spider_direct_sql;
UNINSTALL SONAME "ha_spider.so";
DROP TABLE IF EXISTS mysql.spider_xa;
DROP TABLE IF EXISTS mysql.spider_xa_member;
DROP TABLE IF EXISTS mysql.spider_xa_failed_log;
DROP TABLE IF EXISTS mysql.spider_link_mon_servers;
DROP TABLE IF EXISTS mysql.spider_link_failed_log;
DROP TABLE IF EXISTS mysql.spider_table_position_for_recovery;
DROP TABLE IF EXISTS mysql.spider_table_sts;
DROP TABLE IF EXISTS mysql.spider_table_crd;
#
# MDEV-28218 Spider: thread hang/deadlock as result of INSTALL PLUGIN and DROP TABLE
#
show create table mysql.spider_tables;
ERROR 42S02: Table 'mysql.spider_tables' doesn't exist
#
# MDEV-28218 Spider: thread hang/deadlock as result of INSTALL PLUGIN and DROP TABLE
#
show create table mysql.spider_tables;
Table Create Table
spider_tables CREATE TABLE `spider_tables` (
`db_name` char(64) NOT NULL DEFAULT '',
`table_name` char(199) NOT NULL DEFAULT '',
`link_id` int(11) NOT NULL DEFAULT 0,
`priority` bigint(20) NOT NULL DEFAULT 0,
`server` char(64) DEFAULT NULL,
`scheme` char(64) DEFAULT NULL,
`host` char(64) DEFAULT NULL,
`port` char(5) DEFAULT NULL,
`socket` text DEFAULT NULL,
`username` char(64) DEFAULT NULL,
`password` char(64) DEFAULT NULL,
`ssl_ca` text DEFAULT NULL,
`ssl_capath` text DEFAULT NULL,
`ssl_cert` text DEFAULT NULL,
`ssl_cipher` char(64) DEFAULT NULL,
`ssl_key` text DEFAULT NULL,
`ssl_verify_server_cert` tinyint(4) NOT NULL DEFAULT 0,
`monitoring_binlog_pos_at_failing` tinyint(4) NOT NULL DEFAULT 0,
`default_file` text DEFAULT NULL,
`default_group` char(64) DEFAULT NULL,
`dsn` char(64) DEFAULT NULL,
`filedsn` text DEFAULT NULL,
`driver` char(64) DEFAULT NULL,
`tgt_db_name` char(64) DEFAULT NULL,
`tgt_table_name` char(64) DEFAULT NULL,
`link_status` tinyint(4) NOT NULL DEFAULT 1,
`block_status` tinyint(4) NOT NULL DEFAULT 0,
`static_link_id` char(64) DEFAULT NULL,
PRIMARY KEY (`db_name`,`table_name`,`link_id`),
UNIQUE KEY `uidx1` (`db_name`,`table_name`,`static_link_id`),
KEY `idx1` (`priority`)
) ENGINE=Aria DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_bin PAGE_CHECKSUM=1 TRANSACTIONAL=1
#
# MDEV-30370 mariadbd hangs when running with --wsrep-recover and --plugin-load-add=ha_spider.so
#
# Kill the server
# restart
#
# plugin-load-add=ha_spider
#
select * from mysql.plugin;
name dl
create table t (c int) Engine=SPIDER;
drop table t;
#
# plugin-load-add=ha_spider
#
select * from mysql.plugin;
name dl
create table t (c int) Engine=SPIDER;
drop table t;
--echo MDEV-22979 "mysqld --bootstrap" / mysql_install_db hangs when Spider is installed
# This test is not the most faithful, as it does not have any
# dependency problems on the existence of the `mysql` database. To
# test MDEV-22979 faithfully, a mysql_install_db invocation with
# --plugin-load-add=ha_spider should be run. We cannot run it in mtr
# because we do not have access to --srcdir.
let $MYSQLD_DATADIR= `select @@datadir`;
let $PLUGIN_DIR=`select @@plugin_dir`;
--source include/kill_mysqld.inc
--write_file $MYSQLTEST_VARDIR/tmp/mdev_22979.sql
drop table if exists foo.bar;
EOF
--exec $MYSQLD_CMD --datadir=$MYSQLD_DATADIR --bootstrap --plugin-dir=$PLUGIN_DIR --plugin-load-add=ha_spider < $MYSQLTEST_VARDIR/tmp/mdev_22979.sql
--source include/start_mysqld.inc
--init-file=$MYSQL_TEST_DIR/../storage/spider/mysql-test/spider/bugfix/t/mdev_27233.sql
INSTALL SONAME 'ha_spider.so';
USE test;
CREATE TABLE t (c INT) ENGINE=SPIDER;
--echo #
--echo # MDEV-27233 Server hangs when using --init-file which loads Spider and creates a Spider table
--echo #
show create table t;
--echo #
--echo # MDEV-28218 Spider: thread hang/deadlock as result of INSTALL PLUGIN and DROP TABLE
--echo #
INSTALL SONAME 'ha_spider.so';
DROP TABLE IF EXISTS mysql.spider_tables;
--error ER_NO_SUCH_TABLE
show create table mysql.spider_tables;
# deinit spider
DROP FUNCTION spider_flush_table_mon_cache;
DROP FUNCTION spider_copy_tables;
DROP FUNCTION spider_ping_table;
DROP FUNCTION spider_bg_direct_sql;
DROP FUNCTION spider_direct_sql;
UNINSTALL SONAME "ha_spider.so";
DROP TABLE IF EXISTS mysql.spider_xa;
DROP TABLE IF EXISTS mysql.spider_xa_member;
DROP TABLE IF EXISTS mysql.spider_xa_failed_log;
DROP TABLE IF EXISTS mysql.spider_link_mon_servers;
DROP TABLE IF EXISTS mysql.spider_link_failed_log;
DROP TABLE IF EXISTS mysql.spider_table_position_for_recovery;
DROP TABLE IF EXISTS mysql.spider_table_sts;
DROP TABLE IF EXISTS mysql.spider_table_crd;
--init-file=$MYSQL_TEST_DIR/../storage/spider/mysql-test/spider/bugfix/t/mdev_28218_init_file.sql
INSTALL PLUGIN spider SONAME 'ha_spider.so';
DROP TABLE IF EXISTS mysql.spider_tables;
--echo #
--echo # MDEV-28218 Spider: thread hang/deadlock as result of INSTALL PLUGIN and DROP TABLE
--echo #
--error ER_NO_SUCH_TABLE
show create table mysql.spider_tables;
--plugin-load-add=ha_spider
--init-file=$MYSQL_TEST_DIR/../storage/spider/mysql-test/spider/bugfix/t/mdev_28218_mixed.sql
--echo #
--echo # MDEV-28218 Spider: thread hang/deadlock as result of INSTALL PLUGIN and DROP TABLE
--echo #
show create table mysql.spider_tables;
......@@ -2,4 +2,8 @@
--echo # MDEV-30370 mariadbd hangs when running with --wsrep-recover and --plugin-load-add=ha_spider.so
--echo #
--exec $MYSQLD_BOOTSTRAP_CMD --wsrep-recover --plugin-load-add=ha_spider.so
let $MYSQLD_DATADIR= `select @@datadir`;
let $PLUGIN_DIR=`select @@plugin_dir`;
--source include/kill_mysqld.inc
--exec $MYSQLD_CMD --datadir=$MYSQLD_DATADIR --wsrep-recover --plugin-dir=$PLUGIN_DIR --plugin-load-add=ha_spider
--source include/start_mysqld.inc
--echo #
--echo # plugin-load-add=ha_spider
--echo #
select * from mysql.plugin;
create table t (c int) Engine=SPIDER;
drop table t;
--echo #
--echo # plugin-load-add=ha_spider
--echo #
select * from mysql.plugin;
create table t (c int) Engine=SPIDER;
drop table t;
......@@ -698,6 +698,5 @@ static LEX_STRING spider_init_queries[] = {
" create function if not exists spider_flush_table_mon_cache returns int"
" soname 'ha_spider.dll';"
"end if;"
)},
{C_STRING_WITH_LEN("")}
)}
};
......@@ -125,9 +125,6 @@ const char **spd_mysqld_unix_port;
uint *spd_mysqld_port;
bool volatile *spd_abort_loop;
Time_zone *spd_tz_system;
static int *spd_mysqld_server_started;
static pthread_mutex_t *spd_LOCK_server_started;
static pthread_cond_t *spd_COND_server_started;
extern long spider_conn_mutex_id;
handlerton *spider_hton_ptr;
SPIDER_DBTON spider_dbton[SPIDER_DBTON_SIZE];
......@@ -6287,13 +6284,32 @@ handler* spider_create_handler(
) {
DBUG_ENTER("spider_create_handler");
SPIDER_THREAD *thread = &spider_table_sts_threads[0];
/* If spider is still initialising */
if (unlikely(thread->init_command))
{
/* Give up if the the server is not ready to take queries yet,
because such an early stage call to this function indicates the
upstream callsite is probably in mysqld_main() and waiting would
cause deadlock */
if (!mysqld_server_query_ready)
{
my_printf_error(HA_ERR_GENERIC, "[SPIDER] server not ready yet", MYF(0));
DBUG_RETURN(NULL);
}
THD *thd = current_thd;
pthread_cond_t *cond = thd->mysys_var->current_cond;
pthread_mutex_t *mutex = thd->mysys_var->current_mutex;
/* wait for finishing init_command */
pthread_mutex_lock(&thread->mutex);
/* Wait for finishing init_command, and give up after 1s */
int sleep_cnt = 0;
while (pthread_mutex_trylock(&thread->mutex) == EBUSY)
{
if (sleep_cnt++ > 500)
{
my_printf_error(HA_ERR_LOCK_WAIT_TIMEOUT, "[SPIDER] timeout", MYF(0));
DBUG_RETURN(NULL);
}
my_sleep(10000); /* Wait for 10ms */
}
if (unlikely(thread->init_command))
{
thd->mysys_var->current_cond = &thread->sync_cond;
......@@ -6561,6 +6577,45 @@ int spider_panic(
DBUG_RETURN(0);
}
/**
Run spider init queries
@retval false success
@retval true failure
*/
bool spider_run_init_queries()
{
DBUG_ENTER("spider_init_system_tables");
MYSQL *mysql= mysql_init(NULL);
if (!mysql)
DBUG_RETURN(TRUE);
if (!mysql_real_connect_local(mysql))
{
mysql_close(mysql);
DBUG_RETURN(TRUE);
}
int size= sizeof(spider_init_queries) / sizeof(spider_init_queries[0]);
for (int i= 0; i < size; i++)
{
if (mysql_real_query(mysql, spider_init_queries[i].str,
spider_init_queries[i].length))
{
fprintf(stderr,
"[ERROR] SPIDER plugin initialization failed at '%s' by '%s'\n",
spider_init_queries[i].str, mysql_error(mysql));
mysql_close(mysql);
DBUG_RETURN(TRUE);
}
if (MYSQL_RES *res= mysql_store_result(mysql))
mysql_free_result(res);
}
mysql_close(mysql);
DBUG_RETURN(FALSE);
}
int spider_db_init(
void *p
) {
......@@ -6575,16 +6630,6 @@ int spider_db_init(
#ifdef HTON_CAN_READ_CONNECT_STRING_IN_PARTITION
spider_hton->flags |= HTON_CAN_READ_CONNECT_STRING_IN_PARTITION;
#endif
/* spider_hton->db_type = DB_TYPE_SPIDER; */
/*
spider_hton->savepoint_offset;
spider_hton->savepoint_set = spider_savepoint_set;
spider_hton->savepoint_rollback = spider_savepoint_rollback;
spider_hton->savepoint_release = spider_savepoint_release;
spider_hton->create_cursor_read_view = spider_create_cursor_read_view;
spider_hton->set_cursor_read_view = spider_set_cursor_read_view;
spider_hton->close_cursor_read_view = spider_close_cursor_read_view;
*/
spider_hton->panic = spider_panic;
spider_hton->close_connection = spider_close_connection;
spider_hton->start_consistent_snapshot = spider_start_consistent_snapshot;
......@@ -6645,9 +6690,6 @@ int spider_db_init(
spd_mysqld_port = &mysqld_port;
spd_abort_loop = &abort_loop;
spd_tz_system = my_tz_SYSTEM;
spd_mysqld_server_started = &mysqld_server_started;
spd_LOCK_server_started = &LOCK_server_started;
spd_COND_server_started = &COND_server_started;
#ifdef HAVE_PSI_INTERFACE
init_spider_psi_keys();
......@@ -6834,6 +6876,13 @@ int spider_db_init(
NullS))
)
goto error_alloc_mon_mutxes;
/* Run init queries synchronously if possible */
if (mysqld_server_query_ready)
{
if (spider_run_init_queries())
goto error_alloc_mon_mutxes;
} else
spider_table_sts_threads[0].init_command = TRUE;
for (roop_count = 0;
......@@ -9135,38 +9184,38 @@ void *spider_table_bg_sts_action(
if (thread->init_command)
{
uint i = 0;
tmp_disable_binlog(thd);
thd->security_ctx->skip_grants();
thd->client_capabilities |= CLIENT_MULTI_RESULTS;
if (!(*spd_mysqld_server_started) && !thd->killed && !thread->killed)
if (!mysqld_server_query_ready && !thd->killed && !thread->killed)
{
pthread_mutex_lock(spd_LOCK_server_started);
thd->mysys_var->current_cond = spd_COND_server_started;
thd->mysys_var->current_mutex = spd_LOCK_server_started;
if (!(*spd_mysqld_server_started) && !thd->killed && !thread->killed &&
pthread_mutex_lock(&LOCK_server_query_ready);
thd->mysys_var->current_cond = &COND_server_query_ready;
thd->mysys_var->current_mutex = &LOCK_server_query_ready;
if (!mysqld_server_query_ready && !thd->killed && !thread->killed &&
thread->init_command)
{
do
{
struct timespec abstime;
set_timespec_nsec(abstime, 1000);
error_num = pthread_cond_timedwait(spd_COND_server_started,
spd_LOCK_server_started, &abstime);
error_num = pthread_cond_timedwait(&COND_server_query_ready,
&LOCK_server_query_ready, &abstime);
} while (
(error_num == ETIMEDOUT || error_num == ETIME) &&
!(*spd_mysqld_server_started) && !thd->killed && !thread->killed &&
!mysqld_server_query_ready && !thd->killed && !thread->killed &&
thread->init_command
);
}
pthread_mutex_unlock(spd_LOCK_server_started);
pthread_mutex_unlock(&LOCK_server_query_ready);
thd->mysys_var->current_cond = &thread->cond;
thd->mysys_var->current_mutex = &thread->mutex;
}
bool spd_wsrep_on = thd->variables.wsrep_on;
thd->variables.wsrep_on = false;
while (spider_init_queries[i].length && !thd->killed && !thread->killed &&
thread->init_command)
int size= sizeof(spider_init_queries) / sizeof(spider_init_queries[0]);
for (int i= 0; i < size && !thd->killed && !thread->killed &&
thread->init_command; i++)
{
dispatch_command(COM_QUERY, thd, spider_init_queries[i].str,
(uint) spider_init_queries[i].length);
......@@ -9176,7 +9225,6 @@ void *spider_table_bg_sts_action(
thd->clear_error();
break;
}
++i;
}
thd->variables.wsrep_on = spd_wsrep_on;
thd->mysys_var->current_cond = &thread->cond;
......@@ -9190,10 +9238,6 @@ void *spider_table_bg_sts_action(
{
thread->killed = TRUE;
}
if (thd->killed)
{
thread->killed = TRUE;
}
while (TRUE)
{
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment