1
0
mirror of https://github.com/MariaDB/server.git synced 2025-08-08 11:22:35 +03:00

MDEV-19529 InnoDB hang on DROP FULLTEXT INDEX

Problem:
=======
  During dropping of fts index, InnoDB waits for fts_optimize_remove_table()
and it holds dict_sys->mutex and dict_operaiton_lock even though the
table id is not present in the queue. But fts_optimize_thread does wait
for dict_sys->mutex to process the unrelated table id from the slot.

Solution:
========
  Whenever table is added to fts_optimize_wq, update the fts_status
of in-memory fts subsystem to TABLE_IN_QUEUE. Whenever drop index
wants to remove table from the queue, it can check the fts_status
to decide whether it should send the MSG_DELETE_TABLE to the queue.

Removed the following functions because these are all deadcode.
dict_table_wait_for_bg_threads_to_exit(),
fts_wait_for_background_thread_to_start(),fts_start_shutdown(), fts_shudown().
This commit is contained in:
Thirunarayanan Balathandayuthapani
2019-09-18 13:22:08 +05:30
parent 708f1e3419
commit 8a79fa0e4d
26 changed files with 169 additions and 490 deletions

View File

@@ -6,3 +6,21 @@ REPLACE INTO t1(a) VALUES('aaa');
SET DEBUG_SYNC = 'now SIGNAL race'; SET DEBUG_SYNC = 'now SIGNAL race';
SET DEBUG_SYNC = 'RESET'; SET DEBUG_SYNC = 'RESET';
DROP TABLE t1; DROP TABLE t1;
#
# MDEV-19529 InnoDB hang on DROP FULLTEXT INDEX
#
CREATE TABLE t1(f1 CHAR(100), FULLTEXT(f1))ENGINE=InnoDB;
INSERT INTO t1 VALUES('test');
CREATE TABLE t2 (f1 char(100), FULLTEXT idx1(f1))ENGINE=InnoDB;
INSERT INTO t2 VALUES('mariadb');
SET GLOBAL debug_dbug ='+d,fts_instrument_sync_request,ib_optimize_wq_hang';
SET DEBUG_SYNC= 'fts_instrument_sync_request
SIGNAL drop_index_start WAIT_FOR sync_op';
INSERT INTO t1 VALUES('Keyword');
SET DEBUG_SYNC='now WAIT_FOR drop_index_start';
SET DEBUG_SYNC= 'norebuild_fts_drop SIGNAL sync_op WAIT_FOR fts_drop_index';
ALTER TABLE t2 drop index idx1;
set DEBUG_SYNC= 'now SIGNAL fts_drop_index';
SET global DEBUG_DBUG=RESET;
drop table t1, t2;
set DEBUG_SYNC=RESET;

View File

@@ -18,3 +18,34 @@ reap;
SET DEBUG_SYNC = 'RESET'; SET DEBUG_SYNC = 'RESET';
DROP TABLE t1; DROP TABLE t1;
--echo #
--echo # MDEV-19529 InnoDB hang on DROP FULLTEXT INDEX
--echo #
CREATE TABLE t1(f1 CHAR(100), FULLTEXT(f1))ENGINE=InnoDB;
INSERT INTO t1 VALUES('test');
CREATE TABLE t2 (f1 char(100), FULLTEXT idx1(f1))ENGINE=InnoDB;
INSERT INTO t2 VALUES('mariadb');
connection default;
SET GLOBAL debug_dbug ='+d,fts_instrument_sync_request,ib_optimize_wq_hang';
SET DEBUG_SYNC= 'fts_instrument_sync_request
SIGNAL drop_index_start WAIT_FOR sync_op';
send INSERT INTO t1 VALUES('Keyword');
connect(con1,localhost,root,,,);
SET DEBUG_SYNC='now WAIT_FOR drop_index_start';
SET DEBUG_SYNC= 'norebuild_fts_drop SIGNAL sync_op WAIT_FOR fts_drop_index';
send ALTER TABLE t2 drop index idx1;
connection default;
reap;
set DEBUG_SYNC= 'now SIGNAL fts_drop_index';
connection con1;
reap;
SET global DEBUG_DBUG=RESET;
drop table t1, t2;
connection default;
set DEBUG_SYNC=RESET;

View File

