Commit ddcdacb2 authored by Kristofer Pettersson's avatar Kristofer Pettersson

Bug#38551 query cache can still consume [very little] cpu time even when it is off.

      
When the query cache is disabled, the server shouldn't attempt to take the 
query cache mutex.
                             
By using the command line option --query_cache_type=0, the user can disable
   
(backport from mysql-pe)


mysql-test/t/query_cache_disabled-master.opt:
  * added test case for bug38551
mysql-test/t/query_cache_disabled.test:
  * added test case for bug38551
sql/set_var.cc:
  * Added before-trigger to verify that query_cache_type wasn't turned off or on during
  runtime.
sql/set_var.h:
  * Changed order on how the enumeration is processed. By first projecting the
  character representation of the variable to a temporary integer we can have
  one function instead of two to check if the value is valid.
sql/share/errmsg-utf8.txt:
  * Added error message for query cache disabled state
sql/sql_cache.cc:
  * If the query cache is disabled at start up, shorten the execution path and avoid
  grabbing the query cache mutex each time the invalidate interface methods are called.
sql/sql_cache.h:
  * Added new methods to set the query cache into a disabled state.
parent 42cdc4df
-- source include/have_query_cache.inc
#
# Bug#38551 query cache can still consume [very little] cpu time even when it is off.
#
SHOW GLOBAL VARIABLES LIKE 'query_cache_type';
--error ER_QUERY_CACHE_DISABLED
SET GLOBAL query_cache_type=ON;
--error ER_QUERY_CACHE_DISABLED
SET GLOBAL query_cache_type=DEMAND;
--error ER_QUERY_CACHE_DISABLED
SET GLOBAL query_cache_type=OFF;
SET GLOBAL query_cache_size=1024*1024;
SHOW GLOBAL VARIABLES LIKE 'query_cache_size';
SET GLOBAL query_cache_size=0;
......@@ -125,8 +125,10 @@ static void fix_net_read_timeout(THD *thd, enum_var_type type);
static void fix_net_write_timeout(THD *thd, enum_var_type type);
static void fix_net_retry_count(THD *thd, enum_var_type type);
static void fix_max_join_size(THD *thd, enum_var_type type);
#ifdef HAVE_QUERY_CACHE
static void fix_query_cache_size(THD *thd, enum_var_type type);
static void fix_query_cache_min_res_unit(THD *thd, enum_var_type type);
#endif
static void fix_myisam_max_sort_file_size(THD *thd, enum_var_type type);
static void fix_max_binlog_size(THD *thd, enum_var_type type);
static void fix_max_relay_log_size(THD *thd, enum_var_type type);
......@@ -493,9 +495,6 @@ static sys_var_thd_ulong sys_div_precincrement(&vars, "div_precision_increment",
&SV::div_precincrement);
static sys_var_long_ptr sys_rpl_recovery_rank(&vars, "rpl_recovery_rank",
&rpl_recovery_rank);
static sys_var_long_ptr sys_query_cache_size(&vars, "query_cache_size",
&query_cache_size,
fix_query_cache_size);
static sys_var_thd_ulong sys_range_alloc_block_size(&vars, "range_alloc_block_size",
&SV::range_alloc_block_size);
......@@ -557,14 +556,20 @@ sys_var_enum_const sys_thread_handling(&vars, "thread_handling",
NULL);
#ifdef HAVE_QUERY_CACHE
static sys_var_long_ptr sys_query_cache_size(&vars, "query_cache_size",
&query_cache_size,
fix_query_cache_size);
static sys_var_long_ptr sys_query_cache_limit(&vars, "query_cache_limit",
&query_cache.query_cache_limit);
static sys_var_long_ptr sys_query_cache_min_res_unit(&vars, "query_cache_min_res_unit",
static sys_var_long_ptr
sys_query_cache_min_res_unit(&vars, "query_cache_min_res_unit",
&query_cache_min_res_unit,
fix_query_cache_min_res_unit);
static int check_query_cache_type(THD *thd, set_var *var);
static sys_var_thd_enum sys_query_cache_type(&vars, "query_cache_type",
&SV::query_cache_type,
&query_cache_type_typelib);
&query_cache_type_typelib, NULL,
check_query_cache_type);
static sys_var_thd_bool
sys_query_cache_wlock_invalidate(&vars, "query_cache_wlock_invalidate",
&SV::query_cache_wlock_invalidate);
......@@ -1131,10 +1136,9 @@ static void fix_net_retry_count(THD *thd __attribute__((unused)),
{}
#endif /* HAVE_REPLICATION */
#ifdef HAVE_QUERY_CACHE
static void fix_query_cache_size(THD *thd, enum_var_type type)
{
#ifdef HAVE_QUERY_CACHE
ulong new_cache_size= query_cache.resize(query_cache_size);
/*
......@@ -1148,11 +1152,35 @@ static void fix_query_cache_size(THD *thd, enum_var_type type)
query_cache_size, new_cache_size);
query_cache_size= new_cache_size;
#endif
}
#ifdef HAVE_QUERY_CACHE
/**
Trigger before query_cache_type variable is updated.
@param thd Thread handler
@param var Pointer to the new variable status
@return Status code
@retval 1 Failure
@retval 0 Success
*/
static int check_query_cache_type(THD *thd, set_var *var)
{
/*
Don't allow changes of the query_cache_type if the query cache
is disabled.
*/
if (query_cache.is_disabled())
{
my_error(ER_QUERY_CACHE_DISABLED,MYF(0));
return 1;
}
return 0;
}
static void fix_query_cache_min_res_unit(THD *thd, enum_var_type type)
{
query_cache_min_res_unit=
......@@ -3601,6 +3629,16 @@ bool not_all_support_one_shot(List<set_var_base> *var_list)
Functions to handle SET mysql_internal_variable=const_expr
*****************************************************************************/
/**
Verify that the supplied value is correct.
@param thd Thread handler
@return status code
@retval -1 Failure
@retval 0 Success
*/
int set_var::check(THD *thd)
{
if (var->is_readonly())
......
......@@ -543,10 +543,16 @@ class sys_var_thd_enum :public sys_var_thd
{ chain_sys_var(chain); }
bool check(THD *thd, set_var *var)
{
int ret= 0;
if (check_func)
ret= (*check_func)(thd, var);
return ret ? ret : check_enum(thd, var, enum_names);
/*
check_enum fails if the character representation supplied was wrong
or that the integer value was wrong or missing.
*/
if (check_enum(thd, var, enum_names))
return TRUE;
else if ((check_func && (*check_func)(thd, var)))
return TRUE;
else
return FALSE;
}
bool update(THD *thd, set_var *var);
void set_default(THD *thd, enum_var_type type);
......
......@@ -6241,3 +6241,6 @@ ER_UNKNOWN_LOCALE
ER_SLAVE_IGNORE_SERVER_IDS
eng "The requested server id %d clashes with the slave startup option --replicate-same-server-id"
ER_QUERY_CACHE_DISABLED
eng "Query cache is disabled; restart the server with query_cache_type=1 to enable it"
......@@ -286,6 +286,7 @@ The query cache interfaces with the rest of the server code through 7
if (and only if) this query has a registered result set writer
(thd->net.query_cache_query).
4. Query_cache::invalidate
Query_cache::invalidate_locked_for_write
- Called from various places to invalidate query cache based on data-
base, table and myisam file name. During an on going invalidation
the query cache is temporarily disabled.
......@@ -851,7 +852,7 @@ Query_cache::insert(Query_cache_tls *query_cache_tls,
DBUG_ENTER("Query_cache::insert");
/* See the comment on double-check locking usage above. */
if (query_cache_tls->first_query_block == NULL)
if (is_disabled() || query_cache_tls->first_query_block == NULL)
DBUG_VOID_RETURN;
DBUG_EXECUTE_IF("wait_in_query_cache_insert",
......@@ -913,7 +914,7 @@ Query_cache::abort(Query_cache_tls *query_cache_tls)
THD *thd= current_thd;
/* See the comment on double-check locking usage above. */
if (query_cache_tls->first_query_block == NULL)
if (is_disabled() || query_cache_tls->first_query_block == NULL)
DBUG_VOID_RETURN;
if (try_lock())
......@@ -1055,7 +1056,7 @@ Query_cache::Query_cache(ulong query_cache_limit_arg,
min_result_data_size(ALIGN_SIZE(min_result_data_size_arg)),
def_query_hash_size(ALIGN_SIZE(def_query_hash_size_arg)),
def_table_hash_size(ALIGN_SIZE(def_table_hash_size_arg)),
initialized(0)
initialized(0), m_query_cache_is_disabled(FALSE)
{
ulong min_needed= (ALIGN_SIZE(sizeof(Query_cache_block)) +
ALIGN_SIZE(sizeof(Query_cache_block_table)) +
......@@ -1362,8 +1363,8 @@ Query_cache::send_result_to_client(THD *thd, char *sql, uint query_length)
See also a note on double-check locking usage above.
*/
if (thd->locked_tables || thd->variables.query_cache_type == 0 ||
query_cache_size == 0)
if (is_disabled() || thd->locked_tables ||
thd->variables.query_cache_type == 0 || query_cache_size == 0)
goto err;
if (!thd->lex->safe_to_cache_query)
......@@ -1669,6 +1670,8 @@ void Query_cache::invalidate(THD *thd, TABLE_LIST *tables_used,
my_bool using_transactions)
{
DBUG_ENTER("Query_cache::invalidate (table list)");
if (is_disabled())
DBUG_VOID_RETURN;
using_transactions= using_transactions &&
(thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN));
......@@ -1699,6 +1702,9 @@ void Query_cache::invalidate(THD *thd, TABLE_LIST *tables_used,
void Query_cache::invalidate(CHANGED_TABLE_LIST *tables_used)
{
DBUG_ENTER("Query_cache::invalidate (changed table list)");
if (is_disabled())
DBUG_VOID_RETURN;
THD *thd= current_thd;
for (; tables_used; tables_used= tables_used->next)
{
......@@ -1724,8 +1730,11 @@ void Query_cache::invalidate(CHANGED_TABLE_LIST *tables_used)
*/
void Query_cache::invalidate_locked_for_write(TABLE_LIST *tables_used)
{
THD *thd= current_thd;
DBUG_ENTER("Query_cache::invalidate_locked_for_write");
if (is_disabled())
DBUG_VOID_RETURN;
THD *thd= current_thd;
for (; tables_used; tables_used= tables_used->next_local)
{
thd_proc_info(thd, "invalidating query cache entries (table)");
......@@ -1746,6 +1755,8 @@ void Query_cache::invalidate(THD *thd, TABLE *table,
my_bool using_transactions)
{
DBUG_ENTER("Query_cache::invalidate (table)");
if (is_disabled())
DBUG_VOID_RETURN;
using_transactions= using_transactions &&
(thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN));
......@@ -1763,6 +1774,8 @@ void Query_cache::invalidate(THD *thd, const char *key, uint32 key_length,
my_bool using_transactions)
{
DBUG_ENTER("Query_cache::invalidate (key)");
if (is_disabled())
DBUG_VOID_RETURN;
using_transactions= using_transactions &&
(thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN));
......@@ -1781,9 +1794,12 @@ void Query_cache::invalidate(THD *thd, const char *key, uint32 key_length,
void Query_cache::invalidate(char *db)
{
bool restart= FALSE;
DBUG_ENTER("Query_cache::invalidate (db)");
if (is_disabled())
DBUG_VOID_RETURN;
bool restart= FALSE;
/*
Lock the query cache and queue all invalidation attempts to avoid
the risk of a race between invalidation, cache inserts and flushes.
......@@ -1868,6 +1884,9 @@ void Query_cache::invalidate_by_MyISAM_filename(const char *filename)
void Query_cache::flush()
{
DBUG_ENTER("Query_cache::flush");
if (is_disabled())
DBUG_VOID_RETURN;
DBUG_EXECUTE_IF("wait_in_query_cache_flush1",
debug_wait_for_kill("wait_in_query_cache_flush1"););
......@@ -1899,6 +1918,9 @@ void Query_cache::pack(ulong join_limit, uint iteration_limit)
{
DBUG_ENTER("Query_cache::pack");
if (is_disabled())
DBUG_VOID_RETURN;
/*
If the entire qc is being invalidated we can bail out early
instead of waiting for the lock.
......@@ -1956,6 +1978,15 @@ void Query_cache::init()
pthread_cond_init(&COND_cache_status_changed, NULL);
m_cache_lock_status= Query_cache::UNLOCKED;
initialized = 1;
/*
If we explicitly turn off query cache from the command line query cache will
be disabled for the reminder of the server life time. This is because we
want to avoid locking the QC specific mutex if query cache isn't going to
be used.
*/
if (global_system_variables.query_cache_type == 0)
query_cache.disable_query_cache();
DBUG_VOID_RETURN;
}
......@@ -4660,3 +4691,4 @@ my_bool Query_cache::in_table_list(Query_cache_block_table * root,
#endif /* DBUG_OFF */
#endif /*HAVE_QUERY_CACHE*/
......@@ -281,8 +281,11 @@ class Query_cache
enum Cache_lock_status { UNLOCKED, LOCKED_NO_WAIT, LOCKED };
Cache_lock_status m_cache_lock_status;
bool m_query_cache_is_disabled;
void free_query_internal(Query_cache_block *point);
void invalidate_table_internal(THD *thd, uchar *key, uint32 key_length);
void disable_query_cache(void) { m_query_cache_is_disabled= TRUE; }
protected:
/*
......@@ -426,6 +429,8 @@ class Query_cache
uint def_query_hash_size = QUERY_CACHE_DEF_QUERY_HASH_SIZE,
uint def_table_hash_size = QUERY_CACHE_DEF_TABLE_HASH_SIZE);
bool is_disabled(void) { return m_query_cache_is_disabled; }
/* initialize cache (mutex) */
void init();
/* resize query cache (return real query size, 0 if disabled) */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment