Skip to content

Commit 802b76a

Browse files
author
Shaohua Wang
committed
Followup: BUG#22516559 MYSQL INSTANCE STALLS WHEN SYNCING FTS INDEX
BUG#23320569 INNODB FTS:TRY TO ACQUIRE DICT_OPERATION_LOCK AGAIN WHEN HOLD IT IN FTS SYNC Problem: We hold dict_operation_lock when sync fts in background to prevent DDL like DROP INDEX, etc. But if sync fails, we need to rollback (row_undo()), in which we acquire dict_operation_lock again, so assert fails. Solution: Set trx->dict_operation_lock_mode in fts_sync() to avoid acquiring dict_operation_lock again in rollback. Reviewed-by: Jimmy Yang <[email protected]> RB: 12840
1 parent f956ccc commit 802b76a

File tree

7 files changed

+66
-9
lines changed

7 files changed

+66
-9
lines changed

mysql-test/suite/innodb_fts/r/sync.result

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,3 +100,19 @@ FTS_DOC_ID title
100100
1 database
101101
2 mysql
102102
DROP TABLE t1;
103+
# Case 4: Test sync commit & rollback in background
104+
CREATE TABLE t1(
105+
id INT AUTO_INCREMENT,
106+
title VARCHAR(100),
107+
FULLTEXT(title),
108+
PRIMARY KEY(id)) ENGINE=InnoDB;
109+
SET SESSION debug="+d,fts_instrument_sync";
110+
INSERT INTO t1(title) VALUES('database');
111+
SET GLOBAL debug="+d,fts_instrument_sync,fts_instrument_sync_interrupted";
112+
INSERT INTO t1(title) VALUES('mysql');
113+
SET GLOBAL debug="-d,fts_instrument_sync,fts_instrument_sync_interrupted";
114+
SELECT * FROM t1 WHERE MATCH(title) AGAINST ('mysql database');
115+
id title
116+
1 database
117+
2 mysql
118+
DROP TABLE t1;

mysql-test/suite/innodb_fts/t/sync.test

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,4 +139,25 @@ SELECT * FROM t1 WHERE MATCH(title) AGAINST ('mysql database');
139139

140140
DROP TABLE t1;
141141

142+
--echo # Case 4: Test sync commit & rollback in background
143+
CREATE TABLE t1(
144+
id INT AUTO_INCREMENT,
145+
title VARCHAR(100),
146+
FULLTEXT(title),
147+
PRIMARY KEY(id)) ENGINE=InnoDB;
148+
149+
SET SESSION debug="+d,fts_instrument_sync";
150+
151+
INSERT INTO t1(title) VALUES('database');
152+
153+
SET GLOBAL debug="+d,fts_instrument_sync,fts_instrument_sync_interrupted";
154+
155+
INSERT INTO t1(title) VALUES('mysql');
156+
157+
SET GLOBAL debug="-d,fts_instrument_sync,fts_instrument_sync_interrupted";
158+
159+
SELECT * FROM t1 WHERE MATCH(title) AGAINST ('mysql database');
160+
161+
DROP TABLE t1;
162+
142163
--source include/wait_until_count_sessions.inc

storage/innobase/fts/fts0fts.cc

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -265,13 +265,15 @@ FTS auxiliary INDEX table and clear the cache at the end.
265265
@param[in,out] sync sync state
266266
@param[in] unlock_cache whether unlock cache lock when write node
267267
@param[in] wait whether wait when a sync is in progress
268+
@param[in] has_dict whether has dict operation lock
268269
@return DB_SUCCESS if all OK */
269270
static
270271
dberr_t
271272
fts_sync(
272273
fts_sync_t* sync,
273274
bool unlock_cache,
274-
bool wait);
275+
bool wait,
276+
bool has_dict);
275277

276278
/****************************************************************//**
277279
Release all resources help by the words rb tree e.g., the node ilist. */
@@ -3566,7 +3568,7 @@ fts_add_doc_by_id(
35663568

35673569
DBUG_EXECUTE_IF(
35683570
"fts_instrument_sync_debug",
3569-
fts_sync(cache->sync, true, true);
3571+
fts_sync(cache->sync, true, true, false);
35703572
);
35713573

35723574
DEBUG_SYNC_C("fts_instrument_sync_request");
@@ -4467,6 +4469,8 @@ fts_sync_commit(
44674469
(double) n_nodes/ (double) elapsed_time);
44684470
}
44694471

4472+
/* Avoid assertion in trx_free(). */
4473+
trx->dict_operation_lock_mode = 0;
44704474
trx_free_for_background(trx);
44714475

44724476
return(error);
@@ -4514,6 +4518,9 @@ fts_sync_rollback(
45144518
rw_lock_x_unlock(&cache->lock);
45154519

45164520
fts_sql_rollback(trx);
4521+
4522+
/* Avoid assertion in trx_free(). */
4523+
trx->dict_operation_lock_mode = 0;
45174524
trx_free_for_background(trx);
45184525
}
45194526

