From bd22650bbcb3d4d64da186311fd04fb935096cd2 Mon Sep 17 00:00:00 2001 From: Thirunarayanan Balathandayuthapani Date: Wed, 23 Oct 2019 17:00:12 +0530 Subject: [PATCH 1/2] MDEV-19073 FTS row mismatch after crash recovery InnoDB stores synced_doc_id + 1 value in FTS_CONFIG table. But while reading the synced doc id from FTS_CONFIG table after restart, InnoDB should read synced_doc_id - 1 to get the actual synced doc id value. --- .../suite/innodb_fts/r/crash_recovery.result | 36 ++++++++++++ .../suite/innodb_fts/t/crash_recovery.test | 55 +++++++++++++++++++ storage/innobase/fts/fts0fts.cc | 4 ++ storage/xtradb/fts/fts0fts.cc | 4 ++ 4 files changed, 99 insertions(+) diff --git a/mysql-test/suite/innodb_fts/r/crash_recovery.result b/mysql-test/suite/innodb_fts/r/crash_recovery.result index e21631471d2..648128424a8 100644 --- a/mysql-test/suite/innodb_fts/r/crash_recovery.result +++ b/mysql-test/suite/innodb_fts/r/crash_recovery.result @@ -67,6 +67,31 @@ INSERT INTO articles VALUES BEGIN; INSERT INTO articles VALUES (100, 200, 'MySQL Tutorial','DBMS stands for DataBase ...'); +# +# MDEV-19073 FTS row mismatch after crash recovery +# +CREATE TABLE mdev19073(id SERIAL, title VARCHAR(200), body TEXT, +FULLTEXT(title,body)) ENGINE=InnoDB; +INSERT INTO mdev19073 (title, body) VALUES +('MySQL Tutorial', 'DBMS stands for Database...'); +CREATE FULLTEXT INDEX idx ON mdev19073(title, body); +CREATE TABLE mdev19073_2 LIKE mdev19073; +INSERT INTO mdev19073_2 (title, body) VALUES +('MySQL Tutorial', 'DBMS stands for Database...'); +INSERT INTO mdev19073 (title, body) VALUES +('MariaDB Tutorial', 'DB means Database ...'); +INSERT INTO mdev19073_2 (title, body) VALUES +('MariaDB Tutorial', 'DB means Database ...'); +SELECT * FROM mdev19073 WHERE MATCH (title, body) +AGAINST ('Database' IN NATURAL LANGUAGE MODE); +id title body +1 MySQL Tutorial DBMS stands for Database... +2 MariaDB Tutorial DB means Database ... +SELECT * FROM mdev19073_2 WHERE MATCH (title, body) +AGAINST ('Database' IN NATURAL LANGUAGE MODE); +id title body +1 MySQL Tutorial DBMS stands for Database... +2 MariaDB Tutorial DB means Database ... # Kill and restart INSERT INTO articles VALUES (8, 12, 'MySQL Tutorial','DBMS stands for DataBase ...'); SELECT * FROM articles WHERE MATCH (title, body) @@ -76,3 +101,14 @@ id FTS_DOC_ID title body 1 10 MySQL Tutorial DBMS stands for DataBase ... 8 12 MySQL Tutorial DBMS stands for DataBase ... DROP TABLE articles; +SELECT * FROM mdev19073 WHERE MATCH (title, body) +AGAINST ('Database' IN NATURAL LANGUAGE MODE); +id title body +1 MySQL Tutorial DBMS stands for Database... +2 MariaDB Tutorial DB means Database ... +SELECT * FROM mdev19073_2 WHERE MATCH (title, body) +AGAINST ('Database' IN NATURAL LANGUAGE MODE); +id title body +1 MySQL Tutorial DBMS stands for Database... +2 MariaDB Tutorial DB means Database ... +DROP TABLE mdev19073, mdev19073_2; diff --git a/mysql-test/suite/innodb_fts/t/crash_recovery.test b/mysql-test/suite/innodb_fts/t/crash_recovery.test index ec055930672..861dfdfd658 100644 --- a/mysql-test/suite/innodb_fts/t/crash_recovery.test +++ b/mysql-test/suite/innodb_fts/t/crash_recovery.test @@ -6,6 +6,7 @@ --source include/have_innodb.inc # The embedded server tests do not support restarting. --source include/not_embedded.inc +--source include/maybe_debug.inc # Following are test for crash recovery on FTS index, the first scenario # is for bug Bug #14586855 INNODB: FAILING ASSERTION: (DICT_INDEX_GET_N_UNIQUE( @@ -62,13 +63,17 @@ INSERT INTO articles (title,body) VALUES ('MySQL vs. YourSQL','In the following database comparison ...'), ('MySQL Security','When configured properly, MySQL ...'); +connect(dml, localhost, root,,); BEGIN; INSERT INTO articles (title,body) VALUES ('MySQL Tutorial','DBMS stands for DataBase ...'); +connection default; --source include/kill_and_restart_mysqld.inc +disconnect dml; + # This insert will re-initialize the Doc ID counter, it should not crash INSERT INTO articles (title,body) VALUES ('MySQL Tutorial','DBMS stands for DataBase ...'); @@ -101,12 +106,55 @@ INSERT INTO articles VALUES (5, 6, 'MySQL vs. YourSQL','In the following database comparison ...'), (7, 4, 'MySQL Security','When configured properly, MySQL ...'); +connect(dml, localhost, root,,); BEGIN; INSERT INTO articles VALUES (100, 200, 'MySQL Tutorial','DBMS stands for DataBase ...'); +connect(dml2, localhost, root,,); + +--echo # +--echo # MDEV-19073 FTS row mismatch after crash recovery +--echo # + +CREATE TABLE mdev19073(id SERIAL, title VARCHAR(200), body TEXT, + FULLTEXT(title,body)) ENGINE=InnoDB; +INSERT INTO mdev19073 (title, body) VALUES + ('MySQL Tutorial', 'DBMS stands for Database...'); +CREATE FULLTEXT INDEX idx ON mdev19073(title, body); +CREATE TABLE mdev19073_2 LIKE mdev19073; +if ($have_debug) +{ +--disable_query_log +SET @saved_dbug = @@debug_dbug; +SET DEBUG_DBUG = '+d,fts_instrument_sync_debug'; +--enable_query_log +} +INSERT INTO mdev19073_2 (title, body) VALUES + ('MySQL Tutorial', 'DBMS stands for Database...'); +if ($have_debug) +{ +--disable_query_log +SET DEBUG_DBUG = @saved_dbug; +--enable_query_log +} + +INSERT INTO mdev19073 (title, body) VALUES + ('MariaDB Tutorial', 'DB means Database ...'); +INSERT INTO mdev19073_2 (title, body) VALUES + ('MariaDB Tutorial', 'DB means Database ...'); + +# Should return 2 rows +SELECT * FROM mdev19073 WHERE MATCH (title, body) +AGAINST ('Database' IN NATURAL LANGUAGE MODE); +SELECT * FROM mdev19073_2 WHERE MATCH (title, body) +AGAINST ('Database' IN NATURAL LANGUAGE MODE); + +connection default; --source include/kill_and_restart_mysqld.inc +disconnect dml; +disconnect dml2; # This would re-initialize the FTS index and do the re-tokenization # of above records @@ -116,3 +164,10 @@ SELECT * FROM articles WHERE MATCH (title, body) AGAINST ('Tutorial' IN NATURAL LANGUAGE MODE); DROP TABLE articles; + +# Should return 2 rows +SELECT * FROM mdev19073 WHERE MATCH (title, body) +AGAINST ('Database' IN NATURAL LANGUAGE MODE); +SELECT * FROM mdev19073_2 WHERE MATCH (title, body) +AGAINST ('Database' IN NATURAL LANGUAGE MODE); +DROP TABLE mdev19073, mdev19073_2; diff --git a/storage/innobase/fts/fts0fts.cc b/storage/innobase/fts/fts0fts.cc index 1cf5c818a9e..6dbe5e0e2a0 100644 --- a/storage/innobase/fts/fts0fts.cc +++ b/storage/innobase/fts/fts0fts.cc @@ -2739,6 +2739,10 @@ retry: } if (read_only) { + /* InnoDB stores actual synced_doc_id value + 1 in + FTS_CONFIG table. Reduce the value by 1 while reading + after startup. */ + if (*doc_id) *doc_id -= 1; goto func_exit; } diff --git a/storage/xtradb/fts/fts0fts.cc b/storage/xtradb/fts/fts0fts.cc index 1cf5c818a9e..6dbe5e0e2a0 100644 --- a/storage/xtradb/fts/fts0fts.cc +++ b/storage/xtradb/fts/fts0fts.cc @@ -2739,6 +2739,10 @@ retry: } if (read_only) { + /* InnoDB stores actual synced_doc_id value + 1 in + FTS_CONFIG table. Reduce the value by 1 while reading + after startup. */ + if (*doc_id) *doc_id -= 1; goto func_exit; } From a41d429765c7ddb528b9b438c68b25ff55d3bd55 Mon Sep 17 00:00:00 2001 From: Thirunarayanan Balathandayuthapani Date: Tue, 22 Oct 2019 12:05:42 +0530 Subject: [PATCH 2/2] MDEV-20621 FULLTEXT INDEX activity causes InnoDB hang - fts_optimize_thread() uses dict_table_t object instead of table id. So that it doesn't acquire dict_sys->mutex. It leads to remove the hang of dict_sys->mutex between fts_optimize_thread() and other threads. - in_queue to indicate whether the table is in fts_optimize_queue. It is protected by fts_optimize_wq->mutex to avoid any race condition. - fts_optimize_init() adds the fts table to the fts_optimize_wq --- storage/innobase/dict/dict0load.cc | 7 +- storage/innobase/fts/fts0opt.cc | 174 ++++++++++++--------------- storage/innobase/include/fts0fts.h | 16 +-- storage/innobase/include/fts0opt.h | 3 + storage/innobase/include/ut0wqueue.h | 33 ++--- storage/innobase/ut/ut0wqueue.cc | 22 ++-- storage/xtradb/dict/dict0load.cc | 7 +- storage/xtradb/fts/fts0opt.cc | 174 ++++++++++++--------------- storage/xtradb/include/fts0fts.h | 16 +-- storage/xtradb/include/fts0opt.h | 3 + storage/xtradb/include/ut0wqueue.h | 33 ++--- storage/xtradb/ut/ut0wqueue.cc | 22 ++-- 12 files changed, 246 insertions(+), 264 deletions(-) diff --git a/storage/innobase/dict/dict0load.cc b/storage/innobase/dict/dict0load.cc index dd9ddc5f20e..3a6851cff89 100644 --- a/storage/innobase/dict/dict0load.cc +++ b/storage/innobase/dict/dict0load.cc @@ -46,6 +46,7 @@ Created 4/24/1996 Heikki Tuuri #include "dict0priv.h" #include "ha_prototypes.h" /* innobase_casedn_str() */ #include "fts0priv.h" +#include "fts0opt.h" /** Following are the InnoDB system tables. The positions in this array are referenced by enum dict_system_table_id. */ @@ -2548,8 +2549,12 @@ func_exit: FTS */ fts_optimize_remove_table(table); fts_free(table); - } else { + } else if (fts_optimize_wq) { fts_optimize_add_table(table); + } else { + /* fts_optimize_thread is not started yet. + So make the table as non-evictable from cache. */ + dict_table_move_from_lru_to_non_lru(table); } } diff --git a/storage/innobase/fts/fts0opt.cc b/storage/innobase/fts/fts0opt.cc index 3c15b514102..7f498443544 100644 --- a/storage/innobase/fts/fts0opt.cc +++ b/storage/innobase/fts/fts0opt.cc @@ -34,6 +34,7 @@ Completed 2011/7/10 Sunny and Jimmy Yang #include "ut0wqueue.h" #include "srv0start.h" #include "zlib.h" +#include "fts0opt.h" #ifndef UNIV_NONINL #include "fts0types.ic" @@ -41,7 +42,7 @@ Completed 2011/7/10 Sunny and Jimmy Yang #endif /** The FTS optimize thread's work queue. */ -static ib_wqueue_t* fts_optimize_wq; +ib_wqueue_t* fts_optimize_wq; /** The FTS vector to store fts_slot_t */ static ib_vector_t* fts_slots; @@ -169,8 +170,8 @@ struct fts_encode_t { /** We use this information to determine when to start the optimize cycle for a table. */ struct fts_slot_t { - /** table identifier, or 0 if the slot is empty */ - table_id_t table_id; + /** table, or NULL if the slot is unused */ + dict_table_t* table; /** whether this slot is being processed */ bool running; @@ -2456,14 +2457,7 @@ fts_optimize_table_bk( return(DB_SUCCESS); } - dict_table_t* table = dict_table_open_on_id( - slot->table_id, FALSE, DICT_TABLE_OP_NORMAL); - - if (!table) { - slot->last_run = now; - return DB_SUCCESS; - } - + dict_table_t* table = slot->table; dberr_t error; if (fil_table_accessible(table) @@ -2483,8 +2477,6 @@ fts_optimize_table_bk( error = DB_SUCCESS; } - dict_table_close(table, FALSE, FALSE); - return(error); } /*********************************************************************//** @@ -2627,11 +2619,13 @@ UNIV_INTERN void fts_optimize_add_table(dict_table_t* table) msg = fts_optimize_create_msg(FTS_MSG_ADD_TABLE, table); - ib_wqueue_add(fts_optimize_wq, msg, msg->heap); + mutex_enter(&fts_optimize_wq->mutex); + + ib_wqueue_add(fts_optimize_wq, msg, msg->heap, true); - mutex_enter(&table->fts->bg_threads_mutex); table->fts->in_queue = true; - mutex_exit(&table->fts->bg_threads_mutex); + + mutex_exit(&fts_optimize_wq->mutex); } /**********************************************************************//** @@ -2648,7 +2642,7 @@ fts_optimize_remove_table( fts_msg_del_t* remove; /* if the optimize system not yet initialized, return */ - if (!fts_optimize_is_init()) { + if (!fts_optimize_wq) { return; } @@ -2660,12 +2654,10 @@ fts_optimize_remove_table( 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); + mutex_enter(&fts_optimize_wq->mutex); - if (!is_in_optimize_queue) { + if (!table->fts->in_queue) { + mutex_exit(&fts_optimize_wq->mutex); return; } @@ -2681,15 +2673,17 @@ fts_optimize_remove_table( remove->event = event; msg->ptr = remove; - ib_wqueue_add(fts_optimize_wq, msg, msg->heap); + ib_wqueue_add(fts_optimize_wq, msg, msg->heap, true); + + mutex_exit(&fts_optimize_wq->mutex); os_event_wait(event); os_event_free(event); - mutex_enter(&fts->bg_threads_mutex); - fts->in_queue = false; - mutex_exit(&fts->bg_threads_mutex); + ut_d(mutex_enter(&fts_optimize_wq->mutex)); + ut_ad(!table->fts->in_queue); + ut_d(mutex_exit(&fts_optimize_wq->mutex)); } /** Send sync fts cache for the table. @@ -2700,10 +2694,9 @@ fts_optimize_request_sync_table( dict_table_t* table) { fts_msg_t* msg; - table_id_t* table_id; /* if the optimize system not yet initialized, return */ - if (!fts_optimize_is_init()) { + if (!fts_optimize_wq) { return; } @@ -2715,39 +2708,36 @@ fts_optimize_request_sync_table( return; } - msg = fts_optimize_create_msg(FTS_MSG_SYNC_TABLE, NULL); + msg = fts_optimize_create_msg(FTS_MSG_SYNC_TABLE, table); - table_id = static_cast( - mem_heap_alloc(msg->heap, sizeof(table_id_t))); - *table_id = table->id; - msg->ptr = table_id; + mutex_enter(&fts_optimize_wq->mutex); - ib_wqueue_add(fts_optimize_wq, msg, msg->heap); + ib_wqueue_add(fts_optimize_wq, msg, msg->heap, true); - mutex_enter(&table->fts->bg_threads_mutex); table->fts->in_queue = true; - mutex_exit(&table->fts->bg_threads_mutex); + + mutex_exit(&fts_optimize_wq->mutex); } /** Add a table to fts_slots if it doesn't already exist. */ static bool fts_optimize_new_table(dict_table_t* table) { + ut_ad(table); + ulint i; fts_slot_t* slot; fts_slot_t* empty = NULL; - const table_id_t table_id = table->id; - ut_ad(table_id); /* Search for duplicates, also find a free slot if one exists. */ for (i = 0; i < ib_vector_size(fts_slots); ++i) { slot = static_cast(ib_vector_get(fts_slots, i)); - if (!slot->table_id) { + if (!slot->table) { empty = slot; - } else if (slot->table_id == table_id) { + } else if (slot->table == table) { /* Already exists in our optimize queue. */ - return(FALSE); + return false; } } @@ -2756,37 +2746,36 @@ static bool fts_optimize_new_table(dict_table_t* table) memset(slot, 0x0, sizeof(*slot)); - slot->table_id = table->id; - slot->running = false; - - return(TRUE); + slot->table = table; + return true; } /** Remove a table from fts_slots if it exists. @param[in,out] table table to be removed from fts_slots */ static bool fts_optimize_del_table(const dict_table_t* table) { - const table_id_t table_id = table->id; - ut_ad(table_id); - for (ulint i = 0; i < ib_vector_size(fts_slots); ++i) { fts_slot_t* slot; slot = static_cast(ib_vector_get(fts_slots, i)); - if (slot->table_id == table_id) { + if (slot->table == table) { if (fts_enable_diag_print) { ib_logf(IB_LOG_LEVEL_INFO, "FTS Optimize Removing table %s", table->name); } - slot->table_id = 0; - return(TRUE); + mutex_enter(&fts_optimize_wq->mutex); + slot->table->fts->in_queue = false; + mutex_exit(&fts_optimize_wq->mutex); + + slot->table = NULL; + return true; } } - return(FALSE); + return false; } /**********************************************************************//** @@ -2800,7 +2789,7 @@ static ulint fts_optimize_how_many() for (ulint i = 0; i < ib_vector_size(fts_slots); ++i) { const fts_slot_t* slot = static_cast( ib_vector_get_const(fts_slots, i)); - if (slot->table_id == 0) { + if (!slot->table) { continue; } @@ -2836,22 +2825,14 @@ static bool fts_is_sync_needed() const fts_slot_t* slot = static_cast( ib_vector_get_const(fts_slots, i)); - if (slot->table_id == 0) { + if (!slot->table) { continue; } - dict_table_t* table = dict_table_open_on_id( - slot->table_id, FALSE, DICT_TABLE_OP_NORMAL); - if (!table) { - continue; + if (slot->table->fts && slot->table->fts->cache) { + total_memory += slot->table->fts->cache->total_size; } - if (table->fts && table->fts->cache) { - total_memory += table->fts->cache->total_size; - } - - dict_table_close(table, FALSE, FALSE); - if (total_memory > fts_max_total_cache_size) { return(true); } @@ -2861,22 +2842,16 @@ static bool fts_is_sync_needed() } /** Sync fts cache of a table -@param[in] table_id table id */ -static void fts_optimize_sync_table(table_id_t table_id) +@param[in,out] table table to be synced */ +static void fts_optimize_sync_table(dict_table_t* table) { - if (dict_table_t* table = dict_table_open_on_id( - table_id, FALSE, DICT_TABLE_OP_NORMAL)) { - if (fil_table_accessible(table) - && table->fts && table->fts->cache) { - fts_sync_table(table, true, false, false); - } - - DBUG_EXECUTE_IF( - "ib_optimize_wq_hang", - os_thread_sleep(6000000);); - - dict_table_close(table, FALSE, FALSE); + if (fil_table_accessible(table) + && table->fts && table->fts->cache) { + fts_sync_table(table, true, false, false); } + + DBUG_EXECUTE_IF("ib_optimize_wq_hang", + os_thread_sleep(6000000);); } /**********************************************************************//** @@ -2918,7 +2893,7 @@ fts_optimize_thread( ib_vector_get(fts_slots, current)); /* Handle the case of empty slots. */ - if (slot->table_id) { + if (slot->table) { slot->running = true; fts_optimize_table_bk(slot); } @@ -2978,7 +2953,7 @@ fts_optimize_thread( os_thread_sleep(300000);); fts_optimize_sync_table( - *static_cast(msg->ptr)); + static_cast(msg->ptr)); break; default: @@ -2997,8 +2972,8 @@ fts_optimize_thread( fts_slot_t* slot = static_cast( ib_vector_get(fts_slots, i)); - if (table_id_t table_id = slot->table_id) { - fts_optimize_sync_table(table_id); + if (slot->table) { + fts_optimize_sync_table(slot->table); } } } @@ -3028,24 +3003,35 @@ fts_optimize_init(void) ut_ad(!srv_read_only_mode); /* For now we only support one optimize thread. */ - ut_a(!fts_optimize_is_init()); + ut_a(!fts_optimize_wq); fts_optimize_wq = ib_wqueue_create(); ut_a(fts_optimize_wq != NULL); last_check_sync_time = time(NULL); - os_thread_create(fts_optimize_thread, fts_optimize_wq, NULL); -} + /* Add fts tables to fts slots which could be skipped + during dict_load_table() because fts_optimize_thread + wasn't even started. */ + mutex_enter(&dict_sys->mutex); -/**********************************************************************//** -Check whether the work queue is initialized. -@return TRUE if optimze queue is initialized. */ -UNIV_INTERN -ibool -fts_optimize_is_init(void) -/*======================*/ -{ - return(fts_optimize_wq != NULL); + for (dict_table_t* table = UT_LIST_GET_FIRST(dict_sys->table_LRU); + table != NULL; + table = UT_LIST_GET_NEXT(table_LRU, table)) { + + if (!table->fts || !dict_table_has_fts_index(table)) { + continue; + } + + /* fts_optimize_thread is not started yet. So there is no + need to acqquire fts_optimize_wq->mutex for adding the fts + table to the fts slots. */ + ut_ad(!table->can_be_evicted); + fts_optimize_new_table(table); + table->fts->in_queue = true; + } + + mutex_exit(&dict_sys->mutex); + os_thread_create(fts_optimize_thread, fts_optimize_wq, NULL); } /**********************************************************************//** diff --git a/storage/innobase/include/fts0fts.h b/storage/innobase/include/fts0fts.h index 5c50e381f91..3beddd68722 100644 --- a/storage/innobase/include/fts0fts.h +++ b/storage/innobase/include/fts0fts.h @@ -285,9 +285,6 @@ struct fts_t { fts_add_wq. */ ib_mutex_t bg_threads_mutex; - /* Wheter the table was added to fts_optimize_wq(); - protected by bg_threads mutex */ - unsigned in_queue:1; /* Whether the ADDED table record sync-ed after crash recovery; protected by bg_threads mutex */ unsigned added_synced:1; @@ -310,6 +307,11 @@ struct fts_t { ib_vector_t* indexes; /*!< Vector of FTS indexes, this is mainly for caching purposes. */ + + /* Whether the table was added to fts_optimize_wq(); + protected by fts_optimize_wq mutex */ + bool in_queue; + mem_heap_t* fts_heap; /*!< heap for fts_t allocation */ }; @@ -631,14 +633,6 @@ void fts_optimize_init(void); /*====================*/ -/**********************************************************************//** -Check whether the work queue is initialized. -@return TRUE if optimze queue is initialized. */ -UNIV_INTERN -ibool -fts_optimize_is_init(void); -/*======================*/ - /****************************************************************//** Drops index ancillary tables for a FTS index @return DB_SUCCESS or error code */ diff --git a/storage/innobase/include/fts0opt.h b/storage/innobase/include/fts0opt.h index e8bd4be95b7..29bb14e2f64 100644 --- a/storage/innobase/include/fts0opt.h +++ b/storage/innobase/include/fts0opt.h @@ -25,6 +25,9 @@ Created 2011-02-15 Jimmy Yang #ifndef INNODB_FTS0OPT_H #define INNODB_FTS0OPT_H +/** The FTS optimize thread's work queue. */ +extern ib_wqueue_t* fts_optimize_wq; + /******************************************************************** Callback function to fetch the rows in an FTS INDEX record. */ UNIV_INTERN diff --git a/storage/innobase/include/ut0wqueue.h b/storage/innobase/include/ut0wqueue.h index b81b78530ca..4b0014e3091 100644 --- a/storage/innobase/include/ut0wqueue.h +++ b/storage/innobase/include/ut0wqueue.h @@ -56,16 +56,15 @@ ib_wqueue_free( /*===========*/ ib_wqueue_t* wq); /*!< in: work queue */ -/****************************************************************//** -Add a work item to the queue. */ +/** Add a work item to the queue. +@param[in,out] wq work queue +@param[in] item work item +@param[in,out] heap memory heap to use for allocating list node +@param[in] wq_locked work queue mutex locked */ UNIV_INTERN void -ib_wqueue_add( -/*==========*/ - ib_wqueue_t* wq, /*!< in: work queue */ - void* item, /*!< in: work item */ - mem_heap_t* heap); /*!< in: memory heap to use for allocating the - list node */ +ib_wqueue_add(ib_wqueue_t* wq, void* item, mem_heap_t* heap, + bool wq_locked = false); /** Check if queue is empty. @param wq wait queue @@ -107,14 +106,16 @@ ib_wqueue_len( /*==========*/ ib_wqueue_t* wq); /*mutex); + if (!wq_locked) { + mutex_enter(&wq->mutex); + } ib_list_add_last(wq->items, item, heap); os_event_set(wq->event); - mutex_exit(&wq->mutex); + if (!wq_locked) { + mutex_exit(&wq->mutex); + } } /****************************************************************//** diff --git a/storage/xtradb/dict/dict0load.cc b/storage/xtradb/dict/dict0load.cc index ae89ed79b76..292a1752771 100644 --- a/storage/xtradb/dict/dict0load.cc +++ b/storage/xtradb/dict/dict0load.cc @@ -46,6 +46,7 @@ Created 4/24/1996 Heikki Tuuri #include "dict0priv.h" #include "ha_prototypes.h" /* innobase_casedn_str() */ #include "fts0priv.h" +#include "fts0opt.h" /** Following are the InnoDB system tables. The positions in this array are referenced by enum dict_system_table_id. */ @@ -2570,8 +2571,12 @@ func_exit: FTS */ fts_optimize_remove_table(table); fts_free(table); - } else { + } else if (fts_optimize_wq) { fts_optimize_add_table(table); + } else { + /* fts_optimize_thread is not started yet. + So make the table as non-evictable from cache. */ + dict_table_move_from_lru_to_non_lru(table); } } diff --git a/storage/xtradb/fts/fts0opt.cc b/storage/xtradb/fts/fts0opt.cc index 4472482da81..7f498443544 100644 --- a/storage/xtradb/fts/fts0opt.cc +++ b/storage/xtradb/fts/fts0opt.cc @@ -34,6 +34,7 @@ Completed 2011/7/10 Sunny and Jimmy Yang #include "ut0wqueue.h" #include "srv0start.h" #include "zlib.h" +#include "fts0opt.h" #ifndef UNIV_NONINL #include "fts0types.ic" @@ -41,7 +42,7 @@ Completed 2011/7/10 Sunny and Jimmy Yang #endif /** The FTS optimize thread's work queue. */ -static ib_wqueue_t* fts_optimize_wq; +ib_wqueue_t* fts_optimize_wq; /** The FTS vector to store fts_slot_t */ static ib_vector_t* fts_slots; @@ -169,8 +170,8 @@ struct fts_encode_t { /** We use this information to determine when to start the optimize cycle for a table. */ struct fts_slot_t { - /** table identifier, or 0 if the slot is empty */ - table_id_t table_id; + /** table, or NULL if the slot is unused */ + dict_table_t* table; /** whether this slot is being processed */ bool running; @@ -2456,14 +2457,7 @@ fts_optimize_table_bk( return(DB_SUCCESS); } - dict_table_t* table = dict_table_open_on_id( - slot->table_id, FALSE, DICT_TABLE_OP_NORMAL); - - if (!table) { - slot->last_run = now; - return DB_SUCCESS; - } - + dict_table_t* table = slot->table; dberr_t error; if (fil_table_accessible(table) @@ -2483,8 +2477,6 @@ fts_optimize_table_bk( error = DB_SUCCESS; } - dict_table_close(table, FALSE, FALSE); - return(error); } /*********************************************************************//** @@ -2627,11 +2619,13 @@ UNIV_INTERN void fts_optimize_add_table(dict_table_t* table) msg = fts_optimize_create_msg(FTS_MSG_ADD_TABLE, table); - ib_wqueue_add(fts_optimize_wq, msg, msg->heap); + mutex_enter(&fts_optimize_wq->mutex); + + ib_wqueue_add(fts_optimize_wq, msg, msg->heap, true); - mutex_enter(&table->fts->bg_threads_mutex); table->fts->in_queue = true; - mutex_exit(&table->fts->bg_threads_mutex); + + mutex_exit(&fts_optimize_wq->mutex); } /**********************************************************************//** @@ -2648,7 +2642,7 @@ fts_optimize_remove_table( fts_msg_del_t* remove; /* if the optimize system not yet initialized, return */ - if (!fts_optimize_is_init()) { + if (!fts_optimize_wq) { return; } @@ -2660,12 +2654,10 @@ fts_optimize_remove_table( 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); + mutex_enter(&fts_optimize_wq->mutex); - if (!is_in_optimize_queue) { + if (!table->fts->in_queue) { + mutex_exit(&fts_optimize_wq->mutex); return; } @@ -2681,15 +2673,17 @@ fts_optimize_remove_table( remove->event = event; msg->ptr = remove; - ib_wqueue_add(fts_optimize_wq, msg, msg->heap); + ib_wqueue_add(fts_optimize_wq, msg, msg->heap, true); + + mutex_exit(&fts_optimize_wq->mutex); os_event_wait(event); os_event_free(event); - mutex_enter(&fts->bg_threads_mutex); - fts->in_queue = false; - mutex_exit(&fts->bg_threads_mutex); + ut_d(mutex_enter(&fts_optimize_wq->mutex)); + ut_ad(!table->fts->in_queue); + ut_d(mutex_exit(&fts_optimize_wq->mutex)); } /** Send sync fts cache for the table. @@ -2700,10 +2694,9 @@ fts_optimize_request_sync_table( dict_table_t* table) { fts_msg_t* msg; - table_id_t* table_id; /* if the optimize system not yet initialized, return */ - if (!fts_optimize_is_init()) { + if (!fts_optimize_wq) { return; } @@ -2715,39 +2708,36 @@ fts_optimize_request_sync_table( return; } - msg = fts_optimize_create_msg(FTS_MSG_SYNC_TABLE, NULL); + msg = fts_optimize_create_msg(FTS_MSG_SYNC_TABLE, table); - table_id = static_cast( - mem_heap_alloc(msg->heap, sizeof(table_id_t))); - *table_id = table->id; - msg->ptr = table_id; + mutex_enter(&fts_optimize_wq->mutex); - ib_wqueue_add(fts_optimize_wq, msg, msg->heap); + ib_wqueue_add(fts_optimize_wq, msg, msg->heap, true); - mutex_enter(&table->fts->bg_threads_mutex); table->fts->in_queue = true; - mutex_exit(&table->fts->bg_threads_mutex); + + mutex_exit(&fts_optimize_wq->mutex); } /** Add a table to fts_slots if it doesn't already exist. */ static bool fts_optimize_new_table(dict_table_t* table) { + ut_ad(table); + ulint i; fts_slot_t* slot; fts_slot_t* empty = NULL; - const table_id_t table_id = table->id; - ut_ad(table_id); /* Search for duplicates, also find a free slot if one exists. */ for (i = 0; i < ib_vector_size(fts_slots); ++i) { slot = static_cast(ib_vector_get(fts_slots, i)); - if (!slot->table_id) { + if (!slot->table) { empty = slot; - } else if (slot->table_id == table_id) { + } else if (slot->table == table) { /* Already exists in our optimize queue. */ - return(FALSE); + return false; } } @@ -2756,37 +2746,36 @@ static bool fts_optimize_new_table(dict_table_t* table) memset(slot, 0x0, sizeof(*slot)); - slot->table_id = table->id; - slot->running = false; - - return(TRUE); + slot->table = table; + return true; } /** Remove a table from fts_slots if it exists. @param[in,out] table table to be removed from fts_slots */ static bool fts_optimize_del_table(const dict_table_t* table) { - const table_id_t table_id = table->id; - ut_ad(table_id); - for (ulint i = 0; i < ib_vector_size(fts_slots); ++i) { fts_slot_t* slot; slot = static_cast(ib_vector_get(fts_slots, i)); - if (slot->table_id == table_id) { + if (slot->table == table) { if (fts_enable_diag_print) { ib_logf(IB_LOG_LEVEL_INFO, "FTS Optimize Removing table %s", table->name); } - slot->table_id = 0; - return(TRUE); + mutex_enter(&fts_optimize_wq->mutex); + slot->table->fts->in_queue = false; + mutex_exit(&fts_optimize_wq->mutex); + + slot->table = NULL; + return true; } } - return(FALSE); + return false; } /**********************************************************************//** @@ -2800,7 +2789,7 @@ static ulint fts_optimize_how_many() for (ulint i = 0; i < ib_vector_size(fts_slots); ++i) { const fts_slot_t* slot = static_cast( ib_vector_get_const(fts_slots, i)); - if (slot->table_id == 0) { + if (!slot->table) { continue; } @@ -2836,22 +2825,14 @@ static bool fts_is_sync_needed() const fts_slot_t* slot = static_cast( ib_vector_get_const(fts_slots, i)); - if (slot->table_id == 0) { + if (!slot->table) { continue; } - dict_table_t* table = dict_table_open_on_id( - slot->table_id, FALSE, DICT_TABLE_OP_NORMAL); - if (!table) { - continue; + if (slot->table->fts && slot->table->fts->cache) { + total_memory += slot->table->fts->cache->total_size; } - if (table->fts && table->fts->cache) { - total_memory += table->fts->cache->total_size; - } - - dict_table_close(table, FALSE, FALSE); - if (total_memory > fts_max_total_cache_size) { return(true); } @@ -2861,22 +2842,16 @@ static bool fts_is_sync_needed() } /** Sync fts cache of a table -@param[in] table_id table id */ -static void fts_optimize_sync_table(table_id_t table_id) +@param[in,out] table table to be synced */ +static void fts_optimize_sync_table(dict_table_t* table) { - if (dict_table_t* table = dict_table_open_on_id( - table_id, FALSE, DICT_TABLE_OP_NORMAL)) { - if (fil_table_accessible(table) - && table->fts && table->fts->cache) { - fts_sync_table(table, true, false, false); - } - - DBUG_EXECUTE_IF( - "ib_optimize_wq_hang", - os_thread_sleep(6000000);); - - dict_table_close(table, FALSE, FALSE); + if (fil_table_accessible(table) + && table->fts && table->fts->cache) { + fts_sync_table(table, true, false, false); } + + DBUG_EXECUTE_IF("ib_optimize_wq_hang", + os_thread_sleep(6000000);); } /**********************************************************************//** @@ -2918,7 +2893,7 @@ fts_optimize_thread( ib_vector_get(fts_slots, current)); /* Handle the case of empty slots. */ - if (slot->table_id) { + if (slot->table) { slot->running = true; fts_optimize_table_bk(slot); } @@ -2978,7 +2953,7 @@ fts_optimize_thread( os_thread_sleep(300000);); fts_optimize_sync_table( - *static_cast(msg->ptr)); + static_cast(msg->ptr)); break; default: @@ -2997,8 +2972,8 @@ fts_optimize_thread( fts_slot_t* slot = static_cast( ib_vector_get(fts_slots, i)); - if (table_id_t table_id = slot->table_id) { - fts_optimize_sync_table(table_id); + if (slot->table) { + fts_optimize_sync_table(slot->table); } } } @@ -3028,24 +3003,35 @@ fts_optimize_init(void) ut_ad(!srv_read_only_mode); /* For now we only support one optimize thread. */ - ut_a(!fts_optimize_is_init()); + ut_a(!fts_optimize_wq); fts_optimize_wq = ib_wqueue_create(); ut_a(fts_optimize_wq != NULL); last_check_sync_time = time(NULL); - os_thread_create(fts_optimize_thread, fts_optimize_wq, NULL); -} + /* Add fts tables to fts slots which could be skipped + during dict_load_table() because fts_optimize_thread + wasn't even started. */ + mutex_enter(&dict_sys->mutex); -/**********************************************************************//** -Check whether the work queue is initialized. -@return TRUE if optimze queue is initialized. */ -UNIV_INTERN -ibool -fts_optimize_is_init(void) -/*======================*/ -{ - return(fts_optimize_wq != NULL); + for (dict_table_t* table = UT_LIST_GET_FIRST(dict_sys->table_LRU); + table != NULL; + table = UT_LIST_GET_NEXT(table_LRU, table)) { + + if (!table->fts || !dict_table_has_fts_index(table)) { + continue; + } + + /* fts_optimize_thread is not started yet. So there is no + need to acqquire fts_optimize_wq->mutex for adding the fts + table to the fts slots. */ + ut_ad(!table->can_be_evicted); + fts_optimize_new_table(table); + table->fts->in_queue = true; + } + + mutex_exit(&dict_sys->mutex); + os_thread_create(fts_optimize_thread, fts_optimize_wq, NULL); } /**********************************************************************//** diff --git a/storage/xtradb/include/fts0fts.h b/storage/xtradb/include/fts0fts.h index 9c70157a0c8..714f811db27 100644 --- a/storage/xtradb/include/fts0fts.h +++ b/storage/xtradb/include/fts0fts.h @@ -285,9 +285,6 @@ struct fts_t { fts_add_wq. */ ib_mutex_t bg_threads_mutex; - /* Whether the table was added to fts_optimize_wq(); - protected by bg_threads mutex */ - unsigned in_queue:1; /* Whether the ADDED table record sync-ed afer crash recovery; protected by bg_threads mutex */ unsigned added_synced:1; @@ -310,6 +307,11 @@ struct fts_t { ib_vector_t* indexes; /*!< Vector of FTS indexes, this is mainly for caching purposes. */ + + /* Whether the table was added to fts_optimize_wq(); + protected by fts_optimize_wq mutex */ + bool in_queue; + mem_heap_t* fts_heap; /*!< heap for fts_t allocation */ }; @@ -631,14 +633,6 @@ void fts_optimize_init(void); /*====================*/ -/**********************************************************************//** -Check whether the work queue is initialized. -@return TRUE if optimze queue is initialized. */ -UNIV_INTERN -ibool -fts_optimize_is_init(void); -/*======================*/ - /****************************************************************//** Drops index ancillary tables for a FTS index @return DB_SUCCESS or error code */ diff --git a/storage/xtradb/include/fts0opt.h b/storage/xtradb/include/fts0opt.h index e8bd4be95b7..29bb14e2f64 100644 --- a/storage/xtradb/include/fts0opt.h +++ b/storage/xtradb/include/fts0opt.h @@ -25,6 +25,9 @@ Created 2011-02-15 Jimmy Yang #ifndef INNODB_FTS0OPT_H #define INNODB_FTS0OPT_H +/** The FTS optimize thread's work queue. */ +extern ib_wqueue_t* fts_optimize_wq; + /******************************************************************** Callback function to fetch the rows in an FTS INDEX record. */ UNIV_INTERN diff --git a/storage/xtradb/include/ut0wqueue.h b/storage/xtradb/include/ut0wqueue.h index 9bd58609205..4b0014e3091 100644 --- a/storage/xtradb/include/ut0wqueue.h +++ b/storage/xtradb/include/ut0wqueue.h @@ -56,16 +56,15 @@ ib_wqueue_free( /*===========*/ ib_wqueue_t* wq); /*!< in: work queue */ -/****************************************************************//** -Add a work item to the queue. */ +/** Add a work item to the queue. +@param[in,out] wq work queue +@param[in] item work item +@param[in,out] heap memory heap to use for allocating list node +@param[in] wq_locked work queue mutex locked */ UNIV_INTERN void -ib_wqueue_add( -/*==========*/ - ib_wqueue_t* wq, /*!< in: work queue */ - void* item, /*!< in: work item */ - mem_heap_t* heap); /*!< in: memory heap to use for allocating the - list node */ +ib_wqueue_add(ib_wqueue_t* wq, void* item, mem_heap_t* heap, + bool wq_locked = false); /** Check if queue is empty. @param wq wait queue @@ -99,7 +98,6 @@ ib_wqueue_nowait( /*=============*/ ib_wqueue_t* wq); /*mutex); + if (!wq_locked) { + mutex_enter(&wq->mutex); + } ib_list_add_last(wq->items, item, heap); os_event_set(wq->event); - mutex_exit(&wq->mutex); + if (!wq_locked) { + mutex_exit(&wq->mutex); + } } /****************************************************************//**