@@ -2911,33 +2911,6 @@ dict_table_copy_types(
} }
} }
/********************************************************************
Wait until all the background threads of the given table have exited, i.e.,
bg_threads == 0. Note: bg_threads_mutex must be reserved when
calling this. */
UNIV_INTERN
void
dict_table_wait_for_bg_threads_to_exit(
/*===================================*/
dict_table_t* table, /*< in: table */
ulint delay) /*< in: time in microseconds to wait between
checks of bg_threads. */
{
fts_t* fts = table->fts;
#ifdef UNIV_SYNC_DEBUG
ut_ad(mutex_own(&fts->bg_threads_mutex));
#endif /* UNIV_SYNC_DEBUG */
while (fts->bg_threads > 0) {
mutex_exit(&fts->bg_threads_mutex);
os_thread_sleep(delay);
mutex_enter(&fts->bg_threads_mutex);
}
}
/*******************************************************************//** /*******************************************************************//**
Builds the internal dictionary cache representation for a clustered Builds the internal dictionary cache representation for a clustered
index, containing also system fields not defined by the user. index, containing also system fields not defined by the user.

View File

@@ -224,8 +224,7 @@ fts_config_set_value(
pars_info_bind_varchar_literal(info, "value", pars_info_bind_varchar_literal(info, "value",
value->f_str, value->f_len); value->f_str, value->f_len);
const bool dict_locked = fts_table->table->fts->fts_status const bool dict_locked = fts_table->table->fts->dict_locked;
& TABLE_DICT_LOCKED;
fts_table->suffix = "CONFIG"; fts_table->suffix = "CONFIG";
fts_get_table_name(fts_table, table_name, dict_locked); fts_get_table_name(fts_table, table_name, dict_locked);

View File

@@ -458,7 +458,7 @@ fts_load_user_stopword(
dberr_t error = DB_SUCCESS; dberr_t error = DB_SUCCESS;
ibool ret = TRUE; ibool ret = TRUE;
trx_t* trx; trx_t* trx;
ibool has_lock = fts->fts_status & TABLE_DICT_LOCKED; ibool has_lock = fts->dict_locked;
trx = trx_allocate_for_background(); trx = trx_allocate_for_background();
trx->op_info = "Load user stopword table into FTS cache"; trx->op_info = "Load user stopword table into FTS cache";
@@ -933,18 +933,16 @@ fts_que_graph_free_check_lock(
const fts_index_cache_t*index_cache, /*!< in: FTS index cache */ const fts_index_cache_t*index_cache, /*!< in: FTS index cache */
que_t* graph) /*!< in: query graph */ que_t* graph) /*!< in: query graph */
{ {
ibool has_dict = FALSE; bool has_dict = FALSE;
if (fts_table && fts_table->table) { if (fts_table && fts_table->table) {
ut_ad(fts_table->table->fts); ut_ad(fts_table->table->fts);
has_dict = fts_table->table->fts->fts_status has_dict = fts_table->table->fts->dict_locked;
& TABLE_DICT_LOCKED;
} else if (index_cache) { } else if (index_cache) {
ut_ad(index_cache->index->table->fts); ut_ad(index_cache->index->table->fts);
has_dict = index_cache->index->table->fts->fts_status has_dict = index_cache->index->table->fts->dict_locked;
& TABLE_DICT_LOCKED;
} }
if (!has_dict) { if (!has_dict) {
@@ -2831,7 +2829,7 @@ fts_update_sync_doc_id(
pars_info_bind_varchar_literal(info, "doc_id", id, id_len); pars_info_bind_varchar_literal(info, "doc_id", id, id_len);
fts_get_table_name(&fts_table, fts_name, fts_get_table_name(&fts_table, fts_name,
table->fts->fts_status & TABLE_DICT_LOCKED); table->fts->dict_locked);
pars_info_bind_id(info, true, "table_name", fts_name); pars_info_bind_id(info, true, "table_name", fts_name);
graph = fts_parse_sql( graph = fts_parse_sql(
@@ -2947,7 +2945,7 @@ fts_delete(
into cache from last crash (delete Doc will not initialize the into cache from last crash (delete Doc will not initialize the
sync). Avoid any added counter accounting until the FTS cache sync). Avoid any added counter accounting until the FTS cache
is re-established and sync-ed */ is re-established and sync-ed */
if (table->fts->fts_status & ADDED_TABLE_SYNCED if (table->fts->added_synced
&& doc_id > cache->synced_doc_id) { && doc_id > cache->synced_doc_id) {
mutex_enter(&table->fts->cache->deleted_lock); mutex_enter(&table->fts->cache->deleted_lock);
@@ -3385,7 +3383,7 @@ fts_add_doc_by_id(
/* If Doc ID has been supplied by the user, then the table /* If Doc ID has been supplied by the user, then the table
might not yet be sync-ed */ might not yet be sync-ed */
if (!(ftt->table->fts->fts_status & ADDED_TABLE_SYNCED)) { if (!ftt->table->fts->added_synced) {
fts_init_index(ftt->table, FALSE); fts_init_index(ftt->table, FALSE);
} }
@@ -4933,7 +4931,7 @@ fts_init_doc_id(
fts_init_index((dict_table_t*) table, TRUE); fts_init_index((dict_table_t*) table, TRUE);
} }
table->fts->fts_status |= ADDED_TABLE_SYNCED; table->fts->added_synced = true;
table->fts->cache->first_doc_id = max_doc_id; table->fts->cache->first_doc_id = max_doc_id;
@@ -5386,69 +5384,6 @@ fts_cache_append_deleted_doc_ids(
mutex_exit((ib_mutex_t*) &cache->deleted_lock); mutex_exit((ib_mutex_t*) &cache->deleted_lock);
} }
/*********************************************************************//**
Wait for the background thread to start. We poll to detect change
of state, which is acceptable, since the wait should happen only
once during startup.
@return true if the thread started else FALSE (i.e timed out) */
UNIV_INTERN
ibool
fts_wait_for_background_thread_to_start(
/*====================================*/
dict_table_t* table, /*!< in: table to which the thread
is attached */
ulint max_wait) /*!< in: time in microseconds, if
set to 0 then it disables
timeout checking */
{
ulint count = 0;
ibool done = FALSE;
ut_a(max_wait == 0 || max_wait >= FTS_MAX_BACKGROUND_THREAD_WAIT);
for (;;) {
fts_t* fts = table->fts;
mutex_enter(&fts->bg_threads_mutex);
if (fts->fts_status & BG_THREAD_READY) {
done = TRUE;
}
mutex_exit(&fts->bg_threads_mutex);
if (!done) {
os_thread_sleep(FTS_MAX_BACKGROUND_THREAD_WAIT);
if (max_wait > 0) {
max_wait -= FTS_MAX_BACKGROUND_THREAD_WAIT;
/* We ignore the residual value. */
if (max_wait < FTS_MAX_BACKGROUND_THREAD_WAIT) {
break;
}
}
++count;
} else {
break;
}
if (count >= FTS_BACKGROUND_THREAD_WAIT_COUNT) {
ut_print_timestamp(stderr);
fprintf(stderr, " InnoDB: Error the background thread "
"for the FTS table %s refuses to start\n",
table->name);
count = 0;
}
}
return(done);
}
/*********************************************************************//** /*********************************************************************//**
Add the FTS document id hidden column. */ Add the FTS document id hidden column. */
UNIV_INTERN UNIV_INTERN
@@ -5589,42 +5524,6 @@ fts_free(
table->fts = NULL; table->fts = NULL;
} }
/*********************************************************************//**
Signal FTS threads to initiate shutdown. */
UNIV_INTERN
void
fts_start_shutdown(
/*===============*/
dict_table_t* table, /*!< in: table with FTS indexes */
fts_t* fts) /*!< in: fts instance that needs
to be informed about shutdown */
{
mutex_enter(&fts->bg_threads_mutex);
fts->fts_status |= BG_THREAD_STOP;
mutex_exit(&fts->bg_threads_mutex);
}
/*********************************************************************//**
Wait for FTS threads to shutdown. */
UNIV_INTERN
void
fts_shutdown(
/*=========*/
dict_table_t* table, /*!< in: table with FTS indexes */
fts_t* fts) /*!< in: fts instance to shutdown */
{
mutex_enter(&fts->bg_threads_mutex);
ut_a(fts->fts_status & BG_THREAD_STOP);
dict_table_wait_for_bg_threads_to_exit(table, 20000);
mutex_exit(&fts->bg_threads_mutex);
}
/*********************************************************************//** /*********************************************************************//**
Take a FTS savepoint. */ Take a FTS savepoint. */
UNIV_INLINE UNIV_INLINE
@@ -7639,7 +7538,7 @@ fts_init_index(
} }
rw_lock_x_unlock(&cache->init_lock); rw_lock_x_unlock(&cache->init_lock);
if (table->fts->fts_status & ADDED_TABLE_SYNCED) { if (table->fts->added_synced) {
goto func_exit; goto func_exit;
} }
@@ -7681,7 +7580,7 @@ fts_init_index(
} }
} }
table->fts->fts_status |= ADDED_TABLE_SYNCED; table->fts->added_synced = true;
fts_get_docs_clear(cache->get_docs); fts_get_docs_clear(cache->get_docs);

View File

@@ -2628,6 +2628,10 @@ UNIV_INTERN void fts_optimize_add_table(dict_table_t* table)
msg = fts_optimize_create_msg(FTS_MSG_ADD_TABLE, table); msg = fts_optimize_create_msg(FTS_MSG_ADD_TABLE, table);
ib_wqueue_add(fts_optimize_wq, msg, msg->heap); ib_wqueue_add(fts_optimize_wq, msg, msg->heap);
mutex_enter(&table->fts->bg_threads_mutex);
table->fts->in_queue = true;
mutex_exit(&table->fts->bg_threads_mutex);
} }
/**********************************************************************//** /**********************************************************************//**
@@ -2656,6 +2660,15 @@ fts_optimize_remove_table(
return; return;
} }
fts_t* fts = table->fts;
mutex_enter(&fts->bg_threads_mutex);
bool is_in_optimize_queue = fts->in_queue;
mutex_exit(&fts->bg_threads_mutex);
if (!is_in_optimize_queue) {
return;
}
msg = fts_optimize_create_msg(FTS_MSG_DEL_TABLE, NULL); msg = fts_optimize_create_msg(FTS_MSG_DEL_TABLE, NULL);
/* We will wait on this event until signalled by the consumer. */ /* We will wait on this event until signalled by the consumer. */
@@ -2673,6 +2686,10 @@ fts_optimize_remove_table(
os_event_wait(event); os_event_wait(event);
os_event_free(event); os_event_free(event);
mutex_enter(&fts->bg_threads_mutex);
fts->in_queue = false;
mutex_exit(&fts->bg_threads_mutex);
} }
/** Send sync fts cache for the table. /** Send sync fts cache for the table.
@@ -2706,6 +2723,10 @@ fts_optimize_request_sync_table(
msg->ptr = table_id; msg->ptr = table_id;
ib_wqueue_add(fts_optimize_wq, msg, msg->heap); ib_wqueue_add(fts_optimize_wq, msg, msg->heap);
mutex_enter(&table->fts->bg_threads_mutex);
table->fts->in_queue = true;
mutex_exit(&table->fts->bg_threads_mutex);
} }
/** Add a table to fts_slots if it doesn't already exist. */ /** Add a table to fts_slots if it doesn't already exist. */
@@ -2850,6 +2871,10 @@ static void fts_optimize_sync_table(table_id_t table_id)
fts_sync_table(table, true, false, false); fts_sync_table(table, true, false, false);
} }
DBUG_EXECUTE_IF(
"ib_optimize_wq_hang",
os_thread_sleep(6000000););
dict_table_close(table, FALSE, FALSE); dict_table_close(table, FALSE, FALSE);
} }
} }