@@ -4522,13 +4529,15 @@ FTS auxiliary INDEX table and clear the cache at the end.
45224529
@param[in,out] sync sync state
45234530
@param[in] unlock_cache whether unlock cache lock when write node
45244531
@param[in] wait whether wait when a sync is in progress
4532+
@param[in] has_dict whether has dict operation lock
45254533
@return DB_SUCCESS if all OK */
45264534
static
45274535
dberr_t
45284536
fts_sync(
45294537
fts_sync_t* sync,
45304538
bool unlock_cache,
4531-
bool wait)
4539+
bool wait,
4540+
bool has_dict)
45324541
{
45334542
ulint i;
45344543
dberr_t error = DB_SUCCESS;
@@ -4557,6 +4566,12 @@ fts_sync(
45574566
DEBUG_SYNC_C("fts_sync_begin");
45584567
fts_sync_begin(sync);
45594568

4569+
/* When sync in background, we hold dict operation lock
4570+
to prevent DDL like DROP INDEX, etc. */
4571+
if (has_dict) {
4572+
sync->trx->dict_operation_lock_mode = RW_S_LATCH;
4573+
}
4574+
45604575
begin_sync:
45614576
if (cache->total_size > fts_max_cache_size) {
45624577
/* Avoid the case: sync never finish when
@@ -4635,20 +4650,23 @@ FTS auxiliary INDEX table and clear the cache at the end.
46354650
@param[in,out] table fts table
46364651
@param[in] unlock_cache whether unlock cache when write node
46374652
@param[in] wait whether wait for existing sync to finish
4653+
@param[in] has_dict whether has dict operation lock
46384654
@return DB_SUCCESS on success, error code on failure. */
46394655
UNIV_INTERN
46404656
dberr_t
46414657
fts_sync_table(
46424658
dict_table_t* table,
46434659
bool unlock_cache,
4644-
bool wait)
4660+
bool wait,
4661+
bool has_dict)
46454662
{
46464663
dberr_t err = DB_SUCCESS;
46474664

46484665
ut_ad(table->fts);
46494666

46504667
if (!dict_table_is_discarded(table) && table->fts->cache) {
4651-
err = fts_sync(table->fts->cache->sync, unlock_cache, wait);
4668+
err = fts_sync(table->fts->cache->sync,
4669+
unlock_cache, wait, has_dict);
46524670
}
46534671

46544672
return(err);

storage/innobase/fts/fts0opt.cc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2986,7 +2986,7 @@ fts_optimize_sync_table(
29862986

29872987
if (table) {
29882988
if (dict_table_has_fts_index(table) && table->fts->cache) {
2989-
fts_sync_table(table, true, false);
2989+
fts_sync_table(table, true, false, true);
29902990
}
29912991

29922992
dict_table_close(table, FALSE, FALSE);

storage/innobase/handler/ha_innodb.cc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11395,7 +11395,7 @@ ha_innobase::optimize(
1139511395
if (innodb_optimize_fulltext_only) {
1139611396
if (prebuilt->table->fts && prebuilt->table->fts->cache
1139711397
&& !dict_table_is_discarded(prebuilt->table)) {
11398-
fts_sync_table(prebuilt->table, false, true);
11398+
fts_sync_table(prebuilt->table, false, true, false);
1139911399
fts_optimize_table(prebuilt->table);
1140011400
}
1140111401
return(HA_ADMIN_OK);

storage/innobase/include/fts0fts.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -840,13 +840,15 @@ FTS auxiliary INDEX table and clear the cache at the end.
840840
@param[in,out] table fts table
841841
@param[in] unlock_cache whether unlock cache when write node
842842
@param[in] wait whether wait for existing sync to finish
843+
@param[in] has_dict whether has dict operation lock
843844
@return DB_SUCCESS on success, error code on failure. */
844845
UNIV_INTERN
845846
dberr_t
846847
fts_sync_table(
847848
dict_table_t* table,
848849
bool unlock_cache,
849-
bool wait);
850+
bool wait,
851+
bool has_dict);
850852

851853
/****************************************************************//**
852854
Free the query graph but check whether dict_sys->mutex is already

storage/innobase/row/row0merge.cc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1987,7 +1987,7 @@ row_merge_read_clustered_index(
19871987
/* Sync fts cache for other fts indexes to keep all
19881988
fts indexes consistent in sync_doc_id. */
19891989
err = fts_sync_table(const_cast<dict_table_t*>(new_table),
1990-
false, true);
1990+
false, true, false);
19911991

19921992
if (err == DB_SUCCESS) {
19931993
fts_update_next_doc_id(

0 commit comments

Comments
 (0)