Skip to content

Commit

Permalink
Merge pull request percona#3562 from kamil-holubicki/PS-6113-5.7-v2
Browse files Browse the repository at this point in the history
PS-6113: If ANALYZE TABLE runs more than 600 seconds diagnostic query may crash server
  • Loading branch information
kamil-holubicki authored Dec 20, 2019
2 parents 84b9b20 + 36ca058 commit b3f5986
Show file tree
Hide file tree
Showing 12 changed files with 167 additions and 8 deletions.
13 changes: 13 additions & 0 deletions mysql-test/suite/innodb/r/analyze_index.result
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
CREATE TABLE t1(a int, index inda(a)) ENGINE=INNODB;
INSERT INTO t1 VALUES(1);
SET DEBUG_SYNC='innodb_dict_stats_update_persistent SIGNAL analyze.running WAIT_FOR analyze.finish';
ANALYZE TABLE t1;
SET DEBUG_SYNC='now WAIT_FOR analyze.running';
SELECT ENGINE,SUM(DATA_LENGTH+INDEX_LENGTH),COUNT(ENGINE),SUM(DATA_LENGTH),SUM(INDEX_LENGTH) FROM information_schema.TABLES WHERE TABLE_SCHEMA NOT IN ('information_schema', 'performance_schema', 'mysql') AND ENGINE='InnoDB';
ENGINE SUM(DATA_LENGTH+INDEX_LENGTH) COUNT(ENGINE) SUM(DATA_LENGTH) SUM(INDEX_LENGTH)
InnoDB 49152 2 32768 16384
SET DEBUG_SYNC='now SIGNAL analyze.finish';
Table Op Msg_type Msg_text
test.t1 analyze status OK
SET DEBUG_SYNC= 'RESET';
DROP TABLE t1;
31 changes: 31 additions & 0 deletions mysql-test/suite/innodb/t/analyze_index.test
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
#
# Bug #97828
# PS-6113: If ANALYZE TABLE runs more than 600 seconds diagnostic query may crash server
#

--source include/have_innodb.inc
--source include/have_debug_sync.inc
--source include/count_sessions.inc

CREATE TABLE t1(a int, index inda(a)) ENGINE=INNODB;
INSERT INTO t1 VALUES(1);

SET DEBUG_SYNC='innodb_dict_stats_update_persistent SIGNAL analyze.running WAIT_FOR analyze.finish';
--send ANALYZE TABLE t1

# ANALYZE TABLE is running. Query for stats.

--connect(con1, localhost, root)
SET DEBUG_SYNC='now WAIT_FOR analyze.running';

SELECT ENGINE,SUM(DATA_LENGTH+INDEX_LENGTH),COUNT(ENGINE),SUM(DATA_LENGTH),SUM(INDEX_LENGTH) FROM information_schema.TABLES WHERE TABLE_SCHEMA NOT IN ('information_schema', 'performance_schema', 'mysql') AND ENGINE='InnoDB';

# let the ANALYZE TABLE to finish
SET DEBUG_SYNC='now SIGNAL analyze.finish';
--disconnect con1

--connection default
--reap
SET DEBUG_SYNC= 'RESET';
DROP TABLE t1;

