summaryrefslogtreecommitdiff
path: root/sql/sql_cache.cc
diff options
context:
space:
mode:
authorunknown <kostja@bodhi.(none)>2007-07-16 17:22:33 +0400
committerunknown <kostja@bodhi.(none)>2007-07-16 17:22:33 +0400
commit146b0e14753c3e19451b04065b6d0f29d5a9a069 (patch)
tree2871e2ea453c7f2ac10d1123bf0bf7bc9ab5663c /sql/sql_cache.cc
parent67e3dbc3d84a2b9b5258a8c4ff1710bccfaa10e5 (diff)
parent02a832df2e9743fbe132fe84f906761fc00c1279 (diff)
downloadmariadb-git-146b0e14753c3e19451b04065b6d0f29d5a9a069.tar.gz
Merge bk-internal.mysql.com:/home/bk/mysql-5.1
into bodhi.(none):/opt/local/work/mysql-5.1-runtime client/mysqldump.c: Auto merged mysql-test/r/show_check.result: Auto merged mysql-test/t/show_check.test: Auto merged sql/ha_ndbcluster.cc: Auto merged sql/mysql_priv.h: Auto merged sql/sql_cache.cc: Auto merged sql/sql_class.h: Auto merged sql/share/errmsg.txt: Auto merged sql/sql_parse.cc: Auto merged sql/sql_show.cc: Auto merged mysql-test/r/events_bugs.result: Manual merge.
Diffstat (limited to 'sql/sql_cache.cc')
-rw-r--r--sql/sql_cache.cc712
1 files changed, 444 insertions, 268 deletions
diff --git a/sql/sql_cache.cc b/sql/sql_cache.cc
index 173ef4c02df..f7ad024c143 100644
--- a/sql/sql_cache.cc
+++ b/sql/sql_cache.cc
@@ -268,6 +268,39 @@ are stored in one block.
If join_results allocated new block(s) then we need call pack_cache again.
+7. Interface
+The query cache interfaces with the rest of the server code through 7
+functions:
+ 1. Query_cache::send_result_to_client
+ - Called before parsing and used to match a statement with the stored
+ queries hash.
+ If a match is found the cached result set is sent through repeated
+ calls to net_real_write. (note: calling thread doesn't have a regis-
+ tered result set writer: thd->net.query_cache_query=0)
+ 2. Query_cache::store_query
+ - Called just before handle_select() and is used to register a result
+ set writer to the statement currently being processed
+ (thd->net.query_cache_query).
+ 3. query_cache_insert
+ - Called from net_real_write to append a result set to a cached query
+ if (and only if) this query has a registered result set writer
+ (thd->net.query_cache_query).
+ 4. Query_cache::invalidate
+ - 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.
+ 5. Query_cache::flush
+ - Used when a RESET QUERY CACHE is issued. This clears the entire
+ cache block by block.
+ 6. Query_cache::resize
+ - Used to change the available memory used by the query cache. This
+ will also invalidate the entrie query cache in one free operation.
+ 7. Query_cache::pack
+ - Used when a FLUSH QUERY CACHE is issued. This changes the order of
+ the used memory blocks in physical memory order and move all avail-
+ able memory to the 'bottom' of the memory.
+
+
TODO list:
- Delayed till after-parsing qache answer (for column rights processing)
@@ -615,49 +648,55 @@ void query_cache_insert(NET *net, const char *packet, ulong length)
DBUG_VOID_RETURN;
STRUCT_LOCK(&query_cache.structure_guard_mutex);
+ bool interrupt;
+ query_cache.wait_while_table_flush_is_in_progress(&interrupt);
+ if (interrupt)
+ {
+ STRUCT_UNLOCK(&query_cache.structure_guard_mutex);
+ return;
+ }
- if (unlikely(query_cache.query_cache_size == 0 ||
- query_cache.flush_in_progress))
+ Query_cache_block *query_block= (Query_cache_block*)net->query_cache_query;
+ if (!query_block)
{
+ /*
+ We lost the writer and the currently processed query has been
+ invalidated; there is nothing left to do.
+ */
STRUCT_UNLOCK(&query_cache.structure_guard_mutex);
DBUG_VOID_RETURN;
}
- Query_cache_block *query_block = ((Query_cache_block*)
- net->query_cache_query);
- if (query_block)
- {
- Query_cache_query *header = query_block->query();
- Query_cache_block *result = header->result();
+ Query_cache_query *header= query_block->query();
+ Query_cache_block *result= header->result();
- DUMP(&query_cache);
- BLOCK_LOCK_WR(query_block);
- DBUG_PRINT("qcache", ("insert packet %lu bytes long",length));
+ DUMP(&query_cache);
+ BLOCK_LOCK_WR(query_block);
+ DBUG_PRINT("qcache", ("insert packet %lu bytes long",length));
- /*
- On success STRUCT_UNLOCK(&query_cache.structure_guard_mutex) will be
- done by query_cache.append_result_data if success (if not we need
- query_cache.structure_guard_mutex locked to free query)
- */
- if (!query_cache.append_result_data(&result, length, (uchar*) packet,
- query_block))
- {
- DBUG_PRINT("warning", ("Can't append data"));
- header->result(result);
- DBUG_PRINT("qcache", ("free query 0x%lx", (ulong) query_block));
- // The following call will remove the lock on query_block
- query_cache.free_query(query_block);
- // append_result_data no success => we need unlock
- STRUCT_UNLOCK(&query_cache.structure_guard_mutex);
- DBUG_VOID_RETURN;
- }
+ /*
+ On success, STRUCT_UNLOCK is done by append_result_data. Otherwise, we
+ 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))
+ {
+ DBUG_PRINT("warning", ("Can't append data"));
header->result(result);
- header->last_pkt_nr= net->pkt_nr;
- BLOCK_UNLOCK_WR(query_block);
- DBUG_EXECUTE("check_querycache",query_cache.check_integrity(0););
- }
- else
+ DBUG_PRINT("qcache", ("free query 0x%lx", (ulong) query_block));
+ // The following call will remove the lock on query_block
+ query_cache.free_query(query_block);
+ // append_result_data no success => we need unlock
STRUCT_UNLOCK(&query_cache.structure_guard_mutex);
+ DBUG_VOID_RETURN;
+ }
+
+ header->result(result);
+ header->last_pkt_nr= net->pkt_nr;
+ BLOCK_UNLOCK_WR(query_block);
+ DBUG_EXECUTE("check_querycache",query_cache.check_integrity(0););
+
DBUG_VOID_RETURN;
}
@@ -671,17 +710,21 @@ void query_cache_abort(NET *net)
DBUG_VOID_RETURN;
STRUCT_LOCK(&query_cache.structure_guard_mutex);
-
- if (unlikely(query_cache.query_cache_size == 0 ||
- query_cache.flush_in_progress))
+ bool interrupt;
+ query_cache.wait_while_table_flush_is_in_progress(&interrupt);
+ if (interrupt)
{
STRUCT_UNLOCK(&query_cache.structure_guard_mutex);
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);
- if (query_block) // Test if changed by other thread
+ if (query_block)
{
DUMP(&query_cache);
BLOCK_LOCK_WR(query_block);
@@ -713,13 +756,22 @@ void query_cache_end_of_result(THD *thd)
STRUCT_LOCK(&query_cache.structure_guard_mutex);
- if (unlikely(query_cache.query_cache_size == 0 ||
- query_cache.flush_in_progress))
- goto end;
+ bool interrupt;
+ query_cache.wait_while_table_flush_is_in_progress(&interrupt);
+ if (interrupt)
+ {
+ STRUCT_UNLOCK(&query_cache.structure_guard_mutex);
+ DBUG_VOID_RETURN;
+ }
query_block= ((Query_cache_block*) thd->net.query_cache_query);
if (query_block)
{
+ /*
+ The writer is still present; finish last result block by chopping it to
+ suitable size if needed and setting block type. Since this is the last
+ block, the writer should be dropped.
+ */
DUMP(&query_cache);
BLOCK_LOCK_WR(query_block);
Query_cache_query *header= query_block->query();
@@ -741,19 +793,22 @@ void query_cache_end_of_result(THD *thd)
query_cache.wreck() switched query cache off but left content
untouched for investigation (it is debugging method).
*/
- goto end;
+ STRUCT_UNLOCK(&query_cache.structure_guard_mutex);
+ DBUG_VOID_RETURN;
}
#endif
header->found_rows(current_thd->limit_found_rows);
header->result()->type= Query_cache_block::RESULT;
+
+ /* Drop the writer. */
header->writer(0);
thd->net.query_cache_query= 0;
+
BLOCK_UNLOCK_WR(query_block);
DBUG_EXECUTE("check_querycache",query_cache.check_integrity(1););
}
-end:
STRUCT_UNLOCK(&query_cache.structure_guard_mutex);
DBUG_VOID_RETURN;
}
@@ -813,9 +868,9 @@ ulong Query_cache::resize(ulong query_cache_size_arg)
DBUG_ASSERT(initialized);
STRUCT_LOCK(&structure_guard_mutex);
- while (flush_in_progress)
- pthread_cond_wait(&COND_flush_finished, &structure_guard_mutex);
- flush_in_progress= TRUE;
+ while (is_flushing())
+ pthread_cond_wait(&COND_cache_status_changed, &structure_guard_mutex);
+ m_cache_status= Query_cache::FLUSH_IN_PROGRESS;
STRUCT_UNLOCK(&structure_guard_mutex);
free_cache();
@@ -826,8 +881,8 @@ ulong Query_cache::resize(ulong query_cache_size_arg)
DBUG_EXECUTE("check_querycache",check_integrity(0););
STRUCT_LOCK(&structure_guard_mutex);
- flush_in_progress= FALSE;
- pthread_cond_signal(&COND_flush_finished);
+ m_cache_status= Query_cache::NO_FLUSH_IN_PROGRESS;
+ pthread_cond_signal(&COND_cache_status_changed);
STRUCT_UNLOCK(&structure_guard_mutex);
DBUG_RETURN(new_query_cache_size);
@@ -922,8 +977,13 @@ def_week_frmt: %lu",
ha_release_temporary_latches(thd);
STRUCT_LOCK(&structure_guard_mutex);
- if (query_cache_size == 0 || flush_in_progress)
+ if (query_cache_size == 0 || is_flushing())
{
+ /*
+ A table- or a full flush operation can potentially take a long time to
+ finish. We choose not to wait for them and skip caching statements
+ instead.
+ */
STRUCT_UNLOCK(&structure_guard_mutex);
DBUG_VOID_RETURN;
}
@@ -966,7 +1026,7 @@ def_week_frmt: %lu",
Query_cache_block *query_block;
query_block= write_block_data(tot_length, (uchar*) thd->query,
ALIGN_SIZE(sizeof(Query_cache_query)),
- Query_cache_block::QUERY, local_tables, 1);
+ Query_cache_block::QUERY, local_tables);
if (query_block != 0)
{
DBUG_PRINT("qcache", ("query block 0x%lx allocated, %lu",
@@ -1100,13 +1160,21 @@ Query_cache::send_result_to_client(THD *thd, char *sql, uint query_length)
}
STRUCT_LOCK(&structure_guard_mutex);
- if (query_cache_size == 0 || flush_in_progress)
+
+ if (query_cache_size == 0)
+ goto err_unlock;
+
+ if (is_flushing())
{
- DBUG_PRINT("qcache", ("query cache disabled"));
+ /* Return; Query cache is temporarily disabled while we flush. */
+ DBUG_PRINT("qcache",("query cache disabled"));
goto err_unlock;
}
- /* Check that we haven't forgot to reset the query cache variables */
+ /*
+ 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;
@@ -1279,7 +1347,7 @@ def_week_frmt: %lu",
("Handler require invalidation queries of %s.%s %lu-%lu",
table_list.db, table_list.alias,
(ulong) engine_data, (ulong) table->engine_data()));
- invalidate_table((uchar *) table->db(), table->key_length());
+ invalidate_table(thd, (uchar *) table->db(), table->key_length());
}
else
thd->lex->safe_to_cache_query= 0; // Don't try to cache this
@@ -1342,32 +1410,26 @@ void Query_cache::invalidate(THD *thd, TABLE_LIST *tables_used,
my_bool using_transactions)
{
DBUG_ENTER("Query_cache::invalidate (table list)");
- STRUCT_LOCK(&structure_guard_mutex);
- if (query_cache_size > 0 && !flush_in_progress)
- {
- DUMP(this);
- using_transactions= using_transactions &&
- (thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN));
- for (; tables_used; tables_used= tables_used->next_local)
- {
- DBUG_ASSERT(!using_transactions || tables_used->table!=0);
- if (tables_used->derived)
- continue;
- if (using_transactions &&
- (tables_used->table->file->table_cache_type() ==
- HA_CACHE_TBL_TRANSACT))
- /*
- Tables_used->table can't be 0 in transaction.
- Only 'drop' invalidate not opened table, but 'drop'
- force transaction finish.
- */
- thd->add_changed_table(tables_used->table);
- else
- invalidate_table(tables_used);
- }
+ using_transactions= using_transactions &&
+ (thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN));
+ for (; tables_used; tables_used= tables_used->next_local)
+ {
+ DBUG_ASSERT(!using_transactions || tables_used->table!=0);
+ if (tables_used->derived)
+ continue;
+ if (using_transactions &&
+ (tables_used->table->file->table_cache_type() ==
+ HA_CACHE_TBL_TRANSACT))
+ /*
+ tables_used->table can't be 0 in transaction.
+ Only 'drop' invalidate not opened table, but 'drop'
+ force transaction finish.
+ */
+ thd->add_changed_table(tables_used->table);
+ else
+ invalidate_table(thd, tables_used);
}
- STRUCT_UNLOCK(&structure_guard_mutex);
DBUG_VOID_RETURN;
}
@@ -1375,21 +1437,13 @@ 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 (tables_used)
+ THD *thd= current_thd;
+ for (; tables_used; tables_used= tables_used->next)
{
- STRUCT_LOCK(&structure_guard_mutex);
- if (query_cache_size > 0 && !flush_in_progress)
- {
- DUMP(this);
- for (; tables_used; tables_used= tables_used->next)
- {
- invalidate_table((uchar*) tables_used->key, tables_used->key_length);
- DBUG_PRINT("qcache", ("db: %s table: %s", tables_used->key,
- tables_used->key+
- strlen(tables_used->key)+1));
- }
- }
- STRUCT_UNLOCK(&structure_guard_mutex);
+ invalidate_table(thd, (uchar*) tables_used->key, tables_used->key_length);
+ DBUG_PRINT("qcache", ("db: %s table: %s", tables_used->key,
+ tables_used->key+
+ strlen(tables_used->key)+1));
}
DBUG_VOID_RETURN;
}
@@ -1408,20 +1462,14 @@ void Query_cache::invalidate(CHANGED_TABLE_LIST *tables_used)
void Query_cache::invalidate_locked_for_write(TABLE_LIST *tables_used)
{
DBUG_ENTER("Query_cache::invalidate_locked_for_write");
- if (tables_used)
+ for (; tables_used; tables_used= tables_used->next_local)
{
- STRUCT_LOCK(&structure_guard_mutex);
- if (query_cache_size > 0 && !flush_in_progress)
+ if (tables_used->lock_type & (TL_WRITE_LOW_PRIORITY | TL_WRITE) &&
+ tables_used->table)
{
- DUMP(this);
- for (; tables_used; tables_used= tables_used->next_local)
- {
- if (tables_used->lock_type & (TL_WRITE_LOW_PRIORITY | TL_WRITE) &&
- tables_used->table)
- invalidate_table(tables_used->table);
- }
+ THD *thd= current_thd;
+ invalidate_table(thd, tables_used->table);
}
- STRUCT_UNLOCK(&structure_guard_mutex);
}
DBUG_VOID_RETURN;
}
@@ -1435,18 +1483,14 @@ void Query_cache::invalidate(THD *thd, TABLE *table,
{
DBUG_ENTER("Query_cache::invalidate (table)");
- STRUCT_LOCK(&structure_guard_mutex);
- if (query_cache_size > 0 && !flush_in_progress)
- {
- using_transactions= using_transactions &&
- (thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN));
- if (using_transactions &&
- (table->file->table_cache_type() == HA_CACHE_TBL_TRANSACT))
- thd->add_changed_table(table);
- else
- invalidate_table(table);
- }
- STRUCT_UNLOCK(&structure_guard_mutex);
+ using_transactions= using_transactions &&
+ (thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN));
+ if (using_transactions &&
+ (table->file->table_cache_type() == HA_CACHE_TBL_TRANSACT))
+ thd->add_changed_table(table);
+ else
+ invalidate_table(thd, table);
+
DBUG_VOID_RETURN;
}
@@ -1455,31 +1499,80 @@ void Query_cache::invalidate(THD *thd, const char *key, uint32 key_length,
my_bool using_transactions)
{
DBUG_ENTER("Query_cache::invalidate (key)");
-
- STRUCT_LOCK(&structure_guard_mutex);
- if (query_cache_size > 0 && !flush_in_progress)
- {
- using_transactions= using_transactions &&
- (thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN));
- if (using_transactions) // used for innodb => has_transactions() is TRUE
- thd->add_changed_table(key, key_length);
- else
- invalidate_table((uchar*)key, key_length);
- }
- STRUCT_UNLOCK(&structure_guard_mutex);
+
+ using_transactions= using_transactions &&
+ (thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN));
+ if (using_transactions) // used for innodb => has_transactions() is TRUE
+ thd->add_changed_table(key, key_length);
+ else
+ invalidate_table(thd, (uchar*)key, key_length);
DBUG_VOID_RETURN;
}
+
+/**
+ @brief Synchronize the thread with any flushing operations.
+
+ This helper function is called whenever a thread needs to operate on the
+ query cache structure (example: during invalidation). If a table flush is in
+ progress this function will wait for it to stop. If a full flush is in
+ progress, the function will set the interrupt parameter to indicate that the
+ current operation is redundant and should be interrupted.
+
+ @param[out] interrupt This out-parameter will be set to TRUE if the calling
+ function is redundant and should be interrupted.
+
+ @return If the interrupt-parameter is TRUE then m_cache_status is set to
+ NO_FLUSH_IN_PROGRESS. If the interrupt-parameter is FALSE then
+ m_cache_status is set to FLUSH_IN_PROGRESS.
+ The structure_guard_mutex will in any case be locked.
+*/
+
+void Query_cache::wait_while_table_flush_is_in_progress(bool *interrupt)
+{
+ while (is_flushing())
+ {
+ /*
+ If there already is a full flush in progress query cache isn't enabled
+ and additional flushes are redundant; just return instead.
+ */
+ if (m_cache_status == Query_cache::FLUSH_IN_PROGRESS)
+ {
+ *interrupt= TRUE;
+ return;
+ }
+ /*
+ If a table flush is in progress; wait on cache status to change.
+ */
+ if (m_cache_status == Query_cache::TABLE_FLUSH_IN_PROGRESS)
+ pthread_cond_wait(&COND_cache_status_changed, &structure_guard_mutex);
+ }
+ *interrupt= FALSE;
+}
+
+
/**
@brief Remove all cached queries that uses the given database
*/
+
void Query_cache::invalidate(char *db)
{
bool restart= FALSE;
DBUG_ENTER("Query_cache::invalidate (db)");
+
STRUCT_LOCK(&structure_guard_mutex);
- if (query_cache_size > 0 && !flush_in_progress)
+ bool interrupt;
+ wait_while_table_flush_is_in_progress(&interrupt);
+ if (interrupt)
+ {
+ STRUCT_UNLOCK(&structure_guard_mutex);
+ return;
+ }
+
+ THD *thd= current_thd;
+
+ if (query_cache_size > 0)
{
if (tables_blocks)
{
@@ -1491,7 +1584,10 @@ void Query_cache::invalidate(char *db)
Query_cache_block *next= table_block->next;
Query_cache_table *table = table_block->table();
if (strcmp(table->db(),db) == 0)
- invalidate_table(table_block);
+ {
+ Query_cache_block_table *list_root= table_block->table(0);
+ invalidate_query_block_list(thd,list_root);
+ }
table_block= next;
@@ -1538,21 +1634,12 @@ void Query_cache::invalidate_by_MyISAM_filename(const char *filename)
{
DBUG_ENTER("Query_cache::invalidate_by_MyISAM_filename");
- STRUCT_LOCK(&structure_guard_mutex);
- if (query_cache_size > 0 && !flush_in_progress)
- {
- /* Calculate the key outside the lock to make the lock shorter */
- char key[MAX_DBKEY_LENGTH];
- uint32 db_length;
- uint key_length= filename_2_table_key(key, filename, &db_length);
- Query_cache_block *table_block;
- if ((table_block = (Query_cache_block*) hash_search(&tables,
- (uchar*) key,
- key_length)))
- invalidate_table(table_block);
- }
- STRUCT_UNLOCK(&structure_guard_mutex);
-
+ /* Calculate the key outside the lock to make the lock shorter */
+ char key[MAX_DBKEY_LENGTH];
+ uint32 db_length;
+ uint key_length= filename_2_table_key(key, filename, &db_length);
+ THD *thd= current_thd;
+ invalidate_table(thd,(uchar *)key, key_length);
DBUG_VOID_RETURN;
}
@@ -1574,16 +1661,43 @@ void Query_cache::flush()
DBUG_VOID_RETURN;
}
- /* Join result in cache in 1 block (if result length > join_limit) */
+
+/**
+ @brief Rearrange the memory blocks and join result in cache in 1 block (if
+ result length > join_limit)
+
+ @param[in] join_limit If the minimum length of a result block to be joined.
+ @param[in] iteration_limit The maximum number of packing and joining
+ sequences.
+
+*/
void Query_cache::pack(ulong join_limit, uint iteration_limit)
{
DBUG_ENTER("Query_cache::pack");
+
+ bool interrupt;
+ STRUCT_LOCK(&structure_guard_mutex);
+ wait_while_table_flush_is_in_progress(&interrupt);
+ if (interrupt)
+ {
+ STRUCT_UNLOCK(&structure_guard_mutex);
+ DBUG_VOID_RETURN;
+ }
+
+ if (query_cache_size == 0)
+ {
+ STRUCT_UNLOCK(&structure_guard_mutex);
+ DBUG_VOID_RETURN;
+ }
+
uint i = 0;
do
{
pack_cache();
} while ((++i < iteration_limit) && join_results(join_limit));
+
+ STRUCT_UNLOCK(&structure_guard_mutex);
DBUG_VOID_RETURN;
}
@@ -1602,7 +1716,7 @@ void Query_cache::destroy()
free_cache();
STRUCT_UNLOCK(&structure_guard_mutex);
- pthread_cond_destroy(&COND_flush_finished);
+ pthread_cond_destroy(&COND_cache_status_changed);
pthread_mutex_destroy(&structure_guard_mutex);
initialized = 0;
}
@@ -1618,8 +1732,8 @@ void Query_cache::init()
{
DBUG_ENTER("Query_cache::init");
pthread_mutex_init(&structure_guard_mutex,MY_MUTEX_INIT_FAST);
- pthread_cond_init(&COND_flush_finished, NULL);
- flush_in_progress= FALSE;
+ pthread_cond_init(&COND_cache_status_changed, NULL);
+ m_cache_status= Query_cache::NO_FLUSH_IN_PROGRESS;
initialized = 1;
DBUG_VOID_RETURN;
}
@@ -1821,9 +1935,10 @@ void Query_cache::make_disabled()
/**
@class Query_cache
@brief Free all resources allocated by the cache.
- @details This function frees all resources allocated by the cache. You
- have to call init_cache() before using the cache again. This function requires
- the structure_guard_mutex to be locked.
+
+ This function frees all resources allocated by the cache. You
+ have to call init_cache() before using the cache again. This function
+ requires the structure_guard_mutex to be locked.
*/
void Query_cache::free_cache()
@@ -1842,24 +1957,17 @@ void Query_cache::free_cache()
*****************************************************************************/
-/*
- flush_cache() - flush the cache.
-
- SYNOPSIS
- flush_cache()
-
- DESCRIPTION
- This function will flush cache contents. It assumes we have
- 'structure_guard_mutex' locked. The function sets the
- flush_in_progress flag and releases the lock, so other threads may
- proceed skipping the cache as if it is disabled. Concurrent
- flushes are performed in turn.
-
- After flush_cache() call, the cache is flushed, all the freed
- memory is accumulated in bin[0], and the 'structure_guard_mutex'
- is locked. However, since we could release the mutex during
- execution, the rest of the cache state could have been changed,
- and should not be relied on.
+/**
+ @brief Flush the cache.
+
+ This function will flush cache contents. It assumes we have
+ 'structure_guard_mutex' locked. The function sets the m_cache_status flag and
+ releases the lock, so other threads may proceed skipping the cache as if it
+ is disabled. Concurrent flushes are performed in turn.
+ After flush_cache() call, the cache is flushed, all the freed memory is
+ accumulated in bin[0], and the 'structure_guard_mutex' is locked. However,
+ since we could release the mutex during execution, the rest of the cache
+ state could have been changed, and should not be relied on.
*/
void Query_cache::flush_cache()
@@ -1871,15 +1979,15 @@ void Query_cache::flush_cache()
Query_cache::free_cache()) depends on the fact that after the
flush the cache is empty.
*/
- while (flush_in_progress)
- pthread_cond_wait(&COND_flush_finished, &structure_guard_mutex);
+ while (is_flushing())
+ pthread_cond_wait(&COND_cache_status_changed, &structure_guard_mutex);
/*
- Setting 'flush_in_progress' will prevent other threads from using
+ Setting 'FLUSH_IN_PROGRESS' will prevent other threads from using
the cache while we are in the middle of the flush, and we release
the lock so that other threads won't block.
*/
- flush_in_progress= TRUE;
+ m_cache_status= Query_cache::FLUSH_IN_PROGRESS;
STRUCT_UNLOCK(&structure_guard_mutex);
my_hash_reset(&queries);
@@ -1890,8 +1998,8 @@ void Query_cache::flush_cache()
}
STRUCT_LOCK(&structure_guard_mutex);
- flush_in_progress= FALSE;
- pthread_cond_signal(&COND_flush_finished);
+ m_cache_status= Query_cache::NO_FLUSH_IN_PROGRESS;
+ pthread_cond_signal(&COND_cache_status_changed);
}
/*
@@ -1909,7 +2017,7 @@ my_bool Query_cache::free_old_query()
sequence is breached.
Also we don't need remove locked queries at this point.
*/
- Query_cache_block *query_block = 0;
+ Query_cache_block *query_block= 0;
if (queries_blocks != 0)
{
Query_cache_block *block = queries_blocks;
@@ -2047,8 +2155,7 @@ Query_cache_block *
Query_cache::write_block_data(ulong data_len, uchar* data,
ulong header_len,
Query_cache_block::block_type type,
- TABLE_COUNTER_TYPE ntab,
- my_bool under_guard)
+ TABLE_COUNTER_TYPE ntab)
{
ulong all_headers_len = (ALIGN_SIZE(sizeof(Query_cache_block)) +
ALIGN_SIZE(ntab*sizeof(Query_cache_block_table)) +
@@ -2058,9 +2165,8 @@ Query_cache::write_block_data(ulong data_len, uchar* data,
DBUG_ENTER("Query_cache::write_block_data");
DBUG_PRINT("qcache", ("data: %ld, header: %ld, all header: %ld",
data_len, header_len, all_headers_len));
- Query_cache_block *block = allocate_block(max(align_len,
- min_allocation_unit),
- 1, 0, under_guard);
+ Query_cache_block *block= allocate_block(max(align_len,
+ min_allocation_unit),1, 0);
if (block != 0)
{
block->type = type;
@@ -2277,8 +2383,7 @@ my_bool Query_cache::allocate_data_chain(Query_cache_block **result_block,
if (!(new_block= allocate_block(max(min_size, align_len),
min_result_data_size == 0,
- all_headers_len + min_result_data_size,
- 1)))
+ all_headers_len + min_result_data_size)))
{
DBUG_PRINT("warning", ("Can't allocate block for results"));
DBUG_RETURN(FALSE);
@@ -2320,51 +2425,94 @@ my_bool Query_cache::allocate_data_chain(Query_cache_block **result_block,
Invalidate the first table in the table_list
*/
-void Query_cache::invalidate_table(TABLE_LIST *table_list)
+void Query_cache::invalidate_table(THD *thd, TABLE_LIST *table_list)
{
if (table_list->table != 0)
- invalidate_table(table_list->table); // Table is open
+ invalidate_table(thd, table_list->table); // Table is open
else
{
char key[MAX_DBKEY_LENGTH];
uint key_length;
- Query_cache_block *table_block;
+
key_length=(uint) (strmov(strmov(key,table_list->db)+1,
table_list->table_name) -key)+ 1;
// We don't store temporary tables => no key_length+=4 ...
- if ((table_block = (Query_cache_block*)
- hash_search(&tables,(uchar*) key,key_length)))
- invalidate_table(table_block);
+ invalidate_table(thd, (uchar *)key, key_length);
}
}
-void Query_cache::invalidate_table(TABLE *table)
+void Query_cache::invalidate_table(THD *thd, TABLE *table)
{
- invalidate_table((uchar*) table->s->table_cache_key.str,
+ invalidate_table(thd, (uchar*) table->s->table_cache_key.str,
table->s->table_cache_key.length);
}
-void Query_cache::invalidate_table(uchar * key, uint32 key_length)
+void Query_cache::invalidate_table(THD *thd, uchar * key, uint32 key_length)
{
- Query_cache_block *table_block;
- if ((table_block = ((Query_cache_block*)
- hash_search(&tables, key, key_length))))
- invalidate_table(table_block);
+ bool interrupt;
+ STRUCT_LOCK(&structure_guard_mutex);
+ wait_while_table_flush_is_in_progress(&interrupt);
+ if (interrupt)
+ {
+ STRUCT_UNLOCK(&structure_guard_mutex);
+ return;
+ }
+
+ /*
+ Setting 'TABLE_FLUSH_IN_PROGRESS' will temporarily disable the cache
+ so that structural changes to cache won't block the entire server.
+ However, threads requesting to change the query cache will still have
+ to wait for the flush to finish.
+ */
+ m_cache_status= Query_cache::TABLE_FLUSH_IN_PROGRESS;
+ STRUCT_UNLOCK(&structure_guard_mutex);
+
+ Query_cache_block *table_block=
+ (Query_cache_block*)hash_search(&tables, key, key_length);
+ if (query_cache_size > 0 && table_block)
+ {
+ Query_cache_block_table *list_root= table_block->table(0);
+ invalidate_query_block_list(thd, list_root);
+ }
+
+ STRUCT_LOCK(&structure_guard_mutex);
+ m_cache_status= Query_cache::NO_FLUSH_IN_PROGRESS;
+
+ /*
+ net_real_write might be waiting on a change on the m_cache_status
+ variable.
+ */
+ pthread_cond_signal(&COND_cache_status_changed);
+ STRUCT_UNLOCK(&structure_guard_mutex);
}
-void Query_cache::invalidate_table(Query_cache_block *table_block)
+
+/**
+ @brief Invalidate a linked list of query cache blocks.
+
+ Each block tries to aquire a block level lock before
+ free_query is a called. This function will in turn affect
+ related table- and result-blocks.
+
+ @param[in,out] thd Thread context.
+ @param[in,out] list_root A pointer to a circular list of query blocks.
+
+*/
+
+void
+Query_cache::invalidate_query_block_list(THD *thd,
+ Query_cache_block_table *list_root)
{
- Query_cache_block_table *list_root = table_block->table(0);
while (list_root->next != list_root)
{
- Query_cache_block *query_block = list_root->next->block();
+ Query_cache_block *query_block= list_root->next->block();
BLOCK_LOCK_WR(query_block);
free_query(query_block);
+ DBUG_EXECUTE_IF("debug_cache_locks", sleep(10););
}
}
-
/*
Register given table list begining with given position in tables table of
block
@@ -2507,9 +2655,13 @@ my_bool Query_cache::register_all_tables(Query_cache_block *block,
return (n);
}
-/*
- Insert used tablename in cache
- Returns 0 on error
+
+/**
+ @brief Insert used table name into the cache.
+
+ @return Error status
+ @retval FALSE On error
+ @retval TRUE On success
*/
my_bool
@@ -2523,9 +2675,10 @@ Query_cache::insert_table(uint key_len, char *key,
DBUG_PRINT("qcache", ("insert table node 0x%lx, len %d",
(ulong)node, key_len));
- Query_cache_block *table_block = ((Query_cache_block *)
- hash_search(&tables, (uchar*) key,
- key_len));
+ THD *thd= current_thd;
+
+ Query_cache_block *table_block=
+ (Query_cache_block *)hash_search(&tables, (uchar*) key, key_len);
if (table_block &&
table_block->table()->engine_data() != engine_data)
@@ -2540,7 +2693,11 @@ Query_cache::insert_table(uint key_len, char *key,
as far as we delete all queries with this table, table block will be
deleted, too
*/
- invalidate_table(table_block);
+ {
+ Query_cache_block_table *list_root= table_block->table(0);
+ invalidate_query_block_list(thd, list_root);
+ }
+
table_block= 0;
}
@@ -2548,21 +2705,29 @@ Query_cache::insert_table(uint key_len, char *key,
{
DBUG_PRINT("qcache", ("new table block from 0x%lx (%u)",
(ulong) key, (int) key_len));
- table_block = write_block_data(key_len, (uchar*) key,
- ALIGN_SIZE(sizeof(Query_cache_table)),
- Query_cache_block::TABLE,
- 1, 1);
+ table_block= write_block_data(key_len, (uchar*) key,
+ ALIGN_SIZE(sizeof(Query_cache_table)),
+ Query_cache_block::TABLE, 1);
if (table_block == 0)
{
DBUG_PRINT("qcache", ("Can't write table name to cache"));
DBUG_RETURN(0);
}
- Query_cache_table *header = table_block->table();
+ Query_cache_table *header= table_block->table();
double_linked_list_simple_include(table_block,
- &tables_blocks);
- Query_cache_block_table *list_root = table_block->table(0);
- list_root->n = 0;
- list_root->next = list_root->prev = list_root;
+ &tables_blocks);
+ /*
+ First node in the Query_cache_block_table-chain is the table-type
+ block. This block will only have one Query_cache_block_table (n=0).
+ */
+ Query_cache_block_table *list_root= table_block->table(0);
+ list_root->n= 0;
+
+ /*
+ The node list is circular in nature.
+ */
+ list_root->next= list_root->prev= list_root;
+
if (my_hash_insert(&tables, (const uchar *) table_block))
{
DBUG_PRINT("qcache", ("Can't insert table to hash"));
@@ -2570,20 +2735,37 @@ Query_cache::insert_table(uint key_len, char *key,
free_memory_block(table_block);
DBUG_RETURN(0);
}
- char *db = header->db();
+ char *db= header->db();
header->table(db + db_length + 1);
header->key_length(key_len);
header->type(cache_type);
header->callback(callback);
header->engine_data(engine_data);
+
+ /*
+ We insert this table without the assumption that it isn't refrenenced by
+ any queries.
+ */
+ header->m_cached_query_count= 0;
}
- Query_cache_block_table *list_root = table_block->table(0);
- node->next = list_root->next;
- list_root->next = node;
- node->next->prev = node;
- node->prev = list_root;
- node->parent = table_block->table();
+ /*
+ Table is now in the cache; link the table_block-node associated
+ with the currently processed query into the chain of queries depending
+ on the cached table.
+ */
+ Query_cache_block_table *list_root= table_block->table(0);
+ node->next= list_root->next;
+ list_root->next= node;
+ node->next->prev= node;
+ node->prev= list_root;
+ node->parent= table_block->table();
+ /*
+ Increase the counter to keep track on how long this chain
+ of queries is.
+ */
+ Query_cache_table *table_block_data= table_block->table();
+ table_block_data->m_cached_query_count++;
DBUG_RETURN(1);
}
@@ -2591,15 +2773,27 @@ Query_cache::insert_table(uint key_len, char *key,
void Query_cache::unlink_table(Query_cache_block_table *node)
{
DBUG_ENTER("Query_cache::unlink_table");
- node->prev->next = node->next;
- node->next->prev = node->prev;
- Query_cache_block_table *neighbour = node->next;
+ node->prev->next= node->next;
+ node->next->prev= node->prev;
+ Query_cache_block_table *neighbour= node->next;
+ Query_cache_table *table_block_data= node->parent;
+ table_block_data->m_cached_query_count--;
+
+ DBUG_ASSERT(table_block_data->m_cached_query_count >= 0);
+
if (neighbour->next == neighbour)
{
- // list is empty (neighbor is root of list)
- Query_cache_block *table_block = neighbour->block();
+ DBUG_ASSERT(table_block_data->m_cached_query_count == 0);
+ /*
+ If neighbor is root of list, the list is empty.
+ The root of the list is always a table-type block
+ which contain exactly one Query_cache_block_table
+ node object, thus we can use the block() method
+ to calculate the Query_cache_block address.
+ */
+ Query_cache_block *table_block= neighbour->block();
double_linked_list_exclude(table_block,
- &tables_blocks);
+ &tables_blocks);
hash_delete(&tables,(uchar *) table_block);
free_memory_block(table_block);
}
@@ -2611,12 +2805,11 @@ void Query_cache::unlink_table(Query_cache_block_table *node)
*****************************************************************************/
Query_cache_block *
-Query_cache::allocate_block(ulong len, my_bool not_less, ulong min,
- my_bool under_guard)
+Query_cache::allocate_block(ulong len, my_bool not_less, ulong min)
{
DBUG_ENTER("Query_cache::allocate_block");
- DBUG_PRINT("qcache", ("len %lu, not less %d, min %lu, uder_guard %d",
- len, not_less,min,under_guard));
+ DBUG_PRINT("qcache", ("len %lu, not less %d, min %lu",
+ len, not_less,min));
if (len >= min(query_cache_size, query_cache_limit))
{
@@ -2625,17 +2818,6 @@ Query_cache::allocate_block(ulong len, my_bool not_less, ulong min,
DBUG_RETURN(0); // in any case we don't have such piece of memory
}
- if (!under_guard)
- {
- STRUCT_LOCK(&structure_guard_mutex);
-
- if (unlikely(query_cache.query_cache_size == 0 || flush_in_progress))
- {
- STRUCT_UNLOCK(&structure_guard_mutex);
- DBUG_RETURN(0);
- }
- }
-
/* Free old queries until we have enough memory to store this block */
Query_cache_block *block;
do
@@ -2650,8 +2832,6 @@ Query_cache::allocate_block(ulong len, my_bool not_less, ulong min,
split_block(block,ALIGN_SIZE(len));
}
- if (!under_guard)
- STRUCT_UNLOCK(&structure_guard_mutex);
DBUG_RETURN(block);
}
@@ -2886,9 +3066,7 @@ uint Query_cache::find_bin(ulong size)
}
uint bin = steps[left].idx -
(uint)((size - steps[left].size)/steps[left].increment);
-#ifndef DBUG_OFF
- bins_dump();
-#endif
+
DBUG_PRINT("qcache", ("bin %u step %u, size %lu step size %lu",
bin, left, size, steps[left].size));
DBUG_RETURN(bin);
@@ -3177,18 +3355,17 @@ my_bool Query_cache::ask_handler_allowance(THD *thd,
Packing
*****************************************************************************/
+
+/**
+ @brief Rearrange all memory blocks so that free memory joins at the
+ 'bottom' of the allocated memory block containing all cache data.
+ @see Query_cache::pack(ulong join_limit, uint iteration_limit)
+*/
+
void Query_cache::pack_cache()
{
DBUG_ENTER("Query_cache::pack_cache");
- STRUCT_LOCK(&structure_guard_mutex);
-
- if (unlikely(query_cache_size == 0 || flush_in_progress))
- {
- STRUCT_UNLOCK(&structure_guard_mutex);
- DBUG_VOID_RETURN;
- }
-
DBUG_EXECUTE("check_querycache",query_cache.check_integrity(1););
uchar *border = 0;
@@ -3222,7 +3399,6 @@ void Query_cache::pack_cache()
}
DBUG_EXECUTE("check_querycache",query_cache.check_integrity(1););
- STRUCT_UNLOCK(&structure_guard_mutex);
DBUG_VOID_RETURN;
}
@@ -3497,8 +3673,7 @@ my_bool Query_cache::join_results(ulong join_limit)
my_bool has_moving = 0;
DBUG_ENTER("Query_cache::join_results");
- STRUCT_LOCK(&structure_guard_mutex);
- if (queries_blocks != 0 && !flush_in_progress)
+ if (queries_blocks != 0)
{
DBUG_ASSERT(query_cache_size > 0);
Query_cache_block *block = queries_blocks;
@@ -3561,7 +3736,6 @@ my_bool Query_cache::join_results(ulong join_limit)
block = block->next;
} while ( block != queries_blocks );
}
- STRUCT_UNLOCK(&structure_guard_mutex);
DBUG_RETURN(has_moving);
}
@@ -3797,6 +3971,14 @@ void Query_cache::tables_dump()
}
+/**
+ @brief Checks integrity of the various linked lists
+
+ @return Error status code
+ @retval FALSE Query cache is operational.
+ @retval TRUE Query cache is broken.
+*/
+
my_bool Query_cache::check_integrity(bool locked)
{
my_bool result = 0;
@@ -3806,14 +3988,8 @@ my_bool Query_cache::check_integrity(bool locked)
if (!locked)
STRUCT_LOCK(&structure_guard_mutex);
- if (unlikely(query_cache_size == 0 || flush_in_progress))
- {
- if (!locked)
- STRUCT_UNLOCK(&query_cache.structure_guard_mutex);
-
- DBUG_PRINT("qcache", ("Query Cache not initialized"));
- DBUG_RETURN(0);
- }
+ while (is_flushing())
+ pthread_cond_wait(&COND_cache_status_changed,&structure_guard_mutex);
if (hash_check(&queries))
{