Skip to content

Commit 95302c1

Browse files
author
Shaohua Wang
committed
BUG#22996488 - CRASH IN FTS_SYNC_INDEX WHEN DOING DDL IN A LOOP
Problem: The index cache has been freed by DROP INDEX when we are syncing a index cache in background. Solution: Acquire dict_operation_lock to prevent DDL like purge thread. Reviewed-by: Jimmy Yang <jimmy.yang@oracle.com> RB: 12340
1 parent 7783a27 commit 95302c1

File tree

5 files changed

+57
-21
lines changed

5 files changed

+57
-21
lines changed

storage/innobase/fts/fts0fts.cc

+33-13
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] background whether sync in background
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 background);
275277

276278
/****************************************************************//**
277279
Release all resources help by the words rb tree e.g., the node ilist. */
@@ -1075,13 +1077,12 @@ fts_words_free(
10751077
}
10761078
}
10771079

1078-
/*********************************************************************//**
1079-
Clear cache. */
1080+
/** Clear cache.
1081+
@param[in,out] cache fts cache */
10801082
UNIV_INTERN
10811083
void
10821084
fts_cache_clear(
1083-
/*============*/
1084-
fts_cache_t* cache) /*!< in: cache */
1085+
fts_cache_t* cache)
10851086
{
10861087
ulint i;
10871088

@@ -3567,7 +3568,7 @@ fts_add_doc_by_id(
35673568

35683569
DBUG_EXECUTE_IF(
35693570
"fts_instrument_sync_debug",
3570-
fts_sync(cache->sync, true, true);
3571+
fts_sync(cache->sync, true, true, false);
35713572
);
35723573

35733574
DEBUG_SYNC_C("fts_instrument_sync_request");
@@ -4473,13 +4474,12 @@ fts_sync_commit(
44734474
return(error);
44744475
}
44754476

4476-
/*********************************************************************//**
4477-
Rollback a sync operation */
4477+
/** Rollback a sync operation
4478+
@param[in,out] sync sync state */
44784479
static
44794480
void
44804481
fts_sync_rollback(
4481-
/*==============*/
4482-
fts_sync_t* sync) /*!< in: sync state */
4482+
fts_sync_t* sync)
44834483
{
44844484
trx_t* trx = sync->trx;
44854485
fts_cache_t* cache = sync->table->fts->cache;
@@ -4524,18 +4524,23 @@ FTS auxiliary INDEX table and clear the cache at the end.
45244524
@param[in,out] sync sync state
45254525
@param[in] unlock_cache whether unlock cache lock when write node
45264526
@param[in] wait whether wait when a sync is in progress
4527+
@param[in] has_dict whether has dict operation lock, if true,
4528+
unlock it before return.
45274529
@return DB_SUCCESS if all OK */
45284530
static
45294531
dberr_t
45304532
fts_sync(
45314533
fts_sync_t* sync,
45324534
bool unlock_cache,
4533-
bool wait)
4535+
bool wait,
4536+
bool has_dict)
45344537
{
45354538
ulint i;
45364539
dberr_t error = DB_SUCCESS;
45374540
fts_cache_t* cache = sync->table->fts->cache;
45384541

4542+
ut_ad(!(has_dict & wait));
4543+
45394544
rw_lock_x_lock(&cache->lock);
45404545

45414546
/* Check if cache is being synced.
@@ -4547,6 +4552,10 @@ fts_sync(
45474552
if (wait) {
45484553
os_event_wait(sync->event);
45494554
} else {
4555+
if (has_dict) {
4556+
rw_lock_s_unlock(&dict_operation_lock);
4557+
}
4558+
45504559
return(DB_SUCCESS);
45514560
}
45524561

@@ -4609,7 +4618,15 @@ fts_sync(
46094618
end_sync:
46104619
if (error == DB_SUCCESS && !sync->interrupted) {
46114620
error = fts_sync_commit(sync);
4621+
4622+
if (has_dict) {
4623+
rw_lock_s_unlock(&dict_operation_lock);
4624+
}
46124625
} else {
4626+
if (has_dict) {
4627+
rw_lock_s_unlock(&dict_operation_lock);
4628+
}
4629+
46134630
fts_sync_rollback(sync);
46144631
}
46154632

@@ -4637,20 +4654,23 @@ FTS auxiliary INDEX table and clear the cache at the end.
46374654
@param[in,out] table fts table
46384655
@param[in] unlock_cache whether unlock cache when write node
46394656
@param[in] wait whether wait for existing sync to finish
4657+
@param[in] has_dict whether has dict operation lock, if true
4658+
unlock it before return
46404659
@return DB_SUCCESS on success, error code on failure. */
46414660
UNIV_INTERN
46424661
dberr_t
46434662
fts_sync_table(
46444663
dict_table_t* table,
46454664
bool unlock_cache,
4646-
bool wait)
4665+
bool wait,
4666+
bool has_dict)
46474667
{
46484668
dberr_t err = DB_SUCCESS;
46494669

46504670
ut_ad(table->fts);
46514671

46524672
if (!dict_table_is_discarded(table) && table->fts->cache) {
4653-
err = fts_sync(table->fts->cache->sync, unlock_cache, wait);
4673+
err = fts_sync(table->fts->cache->sync, unlock_cache, wait, has_dict);
46544674
}
46554675

46564676
return(err);

storage/innobase/fts/fts0opt.cc

+15-1
Original file line numberDiff line numberDiff line change
@@ -2975,15 +2975,29 @@ fts_optimize_sync_table(
29752975
{
29762976
dict_table_t* table = NULL;
29772977

2978+
/* Prevent DROP INDEX etc. from running when we are syncing
2979+
cache in background. */
2980+
if (!rw_lock_s_lock_nowait(&dict_operation_lock, __FILE__, __LINE__)) {
2981+
/* Exit when fail to get dict operation lock. */
2982+
return;
2983+
}
2984+
2985+
bool has_dict_lock = true;
2986+
29782987
table = dict_table_open_on_id(table_id, FALSE, DICT_TABLE_OP_NORMAL);
29792988

29802989
if (table) {
29812990
if (dict_table_has_fts_index(table) && table->fts->cache) {
2982-
fts_sync_table(table, true, false);
2991+
fts_sync_table(table, true, false, true);
2992+
has_dict_lock = false;
29832993
}
29842994

29852995
dict_table_close(table, FALSE, FALSE);
29862996
}
2997+
2998+
if (has_dict_lock) {
2999+
rw_lock_s_unlock(&dict_operation_lock);
3000+
}
29873001
}
29883002

29893003
/**********************************************************************//**

storage/innobase/handler/ha_innodb.cc

+1-1
Original file line numberDiff line numberDiff line change
@@ -11392,7 +11392,7 @@ ha_innobase::optimize(
1139211392
if (innodb_optimize_fulltext_only) {
1139311393
if (prebuilt->table->fts && prebuilt->table->fts->cache
1139411394
&& !dict_table_is_discarded(prebuilt->table)) {
11395-
fts_sync_table(prebuilt->table, false, true);
11395+
fts_sync_table(prebuilt->table, false, true, false);
1139611396
fts_optimize_table(prebuilt->table);
1139711397
}
1139811398
return(HA_ADMIN_OK);

storage/innobase/include/fts0fts.h

+7-5
Original file line numberDiff line numberDiff line change
@@ -783,13 +783,12 @@ fts_cache_destroy(
783783
/*==============*/
784784
fts_cache_t* cache); /*!< in: cache*/
785785

786-
/*********************************************************************//**
787-
Clear cache. */
786+
/** Clear cache.
787+
@param[in,out] cache fts cache */
788788
UNIV_INTERN
789789
void
790790
fts_cache_clear(
791-
/*============*/
792-
fts_cache_t* cache); /*!< in: cache */
791+
fts_cache_t* cache);
793792

794793
/*********************************************************************//**
795794
Initialize things in cache. */
@@ -841,13 +840,16 @@ FTS auxiliary INDEX table and clear the cache at the end.
841840
@param[in,out] table fts table
842841
@param[in] unlock_cache whether unlock cache when write node
843842
@param[in] wait whether wait for existing sync to finish
843+
@param[in] has_dict whether has dict operation lock, if true
844+
unlock it before return
844845
@return DB_SUCCESS on success, error code on failure. */
845846
UNIV_INTERN
846847
dberr_t
847848
fts_sync_table(
848849
dict_table_t* table,
849850
bool unlock_cache,
850-
bool wait);
851+
bool wait,
852+
bool has_dict);
851853

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

storage/innobase/row/row0merge.cc

+1-1
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)