37 changes: 37 additions & 0 deletions storage/innobase/dict/dict0dict.cc
Original file line number Diff line number Diff line change
Expand Up @@ -922,6 +922,43 @@ dict_table_autoinc_unlock(
{
mutex_exit(table->autoinc_mutex);
}

/** Create and initialize the analyze index lock for a given table.
This lock is used to serialize two concurrent analyze index operations
@param[in] table_void table whose analyze_index latch to create */
static
void
dict_table_analyze_index_alloc(
void* table_void)
{
dict_table_t* table = static_cast<dict_table_t*>(table_void);
table->analyze_index_mutex = UT_NEW_NOKEY(AnalyzeIndexMutex());
ut_a(table->analyze_index_mutex != NULL);
mutex_create(LATCH_ID_ANALYZE_INDEX_MUTEX, table->analyze_index_mutex);
}

/** Acquire the analyze index lock.
@param[in] table table whose analyze_index latch to lock */
void
dict_table_analyze_index_lock(
dict_table_t* table)
{
os_once::do_or_wait_for_done(
&table->analyze_index_mutex_created,
dict_table_analyze_index_alloc, table);

mutex_enter(table->analyze_index_mutex);
}

/** Release the analyze index lock.
@param[in] table table whose analyze_index latch to unlock */
void
dict_table_analyze_index_unlock(
dict_table_t* table)
{
ut_a(table->analyze_index_mutex != NULL);
mutex_exit(table->analyze_index_mutex);
}
#endif /* !UNIV_HOTBACKUP */

/** Looks for column n in an index.
Expand Down
3 changes: 3 additions & 0 deletions storage/innobase/dict/dict0mem.cc
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,8 @@ dict_mem_table_create(
/* lazy creation of table autoinc latch */
dict_table_autoinc_create_lazy(table);

dict_table_analyze_index_create_lazy(table);

table->autoinc = 0;
table->sess_row_id = 0;
table->sess_trx_id = 0;
Expand Down Expand Up @@ -214,6 +216,7 @@ dict_mem_table_free(
}
}
#ifndef UNIV_HOTBACKUP
dict_table_analyze_index_destroy(table);
dict_table_autoinc_destroy(table);
#endif /* UNIV_HOTBACKUP */

