diff options
author | Monty <monty@mariadb.org> | 2017-12-07 15:15:22 +0200 |
---|---|---|
committer | Monty <monty@mariadb.org> | 2017-12-08 13:44:46 +0200 |
commit | 7891a713daf52b5afd7e423d83c6d80d788ab733 (patch) | |
tree | 62867b47632d5e6601eb5bcc51f91c40d0977048 /sql | |
parent | b3346c2f41d0f7bd0d919826592b37ae09fa41c8 (diff) | |
download | mariadb-git-7891a713daf52b5afd7e423d83c6d80d788ab733.tar.gz |
Don't wait too long in SHOW PROCESSLIST
This will allow show processlist to continue, without blocking
all new connections, if some threads gets stuck while holding
LOCK_thd_data or mysys_var->mutex
Connections that has mutex 'stuck' are marked as 'Busy' in 'Command'
Todo:
Make F_BACKOFF to do 'pause' instead of just (1)
Diffstat (limited to 'sql')
-rw-r--r-- | sql/sql_show.cc | 204 |
1 files changed, 126 insertions, 78 deletions
diff --git a/sql/sql_show.cc b/sql/sql_show.cc index 30cf490c10c..bb6a03fff96 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -137,6 +137,29 @@ static const LEX_CSTRING *view_algorithm(TABLE_LIST *table); bool get_lookup_field_values(THD *, COND *, TABLE_LIST *, LOOKUP_FIELD_VALUES *); +/** + Try to lock a mutex, but give up after a short while to not cause deadlocks + + The loop is short, as the mutex we are trying to lock are mutex the should + never be locked a long time, just over a few instructions. + + @return 0 ok + @return 1 error +*/ + +static bool trylock_short(mysql_mutex_t *mutex) +{ + uint i; + for (i= 0 ; i < 100 ; i++) + { + if (!mysql_mutex_trylock(mutex)) + return 0; + LF_BACKOFF(); + } + return 1; +} + + /*************************************************************************** ** List all table types supported ***************************************************************************/ @@ -2634,7 +2657,8 @@ void mysqld_list_processes(THD *thd,const char *user, bool verbose) while ((tmp=it++)) { Security_context *tmp_sctx= tmp->security_ctx; - struct st_my_thread_var *mysys_var; + struct st_my_thread_var *mysys_var= 0; + bool got_thd_data, got_mysys_lock= 0; if ((tmp->vio_ok() || tmp->system_thread) && (!user || (!tmp->system_thread && tmp_sctx->user && !strcmp(tmp_sctx->user, user)))) @@ -2658,48 +2682,66 @@ void mysqld_list_processes(THD *thd,const char *user, bool verbose) tmp_sctx->host_or_ip : tmp_sctx->host ? tmp_sctx->host : ""); thd_info->command=(int) tmp->get_command(); - mysql_mutex_lock(&tmp->LOCK_thd_data); - if ((thd_info->db= tmp->db)) // Safe test - thd_info->db= thd->strdup(thd_info->db); - if ((mysys_var= tmp->mysys_var)) - mysql_mutex_lock(&mysys_var->mutex); - thd_info->proc_info= (char*) (tmp->killed >= KILL_QUERY ? - "Killed" : 0); - thd_info->state_info= thread_state_info(tmp); - if (mysys_var) - mysql_mutex_unlock(&mysys_var->mutex); - /* Lock THD mutex that protects its data when looking at it. */ - if (tmp->query()) - { - uint length= MY_MIN(max_query_length, tmp->query_length()); - char *q= thd->strmake(tmp->query(),length); - /* Safety: in case strmake failed, we set length to 0. */ - thd_info->query_string= - CSET_STRING(q, q ? length : 0, tmp->query_charset()); - } + if ((got_thd_data= !trylock_short(&tmp->LOCK_thd_data))) + if ((mysys_var= tmp->mysys_var)) + got_mysys_lock= !trylock_short(&mysys_var->mutex); - /* - Progress report. We need to do this under a lock to ensure that all - is from the same stage. - */ - if (tmp->progress.max_counter) + if (got_thd_data) { - uint max_stage= MY_MAX(tmp->progress.max_stage, 1); - thd_info->progress= (((tmp->progress.stage / (double) max_stage) + - ((tmp->progress.counter / - (double) tmp->progress.max_counter) / - (double) max_stage)) * - 100.0); - set_if_smaller(thd_info->progress, 100); + /* This is correct under mysys_lock, otherwise an approximation */ + thd_info->proc_info= (char*) (tmp->killed >= KILL_QUERY ? + "Killed" : 0); + if (got_mysys_lock) + mysql_mutex_unlock(&mysys_var->mutex); + + /* + The following variables are only safe to access under a lock + */ + + if ((thd_info->db= tmp->db)) // Safe test + thd_info->db= thd->strdup(thd_info->db); + + if (tmp->query()) + { + uint length= MY_MIN(max_query_length, tmp->query_length()); + char *q= thd->strmake(tmp->query(),length); + /* Safety: in case strmake failed, we set length to 0. */ + thd_info->query_string= + CSET_STRING(q, q ? length : 0, tmp->query_charset()); + } + + /* + Progress report. We need to do this under a lock to ensure that all + is from the same stage. + */ + if (tmp->progress.max_counter) + { + uint max_stage= MY_MAX(tmp->progress.max_stage, 1); + thd_info->progress= (((tmp->progress.stage / (double) max_stage) + + ((tmp->progress.counter / + (double) tmp->progress.max_counter) / + (double) max_stage)) * + 100.0); + set_if_smaller(thd_info->progress, 100); + } + else + thd_info->progress= 0.0; } else + { + thd_info->proc_info= "Busy"; thd_info->progress= 0.0; + } + + thd_info->state_info= thread_state_info(tmp); thd_info->start_time= tmp->start_utime; ulonglong utime_after_query_snapshot= tmp->utime_after_query; if (thd_info->start_time < utime_after_query_snapshot) thd_info->start_time= utime_after_query_snapshot; // COM_SLEEP - mysql_mutex_unlock(&tmp->LOCK_thd_data); + + if (got_thd_data) + mysql_mutex_unlock(&tmp->LOCK_thd_data); thread_infos.append(thd_info); } } @@ -2938,7 +2980,7 @@ int fill_show_explain(THD *thd, TABLE_LIST *table, COND *cond) explain_req.request_thd= thd; explain_req.failed_to_produce= FALSE; - /* Ok, we have a lock on target->LOCK_thd_data, can call: */ + /* Ok, we have a lock on target->LOCK_thd_kill, can call: */ bres= tmp->apc_target.make_apc_call(thd, &explain_req, timeout_sec, &timed_out); if (bres || explain_req.failed_to_produce) @@ -3017,9 +3059,10 @@ int fill_schema_processlist(THD* thd, TABLE_LIST* tables, COND* cond) while ((tmp= it++)) { Security_context *tmp_sctx= tmp->security_ctx; - struct st_my_thread_var *mysys_var; + struct st_my_thread_var *mysys_var= 0; const char *val, *db; ulonglong max_counter; + bool got_thd_data, got_mysys_lock= 0; if ((!tmp->vio_ok() && !tmp->system_thread) || (user && (tmp->system_thread || !tmp_sctx->user || @@ -3045,23 +3088,33 @@ int fill_schema_processlist(THD* thd, TABLE_LIST* tables, COND* cond) else table->field[2]->store(tmp_sctx->host_or_ip, strlen(tmp_sctx->host_or_ip), cs); - /* DB */ - mysql_mutex_lock(&tmp->LOCK_thd_data); - if ((db= tmp->db)) + + if ((got_thd_data= !trylock_short(&tmp->LOCK_thd_data))) + if ((mysys_var= tmp->mysys_var)) + got_mysys_lock= !trylock_short(&mysys_var->mutex); + + if (got_thd_data) { - table->field[3]->store(db, strlen(db), cs); - table->field[3]->set_notnull(); + /* DB */ + if ((db= tmp->db)) + { + table->field[3]->store(db, strlen(db), cs); + table->field[3]->set_notnull(); + } } - if ((mysys_var= tmp->mysys_var)) - mysql_mutex_lock(&mysys_var->mutex); /* COMMAND */ - if ((val= (char *) ((tmp->killed >= KILL_QUERY ? + if ((val= (char *) (!got_thd_data ? "Busy" : + (tmp->killed >= KILL_QUERY ? "Killed" : 0)))) table->field[4]->store(val, strlen(val), cs); else table->field[4]->store(command_name[tmp->get_command()].str, command_name[tmp->get_command()].length, cs); + + if (got_mysys_lock) + mysql_mutex_unlock(&mysys_var->mutex); + /* MYSQL_TIME */ ulonglong utime= tmp->start_utime; ulonglong utime_after_query_snapshot= tmp->utime_after_query; @@ -3070,6 +3123,7 @@ int fill_schema_processlist(THD* thd, TABLE_LIST* tables, COND* cond) utime= utime && utime < unow ? unow - utime : 0; table->field[5]->store(utime / HRTIME_RESOLUTION, TRUE); + /* STATE */ if ((val= thread_state_info(tmp))) { @@ -3077,45 +3131,39 @@ int fill_schema_processlist(THD* thd, TABLE_LIST* tables, COND* cond) table->field[6]->set_notnull(); } - if (mysys_var) - mysql_mutex_unlock(&mysys_var->mutex); - mysql_mutex_unlock(&tmp->LOCK_thd_data); - - /* TIME_MS */ - table->field[8]->store((double)(utime / (HRTIME_RESOLUTION / 1000.0))); - - /* INFO */ - /* Lock THD mutex that protects its data when looking at it. */ - mysql_mutex_lock(&tmp->LOCK_thd_data); - if (tmp->query()) + if (got_thd_data) { - table->field[7]->store(tmp->query(), - MY_MIN(PROCESS_LIST_INFO_WIDTH, - tmp->query_length()), cs); - table->field[7]->set_notnull(); - } + if (tmp->query()) + { + table->field[7]->store(tmp->query(), + MY_MIN(PROCESS_LIST_INFO_WIDTH, + tmp->query_length()), cs); + table->field[7]->set_notnull(); + + /* INFO_BINARY */ + table->field[16]->store(tmp->query(), + MY_MIN(PROCESS_LIST_INFO_WIDTH, + tmp->query_length()), + &my_charset_bin); + table->field[16]->set_notnull(); + } - /* INFO_BINARY */ - if (tmp->query()) - { - table->field[16]->store(tmp->query(), - MY_MIN(PROCESS_LIST_INFO_WIDTH, - tmp->query_length()), &my_charset_bin); - table->field[16]->set_notnull(); + /* + Progress report. We need to do this under a lock to ensure that all + is from the same stage. + */ + if ((max_counter= tmp->progress.max_counter)) + { + table->field[9]->store((longlong) tmp->progress.stage + 1, 1); + table->field[10]->store((longlong) tmp->progress.max_stage, 1); + table->field[11]->store((double) tmp->progress.counter / + (double) max_counter*100.0); + } + mysql_mutex_unlock(&tmp->LOCK_thd_data); } - /* - Progress report. We need to do this under a lock to ensure that all - is from the same stage. - */ - if ((max_counter= tmp->progress.max_counter)) - { - table->field[9]->store((longlong) tmp->progress.stage + 1, 1); - table->field[10]->store((longlong) tmp->progress.max_stage, 1); - table->field[11]->store((double) tmp->progress.counter / - (double) max_counter*100.0); - } - mysql_mutex_unlock(&tmp->LOCK_thd_data); + /* TIME_MS */ + table->field[8]->store((double)(utime / (HRTIME_RESOLUTION / 1000.0))); /* This may become negative if we free a memory allocated by another |