View File

@@ -165,8 +165,7 @@ fts_parse_sql(
str = ut_str3cat(fts_sql_begin, sql, fts_sql_end); str = ut_str3cat(fts_sql_begin, sql, fts_sql_end);
dict_locked = (fts_table && fts_table->table->fts dict_locked = (fts_table && fts_table->table->fts
&& (fts_table->table->fts->fts_status && fts_table->table->fts->dict_locked);
& TABLE_DICT_LOCKED));
if (!dict_locked) { if (!dict_locked) {
ut_ad(!mutex_own(&(dict_sys->mutex))); ut_ad(!mutex_own(&(dict_sys->mutex)));

View File

@@ -10094,10 +10094,10 @@ ha_innobase::ft_init_ext(
return(NULL); return(NULL);
} }
if (!(ft_table->fts->fts_status & ADDED_TABLE_SYNCED)) { if (!(ft_table->fts->added_synced)) {
fts_init_index(ft_table, FALSE); fts_init_index(ft_table, FALSE);
ft_table->fts->fts_status |= ADDED_TABLE_SYNCED; ft_table->fts->added_synced = true;
} }
error = fts_query(trx, index, flags, query, query_len, &result); error = fts_query(trx, index, flags, query, query_len, &result);

View File

@@ -3245,15 +3245,13 @@ op_ok:
goto error_handling; goto error_handling;
} }
ctx->new_table->fts->fts_status ctx->new_table->fts->dict_locked = true;
|= TABLE_DICT_LOCKED;
error = innobase_fts_load_stopword( error = innobase_fts_load_stopword(
ctx->new_table, ctx->trx, ctx->new_table, ctx->trx,
ctx->prebuilt->trx->mysql_thd) ctx->prebuilt->trx->mysql_thd)
? DB_SUCCESS : DB_ERROR; ? DB_SUCCESS : DB_ERROR;
ctx->new_table->fts->fts_status ctx->new_table->fts->dict_locked = false;
&= ~TABLE_DICT_LOCKED;
if (error != DB_SUCCESS) { if (error != DB_SUCCESS) {
goto error_handling; goto error_handling;
@@ -5624,6 +5622,7 @@ commit_cache_norebuild(
|| (index->type || (index->type
& DICT_CORRUPT)); & DICT_CORRUPT));
DBUG_ASSERT(index->table->fts); DBUG_ASSERT(index->table->fts);
DEBUG_SYNC_C("norebuild_fts_drop");
fts_drop_index(index->table, index, trx); fts_drop_index(index->table, index, trx);
} }

