1
0
mirror of https://github.com/MariaDB/server.git synced 2025-07-30 16:24:05 +03:00

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:
  Update the ABI (NET::query_cache_query is now unused).
include/mysql_com.h:
  NET::query_cache_query is now unused.
sql/mysql_priv.h:
  Update signatures of ex-functios now member functions.
sql/sql_cache.cc:
  Introduce THD::query_cache_tls.
sql/sql_cache.h:
  Introduce THD::query_cache_tls.
sql/sql_class.cc:
  Introduce THD::query_cache_tls.
sql/sql_class.h:
  Introduce THD::query_cache_tls.
sql/sql_parse.cc:
  Use THD::query_cache_tls.
sql/sql_select.cc:
  Use THD::query_cache_tls.
This commit is contained in:
Konstantin Osipov
2009-10-13 22:18:48 +04:00
parent 9a65687bd9
commit 99420dd913
10 changed files with 141 additions and 102 deletions

View File

@ -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);