From ea6a22bfa816ebc373316eee36c18952e812e7f6 Mon Sep 17 00:00:00 2001 From: Konstantin Osipov <kostja@sun.com> Date: Tue, 13 Oct 2009 22:18:48 +0400 Subject: [PATCH] Introduce thd->query_cache_tls (thread local storage for query cache). We need more than one pointer in a thread to represent the query cache and net->query_cache_query can not be used any more (due to ABI compatibility issues and to different life time of NET and THD). This is a backport of the following patch from 6.0: ---------------------------------------------------------- revno: 2476.1157.2 committer: kostja@bodhi.(none) timestamp: Sat 2007-06-16 13:29:24 +0400 --- include/mysql.h.pp | 2 +- include/mysql_com.h | 5 +- sql/mysql_priv.h | 2 + sql/net_serv.cc | 12 ++-- sql/sql_cache.cc | 162 +++++++++++++++++++++++++------------------- sql/sql_cache.h | 27 ++++---- sql/sql_class.cc | 5 +- sql/sql_class.h | 24 +++++++ sql/sql_parse.cc | 2 +- sql/sql_select.cc | 2 +- 10 files changed, 141 insertions(+), 102 deletions(-) diff --git a/include/mysql.h.pp b/include/mysql.h.pp index fcbcea61873..3d1304bf460 100644 --- a/include/mysql.h.pp +++ b/include/mysql.h.pp @@ -32,7 +32,7 @@ typedef struct st_net { my_bool unused2; my_bool compress; my_bool unused3; - unsigned char *query_cache_query; + unsigned char *unused; unsigned int last_errno; unsigned char error; my_bool unused4; diff --git a/include/mysql_com.h b/include/mysql_com.h index 672c359a6f7..5e1b6b8504a 100644 --- a/include/mysql_com.h +++ b/include/mysql_com.h @@ -264,10 +264,9 @@ typedef struct st_net { */ #endif /* - 'query_cache_query' should be accessed only via query cache - functions and methods to maintain proper locking. + Unused, please remove with the next incompatible ABI change. */ - unsigned char *query_cache_query; + unsigned char *unused; unsigned int last_errno; unsigned char error; my_bool unused4; /* Please remove with the next incompatible ABI change. */ diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h index 8af0dc524b3..353f948b122 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -905,6 +905,8 @@ struct Query_cache_query_flags }; #define QUERY_CACHE_FLAGS_SIZE sizeof(Query_cache_query_flags) #include "sql_cache.h" +#define query_cache_abort(A) query_cache.abort(A) +#define query_cache_end_of_result(A) query_cache.end_of_result(A) #define query_cache_store_query(A, B) query_cache.store_query(A, B) #define query_cache_destroy() query_cache.destroy() #define query_cache_result_size_limit(A) query_cache.result_size_limit(A) diff --git a/sql/net_serv.cc b/sql/net_serv.cc index 7a830e0961a..4945ee8d94f 100644 --- a/sql/net_serv.cc +++ b/sql/net_serv.cc @@ -95,8 +95,8 @@ extern pthread_mutex_t LOCK_bytes_sent , LOCK_bytes_received; #ifndef MYSQL_INSTANCE_MANAGER #ifdef HAVE_QUERY_CACHE #define USE_QUERY_CACHE -extern void query_cache_init_query(NET *net); -extern void query_cache_insert(NET *net, const char *packet, ulong length); +extern void query_cache_insert(const char *packet, ulong length, + unsigned pkt_nr); #endif // HAVE_QUERY_CACHE #define update_statistics(A) A #endif /* MYSQL_INSTANCE_MANGER */ @@ -132,11 +132,7 @@ my_bool my_net_init(NET *net, Vio* vio) net->compress=0; net->reading_or_writing=0; net->where_b = net->remain_in_buf=0; net->last_errno=0; -#ifdef USE_QUERY_CACHE - query_cache_init_query(net); -#else - net->query_cache_query= 0; -#endif + net->unused= 0; if (vio != 0) /* If real connection */ { @@ -586,7 +582,7 @@ net_real_write(NET *net,const uchar *packet, size_t len) DBUG_ENTER("net_real_write"); #if defined(MYSQL_SERVER) && defined(USE_QUERY_CACHE) - query_cache_insert(net, (char*) packet, len); + query_cache_insert((char*) packet, len, net->pkt_nr); #endif if (net->error == 2) diff --git a/sql/sql_cache.cc b/sql/sql_cache.cc index 9f427f39265..5c03e156c19 100644 --- a/sql/sql_cache.cc +++ b/sql/sql_cache.cc @@ -789,19 +789,20 @@ uchar *query_cache_query_get_key(const uchar *record, size_t *length, Note on double-check locking (DCL) usage. Below, in query_cache_insert(), query_cache_abort() and - query_cache_end_of_result() we use what is called double-check - locking (DCL) for NET::query_cache_query. I.e. we test it first - without a lock, and, if positive, test again under the lock. + Query_cache::end_of_result() we use what is called double-check + locking (DCL) for Query_cache_tls::first_query_block. + I.e. we test it first without a lock, and, if positive, test again + under the lock. - This means that if we see 'NET::query_cache_query == 0' without a + This means that if we see 'first_query_block == 0' without a lock we will skip the operation. But this is safe here: when we started to cache a query, we called Query_cache::store_query(), and - NET::query_cache_query was set to non-zero in this thread (and the + 'first_query_block' was set to non-zero in this thread (and the thread always sees results of its memory operations, mutex or not). - If later we see 'NET::query_cache_query == 0' without locking a + If later we see 'first_query_block == 0' without locking a mutex, that may only mean that some other thread have reset it by invalidating the query. Skipping the operation in this case is the - right thing to do, as NET::query_cache_query won't get non-zero for + right thing to do, as first_query_block won't get non-zero for this query again. See also comments in Query_cache::store_query() and @@ -810,56 +811,71 @@ uchar *query_cache_query_get_key(const uchar *record, size_t *length, NOTE, however, that double-check locking is not applicable in 'invalidate' functions, as we may erroneously skip invalidation, because the thread doing invalidation may never see non-zero - NET::query_cache_query. + 'first_query_block'. */ -void query_cache_init_query(NET *net) +/** + libmysql convenience wrapper to insert data into query cache. +*/ +void query_cache_insert(const char *packet, ulong length, + unsigned pkt_nr) { + THD *thd= current_thd; + /* - It is safe to initialize 'NET::query_cache_query' without a lock - here, because before it will be accessed from different threads it - will be set in this thread under a lock, and access from the same - thread is always safe. + Current_thd can be NULL when a new connection is immediately ended + due to "Too many connections". thd->store_globals() has not been + called at this time and hence my_pthread_setspecific_ptr(THR_THD, + this) has not been called for this thread. */ - net->query_cache_query= 0; + + if (!thd) + return; + + query_cache.insert(&thd->query_cache_tls, + packet, length, + pkt_nr); } -/* +/** Insert the packet into the query cache. */ -void query_cache_insert(NET *net, const char *packet, ulong length) +void +Query_cache::insert(Query_cache_tls *query_cache_tls, + const char *packet, ulong length, + unsigned pkt_nr) { - DBUG_ENTER("query_cache_insert"); + DBUG_ENTER("Query_cache::insert"); /* See the comment on double-check locking usage above. */ - if (net->query_cache_query == 0) + if (query_cache_tls->first_query_block == NULL) DBUG_VOID_RETURN; DBUG_EXECUTE_IF("wait_in_query_cache_insert", debug_wait_for_kill("wait_in_query_cache_insert"); ); - if (query_cache.try_lock()) + + if (try_lock()) DBUG_VOID_RETURN; - Query_cache_block *query_block= (Query_cache_block*)net->query_cache_query; - if (!query_block) + Query_cache_block *query_block = query_cache_tls->first_query_block; + if (query_block == NULL) { /* We lost the writer and the currently processed query has been invalidated; there is nothing left to do. */ - query_cache.unlock(); + unlock(); DBUG_VOID_RETURN; } - BLOCK_LOCK_WR(query_block); Query_cache_query *header= query_block->query(); Query_cache_block *result= header->result(); - DUMP(&query_cache); + DUMP(this); DBUG_PRINT("qcache", ("insert packet %lu bytes long",length)); /* @@ -867,8 +883,8 @@ void query_cache_insert(NET *net, const char *packet, ulong length) still need structure_guard_mutex to free the query, and therefore unlock it later in this function. */ - if (!query_cache.append_result_data(&result, length, (uchar*) packet, - query_block)) + if (!append_result_data(&result, length, (uchar*) packet, + query_block)) { DBUG_PRINT("warning", ("Can't append data")); header->result(result); @@ -877,60 +893,63 @@ void query_cache_insert(NET *net, const char *packet, ulong length) query_cache.free_query(query_block); query_cache.refused++; // append_result_data no success => we need unlock - query_cache.unlock(); + unlock(); DBUG_VOID_RETURN; } header->result(result); - header->last_pkt_nr= net->pkt_nr; + header->last_pkt_nr= pkt_nr; BLOCK_UNLOCK_WR(query_block); - DBUG_EXECUTE("check_querycache",query_cache.check_integrity(0);); + DBUG_EXECUTE("check_querycache",check_integrity(0);); DBUG_VOID_RETURN; } -void query_cache_abort(NET *net) +void +Query_cache::abort(Query_cache_tls *query_cache_tls) { DBUG_ENTER("query_cache_abort"); THD *thd= current_thd; /* See the comment on double-check locking usage above. */ - if (net->query_cache_query == 0) + if (query_cache_tls->first_query_block == NULL) DBUG_VOID_RETURN; - if (query_cache.try_lock()) + if (try_lock()) DBUG_VOID_RETURN; /* While we were waiting another thread might have changed the status of the writer. Make sure the writer still exists before continue. */ - Query_cache_block *query_block= ((Query_cache_block*) - net->query_cache_query); + Query_cache_block *query_block= query_cache_tls->first_query_block; if (query_block) { thd_proc_info(thd, "storing result in query cache"); - DUMP(&query_cache); + DUMP(this); BLOCK_LOCK_WR(query_block); // The following call will remove the lock on query_block - query_cache.free_query(query_block); - net->query_cache_query= 0; - DBUG_EXECUTE("check_querycache",query_cache.check_integrity(1);); + free_query(query_block); + query_cache_tls->first_query_block= NULL; + DBUG_EXECUTE("check_querycache", check_integrity(1);); } - query_cache.unlock(); + unlock(); + DBUG_VOID_RETURN; } -void query_cache_end_of_result(THD *thd) +void Query_cache::end_of_result(THD *thd) { Query_cache_block *query_block; - DBUG_ENTER("query_cache_end_of_result"); + Query_cache_tls *query_cache_tls= &thd->query_cache_tls; + ulonglong limit_found_rows= thd->limit_found_rows; + DBUG_ENTER("Query_cache::end_of_result"); /* See the comment on double-check locking usage above. */ - if (thd->net.query_cache_query == 0) + if (query_cache_tls->first_query_block == NULL) DBUG_VOID_RETURN; /* Ensure that only complete results are cached. */ @@ -938,19 +957,19 @@ void query_cache_end_of_result(THD *thd) if (thd->killed) { - query_cache_abort(&thd->net); + query_cache_abort(&thd->query_cache_tls); DBUG_VOID_RETURN; } #ifdef EMBEDDED_LIBRARY - query_cache_insert(&thd->net, (char*)thd, - emb_count_querycache_size(thd)); + insert(query_cache_tls, (char*)thd, + emb_count_querycache_size(thd), 0); #endif - if (query_cache.try_lock()) + if (try_lock()) DBUG_VOID_RETURN; - query_block= ((Query_cache_block*) thd->net.query_cache_query); + query_block= query_cache_tls->first_query_block; if (query_block) { /* @@ -959,7 +978,7 @@ void query_cache_end_of_result(THD *thd) block, the writer should be dropped. */ thd_proc_info(thd, "storing result in query cache"); - DUMP(&query_cache); + DUMP(this); BLOCK_LOCK_WR(query_block); Query_cache_query *header= query_block->query(); Query_cache_block *last_result_block; @@ -976,8 +995,8 @@ void query_cache_end_of_result(THD *thd) and removed from QC. */ DBUG_ASSERT(0); - query_cache.free_query(query_block); - query_cache.unlock(); + free_query(query_block); + unlock(); DBUG_VOID_RETURN; } last_result_block= header->result()->prev; @@ -986,17 +1005,17 @@ void query_cache_end_of_result(THD *thd) if (last_result_block->length >= query_cache.min_allocation_unit + len) query_cache.split_block(last_result_block,len); - header->found_rows(current_thd->limit_found_rows); + header->found_rows(limit_found_rows); header->result()->type= Query_cache_block::RESULT; /* Drop the writer. */ header->writer(0); - thd->net.query_cache_query= 0; + query_cache_tls->first_query_block= NULL; BLOCK_UNLOCK_WR(query_block); - DBUG_EXECUTE("check_querycache",query_cache.check_integrity(1);); - + DBUG_EXECUTE("check_querycache", check_integrity(1);); } - query_cache.unlock(); + + unlock(); DBUG_VOID_RETURN; } @@ -1074,7 +1093,7 @@ ulong Query_cache::resize(ulong query_cache_size_arg) Drop the writer; this will cancel any attempts to store the processed statement associated with this writer. */ - query->writer()->query_cache_query= 0; + query->writer()->first_query_block= NULL; query->writer(0); refused++; } @@ -1268,8 +1287,8 @@ def_week_frmt: %lu, in_trans: %d, autocommit: %d", double_linked_list_simple_include(query_block, &queries_blocks); inserts++; queries_in_cache++; - net->query_cache_query= (uchar*) query_block; - header->writer(net); + thd->query_cache_tls.first_query_block= query_block; + header->writer(&thd->query_cache_tls); header->tables_type(tables_type); unlock(); @@ -1325,7 +1344,10 @@ Query_cache::send_result_to_client(THD *thd, char *sql, uint query_length) { ulonglong engine_data; Query_cache_query *query; - Query_cache_block *first_result_block, *result_block; +#ifndef EMBEDDED_LIBRARY + Query_cache_block *first_result_block; +#endif + Query_cache_block *result_block; Query_cache_block_table *block_table, *block_table_end; ulong tot_length; Query_cache_query_flags flags; @@ -1396,12 +1418,6 @@ Query_cache::send_result_to_client(THD *thd, char *sql, uint query_length) if (query_cache_size == 0) goto err_unlock; - /* - Check that we haven't forgot to reset the query cache variables; - make sure there are no attached query cache writer to this thread. - */ - DBUG_ASSERT(thd->net.query_cache_query == 0); - Query_cache_block *query_block; tot_length= query_length + thd->db_length + 1 + QUERY_CACHE_FLAGS_SIZE; @@ -1483,7 +1499,10 @@ def_week_frmt: %lu, in_trans: %d, autocommit: %d", BLOCK_LOCK_RD(query_block); query = query_block->query(); - result_block= first_result_block= query->result(); + result_block= query->result(); +#ifndef EMBEDDED_LIBRARY + first_result_block= result_block; +#endif if (result_block == 0 || result_block->type != Query_cache_block::RESULT) { @@ -2261,7 +2280,7 @@ void Query_cache::free_query_internal(Query_cache_block *query_block) if (query->writer() != 0) { /* Tell MySQL that this query should not be cached anymore */ - query->writer()->query_cache_query= 0; + query->writer()->first_query_block= NULL; query->writer(0); } double_linked_list_exclude(query_block, &queries_blocks); @@ -3474,7 +3493,8 @@ Query_cache::process_and_count_tables(THD *thd, TABLE_LIST *tables_used, */ TABLE_COUNTER_TYPE -Query_cache::is_cacheable(THD *thd, uint32 query_len, char *query, LEX *lex, +Query_cache::is_cacheable(THD *thd, size_t query_len, const char *query, + LEX *lex, TABLE_LIST *tables_used, uint8 *tables_type) { TABLE_COUNTER_TYPE table_count; @@ -3773,10 +3793,10 @@ my_bool Query_cache::move_by_type(uchar **border, If someone is writing to this block, inform the writer that the block has been moved. */ - NET *net = new_block->query()->writer(); - if (net != 0) + Query_cache_tls *query_cache_tls= new_block->query()->writer(); + if (query_cache_tls != NULL) { - net->query_cache_query= (uchar*) new_block; + query_cache_tls->first_query_block= new_block; } /* Fix hash to point at moved block */ hash_replace(&queries, &record_idx, (uchar*) new_block); diff --git a/sql/sql_cache.h b/sql/sql_cache.h index 777ddd39280..a1132adfa5a 100644 --- a/sql/sql_cache.h +++ b/sql/sql_cache.h @@ -64,6 +64,8 @@ struct Query_cache_table; struct Query_cache_query; struct Query_cache_result; class Query_cache; +struct Query_cache_tls; +struct st_lex; /** This class represents a node in the linked chain of queries @@ -137,7 +139,7 @@ struct Query_cache_query ulonglong limit_found_rows; rw_lock_t lock; Query_cache_block *res; - NET *wri; + Query_cache_tls *wri; ulong len; uint8 tbls_type; unsigned int last_pkt_nr; @@ -149,8 +151,8 @@ struct Query_cache_query inline void found_rows(ulonglong rows) { limit_found_rows= rows; } inline Query_cache_block *result() { return res; } inline void result(Query_cache_block *p) { res= p; } - inline NET *writer() { return wri; } - inline void writer(NET *p) { wri= p; } + inline Query_cache_tls *writer() { return wri; } + inline void writer(Query_cache_tls *p) { wri= p; } inline uint8 tables_type() { return tbls_type; } inline void tables_type(uint8 type) { tbls_type= type; } inline ulong length() { return len; } @@ -407,7 +409,8 @@ class Query_cache If query is cacheable return number tables in query (query without tables not cached) */ - TABLE_COUNTER_TYPE is_cacheable(THD *thd, uint32 query_len, char *query, + TABLE_COUNTER_TYPE is_cacheable(THD *thd, size_t query_len, + const char *query, LEX *lex, TABLE_LIST *tables_used, uint8 *tables_type); TABLE_COUNTER_TYPE process_and_count_tables(THD *thd, @@ -462,10 +465,13 @@ class Query_cache void destroy(); - friend void query_cache_init_query(NET *net); - friend void query_cache_insert(NET *net, const char *packet, ulong length); - friend void query_cache_end_of_result(THD *thd); - friend void query_cache_abort(NET *net); + void insert(Query_cache_tls *query_cache_tls, + const char *packet, + ulong length, + unsigned pkt_nr); + + void end_of_result(THD *thd); + void abort(Query_cache_tls *query_cache_tls); /* The following functions are only used when debugging @@ -493,9 +499,4 @@ class Query_cache extern Query_cache query_cache; extern TYPELIB query_cache_type_typelib; -void query_cache_init_query(NET *net); -void query_cache_insert(NET *net, const char *packet, ulong length); -void query_cache_end_of_result(THD *thd); -void query_cache_abort(NET *net); - #endif diff --git a/sql/sql_class.cc b/sql/sql_class.cc index 77f78d80c12..722cdd22794 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -501,9 +501,6 @@ THD::THD() net.vio=0; #endif client_capabilities= 0; // minimalistic client -#ifdef HAVE_QUERY_CACHE - query_cache_init_query(&net); // If error on boot -#endif ull=0; system_thread= NON_SYSTEM_THREAD; cleanup_done= abort_on_warning= no_warnings_for_error= 0; @@ -796,7 +793,7 @@ THD::raise_condition_no_handler(uint sql_errno, MYSQL_ERROR *cond= NULL; DBUG_ENTER("THD::raise_condition_no_handler"); - query_cache_abort(& net); + query_cache_abort(&query_cache_tls); /* FIXME: broken special case */ if (no_warnings_for_error && (level == MYSQL_ERROR::WARN_LEVEL_ERROR)) diff --git a/sql/sql_class.h b/sql/sql_class.h index 4dea3acc5e7..76602e00074 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -315,6 +315,27 @@ typedef enum enum_diag_condition_item_name */ extern const LEX_STRING Diag_condition_item_names[]; +/** + Query_cache_tls -- query cache thread local data. +*/ + +class Query_cache_block; + +struct Query_cache_tls +{ + /* + 'first_query_block' should be accessed only via query cache + functions and methods to maintain proper locking. + */ + Query_cache_block *first_query_block; + void set_first_query_block(Query_cache_block *first_query_block_arg) + { + first_query_block= first_query_block_arg; + } + + Query_cache_tls() :first_query_block(NULL) {} +}; + #include "sql_lex.h" /* Must be here */ class Delayed_insert; @@ -1239,6 +1260,9 @@ class THD :public Statement, fields then. */ struct st_mysql_stmt *current_stmt; +#endif +#ifdef HAVE_QUERY_CACHE + Query_cache_tls query_cache_tls; #endif NET net; // client connection descriptor Protocol *protocol; // Current protocol diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index ff91975f741..43660d525e3 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -6026,7 +6026,7 @@ void mysql_parse(THD *thd, const char *inBuf, uint length, DBUG_PRINT("info",("Command aborted. Fatal_error: %d", thd->is_fatal_error)); - query_cache_abort(&thd->net); + query_cache_abort(&thd->query_cache_tls); } if (thd->lex->sphead) { diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 3e2d85e4951..67221b0aafb 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -9187,7 +9187,7 @@ remove_eq_conds(THD *thd, COND *cond, Item::cond_result *cond_value) thd->substitute_null_with_insert_id)) { #ifdef HAVE_QUERY_CACHE - query_cache_abort(&thd->net); + query_cache_abort(&thd->query_cache_tls); #endif COND *new_cond; if ((new_cond= new Item_func_eq(args[0], -- 2.30.9