View File

@@ -1016,18 +1016,6 @@ dict_table_copy_types(
dtuple_t* tuple, /*!< in/out: data tuple */ dtuple_t* tuple, /*!< in/out: data tuple */
const dict_table_t* table) /*!< in: table */ const dict_table_t* table) /*!< in: table */
MY_ATTRIBUTE((nonnull)); MY_ATTRIBUTE((nonnull));
/********************************************************************
Wait until all the background threads of the given table have exited, i.e.,
bg_threads == 0. Note: bg_threads_mutex must be reserved when
calling this. */
UNIV_INTERN
void
dict_table_wait_for_bg_threads_to_exit(
/*===================================*/
dict_table_t* table, /* in: table */
ulint delay) /* in: time in microseconds to wait between
checks of bg_threads. */
MY_ATTRIBUTE((nonnull));
/**********************************************************************//** /**********************************************************************//**
Looks for an index with the given id. NOTE that we do not reserve Looks for an index with the given id. NOTE that we do not reserve
the dictionary mutex: this function is for emergency purposes like the dictionary mutex: this function is for emergency purposes like

View File

@@ -279,40 +279,21 @@ struct fts_table_t {
index auxiliary table */ index auxiliary table */
}; };
enum fts_status {
BG_THREAD_STOP = 1, /*!< TRUE if the FTS background thread
has finished reading the ADDED table,
meaning more items can be added to
the table. */
BG_THREAD_READY = 2, /*!< TRUE if the FTS background thread
is ready */
ADD_THREAD_STARTED = 4, /*!< TRUE if the FTS add thread
has started */
ADDED_TABLE_SYNCED = 8, /*!< TRUE if the ADDED table record is
sync-ed after crash recovery */
TABLE_DICT_LOCKED = 16 /*!< Set if the table has
dict_sys->mutex */
};
typedef enum fts_status fts_status_t;
/** The state of the FTS sub system. */ /** The state of the FTS sub system. */
struct fts_t { struct fts_t {
/*!< mutex protecting bg_threads* and /*!< mutex protecting bg_threads* and
fts_add_wq. */ fts_add_wq. */
ib_mutex_t bg_threads_mutex; ib_mutex_t bg_threads_mutex;
ulint bg_threads; /*!< number of background threads /* Wheter the table was added to fts_optimize_wq();
accessing this table */ protected by bg_threads mutex */
unsigned in_queue:1;
/*!< TRUE if background threads running /* Whether the ADDED table record sync-ed after
should stop themselves */ crash recovery; protected by bg_threads mutex */
ulint fts_status; /*!< Status bit regarding fts unsigned added_synced:1;
running state */ /* Whether the table hold dict_sys->mutex;
protected by bg_threads mutex */
unsigned dict_locked:1;
ib_wqueue_t* add_wq; /*!< Work queue for scheduling jobs ib_wqueue_t* add_wq; /*!< Work queue for scheduling jobs
for the FTS 'Add' thread, or NULL for the FTS 'Add' thread, or NULL
@@ -614,28 +595,6 @@ void
fts_startup(void); fts_startup(void);
/*==============*/ /*==============*/
/******************************************************************//**
Signal FTS threads to initiate shutdown. */
UNIV_INTERN
void
fts_start_shutdown(
/*===============*/
dict_table_t* table, /*!< in: table with FTS
indexes */
fts_t* fts); /*!< in: fts instance to
shutdown */
/******************************************************************//**
Wait for FTS threads to shutdown. */
UNIV_INTERN
void
fts_shutdown(
/*=========*/
dict_table_t* table, /*!< in: table with FTS
indexes */
fts_t* fts); /*!< in: fts instance to
shutdown */
/******************************************************************//** /******************************************************************//**
Create an instance of fts_t. Create an instance of fts_t.
@return instance of fts_t */ @return instance of fts_t */

View File

@@ -521,20 +521,6 @@ fts_cache_append_deleted_doc_ids(
const fts_cache_t* const fts_cache_t*
cache, /*!< in: cache to use */ cache, /*!< in: cache to use */
ib_vector_t* vector); /*!< in: append to this vector */ ib_vector_t* vector); /*!< in: append to this vector */
/******************************************************************//**
Wait for the background thread to start. We poll to detect change
of state, which is acceptable, since the wait should happen only
once during startup.
@return true if the thread started else FALSE (i.e timed out) */
UNIV_INTERN
ibool
fts_wait_for_background_thread_to_start(
/*====================================*/
dict_table_t* table, /*!< in: table to which the thread
is attached */
ulint max_wait); /*!< in: time in microseconds, if set
to 0 then it disables timeout
checking */
#ifdef FTS_DOC_STATS_DEBUG #ifdef FTS_DOC_STATS_DEBUG
/******************************************************************//** /******************************************************************//**
Get the total number of words in the FTS for a particular FTS index. Get the total number of words in the FTS for a particular FTS index.

View File

@@ -3829,11 +3829,11 @@ next_rec:
DBUG_EXECUTE_IF("ib_trunc_sleep_before_fts_cache_clear", DBUG_EXECUTE_IF("ib_trunc_sleep_before_fts_cache_clear",
os_thread_sleep(10000000);); os_thread_sleep(10000000););
table->fts->fts_status |= TABLE_DICT_LOCKED; table->fts->dict_locked = true;
fts_update_next_doc_id(trx, table, 0); fts_update_next_doc_id(trx, table, 0);
fts_cache_clear(table->fts->cache); fts_cache_clear(table->fts->cache);
fts_cache_init(table->fts->cache); fts_cache_init(table->fts->cache);
table->fts->fts_status &= ~TABLE_DICT_LOCKED; table->fts->dict_locked = false;
} }
} }
@@ -4434,8 +4434,7 @@ do_drop:
/* Need to set TABLE_DICT_LOCKED bit, since /* Need to set TABLE_DICT_LOCKED bit, since
fts_que_graph_free_check_lock would try to acquire fts_que_graph_free_check_lock would try to acquire
dict mutex lock */ dict mutex lock */
table->fts->fts_status |= TABLE_DICT_LOCKED; table->fts->dict_locked = true;
fts_free(table); fts_free(table);
} }

