Skip to content

Commit 64124bb

Browse files
author
Rahul Agarkar
committed
Bug #29008298 MYSQLD CRASHES ITSELF WHEN CREATING INDEX
Problem ------- A long running ALTER TABLE ADD INDEX with concurrent inserts causes sempahore waits and eventually crashes the server. Solution --------- The indexes on the intermediate tables are built using bulk load insert. A concurrent DML at this stage does not acquire the index lock. An index lock acquired on the intermediate table, which is not visible to anyone else, does not block concurrent DMLs. RB# 22482 Reviewed by: Annamalai Gurusami (annamalai.gurusami@oracle.com)
1 parent 1506a1e commit 64124bb

File tree

10 files changed

+129
-11
lines changed

10 files changed

+129
-11
lines changed

mysql-test/r/all_persisted_variables.result

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -37,17 +37,17 @@ include/assert.inc [Expect 500+ variables in the table. Due to open Bugs, we are
3737

3838
# Test SET PERSIST
3939

40-
include/assert.inc [Expect 385 persisted variables in the table.]
40+
include/assert.inc [Expect 386 persisted variables in the table.]
4141

4242
************************************************************
4343
* 3. Restart server, it must preserve the persisted variable
4444
* settings. Verify persisted configuration.
4545
************************************************************
4646
# restart
4747

48-
include/assert.inc [Expect 385 persisted variables in persisted_variables table.]
49-
include/assert.inc [Expect 385 persisted variables shown as PERSISTED in variables_info table.]
50-
include/assert.inc [Expect 385 persisted variables with matching peristed and global values.]
48+
include/assert.inc [Expect 386 persisted variables in persisted_variables table.]
49+
include/assert.inc [Expect 386 persisted variables shown as PERSISTED in variables_info table.]
50+
include/assert.inc [Expect 386 persisted variables with matching peristed and global values.]
5151

5252
************************************************************
5353
* 4. Test RESET PERSIST IF EXISTS. Verify persisted variable
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
#
2+
# Basic test for innodb_semaphore_wait_timeout_debug
3+
#
4+
SELECT @@global.innodb_semaphore_wait_timeout_debug;
5+
@@global.innodb_semaphore_wait_timeout_debug
6+
600
7+
SET GLOBAL innodb_semaphore_wait_timeout_debug = 10;
8+
Warnings:
9+
Warning 1292 Truncated incorrect innodb_semaphore_wait_timeout_de value: '10'
10+
SELECT @@global.innodb_semaphore_wait_timeout_debug;
11+
@@global.innodb_semaphore_wait_timeout_debug
12+
100
13+
SET GLOBAL innodb_semaphore_wait_timeout_debug = 200;
14+
SELECT @@global.innodb_semaphore_wait_timeout_debug;
15+
@@global.innodb_semaphore_wait_timeout_debug
16+
200
17+
SET GLOBAL innodb_semaphore_wait_timeout_debug = dummy;
18+
ERROR 42000: Incorrect argument type to variable 'innodb_semaphore_wait_timeout_debug'
19+
SET innodb_semaphore_wait_timeout_debug = 100;
20+
ERROR HY000: Variable 'innodb_semaphore_wait_timeout_debug' is a GLOBAL variable and should be set with SET GLOBAL
21+
SET GLOBAL innodb_semaphore_wait_timeout_debug = 6000;
22+
Warnings:
23+
Warning 1292 Truncated incorrect innodb_semaphore_wait_timeout_de value: '6000'
24+
SELECT @@global.innodb_semaphore_wait_timeout_debug;
25+
@@global.innodb_semaphore_wait_timeout_debug
26+
600
27+
SET GLOBAL innodb_semaphore_wait_timeout_debug = -1;
28+
Warnings:
29+
Warning 1292 Truncated incorrect innodb_semaphore_wait_timeout_de value: '-1'
30+
SELECT @@global.innodb_semaphore_wait_timeout_debug;
31+
@@global.innodb_semaphore_wait_timeout_debug
32+
100
33+
SET GLOBAL innodb_semaphore_wait_timeout_debug = default;
34+
SELECT @@global.innodb_semaphore_wait_timeout_debug;
35+
@@global.innodb_semaphore_wait_timeout_debug
36+
600
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
--echo #
2+
--echo # Basic test for innodb_semaphore_wait_timeout_debug
3+
--echo #
4+
5+
# The config variable is a debug variable
6+
--source include/have_debug.inc
7+
8+
SELECT @@global.innodb_semaphore_wait_timeout_debug;
9+
10+
SET GLOBAL innodb_semaphore_wait_timeout_debug = 10;
11+
SELECT @@global.innodb_semaphore_wait_timeout_debug;
12+
13+
SET GLOBAL innodb_semaphore_wait_timeout_debug = 200;
14+
SELECT @@global.innodb_semaphore_wait_timeout_debug;
15+
16+
--error ER_WRONG_TYPE_FOR_VAR
17+
SET GLOBAL innodb_semaphore_wait_timeout_debug = dummy;
18+
19+
--error ER_GLOBAL_VARIABLE
20+
SET innodb_semaphore_wait_timeout_debug = 100;
21+
22+
SET GLOBAL innodb_semaphore_wait_timeout_debug = 6000;
23+
SELECT @@global.innodb_semaphore_wait_timeout_debug;
24+
25+
SET GLOBAL innodb_semaphore_wait_timeout_debug = -1;
26+
SELECT @@global.innodb_semaphore_wait_timeout_debug;
27+
28+
SET GLOBAL innodb_semaphore_wait_timeout_debug = default;
29+
SELECT @@global.innodb_semaphore_wait_timeout_debug;

mysql-test/t/all_persisted_variables.test

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@
3939
call mtr.add_suppression("Failed to set up SSL because of the following SSL library error");
4040

4141
let $total_global_vars=`SELECT COUNT(*) FROM performance_schema.global_variables where variable_name NOT LIKE 'ndb_%'`;
42-
let $total_persistent_vars=385;
42+
let $total_persistent_vars=386;
4343

4444
--echo ***************************************************************
4545
--echo * 0. Verify that variables present in performance_schema.global

storage/innobase/btr/btr0bulk.cc

Lines changed: 35 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,11 @@ dberr_t PageBulk::init() {
5656

5757
mtr = static_cast<mtr_t *>(mem_heap_alloc(m_heap, sizeof(mtr_t)));
5858
mtr_start(mtr);
59-
mtr_x_lock(dict_index_get_lock(m_index), mtr);
59+
60+
if (!dict_index_is_online_ddl(m_index)) {
61+
mtr_x_lock(dict_index_get_lock(m_index), mtr);
62+
}
63+
6064
mtr_set_log_mode(mtr, MTR_LOG_NO_REDO);
6165
mtr_set_flush_observer(mtr, m_flush_observer);
6266

@@ -607,7 +611,11 @@ void PageBulk::release() {
607611
/** Start mtr and latch the block */
608612
void PageBulk::latch() {
609613
mtr_start(m_mtr);
610-
mtr_x_lock(dict_index_get_lock(m_index), m_mtr);
614+
615+
if (!dict_index_is_online_ddl(m_index)) {
616+
mtr_x_lock(dict_index_get_lock(m_index), m_mtr);
617+
}
618+
611619
mtr_set_log_mode(m_mtr, MTR_LOG_NO_REDO);
612620
mtr_set_flush_observer(m_mtr, m_flush_observer);
613621

@@ -631,6 +639,15 @@ void PageBulk::latch() {
631639
ut_ad(m_cur_rec > m_page && m_cur_rec < m_heap_top);
632640
}
633641

642+
#ifdef UNIV_DEBUG
643+
/* Check if an index is locked */
644+
bool PageBulk::isIndexXLocked() {
645+
return (dict_index_is_online_ddl(m_index) &&
646+
mtr_memo_contains_flagged(m_mtr, dict_index_get_lock(m_index),
647+
MTR_MEMO_X_LOCK | MTR_MEMO_SX_LOCK));
648+
}
649+
#endif // UNIV_DEBUG
650+
634651
/** Split a page
635652
@param[in] page_bulk page to split
636653
@param[in] next_page_bulk next page
@@ -697,6 +714,15 @@ dberr_t BtrBulk::pageCommit(PageBulk *page_bulk, PageBulk *next_page_bulk,
697714
page_bulk->setNext(FIL_NULL);
698715
}
699716

717+
/* Assert that no locks are held during bulk load operation
718+
in case of a online ddl operation. Insert thread acquires index->lock
719+
to check the online status of index. During bulk load index,
720+
there are no concurrent insert or reads and hence, there is no
721+
need to acquire a lock in that case. */
722+
ut_ad(!page_bulk->isIndexXLocked());
723+
724+
DBUG_EXECUTE_IF("innodb_bulk_load_sleep", os_thread_sleep(1000000););
725+
700726
/* Compress page if it's a compressed table. */
701727
if (page_bulk->isTableCompressed() && !page_bulk->compress()) {
702728
return (pageSplit(page_bulk, next_page_bulk));
@@ -742,6 +768,7 @@ BtrBulk::BtrBulk(dict_index_t *index, trx_id_t trx_id, FlushObserver *observer)
742768
ut_ad(m_flush_observer != nullptr);
743769
#ifdef UNIV_DEBUG
744770
fil_space_inc_redo_skipped_count(m_index->space);
771+
m_index_online = m_index->online_status;
745772
#endif /* UNIV_DEBUG */
746773
}
747774

@@ -917,6 +944,7 @@ dberr_t BtrBulk::insert(dtuple_t *tuple, ulint level) {
917944
return (err);
918945
}
919946

947+
DEBUG_SYNC_C("bulk_load_insert");
920948
m_page_bulks->push_back(new_page_bulk);
921949
ut_ad(level + 1 == m_page_bulks->size());
922950
m_root_level = level;
@@ -1016,6 +1044,11 @@ dberr_t BtrBulk::finish(dberr_t err) {
10161044
ut_ad(m_page_bulks);
10171045
ut_ad(!m_index->table->is_temporary());
10181046

1047+
#ifdef UNIV_DEBUG
1048+
/* Assert that the index online status has not changed */
1049+
ut_ad(m_index->online_status == m_index_online);
1050+
#endif // UNIV_DEBUG
1051+
10191052
page_no_t last_page_no = FIL_NULL;
10201053

10211054
if (m_page_bulks->size() == 0) {

storage/innobase/handler/ha_innodb.cc

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21175,6 +21175,13 @@ static MYSQL_SYSVAR_UINT(
2117521175
" cache by the specified value dynamically, at the time.",
2117621176
NULL, innodb_merge_threshold_set_all_debug_update,
2117721177
DICT_INDEX_MERGE_THRESHOLD_DEFAULT, 1, 50, 0);
21178+
21179+
static MYSQL_SYSVAR_ULONG(
21180+
semaphore_wait_timeout_debug, srv_fatal_semaphore_wait_threshold,
21181+
PLUGIN_VAR_RQCMDARG,
21182+
"Number of seconds that a semaphore can be held. If semaphore wait crosses"
21183+
"this value, server will crash",
21184+
NULL, NULL, 600, 100, 600, 0);
2117821185
#endif /* UNIV_DEBUG */
2117921186

2118021187
static MYSQL_SYSVAR_ULONG(
@@ -22390,6 +22397,7 @@ static SYS_VAR *innobase_system_variables[] = {
2239022397
MYSQL_SYSVAR(checkpoint_disabled),
2239122398
MYSQL_SYSVAR(buf_flush_list_now),
2239222399
MYSQL_SYSVAR(merge_threshold_set_all_debug),
22400+
MYSQL_SYSVAR(semaphore_wait_timeout_debug),
2239322401
#endif /* UNIV_DEBUG */
2239422402
#if defined UNIV_DEBUG || defined UNIV_PERF_DEBUG
2239522403
MYSQL_SYSVAR(page_hash_locks),

storage/innobase/include/btr0bulk.h

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
/*****************************************************************************
22
3-
Copyright (c) 2014, 2018, Oracle and/or its affiliates. All Rights Reserved.
3+
Copyright (c) 2014, 2019, Oracle and/or its affiliates. All Rights Reserved.
44
55
This program is free software; you can redistribute it and/or modify it under
66
the terms of the GNU General Public License, version 2.0, as published by the
@@ -192,6 +192,11 @@ class PageBulk {
192192
@return true if table is compressed, false otherwise. */
193193
bool isTableCompressed() const { return (m_page_zip != nullptr); }
194194

195+
#ifdef UNIV_DEBUG
196+
/** Check if index is X locked */
197+
bool isIndexXLocked();
198+
#endif // UNIV_DEBUG
199+
195200
private:
196201
/** Get page split point. We split a page in half when compression
197202
fails, and the split record and all following records should be copied
@@ -408,6 +413,13 @@ class BtrBulk {
408413

409414
/** Page cursor vector for all level */
410415
page_bulk_vector *m_page_bulks;
416+
417+
#ifdef UNIV_DEBUG
418+
/** State of the index. Used for asserting at the end of a
419+
bulk load operation to ensure that the online status of the
420+
index does not change */
421+
unsigned m_index_online;
422+
#endif // UNIV_DEBUG
411423
};
412424

413425
#endif

storage/innobase/include/srv0srv.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -672,7 +672,7 @@ extern bool srv_purge_view_update_only_debug;
672672
extern bool srv_master_thread_disabled_debug;
673673
#endif /* UNIV_DEBUG */
674674

675-
extern ulint srv_fatal_semaphore_wait_threshold;
675+
extern ulong srv_fatal_semaphore_wait_threshold;
676676
#define SRV_SEMAPHORE_WAIT_EXTENSION 7200
677677
extern ulint srv_dml_needed_delay;
678678

storage/innobase/lob/lob0lob.cc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -425,7 +425,7 @@ dberr_t btr_store_big_rec_extern_fields(trx_t *trx, btr_pcur_t *pcur,
425425
ut_ad(btr_mtr);
426426
ut_ad(mtr_memo_contains_flagged(btr_mtr, dict_index_get_lock(index),
427427
MTR_MEMO_X_LOCK | MTR_MEMO_SX_LOCK) ||
428-
index->table->is_intrinsic());
428+
index->table->is_intrinsic() || !index->is_committed());
429429
ut_ad(
430430
mtr_is_block_fix(btr_mtr, rec_block, MTR_MEMO_PAGE_X_FIX, index->table));
431431
ut_ad(buf_block_get_frame(rec_block) == page_align(rec));

storage/innobase/srv/srv0srv.cc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ bool srv_upgrade_old_undo_found = false;
108108
#endif /* INNODB_DD_TABLE */
109109

110110
/* The following is the maximum allowed duration of a lock wait. */
111-
ulint srv_fatal_semaphore_wait_threshold = 600;
111+
ulong srv_fatal_semaphore_wait_threshold = 600;
112112

113113
/* How much data manipulation language (DML) statements need to be delayed,
114114
in microseconds, in order to reduce the lagging of the purge thread. */

0 commit comments

Comments
 (0)