summaryrefslogtreecommitdiff
path: root/mysys
diff options
context:
space:
mode:
authorunknown <istruewing@chilla.local>2007-05-14 11:33:47 +0200
committerunknown <istruewing@chilla.local>2007-05-14 11:33:47 +0200
commit7efe2bcddcebfdd8d66be15e9bf99842bef3d768 (patch)
tree71c3bcc0edaadbed95cb86bd2d7a08efb8134aaf /mysys
parent5d40ae9f8c235856878e2e3e8bd3455ae426f1bb (diff)
downloadmariadb-git-7efe2bcddcebfdd8d66be15e9bf99842bef3d768.tar.gz
Bug#17332 - changing key_buffer_size on a running server
can crash under load Post-post-review fixes. Fixed a typo == -> = Optimized normal flush at end of statement (FLUSH_KEEP), but let other flush types be stringent. Added comments. Fixed debugging.
Diffstat (limited to 'mysys')
-rw-r--r--mysys/mf_keycache.c96
1 files changed, 70 insertions, 26 deletions
diff --git a/mysys/mf_keycache.c b/mysys/mf_keycache.c
index 651a2b1070a..7ca07016823 100644
--- a/mysys/mf_keycache.c
+++ b/mysys/mf_keycache.c
@@ -608,6 +608,7 @@ int resize_key_cache(KEY_CACHE *keycache, uint key_cache_block_size,
keycache->can_be_used= 0;
goto finish;
}
+ DBUG_ASSERT(cache_empty(keycache));
/* End the flush phase. */
keycache->resize_in_flush= 0;
@@ -3599,7 +3600,7 @@ static int flush_key_blocks_int(KEY_CACHE *keycache,
So we should not let count become smaller than the fixed buffer.
*/
if (cache == cache_buff)
- count == FLUSH_CACHE;
+ count= FLUSH_CACHE;
}
/* Retrieve the blocks and write them to a buffer to be flushed */
@@ -3718,8 +3719,16 @@ restart:
link_changed(block, &first_in_switch);
}
}
- else
+ else if (type != FLUSH_KEEP)
{
+ /*
+ During the normal flush at end of statement (FLUSH_KEEP) we
+ do not need to ensure that blocks in flush or update by
+ other threads are flushed. They will be flushed by them
+ later. In all other cases we must assure that we do not have
+ any changed block of this file in the cache when this
+ function returns.
+ */
if (block->status & BLOCK_IN_FLUSH)
{
/* Remember the last block found to be in flush. */
@@ -3743,9 +3752,14 @@ restart:
last_errno= error;
}
/*
- Do not restart here. We have now flushed at least all blocks
- that were changed when entering this function.
+ Do not restart here during the normal flush at end of statement
+ (FLUSH_KEEP). We have now flushed at least all blocks that were
+ changed when entering this function. In all other cases we must
+ assure that we do not have any changed block of this file in the
+ cache when this function returns.
*/
+ if (type != FLUSH_KEEP)
+ goto restart;
}
if (last_in_flush)
{
@@ -3996,7 +4010,35 @@ int flush_key_blocks(KEY_CACHE *keycache,
/*
- Flush all blocks in the key cache to disk
+ Flush all blocks in the key cache to disk.
+
+ SYNOPSIS
+ flush_all_key_blocks()
+ keycache pointer to key cache root structure
+
+ DESCRIPTION
+
+ Flushing of the whole key cache is done in two phases.
+
+ 1. Flush all changed blocks, waiting for them if necessary. Loop
+ until there is no changed block left in the cache.
+
+ 2. Free all clean blocks. Normally this means free all blocks. The
+ changed blocks were flushed in phase 1 and became clean. However we
+ may need to wait for blocks that are read by other threads. While we
+ wait, a clean block could become changed if that operation started
+ before the resize operation started. To be safe we must restart at
+ phase 1.
+
+ When we can run through the changed_blocks and file_blocks hashes
+ without finding a block any more, then we are done.
+
+ Note that we hold keycache->cache_lock all the time unless we need
+ to wait for something.
+
+ RETURN
+ 0 OK
+ != 0 Error
*/
static int flush_all_key_blocks(KEY_CACHE *keycache)
@@ -4007,13 +4049,15 @@ static int flush_all_key_blocks(KEY_CACHE *keycache)
uint idx;
DBUG_ENTER("flush_all_key_blocks");
- safe_mutex_assert_owner(&keycache->cache_lock);
-
do
{
+ safe_mutex_assert_owner(&keycache->cache_lock);
total_found= 0;
- /* Flush all changed blocks first. */
+ /*
+ Phase1: Flush all changed blocks, waiting for them if necessary.
+ Loop until there is no changed block left in the cache.
+ */
do
{
found= 0;
@@ -4022,17 +4066,15 @@ static int flush_all_key_blocks(KEY_CACHE *keycache)
{
/*
If an array element is non-empty, use the first block from its
- chain to find a file for flush. All blocks for this file are
- flushed. So the same block will not appear at this place again
- with the next iteration. New writes for blocks are not accepted
- during the flush.
+ chain to find a file for flush. All changed blocks for this
+ file are flushed. So the same block will not appear at this
+ place again with the next iteration. New writes for blocks are
+ not accepted during the flush. If multiple files share the
+ same hash bucket, one of them will be flushed per iteration
+ of the outer loop of phase 1.
*/
if ((block= keycache->changed_blocks[idx]))
{
- /* A block in the changed_blocks hash must have a hash_link. */
- DBUG_ASSERT(block->hash_link);
- DBUG_ASSERT(block->hash_link->block == block);
-
found++;
/*
Flush dirty blocks but do not free them yet. They can be used
@@ -4046,7 +4088,14 @@ static int flush_all_key_blocks(KEY_CACHE *keycache)
} while (found);
- /* Now flush (free) all clean blocks. */
+ /*
+ Phase 2: Free all clean blocks. Normally this means free all
+ blocks. The changed blocks were flushed in phase 1 and became
+ clean. However we may need to wait for blocks that are read by
+ other threads. While we wait, a clean block could become changed
+ if that operation started before the resize operation started. To
+ be safe we must restart at phase 1.
+ */
do
{
found= 0;
@@ -4057,17 +4106,12 @@ static int flush_all_key_blocks(KEY_CACHE *keycache)
If an array element is non-empty, use the first block from its
chain to find a file for flush. All blocks for this file are
freed. So the same block will not appear at this place again
- with the next iteration. Unless it has been read into the cache
- anew. In this case readers and the flusher fight against each
- other. But since the flusher does not need to do I/O for clean
- blocks, and writes for blocks are not accepted during the flush,
- it will win finally.
+ with the next iteration. If multiple files share the
+ same hash bucket, one of them will be flushed per iteration
+ of the outer loop of phase 2.
*/
if ((block= keycache->file_blocks[idx]))
{
- /* A block in the file_blocks hash must have a hash_link. */
- DBUG_ASSERT(block->hash_link);
-
total_found++;
found++;
if (flush_key_blocks_int(keycache, block->hash_link->file,
@@ -4412,7 +4456,7 @@ static int cache_empty(KEY_CACHE *keycache)
for (idx= 0; idx < keycache->hash_links; idx++)
{
HASH_LINK *hash_link= keycache->hash_link_root + idx;
- if (hash_link->block || hash_link->file || hash_link->diskpos)
+ if (hash_link->requests || hash_link->block)
{
fprintf(stderr, "hash_link index: %u\n", idx);
fail_hlink(hash_link);