View File

@@ -1062,17 +1062,7 @@ trx_finalize_for_fts_table(
{ {
fts_t* fts = ftt->table->fts; fts_t* fts = ftt->table->fts;
fts_doc_ids_t* doc_ids = ftt->added_doc_ids; fts_doc_ids_t* doc_ids = ftt->added_doc_ids;
mutex_enter(&fts->bg_threads_mutex);
if (fts->fts_status & BG_THREAD_STOP) {
/* The table is about to be dropped, no use
adding anything to its work queue. */
mutex_exit(&fts->bg_threads_mutex);
} else {
mem_heap_t* heap; mem_heap_t* heap;
mutex_exit(&fts->bg_threads_mutex);
ut_a(fts->add_wq); ut_a(fts->add_wq);
@@ -1083,7 +1073,6 @@ trx_finalize_for_fts_table(
/* fts_trx_table_t no longer owns the list. */ /* fts_trx_table_t no longer owns the list. */
ftt->added_doc_ids = NULL; ftt->added_doc_ids = NULL;
} }
}
/******************************************************************//** /******************************************************************//**
Finalize a transaction containing updates to FTS tables. */ Finalize a transaction containing updates to FTS tables. */

View File

@@ -2921,33 +2921,6 @@ dict_table_copy_types(
} }
} }
/********************************************************************
Wait until all the background threads of the given table have exited, i.e.,
bg_threads == 0. Note: bg_threads_mutex must be reserved when
calling this. */
UNIV_INTERN
void
dict_table_wait_for_bg_threads_to_exit(
/*===================================*/
dict_table_t* table, /*< in: table */
ulint delay) /*< in: time in microseconds to wait between
checks of bg_threads. */
{
fts_t* fts = table->fts;
#ifdef UNIV_SYNC_DEBUG
ut_ad(mutex_own(&fts->bg_threads_mutex));
#endif /* UNIV_SYNC_DEBUG */
while (fts->bg_threads > 0) {
mutex_exit(&fts->bg_threads_mutex);
os_thread_sleep(delay);
mutex_enter(&fts->bg_threads_mutex);
}
}
/*******************************************************************//** /*******************************************************************//**
Builds the internal dictionary cache representation for a clustered Builds the internal dictionary cache representation for a clustered
index, containing also system fields not defined by the user. index, containing also system fields not defined by the user.

View File

@@ -224,8 +224,7 @@ fts_config_set_value(
pars_info_bind_varchar_literal(info, "value", pars_info_bind_varchar_literal(info, "value",
value->f_str, value->f_len); value->f_str, value->f_len);
const bool dict_locked = fts_table->table->fts->fts_status const bool dict_locked = fts_table->table->fts->dict_locked;
& TABLE_DICT_LOCKED;
fts_table->suffix = "CONFIG"; fts_table->suffix = "CONFIG";
fts_get_table_name(fts_table, table_name, dict_locked); fts_get_table_name(fts_table, table_name, dict_locked);

View File

@@ -458,7 +458,7 @@ fts_load_user_stopword(
dberr_t error = DB_SUCCESS; dberr_t error = DB_SUCCESS;
ibool ret = TRUE; ibool ret = TRUE;
trx_t* trx; trx_t* trx;
ibool has_lock = fts->fts_status & TABLE_DICT_LOCKED; ibool has_lock = fts->dict_locked;
trx = trx_allocate_for_background(); trx = trx_allocate_for_background();
trx->op_info = "Load user stopword table into FTS cache"; trx->op_info = "Load user stopword table into FTS cache";
@@ -933,18 +933,16 @@ fts_que_graph_free_check_lock(
const fts_index_cache_t*index_cache, /*!< in: FTS index cache */ const fts_index_cache_t*index_cache, /*!< in: FTS index cache */
que_t* graph) /*!< in: query graph */ que_t* graph) /*!< in: query graph */
{ {
ibool has_dict = FALSE; bool has_dict = FALSE;
if (fts_table && fts_table->table) { if (fts_table && fts_table->table) {
ut_ad(fts_table->table->fts); ut_ad(fts_table->table->fts);
has_dict = fts_table->table->fts->fts_status has_dict = fts_table->table->fts->dict_locked;
& TABLE_DICT_LOCKED;
} else if (index_cache) { } else if (index_cache) {
ut_ad(index_cache->index->table->fts); ut_ad(index_cache->index->table->fts);
has_dict = index_cache->index->table->fts->fts_status has_dict = index_cache->index->table->fts->dict_locked;
& TABLE_DICT_LOCKED;
} }
if (!has_dict) { if (!has_dict) {
@@ -2831,7 +2829,7 @@ fts_update_sync_doc_id(
pars_info_bind_varchar_literal(info, "doc_id", id, id_len); pars_info_bind_varchar_literal(info, "doc_id", id, id_len);
fts_get_table_name(&fts_table, fts_name, fts_get_table_name(&fts_table, fts_name,
table->fts->fts_status & TABLE_DICT_LOCKED); table->fts->dict_locked);
pars_info_bind_id(info, true, "table_name", fts_name); pars_info_bind_id(info, true, "table_name", fts_name);
graph = fts_parse_sql( graph = fts_parse_sql(
@@ -2947,7 +2945,7 @@ fts_delete(
into cache from last crash (delete Doc will not initialize the into cache from last crash (delete Doc will not initialize the
sync). Avoid any added counter accounting until the FTS cache sync). Avoid any added counter accounting until the FTS cache
is re-established and sync-ed */ is re-established and sync-ed */
if (table->fts->fts_status & ADDED_TABLE_SYNCED if (table->fts->added_synced
&& doc_id > cache->synced_doc_id) { && doc_id > cache->synced_doc_id) {
mutex_enter(&table->fts->cache->deleted_lock); mutex_enter(&table->fts->cache->deleted_lock);
@@ -3385,7 +3383,7 @@ fts_add_doc_by_id(
/* If Doc ID has been supplied by the user, then the table /* If Doc ID has been supplied by the user, then the table
might not yet be sync-ed */ might not yet be sync-ed */
if (!(ftt->table->fts->fts_status & ADDED_TABLE_SYNCED)) { if (!ftt->table->fts->added_synced) {
fts_init_index(ftt->table, FALSE); fts_init_index(ftt->table, FALSE);
} }
@@ -4933,7 +4931,7 @@ fts_init_doc_id(
fts_init_index((dict_table_t*) table, TRUE); fts_init_index((dict_table_t*) table, TRUE);
} }
table->fts->fts_status |= ADDED_TABLE_SYNCED; table->fts->added_synced = true;
table->fts->cache->first_doc_id = max_doc_id; table->fts->cache->first_doc_id = max_doc_id;
@@ -5386,69 +5384,6 @@ fts_cache_append_deleted_doc_ids(
mutex_exit((ib_mutex_t*) &cache->deleted_lock); mutex_exit((ib_mutex_t*) &cache->deleted_lock);
} }
/*********************************************************************//**
Wait for the background thread to start. We poll to detect change
of state, which is acceptable, since the wait should happen only
once during startup.
@return true if the thread started else FALSE (i.e timed out) */
UNIV_INTERN
ibool
fts_wait_for_background_thread_to_start(
/*====================================*/
dict_table_t* table, /*!< in: table to which the thread
is attached */
ulint max_wait) /*!< in: time in microseconds, if
set to 0 then it disables
timeout checking */
{
ulint count = 0;
ibool done = FALSE;
ut_a(max_wait == 0 || max_wait >= FTS_MAX_BACKGROUND_THREAD_WAIT);
for (;;) {
fts_t* fts = table->fts;
mutex_enter(&fts->bg_threads_mutex);
if (fts->fts_status & BG_THREAD_READY) {
done = TRUE;
}
mutex_exit(&fts->bg_threads_mutex);
if (!done) {
os_thread_sleep(FTS_MAX_BACKGROUND_THREAD_WAIT);
if (max_wait > 0) {
max_wait -= FTS_MAX_BACKGROUND_THREAD_WAIT;
/* We ignore the residual value. */
if (max_wait < FTS_MAX_BACKGROUND_THREAD_WAIT) {
break;
}
}
++count;
} else {
break;
}
if (count >= FTS_BACKGROUND_THREAD_WAIT_COUNT) {
ut_print_timestamp(stderr);
fprintf(stderr, " InnoDB: Error the background thread "
"for the FTS table %s refuses to start\n",
table->name);
count = 0;
}
}
return(done);
}
/*********************************************************************//** /*********************************************************************//**
Add the FTS document id hidden column. */ Add the FTS document id hidden column. */
UNIV_INTERN UNIV_INTERN
@@ -5589,42 +5524,6 @@ fts_free(
table->fts = NULL; table->fts = NULL;
} }
/*********************************************************************//**
Signal FTS threads to initiate shutdown. */
UNIV_INTERN
void
fts_start_shutdown(
/*===============*/
dict_table_t* table, /*!< in: table with FTS indexes */
fts_t* fts) /*!< in: fts instance that needs
to be informed about shutdown */
{
mutex_enter(&fts->bg_threads_mutex);
fts->fts_status |= BG_THREAD_STOP;
mutex_exit(&fts->bg_threads_mutex);
}
/*********************************************************************//**
Wait for FTS threads to shutdown. */
UNIV_INTERN
void
fts_shutdown(
/*=========*/
dict_table_t* table, /*!< in: table with FTS indexes */
fts_t* fts) /*!< in: fts instance to shutdown */
{
mutex_enter(&fts->bg_threads_mutex);
ut_a(fts->fts_status & BG_THREAD_STOP);
dict_table_wait_for_bg_threads_to_exit(table, 20000);
mutex_exit(&fts->bg_threads_mutex);
}
/*********************************************************************//** /*********************************************************************//**
Take a FTS savepoint. */ Take a FTS savepoint. */
UNIV_INLINE UNIV_INLINE
@@ -7639,7 +7538,7 @@ fts_init_index(
} }
rw_lock_x_unlock(&cache->init_lock); rw_lock_x_unlock(&cache->init_lock);
if (table->fts->fts_status & ADDED_TABLE_SYNCED) { if (table->fts->added_synced) {
goto func_exit; goto func_exit;
} }
@@ -7681,7 +7580,7 @@ fts_init_index(
} }
} }
table->fts->fts_status |= ADDED_TABLE_SYNCED; table->fts->added_synced = true;
fts_get_docs_clear(cache->get_docs); fts_get_docs_clear(cache->get_docs);

View File

@@ -2628,6 +2628,10 @@ UNIV_INTERN void fts_optimize_add_table(dict_table_t* table)
msg = fts_optimize_create_msg(FTS_MSG_ADD_TABLE, table); msg = fts_optimize_create_msg(FTS_MSG_ADD_TABLE, table);
ib_wqueue_add(fts_optimize_wq, msg, msg->heap); ib_wqueue_add(fts_optimize_wq, msg, msg->heap);
mutex_enter(&table->fts->bg_threads_mutex);
table->fts->in_queue = true;
mutex_exit(&table->fts->bg_threads_mutex);
} }
/**********************************************************************//** /**********************************************************************//**
@@ -2656,6 +2660,15 @@ fts_optimize_remove_table(
return; return;
} }
fts_t* fts = table->fts;
mutex_enter(&fts->bg_threads_mutex);
bool is_in_optimize_queue = fts->in_queue;
mutex_exit(&fts->bg_threads_mutex);
if (!is_in_optimize_queue) {
return;
}
msg = fts_optimize_create_msg(FTS_MSG_DEL_TABLE, NULL); msg = fts_optimize_create_msg(FTS_MSG_DEL_TABLE, NULL);
/* We will wait on this event until signalled by the consumer. */ /* We will wait on this event until signalled by the consumer. */
@@ -2673,6 +2686,10 @@ fts_optimize_remove_table(
os_event_wait(event); os_event_wait(event);
os_event_free(event); os_event_free(event);
mutex_enter(&fts->bg_threads_mutex);
fts->in_queue = false;
mutex_exit(&fts->bg_threads_mutex);
} }
/** Send sync fts cache for the table. /** Send sync fts cache for the table.
@@ -2706,6 +2723,10 @@ fts_optimize_request_sync_table(
msg->ptr = table_id; msg->ptr = table_id;
ib_wqueue_add(fts_optimize_wq, msg, msg->heap); ib_wqueue_add(fts_optimize_wq, msg, msg->heap);
mutex_enter(&table->fts->bg_threads_mutex);
table->fts->in_queue = true;
mutex_exit(&table->fts->bg_threads_mutex);
} }
/** Add a table to fts_slots if it doesn't already exist. */ /** Add a table to fts_slots if it doesn't already exist. */
@@ -2850,6 +2871,10 @@ static void fts_optimize_sync_table(table_id_t table_id)
fts_sync_table(table, true, false, false); fts_sync_table(table, true, false, false);
} }
DBUG_EXECUTE_IF(
"ib_optimize_wq_hang",
os_thread_sleep(6000000););
dict_table_close(table, FALSE, FALSE); dict_table_close(table, FALSE, FALSE);
} }
} }