Expand Down
35 changes: 27 additions & 8 deletions storage/innobase/dict/dict0stats.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1908,6 +1908,10 @@ dict_stats_analyze_index(
ulint size;
DBUG_ENTER("dict_stats_analyze_index");

/* stats_latch is created on 1st lock. */
ut_ad(!(index->table->stats_latch_created) ||
!rw_lock_own(index->table->stats_latch, RW_X_LATCH));

DBUG_PRINT("info", ("index: %s, online status: %d", index->name(),
dict_index_get_online_status(index)));

Expand Down Expand Up @@ -2206,7 +2210,9 @@ dict_stats_update_persistent(

DEBUG_PRINTF("%s(table=%s)\n", __func__, table->name);

dict_table_stats_lock(table, RW_X_LATCH);
dict_table_analyze_index_lock(table);

DEBUG_SYNC_C("innodb_dict_stats_update_persistent");

/* analyze the clustered index first */

Expand All @@ -2217,8 +2223,8 @@ dict_stats_update_persistent(
|| (index->type | DICT_UNIQUE) != (DICT_CLUSTERED | DICT_UNIQUE)) {

/* Table definition is corrupt */
dict_table_stats_unlock(table, RW_X_LATCH);
dict_stats_empty_table(table);
dict_table_analyze_index_unlock(table);

return(DB_CORRUPTION);
}
Expand All @@ -2229,13 +2235,13 @@ dict_stats_update_persistent(

ulint n_unique = dict_index_get_n_unique(index);

table->stat_n_rows = index->stat_n_diff_key_vals[n_unique - 1];
ib_uint64_t stat_n_rows_tmp = index->stat_n_diff_key_vals[n_unique - 1];

table->stat_clustered_index_size = index->stat_index_size;
ulint stat_clustered_index_size_tmp = index->stat_index_size;

/* analyze other indexes from the table, if any */

table->stat_sum_of_other_index_sizes = 0;
ulint stat_sum_of_other_index_sizes_tmp = 0;

for (index = dict_table_get_next_index(index);
index != NULL;
Expand All @@ -2257,10 +2263,18 @@ dict_stats_update_persistent(
dict_stats_analyze_index(index);
}

table->stat_sum_of_other_index_sizes
stat_sum_of_other_index_sizes_tmp
+= index->stat_index_size;
}

dict_table_stats_lock(table, RW_X_LATCH);

table->stat_n_rows = stat_n_rows_tmp;

table->stat_clustered_index_size = stat_clustered_index_size_tmp;

table->stat_sum_of_other_index_sizes = stat_sum_of_other_index_sizes_tmp;

table->stats_last_recalc = ut_time_monotonic();

table->stat_modified_counter = 0;
Expand All @@ -2271,6 +2285,8 @@ dict_stats_update_persistent(

dict_table_stats_unlock(table, RW_X_LATCH);

dict_table_analyze_index_unlock(table);

return(DB_SUCCESS);
}

Expand Down Expand Up @@ -3080,10 +3096,13 @@ dict_stats_update_for_index(
if (dict_stats_is_persistent_enabled(index->table)) {

if (dict_stats_persistent_storage_check(false)) {
dict_table_stats_lock(index->table, RW_X_LATCH);
dict_table_analyze_index_lock(index->table);
dict_stats_analyze_index(index);
index->table->stat_sum_of_other_index_sizes += index->stat_index_size;
ulint stat_sum_of_other_index_sizes_tmp = index->stat_index_size;
dict_table_stats_lock(index->table, RW_X_LATCH);
index->table->stat_sum_of_other_index_sizes += stat_sum_of_other_index_sizes_tmp;
dict_table_stats_unlock(index->table, RW_X_LATCH);
dict_table_analyze_index_unlock(index->table);
dict_stats_save(index->table, &index->id);
DBUG_VOID_RETURN;
}
Expand Down
1 change: 1 addition & 0 deletions storage/innobase/handler/ha_innodb.cc
Original file line number Diff line number Diff line change
Expand Up @@ -515,6 +515,7 @@ static PSI_mutex_info all_innodb_mutexes[] = {
PSI_KEY(row_drop_list_mutex),
PSI_KEY(master_key_id_mutex),
PSI_KEY(scrub_stat_mutex),
PSI_KEY(analyze_index_mutex),
};
# endif /* UNIV_PFS_MUTEX */

Expand Down
11 changes: 11 additions & 0 deletions storage/innobase/include/dict0dict.h
Original file line number Diff line number Diff line change
Expand Up @@ -368,6 +368,17 @@ dict_table_autoinc_unlock(
/*======================*/
dict_table_t* table); /*!< in/out: table */

/** Acquire the analyze index lock.
@param[in] table table whose analyze_index latch to lock */
void
dict_table_analyze_index_lock(
dict_table_t* table);

/** Release the analyze index lock.
@param[in] table table whose analyze_index latch to unlock */
void
dict_table_analyze_index_unlock(
dict_table_t* table);
#endif /* !UNIV_HOTBACKUP */
/**********************************************************************//**
Adds system columns to a table object. */
Expand Down
32 changes: 32 additions & 0 deletions storage/innobase/include/dict0mem.h
Original file line number Diff line number Diff line change
Expand Up @@ -1339,6 +1339,7 @@ the table, DML from memcached will be blocked. */
#define DICT_TABLE_IN_DDL -1

typedef ib_bpmutex_t AutoIncMutex;
typedef ib_mutex_t AnalyzeIndexMutex;

/** Data structure for a database table. Most fields will be
initialized to 0, NULL or FALSE in dict_mem_table_create(). */
Expand Down Expand Up @@ -1710,6 +1711,12 @@ struct dict_table_t {

/* @} */

/** Creation state of analyze_index member */
volatile os_once::state_t analyze_index_mutex_created;

/** Mutex protecting the index during analyze. */
AnalyzeIndexMutex* analyze_index_mutex;

/** Count of how many handles are opened to this table from memcached.
DDL on the table is NOT allowed until this count goes to zero. If
it is -1, then there's DDL on the table, DML from memcached will be
Expand Down Expand Up @@ -1804,6 +1811,31 @@ struct dict_foreign_add_to_referenced_table {
}
};

/** Request for lazy creation of the analyze index mutex of a given table.
@param[in,out] table table whose mutex is to be created. */
inline
void
dict_table_analyze_index_create_lazy(
dict_table_t* table)
{
table->analyze_index_mutex = NULL;
table->analyze_index_mutex_created = os_once::NEVER_DONE;
}

/** Destroy the analyze index mutex of the given table.
@param[in,out] table table whose mutex to destroy */
inline
void
dict_table_analyze_index_destroy(
dict_table_t* table)
{
if (table->analyze_index_mutex_created == os_once::DONE
&& table->analyze_index_mutex != NULL) {
mutex_free(table->analyze_index_mutex);
UT_DELETE(table->analyze_index_mutex);
}
}

/** Destroy the autoinc latch of the given table.
This function is only called from either single threaded environment
or from a thread that has not shared the table object with other threads.
Expand Down
1 change: 1 addition & 0 deletions storage/innobase/include/sync0sync.h
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,7 @@ extern mysql_pfs_key_t thread_mutex_key;
extern mysql_pfs_key_t zip_pad_mutex_key;
extern mysql_pfs_key_t row_drop_list_mutex_key;
extern mysql_pfs_key_t master_key_id_mutex_key;
extern mysql_pfs_key_t analyze_index_mutex_key;
#endif /* UNIV_PFS_MUTEX */

#ifdef UNIV_PFS_RWLOCK
Expand Down
2 changes: 2 additions & 0 deletions storage/innobase/include/sync0types.h
Original file line number Diff line number Diff line change
Expand Up @@ -281,6 +281,7 @@ enum latch_level_t {
SYNC_TREE_NODE_FROM_HASH,
SYNC_TREE_NODE_NEW,
SYNC_INDEX_TREE,
SYNC_ANALYZE_INDEX,

SYNC_IBUF_PESS_INSERT_MUTEX,
SYNC_IBUF_HEADER,
Expand Down Expand Up @@ -411,6 +412,7 @@ enum latch_id_t {
LATCH_ID_FIL_CRYPT_DATA_MUTEX,
LATCH_ID_FIL_CRYPT_THREADS_MUTEX,
LATCH_ID_FIL_CRYPT_START_ROTATE_MUTEX,
LATCH_ID_ANALYZE_INDEX_MUTEX,
LATCH_ID_TEST_MUTEX,
LATCH_ID_MAX = LATCH_ID_TEST_MUTEX
};
Expand Down
8 changes: 8 additions & 0 deletions storage/innobase/sync/sync0debug.cc
Original file line number Diff line number Diff line change
Expand Up @@ -528,6 +528,7 @@ LatchDebug::LatchDebug()
LEVEL_MAP_INSERT(SYNC_TREE_NODE);
LEVEL_MAP_INSERT(SYNC_TREE_NODE_FROM_HASH);
LEVEL_MAP_INSERT(SYNC_TREE_NODE_NEW);
LEVEL_MAP_INSERT(SYNC_ANALYZE_INDEX);
LEVEL_MAP_INSERT(SYNC_INDEX_TREE);
LEVEL_MAP_INSERT(SYNC_IBUF_PESS_INSERT_MUTEX);
LEVEL_MAP_INSERT(SYNC_IBUF_HEADER);
Expand Down Expand Up @@ -954,6 +955,11 @@ LatchDebug::check_order(
basic_check(latches, level, SYNC_TREE_NODE - 1);
break;

case SYNC_ANALYZE_INDEX:

basic_check(latches, level, SYNC_ANALYZE_INDEX - 1);
break;

case SYNC_IBUF_TREE_NODE:

ut_a(find(latches, SYNC_IBUF_INDEX_TREE) != 0
Expand Down Expand Up @@ -1613,6 +1619,8 @@ sync_latch_meta_init()
LATCH_ADD_MUTEX(FIL_CRYPT_START_ROTATE_MUTEX, SYNC_NO_ORDER_CHECK,
PFS_NOT_INSTRUMENTED);

LATCH_ADD_MUTEX(ANALYZE_INDEX_MUTEX, SYNC_ANALYZE_INDEX,
analyze_index_mutex_key);

latch_id_t id = LATCH_ID_NONE;

Expand Down
1 change: 1 addition & 0 deletions storage/innobase/sync/sync0sync.cc
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,7 @@ mysql_pfs_key_t thread_mutex_key;
mysql_pfs_key_t zip_pad_mutex_key;
mysql_pfs_key_t row_drop_list_mutex_key;
mysql_pfs_key_t master_key_id_mutex_key;
mysql_pfs_key_t analyze_index_mutex_key;

#endif /* UNIV_PFS_MUTEX */

Expand Down

0 comments on commit b3f5986

Please sign in to comment.