diff options
author | unknown <igor@rurik.mysql.com> | 2003-11-25 10:40:14 -0800 |
---|---|---|
committer | unknown <igor@rurik.mysql.com> | 2003-11-25 10:40:14 -0800 |
commit | bbcd1eccf7f37c02061bf6ea6b98322fccb3fcef (patch) | |
tree | 38a6a1d1179e36741712388861aed524e37ab5aa /mysys | |
parent | 920c30b43a3be8955db4e03cc393dd56ac8f5239 (diff) | |
download | mariadb-git-bbcd1eccf7f37c02061bf6ea6b98322fccb3fcef.tar.gz |
mf_keycache.c, keycache.h:
Fix for the resize key cache operation.
include/keycache.h:
Fix for the resize key cache operation.
mysys/mf_keycache.c:
Fix for the resize key cache operation.
Diffstat (limited to 'mysys')
-rw-r--r-- | mysys/mf_keycache.c | 222 |
1 files changed, 189 insertions, 33 deletions
diff --git a/mysys/mf_keycache.c b/mysys/mf_keycache.c index b1ef03fdcd2..6b1535e1a65 100644 --- a/mysys/mf_keycache.c +++ b/mysys/mf_keycache.c @@ -141,6 +141,11 @@ KEY_CACHE *dflt_key_cache= &dflt_key_cache_var; #define FLUSH_CACHE 2000 /* sort this many blocks at once */ static int flush_all_key_blocks(KEY_CACHE *keycache); +static inline void link_into_queue(KEYCACHE_WQUEUE *wqueue, + struct st_my_thread_var *thread); +static inline void unlink_from_queue(KEYCACHE_WQUEUE *wqueue, + struct st_my_thread_var *thread); +static void free_block(KEY_CACHE *keycache, BLOCK_LINK *block); static void test_key_cache(KEY_CACHE *keycache, const char *where, my_bool lock); @@ -225,10 +230,10 @@ static int keycache_pthread_cond_broadcast(pthread_cond_t *cond); static uint next_power(uint value) { - uint old_value=1; + uint old_value= 1; while (value) { - old_value=value; + old_value= value; value&= value-1; } return (old_value << 1); @@ -239,8 +244,8 @@ static uint next_power(uint value) Initialize a key cache SYNOPSIS - init_ky_cache() - keycache pointer to the key cache handle to initialize + init_key_cache() + keycache pointer to a key cache data structure key_cache_block_size size of blocks to keep cached data use_mem total memory to use for the key cache division_limit division limit (may be zero) @@ -366,11 +371,16 @@ int init_key_cache(KEY_CACHE *keycache, uint key_cache_block_size, blocks * age_threshold / 100 : blocks); + keycache->cnt_for_resize_op= 0; + keycache->resize_in_flush= 0; + keycache->can_be_used= 1; + keycache->resize_queue.last_thread= NULL; + keycache->waiting_for_hash_link.last_thread= NULL; keycache->waiting_for_block.last_thread= NULL; DBUG_PRINT("exit", - ("disk_blocks: %d block_root: %lx hash_entries: %d hash_root: %lx \ - hash_links: %d hash_link_root %lx", + ("disk_blocks: %d block_root: %lx hash_entries: %d\ + hash_root: %lx hash_links: %d hash_link_root %lx", keycache->disk_blocks, keycache->block_root, keycache->hash_entries, keycache->hash_root, keycache->hash_links, keycache->hash_link_root)); @@ -398,6 +408,7 @@ err: keycache->block_root= NULL; } my_errno= error; + keycache->can_be_used= 0; DBUG_RETURN(0); } @@ -407,8 +418,8 @@ err: SYNOPSIS resize_key_cache() - keycache in/out key cache handle - key_cache_block_size size of blocks to keep cached data + keycache pointer to a key cache data structure + key_cache_block_size size of blocks to keep cached data use_mem total memory to use for the new key cache division_limit new division limit (if not zero) age_threshold new age threshold (if not zero) @@ -425,6 +436,10 @@ err: old key cache blocks by calling the end_key_cache function and then rebuilds the key cache with new blocks by calling init_key_cache. + + The function starts the operation only when all other threads + performing operations with the key cache let her to proceed + (when cnt_for_resize=0). */ int resize_key_cache(KEY_CACHE *keycache, uint key_cache_block_size, @@ -432,35 +447,90 @@ int resize_key_cache(KEY_CACHE *keycache, uint key_cache_block_size, uint age_threshold) { int blocks; + struct st_my_thread_var *thread; + KEYCACHE_WQUEUE *wqueue; DBUG_ENTER("resize_key_cache"); - if (!keycache->key_cache_inited || - (key_cache_block_size == keycache->key_cache_block_size && - use_mem == keycache->key_cache_mem_size)) + if (!keycache->key_cache_inited) DBUG_RETURN(keycache->disk_blocks); + + if(key_cache_block_size == keycache->key_cache_block_size && + use_mem == keycache->key_cache_mem_size) + { + change_key_cache_param(keycache, division_limit, age_threshold); + DBUG_RETURN(keycache->disk_blocks); + } keycache_pthread_mutex_lock(&keycache->cache_lock); + + wqueue= &keycache->resize_queue; + thread=my_thread_var; + link_into_queue(wqueue, thread); + + while (wqueue->last_thread->next != thread) + { + keycache_pthread_cond_wait(&thread->suspend, &keycache->cache_lock); + } + + keycache->resize_in_flush= 1; if (flush_all_key_blocks(keycache)) { /* TODO: if this happens, we should write a warning in the log file ! */ - keycache_pthread_mutex_unlock(&keycache->cache_lock); - DBUG_RETURN(0); + keycache->resize_in_flush= 0; + blocks= 0; + goto finish; } + keycache->resize_in_flush= 0; + keycache->can_be_used= 0; + while (keycache->cnt_for_resize_op) + { + keycache_pthread_cond_wait(&thread->suspend, &keycache->cache_lock); + } + end_key_cache(keycache, 0); /* Don't free mutex */ - /* the following will work even if use_mem is 0 */ + /* The following will work even if use_mem is 0 */ blocks= init_key_cache(keycache, key_cache_block_size, use_mem, division_limit, age_threshold); + +finish: + unlink_from_queue(wqueue, thread); + /* Signal for the next resize request to proceeed if any */ + if (wqueue->last_thread) + keycache_pthread_cond_signal(&wqueue->last_thread->next->suspend); + + keycache->can_be_used= blocks <= 0; keycache_pthread_mutex_unlock(&keycache->cache_lock); return blocks; } /* + Increment counter blocking resize key cache operation +*/ +static inline void inc_counter_for_resize_op(KEY_CACHE *keycache) +{ + keycache->cnt_for_resize_op++; +} + + +/* + Decrement counter blocking resize key cache operation; + Signal the operation to proceed when counter becomes equal zero +*/ +static inline void dec_counter_for_resize_op(KEY_CACHE *keycache) +{ + struct st_my_thread_var *last_thread; + if (!--keycache->cnt_for_resize_op && + (last_thread= keycache->resize_queue.last_thread)) + keycache_pthread_cond_signal(&last_thread->next->suspend); +} + +/* Change the key cache parameters SYNOPSIS change_key_cache_param() - keycache key cache handle + keycache pointer to a key cache data structure division_limit new division limit (if not zero) age_threshold new age threshold (if not zero) @@ -478,12 +548,14 @@ void change_key_cache_param(KEY_CACHE *keycache, uint division_limit, { DBUG_ENTER("change_key_cache_param"); + keycache_pthread_mutex_lock(&keycache->cache_lock); if (division_limit) keycache->min_warm_blocks= (keycache->disk_blocks * division_limit / 100 + 1); if (age_threshold) keycache->age_threshold= (keycache->disk_blocks * age_threshold / 100); + keycache_pthread_mutex_unlock(&keycache->cache_lock); DBUG_VOID_RETURN; } @@ -1018,7 +1090,7 @@ static void unlink_hash(KEY_CACHE *keycache, HASH_LINK *hash_link) hash_link->block= NULL; if (keycache->waiting_for_hash_link.last_thread) { - /* Signal that A free hash link appeared */ + /* Signal that a free hash link has appeared */ struct st_my_thread_var *last_thread= keycache->waiting_for_hash_link.last_thread; struct st_my_thread_var *first_thread= last_thread->next; @@ -1204,9 +1276,57 @@ restart: block->hash_link == hash_link && (block->status & BLOCK_READ)) page_status= PAGE_READ; - if (page_status == PAGE_READ && (block->status & BLOCK_IN_SWITCH)) + if (wrmode && keycache->resize_in_flush) + { + /* This is a write request during the flush phase of a resize operation */ + + if (page_status != PAGE_READ) + { + /* We don't need the page in the cache: we are going to write on disk */ + hash_link->requests--; + unlink_hash(keycache, hash_link); + return 0; + } + if (!(block->status & BLOCK_IN_FLUSH)) + { + hash_link->requests--; + /* + Remove block to invalidate the page in the block buffer + as we are going to write directly on disk. + Although we have an exlusive lock for the updated key part + the control can be yieded by the current thread as we might + have unfinished readers of other key parts in the block + buffer. Still we are guaranteed not to have any readers + of the key part we are writing into until the block is + removed from the cache as we set the BLOCL_REASSIGNED + flag (see the code below that handles reading requests). + */ + free_block(keycache, block); + return 0; + } + /* Wait intil the page is flushed on disk */ + hash_link->requests--; + { + struct st_my_thread_var *thread= my_thread_var; + add_to_queue(&block->wqueue[COND_FOR_SAVED], thread); + do + { + keycache_pthread_cond_wait(&thread->suspend, + &keycache->cache_lock); + } + while(thread->next); + } + /* Invalidate page in the block if it has not been done yet */ + if (block->status) + free_block(keycache, block); + return 0; + } + + if (page_status == PAGE_READ && + (block->status & (BLOCK_IN_SWITCH | BLOCK_REASSIGNED))) { /* This is a request for a page to be removed from cache */ + KEYCACHE_DBUG_PRINT("find_key_block", ("request for old page in block %u",BLOCK_NUMBER(block))); /* @@ -1270,10 +1390,11 @@ restart: else { /* There are no never used blocks, use a block from the LRU chain */ + /* - Wait until a new block is added to the LRU chain; - several threads might wait here for the same page, - all of them must get the same block + Wait until a new block is added to the LRU chain; + several threads might wait here for the same page, + all of them must get the same block */ if (! keycache->used_last) @@ -1521,7 +1642,7 @@ byte *key_cache_read(KEY_CACHE *keycache, DBUG_PRINT("enter", ("file %u, filepos %lu, length %u", (uint) file, (ulong) filepos, length)); - if (keycache->disk_blocks > 0) + if (keycache->can_be_used) { /* Key cache is used */ reg1 BLOCK_LINK *block; @@ -1533,7 +1654,7 @@ byte *key_cache_read(KEY_CACHE *keycache, do { keycache_pthread_mutex_lock(&keycache->cache_lock); - if (keycache->disk_blocks <= 0) /* Resize failed */ + if (!keycache->can_be_used) { keycache_pthread_mutex_unlock(&keycache->cache_lock); goto no_key_cache; @@ -1548,6 +1669,7 @@ byte *key_cache_read(KEY_CACHE *keycache, return_buffer=0; #endif + inc_counter_for_resize_op(keycache); keycache->global_cache_r_requests++; block=find_key_block(keycache, file, filepos, level, 0, &page_st); if (block->status != BLOCK_ERROR && page_st != PAGE_READ) @@ -1598,6 +1720,8 @@ byte *key_cache_read(KEY_CACHE *keycache, */ unreg_request(keycache, block, 1); + dec_counter_for_resize_op(keycache); + keycache_pthread_mutex_unlock(&keycache->cache_lock); if (status & BLOCK_ERROR) @@ -1654,7 +1778,7 @@ int key_cache_insert(KEY_CACHE *keycache, DBUG_PRINT("enter", ("file %u, filepos %lu, length %u", (uint) file,(ulong) filepos, length)); - if (keycache->disk_blocks > 0) + if (keycache->can_be_used) { /* Key cache is used */ reg1 BLOCK_LINK *block; @@ -1666,7 +1790,7 @@ int key_cache_insert(KEY_CACHE *keycache, { uint offset; keycache_pthread_mutex_lock(&keycache->cache_lock); - if (keycache->disk_blocks <= 0) /* Resize failed */ + if (!keycache->can_be_used) { keycache_pthread_mutex_unlock(&keycache->cache_lock); DBUG_RETURN(0); @@ -1678,6 +1802,7 @@ int key_cache_insert(KEY_CACHE *keycache, /* Read data into key cache from buff in key_cache_block_size incr. */ filepos-= offset; + inc_counter_for_resize_op(keycache); keycache->global_cache_r_requests++; block= find_key_block(keycache, file, filepos, level, 0, &page_st); if (block->status != BLOCK_ERROR && page_st != PAGE_READ) @@ -1708,6 +1833,9 @@ int key_cache_insert(KEY_CACHE *keycache, unreg_request(keycache, block, 1); error= (block->status & BLOCK_ERROR); + + dec_counter_for_resize_op(keycache); + keycache_pthread_mutex_unlock(&keycache->cache_lock); if (error) @@ -1776,7 +1904,7 @@ int key_cache_write(KEY_CACHE *keycache, test_key_cache(keycache, "start of key_cache_write", 1);); #endif - if (keycache->disk_blocks > 0) + if (keycache->can_be_used) { /* Key cache is used */ uint read_length; @@ -1786,7 +1914,7 @@ int key_cache_write(KEY_CACHE *keycache, { uint offset; keycache_pthread_mutex_lock(&keycache->cache_lock); - if (keycache->disk_blocks <= 0) /* Resize failed */ + if (!keycache->can_be_used) { keycache_pthread_mutex_unlock(&keycache->cache_lock); goto no_key_cache; @@ -1798,8 +1926,25 @@ int key_cache_write(KEY_CACHE *keycache, /* Write data in key_cache_block_size increments */ filepos-= offset; + inc_counter_for_resize_op(keycache); keycache->global_cache_w_requests++; block= find_key_block(keycache, file, filepos, level, 1, &page_st); + if (!block) + { + /* It happens only for requests submitted during resize operation */ + dec_counter_for_resize_op(keycache); + keycache_pthread_mutex_unlock(&keycache->cache_lock); + if (dont_write) + { + keycache->global_cache_w_requests++; + keycache->global_cache_write++; + if (my_pwrite(file, (byte*) buff, length, filepos, + MYF(MY_NABP | MY_WAIT_IF_FULL))) + error=1; + } + goto next_block; + } + if (block->status != BLOCK_ERROR && page_st != PAGE_READ && (offset || read_length < keycache->key_cache_block_size)) read_block(keycache, block, @@ -1841,8 +1986,11 @@ int key_cache_write(KEY_CACHE *keycache, break; } - keycache_pthread_mutex_unlock(&keycache->cache_lock); + dec_counter_for_resize_op(keycache); + keycache_pthread_mutex_unlock(&keycache->cache_lock); + + next_block: buff+= read_length; filepos+= read_length; offset= 0; @@ -1951,6 +2099,12 @@ static int flush_cached_blocks(KEY_CACHE *keycache, if (!last_errno) last_errno= errno ? errno : -1; } + /* + Let to proceed for possible waiting requests to write to the block page. + It might happen only during an operation to resize the key cache. + */ + if (block->wqueue[COND_FOR_SAVED].last_thread) + release_queue(&block->wqueue[COND_FOR_SAVED]); /* type will never be FLUSH_IGNORE_CHANGED here */ if (! (type == FLUSH_KEEP || type == FLUSH_FORCE_WRITE)) { @@ -2187,18 +2341,20 @@ restart: 1 error */ -int flush_key_blocks(KEY_CACHE *key_cache, +int flush_key_blocks(KEY_CACHE *keycache, File file, enum flush_type type) { int res; DBUG_ENTER("flush_key_blocks"); - DBUG_PRINT("enter", ("key_cache: %lx", key_cache)); + DBUG_PRINT("enter", ("keycache: %lx", keycache)); - if (key_cache->disk_blocks <= 0) + if (keycache->disk_blocks <= 0) DBUG_RETURN(0); - keycache_pthread_mutex_lock(&key_cache->cache_lock); - res= flush_key_blocks_int(key_cache, file, type); - keycache_pthread_mutex_unlock(&key_cache->cache_lock); + keycache_pthread_mutex_lock(&keycache->cache_lock); + inc_counter_for_resize_op(keycache); + res= flush_key_blocks_int(keycache, file, type); + dec_counter_for_resize_op(keycache); + keycache_pthread_mutex_unlock(&keycache->cache_lock); DBUG_RETURN(res); } |