View File

@@ -165,8 +165,7 @@ fts_parse_sql(
str = ut_str3cat(fts_sql_begin, sql, fts_sql_end); str = ut_str3cat(fts_sql_begin, sql, fts_sql_end);
dict_locked = (fts_table && fts_table->table->fts dict_locked = (fts_table && fts_table->table->fts
&& (fts_table->table->fts->fts_status && fts_table->table->fts->dict_locked);
& TABLE_DICT_LOCKED));
if (!dict_locked) { if (!dict_locked) {
ut_ad(!mutex_own(&(dict_sys->mutex))); ut_ad(!mutex_own(&(dict_sys->mutex)));

View File

@@ -10640,10 +10640,10 @@ ha_innobase::ft_init_ext(
return(NULL); return(NULL);
} }
if (!(ft_table->fts->fts_status & ADDED_TABLE_SYNCED)) { if (!(ft_table->fts->added_synced)) {
fts_init_index(ft_table, FALSE); fts_init_index(ft_table, FALSE);
ft_table->fts->fts_status |= ADDED_TABLE_SYNCED; ft_table->fts->added_synced = true;
} }
error = fts_query(trx, index, flags, query, query_len, &result); error = fts_query(trx, index, flags, query, query_len, &result);

View File

@@ -3252,15 +3252,13 @@ op_ok:
goto error_handling; goto error_handling;
} }
ctx->new_table->fts->fts_status ctx->new_table->fts->dict_locked = true;
|= TABLE_DICT_LOCKED;
error = innobase_fts_load_stopword( error = innobase_fts_load_stopword(
ctx->new_table, ctx->trx, ctx->new_table, ctx->trx,
ctx->prebuilt->trx->mysql_thd) ctx->prebuilt->trx->mysql_thd)
? DB_SUCCESS : DB_ERROR; ? DB_SUCCESS : DB_ERROR;
ctx->new_table->fts->fts_status ctx->new_table->fts->dict_locked = false;
&= ~TABLE_DICT_LOCKED;
if (error != DB_SUCCESS) { if (error != DB_SUCCESS) {
goto error_handling; goto error_handling;
@@ -5642,6 +5640,7 @@ commit_cache_norebuild(
|| (index->type || (index->type
& DICT_CORRUPT)); & DICT_CORRUPT));
DBUG_ASSERT(index->table->fts); DBUG_ASSERT(index->table->fts);
DEBUG_SYNC_C("norebuild_fts_drop");
fts_drop_index(index->table, index, trx); fts_drop_index(index->table, index, trx);
} }

