diff options
author | unknown <igor@rurik.mysql.com> | 2003-11-25 14:09:38 -0800 |
---|---|---|
committer | unknown <igor@rurik.mysql.com> | 2003-11-25 14:09:38 -0800 |
commit | 6307b8f660f34875e9746bb48db265e11ded2249 (patch) | |
tree | f538fdeb4af837774b39c97c4d241a45417f6cbc | |
parent | 1daf856de56dfb5c9c2bf2880b10a8f14f7a12df (diff) | |
parent | 75fa6a6a40ebd2a18bad6a00385b5fc81ddda078 (diff) | |
download | mariadb-git-6307b8f660f34875e9746bb48db265e11ded2249.tar.gz |
Merge ibabaev@bk-internal.mysql.com:/home/bk/mysql-4.1
into rurik.mysql.com:/home/igor/mysql-4.1
-rw-r--r-- | include/keycache.h | 4 | ||||
-rw-r--r-- | mysys/mf_keycache.c | 222 | ||||
-rw-r--r-- | tests/fork_big2.pl | 36 |
3 files changed, 227 insertions, 35 deletions
diff --git a/include/keycache.h b/include/keycache.h index 340ab881fee..b882f3127f5 100644 --- a/include/keycache.h +++ b/include/keycache.h @@ -45,6 +45,8 @@ typedef struct st_keycache_wqueue typedef struct st_key_cache { my_bool key_cache_inited; + my_bool resize_in_flush; /* true during flush of resize operation */ + my_bool can_be_used; /* usage of cache for read/write is allowed */ uint key_cache_shift; ulong key_cache_mem_size; /* specified size of the cache memory */ uint key_cache_block_size; /* size of the page buffer of a cache block */ @@ -58,6 +60,7 @@ typedef struct st_key_cache ulong blocks_used; /* number of currently used blocks */ ulong blocks_changed; /* number of currently dirty blocks */ ulong warm_blocks; /* number of blocks in warm sub-chain */ + ulong cnt_for_resize_op; /* counter to block resize operation */ long blocks_available; /* number of blocks available in the LRU chain */ HASH_LINK **hash_root; /* arr. of entries into hash table buckets */ HASH_LINK *hash_link_root; /* memory for hash table links */ @@ -67,6 +70,7 @@ typedef struct st_key_cache BLOCK_LINK *used_last; /* ptr to the last block of the LRU chain */ BLOCK_LINK *used_ins; /* ptr to the insertion block in LRU chain */ pthread_mutex_t cache_lock; /* to lock access to the cache structure */ + KEYCACHE_WQUEUE resize_queue; /* threads waiting during resize operation */ KEYCACHE_WQUEUE waiting_for_hash_link; /* waiting for a free hash link */ KEYCACHE_WQUEUE waiting_for_block; /* requests waiting for a free block */ BLOCK_LINK *changed_blocks[CHANGED_BLOCKS_HASH]; /* hash for dirty file bl.*/ 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); } diff --git a/tests/fork_big2.pl b/tests/fork_big2.pl index b552b95ba6b..a1c55ac4c11 100644 --- a/tests/fork_big2.pl +++ b/tests/fork_big2.pl @@ -23,14 +23,15 @@ $opt_select_count=$opt_join_count=0; $opt_update=1;$opt_delete=0;
$opt_flush=$opt_check=$opt_repair=$opt_alter=0;
$opt_join_range=100;
+$opt_resize_interval=0; $opt_time=0; $opt_host=$opt_user=$opt_password=""; $opt_db="test"; +$opt_verbose=$opt_debug=$opt_lock_tables=$opt_fast_insert=$opt_fast=$opt_skip_in=$opt_force=undef; # Ignore warnings from these GetOptions("host=s","db=s","user=s","password=s","loop-count=i","skip-create","skip-in","skip-drop",
"verbose","fast-insert","lock-tables","debug","fast","force","thread-factor=i",
"insert=i", "select=i", "join=i", "select-count=i", "join-count=i", "update=i", "delete=i",
- "flush=i", "check=i", "repair=i", "alter=i", "max-join_range=i", "time=i") || die "Aborted"; -$opt_verbose=$opt_debug=$opt_lock_tables=$opt_fast_insert=$opt_fast=$opt_skip_in=$opt_force=undef; # Ignore warnings from these + "flush=i", "check=i", "repair=i", "alter=i", "resize-interval=i", "max-join_range=i", "time=i") || die "Aborted"; print "Test of multiple connections that test the following things:\n"; print "insert, select, delete, update, alter, check, repair and flush\n"; @@ -158,6 +159,11 @@ for ($i=0 ; $i < $opt_alter ; $i ++) test_alter() if (($pid=fork()) == 0); $work{$pid}="alter";
}
$threads+=$i;
+if ($opt_resize_interval != 0) +{ + test_resize() if (($pid=fork()) == 0); $work{$pid}="resize"; + $threads+=1; +} print "Started $threads threads\n"; @@ -590,6 +596,32 @@ sub test_flush exit(0); } +# +# Do a resize key cache every periodically +# + +sub test_resize +{ + my ($dbh, $key_buffer_size); + + $dbh = DBI->connect("DBI:mysql:$opt_db:$opt_host", + $opt_user, $opt_password, + { PrintError => 0}) || die $DBI::errstr; + + $count=0; + $key_buffer_size=1024*64; + while (!test_if_abort($dbh)) + { + sleep($opt_resize_interval); + $dbh->do("set global key_buffer_size=$key_buffer_size") || + die "Got error on resize key cache $DBI::errstr\n"; + $key_buffer_size+=1024*16; + $count++; + } + $dbh->disconnect; $dbh=0; + print "Test_resize: Executed $count times resize key cache\n"; + exit(0); +} # # Test all tables in a database |