View File

@@ -1015,18 +1015,6 @@ dict_table_copy_types(
dtuple_t* tuple, /*!< in/out: data tuple */ dtuple_t* tuple, /*!< in/out: data tuple */
const dict_table_t* table) /*!< in: table */ const dict_table_t* table) /*!< in: table */
MY_ATTRIBUTE((nonnull)); MY_ATTRIBUTE((nonnull));
/********************************************************************
Wait until all the background threads of the given table have exited, i.e.,
bg_threads == 0. Note: bg_threads_mutex must be reserved when
calling this. */
UNIV_INTERN
void
dict_table_wait_for_bg_threads_to_exit(
/*===================================*/
dict_table_t* table, /* in: table */
ulint delay) /* in: time in microseconds to wait between
checks of bg_threads. */
MY_ATTRIBUTE((nonnull));
/**********************************************************************//** /**********************************************************************//**
Looks for an index with the given id. NOTE that we do not reserve Looks for an index with the given id. NOTE that we do not reserve
the dictionary mutex: this function is for emergency purposes like the dictionary mutex: this function is for emergency purposes like

View File

@@ -279,40 +279,21 @@ struct fts_table_t {
index auxiliary table */ index auxiliary table */
}; };
enum fts_status {
BG_THREAD_STOP = 1, /*!< TRUE if the FTS background thread
has finished reading the ADDED table,
meaning more items can be added to
the table. */
BG_THREAD_READY = 2, /*!< TRUE if the FTS background thread
is ready */
ADD_THREAD_STARTED = 4, /*!< TRUE if the FTS add thread
has started */
ADDED_TABLE_SYNCED = 8, /*!< TRUE if the ADDED table record is
sync-ed after crash recovery */
TABLE_DICT_LOCKED = 16 /*!< Set if the table has
dict_sys->mutex */
};
typedef enum fts_status fts_status_t;
/** The state of the FTS sub system. */ /** The state of the FTS sub system. */
struct fts_t { struct fts_t {
/*!< mutex protecting bg_threads* and /*!< mutex protecting bg_threads* and
fts_add_wq. */ fts_add_wq. */
ib_mutex_t bg_threads_mutex; ib_mutex_t bg_threads_mutex;
ulint bg_threads; /*!< number of background threads /* Whether the table was added to fts_optimize_wq();
accessing this table */ protected by bg_threads mutex */
unsigned in_queue:1;
/*!< TRUE if background threads running /* Whether the ADDED table record sync-ed afer
should stop themselves */ crash recovery; protected by bg_threads mutex */
ulint fts_status; /*!< Status bit regarding fts unsigned added_synced:1;
running state */ /* Whether the table hold dict_sys->mutex;
protected by bg_threads mutex */
unsigned dict_locked:1;
ib_wqueue_t* add_wq; /*!< Work queue for scheduling jobs ib_wqueue_t* add_wq; /*!< Work queue for scheduling jobs
for the FTS 'Add' thread, or NULL for the FTS 'Add' thread, or NULL
@@ -614,28 +595,6 @@ void
fts_startup(void); fts_startup(void);
/*==============*/ /*==============*/
/******************************************************************//**
Signal FTS threads to initiate shutdown. */
UNIV_INTERN
void
fts_start_shutdown(
/*===============*/
dict_table_t* table, /*!< in: table with FTS
indexes */
fts_t* fts); /*!< in: fts instance to
shutdown */
/******************************************************************//**
Wait for FTS threads to shutdown. */
UNIV_INTERN
void
fts_shutdown(
/*=========*/
dict_table_t* table, /*!< in: table with FTS
indexes */
fts_t* fts); /*!< in: fts instance to
shutdown */
/******************************************************************//** /******************************************************************//**
Create an instance of fts_t. Create an instance of fts_t.
@return instance of fts_t */ @return instance of fts_t */

View File

@@ -521,20 +521,6 @@ fts_cache_append_deleted_doc_ids(
const fts_cache_t* const fts_cache_t*
cache, /*!< in: cache to use */ cache, /*!< in: cache to use */
ib_vector_t* vector); /*!< in: append to this vector */ ib_vector_t* vector); /*!< in: append to this vector */
/******************************************************************//**
Wait for the background thread to start. We poll to detect change
of state, which is acceptable, since the wait should happen only
once during startup.
@return true if the thread started else FALSE (i.e timed out) */
UNIV_INTERN
ibool
fts_wait_for_background_thread_to_start(
/*====================================*/
dict_table_t* table, /*!< in: table to which the thread
is attached */
ulint max_wait); /*!< in: time in microseconds, if set
to 0 then it disables timeout
checking */
#ifdef FTS_DOC_STATS_DEBUG #ifdef FTS_DOC_STATS_DEBUG
/******************************************************************//** /******************************************************************//**
Get the total number of words in the FTS for a particular FTS index. Get the total number of words in the FTS for a particular FTS index.

View File

@@ -3839,11 +3839,11 @@ next_rec:
DBUG_EXECUTE_IF("ib_trunc_sleep_before_fts_cache_clear", DBUG_EXECUTE_IF("ib_trunc_sleep_before_fts_cache_clear",
os_thread_sleep(10000000);); os_thread_sleep(10000000););
table->fts->fts_status |= TABLE_DICT_LOCKED; table->fts->dict_locked = true;
fts_update_next_doc_id(trx, table, 0); fts_update_next_doc_id(trx, table, 0);
fts_cache_clear(table->fts->cache); fts_cache_clear(table->fts->cache);
fts_cache_init(table->fts->cache); fts_cache_init(table->fts->cache);
table->fts->fts_status &= ~TABLE_DICT_LOCKED; table->fts->dict_locked = false;
} }
} }
@@ -4444,8 +4444,7 @@ do_drop:
/* Need to set TABLE_DICT_LOCKED bit, since /* Need to set TABLE_DICT_LOCKED bit, since
fts_que_graph_free_check_lock would try to acquire fts_que_graph_free_check_lock would try to acquire
dict mutex lock */ dict mutex lock */
table->fts->fts_status |= TABLE_DICT_LOCKED; table->fts->dict_locked = true;
fts_free(table); fts_free(table);
} }

View File

@@ -1269,17 +1269,7 @@ trx_finalize_for_fts_table(
{ {
fts_t* fts = ftt->table->fts; fts_t* fts = ftt->table->fts;
fts_doc_ids_t* doc_ids = ftt->added_doc_ids; fts_doc_ids_t* doc_ids = ftt->added_doc_ids;
mutex_enter(&fts->bg_threads_mutex);
if (fts->fts_status & BG_THREAD_STOP) {
/* The table is about to be dropped, no use
adding anything to its work queue. */
mutex_exit(&fts->bg_threads_mutex);
} else {
mem_heap_t* heap; mem_heap_t* heap;
mutex_exit(&fts->bg_threads_mutex);
ut_a(fts->add_wq); ut_a(fts->add_wq);
@@ -1290,7 +1280,6 @@ trx_finalize_for_fts_table(
/* fts_trx_table_t no longer owns the list. */ /* fts_trx_table_t no longer owns the list. */
ftt->added_doc_ids = NULL; ftt->added_doc_ids = NULL;
} }
}
/******************************************************************//** /******************************************************************//**
Finalize a transaction containing updates to FTS tables. */ Finalize a transaction containing updates to FTS tables. */