Commit 2813cd1f authored by igor@rurik.mysql.com's avatar igor@rurik.mysql.com

set_var.cc, mysqld.cc, handler.cc, set_var.h, handler.h:

  Added key cache parameters for midpoint insertion strategy
Many files:
  Added midpoint insertion strategy for key cache
mi_test2.c:
  Added a parameter to resize_key_cache
parent 97b82506
......@@ -516,6 +516,8 @@ typedef struct st_key_cache_var
{
ulonglong buff_size; /* size the memory allocated for the cache */
ulong block_size; /* size of the blocks in the key cache */
ulong division_limit; /* min. percentage of warm blocks */
ulong age_threshold; /* determines when hot block is downgraded */
KEY_CACHE_HANDLE cache; /* handles for the current and registered */
ulong blocks_used; /* number of currently used blocks */
ulong blocks_changed; /* number of currently dirty blocks */
......@@ -530,6 +532,7 @@ typedef struct st_key_cache_var
#define DEFAULT_KEY_CACHE_NAME "default"
extern KEY_CACHE_HANDLE *dflt_keycache;
extern KEY_CACHE_VAR dflt_key_cache_var;
#define DFLT_INIT_HITS 3
#include <my_alloc.h>
......@@ -670,17 +673,21 @@ extern long my_clock(void);
extern sig_handler sigtstp_handler(int signal_number);
extern void handle_recived_signals(void);
extern int init_key_cache(KEY_CACHE_HANDLE *pkeycache,
uint key_cache_block_sie,
uint key_cache_block_size,
ulong use_mem, KEY_CACHE_VAR* env);
extern int resize_key_cache(KEY_CACHE_HANDLE *pkeycache, ulong use_mem);
extern int resize_key_cache(KEY_CACHE_HANDLE *pkeycache,
uint key_cache_block_size, ulong use_mem);
extern void change_key_cache_param(KEY_CACHE_HANDLE keycache);
extern byte *key_cache_read(KEY_CACHE_HANDLE keycache,
File file,my_off_t filepos,byte* buff,uint length,
File file, my_off_t filepos, int level,
byte* buff, uint length,
uint block_length,int return_buffer);
extern int key_cache_insert(KEY_CACHE_HANDLE keycache,
File file, my_off_t filepos,
File file, my_off_t filepos, int level,
byte *buff, uint length);
extern int key_cache_write(KEY_CACHE_HANDLE keycache,
File file,my_off_t filepos,byte* buff,uint length,
File file, my_off_t filepos, int level,
byte* buff, uint length,
uint block_length,int force_write);
extern int flush_key_blocks(KEY_CACHE_HANDLE keycache,
int file, enum flush_type type);
......
......@@ -28,7 +28,7 @@ uchar *_nisam_fetch_keypage(register N_INFO *info, N_KEYDEF *keyinfo,
{
uchar *tmp;
tmp=(uchar*) key_cache_read(*dflt_keycache,
info->s->kfile,page,(byte*) buff,
info->s->kfile,page,DFLT_INIT_HITS,(byte*) buff,
(uint) keyinfo->base.block_length,
(uint) keyinfo->base.block_length,
return_buffer);
......@@ -85,7 +85,8 @@ int _nisam_write_keypage(register N_INFO *info, register N_KEYDEF *keyinfo,
}
#endif
return (key_cache_write(*dflt_keycache,
info->s->kfile,page,(byte*) buff,length,
info->s->kfile,page,DFLT_INIT_HITS,
(byte*) buff,length,
(uint) keyinfo->base.block_length,
(int) (info->lock_type != F_UNLCK)));
} /* nisam_write_keypage */
......@@ -102,7 +103,8 @@ int _nisam_dispose(register N_INFO *info, N_KEYDEF *keyinfo, my_off_t pos)
old_link=info->s->state.key_del[keynr];
info->s->state.key_del[keynr]=(ulong) pos;
DBUG_RETURN(key_cache_write(*dflt_keycache,
info->s->kfile,pos,(byte*) &old_link,
info->s->kfile,pos,DFLT_INIT_HITS,
(byte*) &old_link,
sizeof(long),
(uint) keyinfo->base.block_length,
(int) (info->lock_type != F_UNLCK)));
......@@ -130,7 +132,7 @@ ulong _nisam_new(register N_INFO *info, N_KEYDEF *keyinfo)
else
{
if (!key_cache_read(*dflt_keycache,
info->s->kfile,pos,
info->s->kfile,pos,DFLT_INIT_HITS,
(byte*) &info->s->state.key_del[keynr],
(uint) sizeof(long),
(uint) keyinfo->base.block_length,0))
......
......@@ -231,7 +231,8 @@ static int check_k_link(MI_CHECK *param, register MI_INFO *info, uint nr)
next_link & (info->s->blocksize-1))
DBUG_RETURN(1);
if (!(buff=key_cache_read(*info->s->keycache,
info->s->kfile, next_link, (byte*) info->buff,
info->s->kfile, next_link, DFLT_INIT_HITS,
(byte*) info->buff,
myisam_block_size, block_size, 1)))
DBUG_RETURN(1);
next_link=mi_sizekorr(buff);
......@@ -371,8 +372,8 @@ int chk_key(MI_CHECK *param, register MI_INFO *info)
if (share->state.key_root[key] == HA_OFFSET_ERROR &&
(info->state->records == 0 || keyinfo->flag & HA_FULLTEXT))
continue;
if (!_mi_fetch_keypage(info,keyinfo,share->state.key_root[key],info->buff,
0))
if (!_mi_fetch_keypage(info,keyinfo,share->state.key_root[key],
DFLT_INIT_HITS,info->buff,0))
{
mi_check_print_error(param,"Can't read indexpage from filepos: %s",
llstr(share->state.key_root[key],buff));
......@@ -550,7 +551,8 @@ static int chk_index(MI_CHECK *param, MI_INFO *info, MI_KEYDEF *keyinfo,
info->state->key_file_length=(max_length &
~ (my_off_t) (info->s->blocksize-1));
}
if (!_mi_fetch_keypage(info,keyinfo,next_page,temp_buff,0))
if (!_mi_fetch_keypage(info,keyinfo,next_page,
DFLT_INIT_HITS,temp_buff,0))
{
mi_check_print_error(param,"Can't read key from filepos: %s",llstr(next_page,llbuff));
goto err;
......@@ -1443,7 +1445,8 @@ int movepoint(register MI_INFO *info, byte *record, my_off_t oldpos,
nod_flag=mi_test_if_nod(info->buff);
_mi_dpointer(info,info->int_keypos-nod_flag-
info->s->rec_reflength,newpos);
if (_mi_write_keypage(info,keyinfo,info->last_keypage,info->buff))
if (_mi_write_keypage(info,keyinfo,info->last_keypage,
DFLT_INIT_HITS,info->buff))
DBUG_RETURN(-1);
}
else
......@@ -1600,7 +1603,7 @@ static int sort_one_index(MI_CHECK *param, MI_INFO *info, MI_KEYDEF *keyinfo,
mi_check_print_error(param,"Not Enough memory");
DBUG_RETURN(-1);
}
if (!_mi_fetch_keypage(info,keyinfo,pagepos,buff,0))
if (!_mi_fetch_keypage(info,keyinfo,pagepos,DFLT_INIT_HITS,buff,0))
{
mi_check_print_error(param,"Can't read key block from filepos: %s",
llstr(pagepos,llbuff));
......@@ -3334,13 +3337,13 @@ static int sort_insert_key(MI_SORT_PARAM *sort_param,
bzero((byte*) anc_buff+key_block->last_length,
keyinfo->block_length- key_block->last_length);
key_file_length=info->state->key_file_length;
if ((filepos=_mi_new(info,keyinfo)) == HA_OFFSET_ERROR)
if ((filepos=_mi_new(info,keyinfo,DFLT_INIT_HITS)) == HA_OFFSET_ERROR)
DBUG_RETURN(1);
/* If we read the page from the key cache, we have to write it back to it */
if (key_file_length == info->state->key_file_length)
{
if (_mi_write_keypage(info, keyinfo, filepos, anc_buff))
if (_mi_write_keypage(info, keyinfo, filepos, DFLT_INIT_HITS, anc_buff))
DBUG_RETURN(1);
}
else if (my_pwrite(info->s->kfile,(byte*) anc_buff,
......@@ -3438,13 +3441,14 @@ int flush_pending_blocks(MI_SORT_PARAM *sort_param)
_mi_kpointer(info,key_block->end_pos,filepos);
key_file_length=info->state->key_file_length;
bzero((byte*) key_block->buff+length, keyinfo->block_length-length);
if ((filepos=_mi_new(info,keyinfo)) == HA_OFFSET_ERROR)
if ((filepos=_mi_new(info,keyinfo,DFLT_INIT_HITS)) == HA_OFFSET_ERROR)
DBUG_RETURN(1);
/* If we read the page from the key cache, we have to write it back */
if (key_file_length == info->state->key_file_length)
{
if (_mi_write_keypage(info, keyinfo, filepos, key_block->buff))
if (_mi_write_keypage(info, keyinfo, filepos,
DFLT_INIT_HITS, key_block->buff))
DBUG_RETURN(1);
}
else if (my_pwrite(info->s->kfile,(byte*) key_block->buff,
......
......@@ -155,7 +155,7 @@ static int _mi_ck_real_delete(register MI_INFO *info, MI_KEYDEF *keyinfo,
DBUG_RETURN(my_errno=ENOMEM);
}
DBUG_PRINT("info",("root_page: %ld",old_root));
if (!_mi_fetch_keypage(info,keyinfo,old_root,root_buff,0))
if (!_mi_fetch_keypage(info,keyinfo,old_root,DFLT_INIT_HITS,root_buff,0))
{
error= -1;
goto err;
......@@ -179,11 +179,12 @@ static int _mi_ck_real_delete(register MI_INFO *info, MI_KEYDEF *keyinfo,
*root=_mi_kpos(nod_flag,root_buff+2+nod_flag);
else
*root=HA_OFFSET_ERROR;
if (_mi_dispose(info,keyinfo,old_root))
if (_mi_dispose(info,keyinfo,old_root,DFLT_INIT_HITS))
error= -1;
}
else
error=_mi_write_keypage(info,keyinfo,old_root,root_buff);
error=_mi_write_keypage(info,keyinfo,old_root,
DFLT_INIT_HITS,root_buff);
}
}
err:
......@@ -253,7 +254,7 @@ static int d_search(register MI_INFO *info, register MI_KEYDEF *keyinfo,
{
/* the last entry in sub-tree */
DBUG_PRINT("info",("FT2: the last entry"));
_mi_dispose(info, keyinfo, root);
_mi_dispose(info, keyinfo, root,DFLT_INIT_HITS);
/* fall through to normal delete */
}
else
......@@ -268,7 +269,8 @@ static int d_search(register MI_INFO *info, register MI_KEYDEF *keyinfo,
subkeys++;
ft_intXstore(kpos, subkeys);
if (!ret_value)
ret_value=_mi_write_keypage(info,keyinfo,page,anc_buff);
ret_value=_mi_write_keypage(info,keyinfo,page,
DFLT_INIT_HITS,anc_buff);
DBUG_PRINT("exit",("Return: %d",ret_value));
DBUG_RETURN(ret_value);
}
......@@ -287,7 +289,7 @@ static int d_search(register MI_INFO *info, register MI_KEYDEF *keyinfo,
DBUG_PRINT("exit",("Return: %d",-1));
DBUG_RETURN(-1);
}
if (!_mi_fetch_keypage(info,keyinfo,leaf_page,leaf_buff,0))
if (!_mi_fetch_keypage(info,keyinfo,leaf_page,DFLT_INIT_HITS,leaf_buff,0))
goto err;
}
......@@ -319,7 +321,7 @@ static int d_search(register MI_INFO *info, register MI_KEYDEF *keyinfo,
mi_putint(anc_buff,length,nod_flag);
if (!nod_flag)
{ /* On leaf page */
if (_mi_write_keypage(info,keyinfo,page,anc_buff))
if (_mi_write_keypage(info,keyinfo,page,DFLT_INIT_HITS,anc_buff))
{
DBUG_PRINT("exit",("Return: %d",-1));
DBUG_RETURN(-1);
......@@ -354,7 +356,7 @@ static int d_search(register MI_INFO *info, register MI_KEYDEF *keyinfo,
ret_value=_mi_split_page(info,keyinfo,key,anc_buff,lastkey,0) | 2;
}
if (save_flag && ret_value != 1)
ret_value|=_mi_write_keypage(info,keyinfo,page,anc_buff);
ret_value|=_mi_write_keypage(info,keyinfo,page,DFLT_INIT_HITS,anc_buff);
else
{
DBUG_DUMP("page",(byte*) anc_buff,mi_getint(anc_buff));
......@@ -398,7 +400,7 @@ static int del(register MI_INFO *info, register MI_KEYDEF *keyinfo, uchar *key,
if (!(next_buff= (uchar*) my_alloca((uint) keyinfo->block_length+
MI_MAX_KEY_BUFF*2)))
DBUG_RETURN(-1);
if (!_mi_fetch_keypage(info,keyinfo,next_page,next_buff,0))
if (!_mi_fetch_keypage(info,keyinfo,next_page,DFLT_INIT_HITS,next_buff,0))
ret_value= -1;
else
{
......@@ -426,7 +428,7 @@ static int del(register MI_INFO *info, register MI_KEYDEF *keyinfo, uchar *key,
(uchar*) 0,(uchar*) 0,(my_off_t) 0,0);
}
}
if (_mi_write_keypage(info,keyinfo,leaf_page,leaf_buff))
if (_mi_write_keypage(info,keyinfo,leaf_page,DFLT_INIT_HITS,leaf_buff))
goto err;
}
my_afree((byte*) next_buff);
......@@ -436,7 +438,7 @@ static int del(register MI_INFO *info, register MI_KEYDEF *keyinfo, uchar *key,
/* Remove last key from leaf page */
mi_putint(leaf_buff,key_start-leaf_buff,nod_flag);
if (_mi_write_keypage(info,keyinfo,leaf_page,leaf_buff))
if (_mi_write_keypage(info,keyinfo,leaf_page,DFLT_INIT_HITS,leaf_buff))
goto err;
/* Place last key in ancestor page on deleted key position */
......@@ -524,7 +526,7 @@ static int underflow(register MI_INFO *info, register MI_KEYDEF *keyinfo,
goto err;
}
next_page= _mi_kpos(key_reflength,next_keypos);
if (!_mi_fetch_keypage(info,keyinfo,next_page,buff,0))
if (!_mi_fetch_keypage(info,keyinfo,next_page,DFLT_INIT_HITS,buff,0))
goto err;
buff_length=mi_getint(buff);
DBUG_DUMP("next",(byte*) buff,buff_length);
......@@ -563,7 +565,7 @@ static int underflow(register MI_INFO *info, register MI_KEYDEF *keyinfo,
if (buff_length <= keyinfo->block_length)
{ /* Keys in one page */
memcpy((byte*) leaf_buff,(byte*) buff,(size_t) buff_length);
if (_mi_dispose(info,keyinfo,next_page))
if (_mi_dispose(info,keyinfo,next_page,DFLT_INIT_HITS))
goto err;
}
else
......@@ -612,10 +614,10 @@ static int underflow(register MI_INFO *info, register MI_KEYDEF *keyinfo,
(*keyinfo->store_key)(keyinfo,buff+p_length,&s_temp);
mi_putint(buff,length+t_length+p_length,nod_flag);
if (_mi_write_keypage(info,keyinfo,next_page,buff))
if (_mi_write_keypage(info,keyinfo,next_page,DFLT_INIT_HITS,buff))
goto err;
}
if (_mi_write_keypage(info,keyinfo,leaf_page,leaf_buff))
if (_mi_write_keypage(info,keyinfo,leaf_page,DFLT_INIT_HITS,leaf_buff))
goto err;
DBUG_RETURN(anc_length <= ((info->quick_mode ? MI_MIN_BLOCK_LENGTH :
(uint) keyinfo->underflow_block_length)));
......@@ -627,7 +629,7 @@ static int underflow(register MI_INFO *info, register MI_KEYDEF *keyinfo,
if (!keypos)
goto err;
next_page= _mi_kpos(key_reflength,keypos);
if (!_mi_fetch_keypage(info,keyinfo,next_page,buff,0))
if (!_mi_fetch_keypage(info,keyinfo,next_page,DFLT_INIT_HITS,buff,0))
goto err;
buff_length=mi_getint(buff);
endpos=buff+buff_length;
......@@ -671,7 +673,7 @@ static int underflow(register MI_INFO *info, register MI_KEYDEF *keyinfo,
if (buff_length <= keyinfo->block_length)
{ /* Keys in one page */
if (_mi_dispose(info,keyinfo,leaf_page))
if (_mi_dispose(info,keyinfo,leaf_page,DFLT_INIT_HITS))
goto err;
}
else
......@@ -718,11 +720,11 @@ static int underflow(register MI_INFO *info, register MI_KEYDEF *keyinfo,
(size_t) length);
(*keyinfo->store_key)(keyinfo,leaf_buff+p_length,&s_temp);
mi_putint(leaf_buff,length+t_length+p_length,nod_flag);
if (_mi_write_keypage(info,keyinfo,leaf_page,leaf_buff))
if (_mi_write_keypage(info,keyinfo,leaf_page,DFLT_INIT_HITS,leaf_buff))
goto err;
mi_putint(buff,endpos-buff,nod_flag);
}
if (_mi_write_keypage(info,keyinfo,next_page,buff))
if (_mi_write_keypage(info,keyinfo,next_page,DFLT_INIT_HITS,buff))
goto err;
DBUG_RETURN(anc_length <= (uint) keyinfo->block_length/2);
err:
......
......@@ -24,7 +24,8 @@
/* Fetch a key-page in memory */
uchar *_mi_fetch_keypage(register MI_INFO *info, MI_KEYDEF *keyinfo,
my_off_t page, uchar *buff, int return_buffer)
my_off_t page, int level,
uchar *buff, int return_buffer)
{
uchar *tmp;
uint page_size;
......@@ -32,7 +33,7 @@ uchar *_mi_fetch_keypage(register MI_INFO *info, MI_KEYDEF *keyinfo,
DBUG_PRINT("enter",("page: %ld",page));
tmp=(uchar*) key_cache_read(*info->s->keycache,
info->s->kfile,page,(byte*) buff,
info->s->kfile, page, level, (byte*) buff,
(uint) keyinfo->block_length,
(uint) keyinfo->block_length,
return_buffer);
......@@ -62,7 +63,7 @@ uchar *_mi_fetch_keypage(register MI_INFO *info, MI_KEYDEF *keyinfo,
/* Write a key-page on disk */
int _mi_write_keypage(register MI_INFO *info, register MI_KEYDEF *keyinfo,
my_off_t page, uchar *buff)
my_off_t page, int level, uchar *buff)
{
reg3 uint length;
DBUG_ENTER("_mi_write_keypage");
......@@ -94,7 +95,7 @@ int _mi_write_keypage(register MI_INFO *info, register MI_KEYDEF *keyinfo,
}
#endif
DBUG_RETURN((key_cache_write(*info->s->keycache,
info->s->kfile,page,(byte*) buff,length,
info->s->kfile,page, level, (byte*) buff,length,
(uint) keyinfo->block_length,
(int) ((info->lock_type != F_UNLCK) ||
info->s->delay_key_write))));
......@@ -103,7 +104,8 @@ int _mi_write_keypage(register MI_INFO *info, register MI_KEYDEF *keyinfo,
/* Remove page from disk */
int _mi_dispose(register MI_INFO *info, MI_KEYDEF *keyinfo, my_off_t pos)
int _mi_dispose(register MI_INFO *info, MI_KEYDEF *keyinfo, my_off_t pos,
int level)
{
my_off_t old_link;
char buff[8];
......@@ -115,7 +117,7 @@ int _mi_dispose(register MI_INFO *info, MI_KEYDEF *keyinfo, my_off_t pos)
mi_sizestore(buff,old_link);
info->s->state.changed|= STATE_NOT_SORTED_PAGES;
DBUG_RETURN(key_cache_write(*info->s->keycache,
info->s->kfile,pos,buff,
info->s->kfile, pos , level, buff,
sizeof(buff),
(uint) keyinfo->block_length,
(int) (info->lock_type != F_UNLCK)));
......@@ -124,7 +126,7 @@ int _mi_dispose(register MI_INFO *info, MI_KEYDEF *keyinfo, my_off_t pos)
/* Make new page on disk */
my_off_t _mi_new(register MI_INFO *info, MI_KEYDEF *keyinfo)
my_off_t _mi_new(register MI_INFO *info, MI_KEYDEF *keyinfo, int level)
{
my_off_t pos;
char buff[8];
......@@ -144,7 +146,7 @@ my_off_t _mi_new(register MI_INFO *info, MI_KEYDEF *keyinfo)
else
{
if (!key_cache_read(*info->s->keycache,
info->s->kfile,pos,
info->s->kfile, pos, level,
buff,
(uint) sizeof(buff),
(uint) keyinfo->block_length,0))
......
......@@ -88,7 +88,8 @@ int mi_preload(MI_INFO *info, ulonglong key_map, my_bool ignore_leaves)
if (mi_test_if_nod(buff))
{
if (key_cache_insert(*share->keycache,
share->kfile, pos, (byte*) buff, block_length))
share->kfile, pos, DFLT_INIT_HITS,
(byte*) buff, block_length))
goto err;
}
pos+= block_length;
......@@ -99,7 +100,8 @@ int mi_preload(MI_INFO *info, ulonglong key_map, my_bool ignore_leaves)
else
{
if (key_cache_insert(*share->keycache,
share->kfile, pos, (byte*) buff, length))
share->kfile, pos, DFLT_INIT_HITS,
(byte*) buff, length))
goto err;
pos+= length;
}
......
......@@ -142,7 +142,7 @@ static double _mi_search_pos(register MI_INFO *info,
if (pos == HA_OFFSET_ERROR)
DBUG_RETURN(0.5);
if (!(buff=_mi_fetch_keypage(info,keyinfo,pos,info->buff,1)))
if (!(buff=_mi_fetch_keypage(info,keyinfo,pos,DFLT_INIT_HITS,info->buff,1)))
goto err;
flag=(*keyinfo->bin_search)(info,keyinfo,buff,key,key_len,nextflag,
&keypos,info->lastkey, &after_key);
......
......@@ -76,7 +76,7 @@ int _mi_search(register MI_INFO *info, register MI_KEYDEF *keyinfo,
DBUG_RETURN(1); /* Search at upper levels */
}
if (!(buff=_mi_fetch_keypage(info,keyinfo,pos,info->buff,
if (!(buff=_mi_fetch_keypage(info,keyinfo,pos,DFLT_INIT_HITS,info->buff,
test(!(nextflag & SEARCH_SAVE_BUFF)))))
goto err;
DBUG_DUMP("page",(byte*) buff,mi_getint(buff));
......@@ -119,7 +119,7 @@ int _mi_search(register MI_INFO *info, register MI_KEYDEF *keyinfo,
if (pos != info->last_keypage)
{
uchar *old_buff=buff;
if (!(buff=_mi_fetch_keypage(info,keyinfo,pos,info->buff,
if (!(buff=_mi_fetch_keypage(info,keyinfo,pos,DFLT_INIT_HITS,info->buff,
test(!(nextflag & SEARCH_SAVE_BUFF)))))
goto err;
keypos=buff+(keypos-old_buff);
......@@ -1105,7 +1105,7 @@ int _mi_search_next(register MI_INFO *info, register MI_KEYDEF *keyinfo,
if (info->buff_used)
{
if (!_mi_fetch_keypage(info,keyinfo,info->last_search_keypage,
info->buff,0))
DFLT_INIT_HITS,info->buff,0))
DBUG_RETURN(-1);
info->buff_used=0;
}
......@@ -1174,7 +1174,7 @@ int _mi_search_first(register MI_INFO *info, register MI_KEYDEF *keyinfo,
do
{
if (!_mi_fetch_keypage(info,keyinfo,pos,info->buff,0))
if (!_mi_fetch_keypage(info,keyinfo,pos,DFLT_INIT_HITS,info->buff,0))
{
info->lastpos= HA_OFFSET_ERROR;
DBUG_RETURN(-1);
......@@ -1217,7 +1217,7 @@ int _mi_search_last(register MI_INFO *info, register MI_KEYDEF *keyinfo,
buff=info->buff;
do
{
if (!_mi_fetch_keypage(info,keyinfo,pos,buff,0))
if (!_mi_fetch_keypage(info,keyinfo,pos,DFLT_INIT_HITS,buff,0))
{
info->lastpos= HA_OFFSET_ERROR;
DBUG_RETURN(-1);
......
......@@ -275,7 +275,7 @@ int main(int argc, char *argv[])
goto end;
}
if (key_cacheing)
resize_key_cache(dflt_keycache,key_cache_size*2);
resize_key_cache(dflt_keycache,key_cache_block_size,key_cache_size*2);
}
if (!silent)
......
......@@ -290,8 +290,8 @@ int _mi_enlarge_root(MI_INFO *info, MI_KEYDEF *keyinfo, uchar *key,
mi_putint(info->buff,t_length+2+nod_flag,nod_flag);
(*keyinfo->store_key)(keyinfo,info->buff+2+nod_flag,&s_temp);
info->buff_used=info->page_changed=1; /* info->buff is used */
if ((*root= _mi_new(info,keyinfo)) == HA_OFFSET_ERROR ||
_mi_write_keypage(info,keyinfo,*root,info->buff))
if ((*root= _mi_new(info,keyinfo,DFLT_INIT_HITS)) == HA_OFFSET_ERROR ||
_mi_write_keypage(info,keyinfo,*root,DFLT_INIT_HITS,info->buff))
DBUG_RETURN(-1);
DBUG_RETURN(0);
} /* _mi_enlarge_root */
......@@ -322,7 +322,7 @@ static int w_search(register MI_INFO *info, register MI_KEYDEF *keyinfo,
if (!(temp_buff= (uchar*) my_alloca((uint) keyinfo->block_length+
MI_MAX_KEY_BUFF*2)))
DBUG_RETURN(-1);
if (!_mi_fetch_keypage(info,keyinfo,page,temp_buff,0))
if (!_mi_fetch_keypage(info,keyinfo,page,DFLT_INIT_HITS,temp_buff,0))
goto err;
flag=(*keyinfo->bin_search)(info,keyinfo,temp_buff,key,search_key_length,
......@@ -368,7 +368,7 @@ static int w_search(register MI_INFO *info, register MI_KEYDEF *keyinfo,
subkeys--; /* should there be underflow protection ? */
ft_intXstore(keypos, subkeys);
if (!error)
error=_mi_write_keypage(info,keyinfo,page,temp_buff);
error=_mi_write_keypage(info,keyinfo,page,DFLT_INIT_HITS,temp_buff);
my_afree((byte*) temp_buff);
DBUG_RETURN(error);
}
......@@ -391,7 +391,7 @@ static int w_search(register MI_INFO *info, register MI_KEYDEF *keyinfo,
{
error=_mi_insert(info,keyinfo,key,temp_buff,keypos,keybuff,father_buff,
father_keypos,father_page, insert_last);
if (_mi_write_keypage(info,keyinfo,page,temp_buff))
if (_mi_write_keypage(info,keyinfo,page,DFLT_INIT_HITS,temp_buff))
goto err;
}
my_afree((byte*) temp_buff);
......@@ -515,7 +515,7 @@ int _mi_split_page(register MI_INFO *info, register MI_KEYDEF *keyinfo,
}
/* Move middle item to key and pointer to new page */
if ((new_pos=_mi_new(info,keyinfo)) == HA_OFFSET_ERROR)
if ((new_pos=_mi_new(info,keyinfo,DFLT_INIT_HITS)) == HA_OFFSET_ERROR)
DBUG_RETURN(-1);
_mi_kpointer(info,_mi_move_key(keyinfo,key,key_buff),new_pos);
......@@ -531,7 +531,7 @@ int _mi_split_page(register MI_INFO *info, register MI_KEYDEF *keyinfo,
(*keyinfo->store_key)(keyinfo,info->buff+key_ref_length,&s_temp);
mi_putint(info->buff,length+t_length+key_ref_length,nod_flag);
if (_mi_write_keypage(info,keyinfo,new_pos,info->buff))
if (_mi_write_keypage(info,keyinfo,new_pos,DFLT_INIT_HITS,info->buff))
DBUG_RETURN(-1);
DBUG_DUMP("key",(byte*) key,_mi_keylength(keyinfo,key));
DBUG_RETURN(2); /* Middle key up */
......@@ -682,7 +682,7 @@ static int _mi_balance_page(register MI_INFO *info, MI_KEYDEF *keyinfo,
DBUG_PRINT("test",("use left page: %lu",next_page));
} /* father_key_pos ptr to parting key */
if (!_mi_fetch_keypage(info,keyinfo,next_page,info->buff,0))
if (!_mi_fetch_keypage(info,keyinfo,next_page,DFLT_INIT_HITS,info->buff,0))
goto err;
DBUG_DUMP("next",(byte*) info->buff,mi_getint(info->buff));
......@@ -722,8 +722,8 @@ static int _mi_balance_page(register MI_INFO *info, MI_KEYDEF *keyinfo,
memcpy((byte*) buff+2,(byte*) pos+k_length,(size_t) length);
}
if (_mi_write_keypage(info,keyinfo,next_page,info->buff) ||
_mi_write_keypage(info,keyinfo,father_page,father_buff))
if (_mi_write_keypage(info,keyinfo,next_page,DFLT_INIT_HITS,info->buff) ||
_mi_write_keypage(info,keyinfo,father_page,DFLT_INIT_HITS,father_buff))
goto err;
DBUG_RETURN(0);
}
......@@ -763,12 +763,13 @@ static int _mi_balance_page(register MI_INFO *info, MI_KEYDEF *keyinfo,
memcpy((byte*) (right ? key : father_key_pos),pos,(size_t) k_length);
memcpy((byte*) (right ? father_key_pos : key),tmp_part_key, k_length);
if ((new_pos=_mi_new(info,keyinfo)) == HA_OFFSET_ERROR)
if ((new_pos=_mi_new(info,keyinfo,DFLT_INIT_HITS)) == HA_OFFSET_ERROR)
goto err;
_mi_kpointer(info,key+k_length,new_pos);
if (_mi_write_keypage(info,keyinfo,(right ? new_pos : next_page),
info->buff) ||
_mi_write_keypage(info,keyinfo,(right ? next_page : new_pos),extra_buff))
DFLT_INIT_HITS,info->buff) ||
_mi_write_keypage(info,keyinfo,(right ? next_page : new_pos),
DFLT_INIT_HITS,extra_buff))
goto err;
DBUG_RETURN(1); /* Middle key up */
......
......@@ -548,11 +548,12 @@ extern int _mi_search_next(MI_INFO *info,MI_KEYDEF *keyinfo,uchar *key,
extern int _mi_search_first(MI_INFO *info,MI_KEYDEF *keyinfo,my_off_t pos);
extern int _mi_search_last(MI_INFO *info,MI_KEYDEF *keyinfo,my_off_t pos);
extern uchar *_mi_fetch_keypage(MI_INFO *info,MI_KEYDEF *keyinfo,my_off_t page,
uchar *buff,int return_buffer);
int level,uchar *buff,int return_buffer);
extern int _mi_write_keypage(MI_INFO *info,MI_KEYDEF *keyinfo,my_off_t page,
uchar *buff);
extern int _mi_dispose(MI_INFO *info,MI_KEYDEF *keyinfo,my_off_t pos);
extern my_off_t _mi_new(MI_INFO *info,MI_KEYDEF *keyinfo);
int level, uchar *buff);
extern int _mi_dispose(MI_INFO *info,MI_KEYDEF *keyinfo,my_off_t pos,
int level);
extern my_off_t _mi_new(MI_INFO *info,MI_KEYDEF *keyinfo,int level);
extern uint _mi_make_key(MI_INFO *info,uint keynr,uchar *key,
const byte *record,my_off_t filepos);
extern uint _mi_pack_key(MI_INFO *info,uint keynr,uchar *key,uchar *old,
......
......@@ -60,7 +60,7 @@ static int rtree_find_req(MI_INFO *info, MI_KEYDEF *keyinfo, uint search_flag, u
my_errno = HA_ERR_OUT_OF_MEM;
return -1;
}
if (!_mi_fetch_keypage(info, keyinfo, page, page_buf, 0))
if (!_mi_fetch_keypage(info, keyinfo, page, DFLT_INIT_HITS, page_buf, 0))
goto err1;
nod_flag = mi_test_if_nod(page_buf);
......@@ -257,7 +257,7 @@ static int rtree_get_req(MI_INFO *info, MI_KEYDEF *keyinfo, uint key_length,
if (!(page_buf = (uchar*)my_alloca((uint)keyinfo->block_length)))
return -1;
if (!_mi_fetch_keypage(info, keyinfo, page, page_buf, 0))
if (!_mi_fetch_keypage(info, keyinfo, page, DFLT_INIT_HITS, page_buf, 0))
goto err1;
nod_flag = mi_test_if_nod(page_buf);
......@@ -429,7 +429,7 @@ static int rtree_insert_req(MI_INFO *info, MI_KEYDEF *keyinfo, uchar *key,
my_errno = HA_ERR_OUT_OF_MEM;
return -1;
}
if (!_mi_fetch_keypage(info, keyinfo, page, page_buf, 0))
if (!_mi_fetch_keypage(info, keyinfo, page, DFLT_INIT_HITS, page_buf, 0))
goto err1;
nod_flag = mi_test_if_nod(page_buf);
......@@ -445,7 +445,7 @@ static int rtree_insert_req(MI_INFO *info, MI_KEYDEF *keyinfo, uchar *key,
case 0: /* child was not split */
{
rtree_combine_rect(keyinfo->seg, k, key, k, key_length);
if (_mi_write_keypage(info, keyinfo, page, page_buf))
if (_mi_write_keypage(info, keyinfo, page, DFLT_INIT_HITS, page_buf))
goto err1;
goto ok;
}
......@@ -462,7 +462,7 @@ static int rtree_insert_req(MI_INFO *info, MI_KEYDEF *keyinfo, uchar *key,
goto err1;
res = rtree_add_key(info, keyinfo, new_key, key_length,
page_buf, new_page);
if (_mi_write_keypage(info, keyinfo, page, page_buf))
if (_mi_write_keypage(info, keyinfo, page, DFLT_INIT_HITS, page_buf))
goto err1;
goto ok;
}
......@@ -476,7 +476,7 @@ static int rtree_insert_req(MI_INFO *info, MI_KEYDEF *keyinfo, uchar *key,
else
{
res = rtree_add_key(info, keyinfo, key, key_length, page_buf, new_page);
if (_mi_write_keypage(info, keyinfo, page, page_buf))
if (_mi_write_keypage(info, keyinfo, page, DFLT_INIT_HITS, page_buf))
goto err1;
goto ok;
}
......@@ -509,12 +509,12 @@ static int rtree_insert_level(MI_INFO *info, uint keynr, uchar *key,
{
int res;
if ((old_root = _mi_new(info, keyinfo)) == HA_OFFSET_ERROR)
if ((old_root = _mi_new(info, keyinfo, DFLT_INIT_HITS)) == HA_OFFSET_ERROR)
return -1;
info->buff_used = 1;
mi_putint(info->buff, 2, 0);
res = rtree_add_key(info, keyinfo, key, key_length, info->buff, NULL);
if (_mi_write_keypage(info, keyinfo, old_root, info->buff))
if (_mi_write_keypage(info, keyinfo, old_root, DFLT_INIT_HITS, info->buff))
return 1;
info->s->state.key_root[keynr] = old_root;
return res;
......@@ -542,7 +542,8 @@ static int rtree_insert_level(MI_INFO *info, uint keynr, uchar *key,
}
mi_putint(new_root_buf, 2, nod_flag);
if ((new_root = _mi_new(info, keyinfo)) == HA_OFFSET_ERROR)
if ((new_root = _mi_new(info, keyinfo, DFLT_INIT_HITS)) ==
HA_OFFSET_ERROR)
goto err1;
new_key = new_root_buf + keyinfo->block_length + nod_flag;
......@@ -559,7 +560,8 @@ static int rtree_insert_level(MI_INFO *info, uint keynr, uchar *key,
if (rtree_add_key(info, keyinfo, new_key, key_length, new_root_buf, NULL)
== -1)
goto err1;
if (_mi_write_keypage(info, keyinfo, new_root, new_root_buf))
if (_mi_write_keypage(info, keyinfo, new_root,
DFLT_INIT_HITS, new_root_buf))
goto err1;
info->s->state.key_root[keynr] = new_root;
......@@ -636,7 +638,7 @@ static int rtree_delete_req(MI_INFO *info, MI_KEYDEF *keyinfo, uchar *key,
my_errno = HA_ERR_OUT_OF_MEM;
return -1;
}
if (!_mi_fetch_keypage(info, keyinfo, page, page_buf, 0))
if (!_mi_fetch_keypage(info, keyinfo, page, DFLT_INIT_HITS, page_buf, 0))
goto err1;
nod_flag = mi_test_if_nod(page_buf);
......@@ -662,7 +664,8 @@ static int rtree_delete_req(MI_INFO *info, MI_KEYDEF *keyinfo, uchar *key,
if (rtree_set_key_mbr(info, keyinfo, k, key_length,
_mi_kpos(nod_flag, k)))
goto err1;
if (_mi_write_keypage(info, keyinfo, page, page_buf))
if (_mi_write_keypage(info, keyinfo, page,
DFLT_INIT_HITS, page_buf))
goto err1;
}
else
......@@ -672,7 +675,8 @@ static int rtree_delete_req(MI_INFO *info, MI_KEYDEF *keyinfo, uchar *key,
level + 1))
goto err1;
rtree_delete_key(info, page_buf, k, key_length, nod_flag);
if (_mi_write_keypage(info, keyinfo, page, page_buf))
if (_mi_write_keypage(info, keyinfo, page,
DFLT_INIT_HITS, page_buf))
goto err1;
*page_size = mi_getint(page_buf);
}
......@@ -686,7 +690,8 @@ static int rtree_delete_req(MI_INFO *info, MI_KEYDEF *keyinfo, uchar *key,
case 2: /* vacuous case: last key in the leaf */
{
rtree_delete_key(info, page_buf, k, key_length, nod_flag);
if (_mi_write_keypage(info, keyinfo, page, page_buf))
if (_mi_write_keypage(info, keyinfo, page,
DFLT_INIT_HITS, page_buf))
goto err1;
*page_size = mi_getint(page_buf);
res = 0;
......@@ -711,13 +716,13 @@ static int rtree_delete_req(MI_INFO *info, MI_KEYDEF *keyinfo, uchar *key,
{
/* last key in the leaf */
res = 2;
if (_mi_dispose(info, keyinfo, page))
if (_mi_dispose(info, keyinfo, page, DFLT_INIT_HITS))
goto err1;
}
else
{
res = 0;
if (_mi_write_keypage(info, keyinfo, page, page_buf))
if (_mi_write_keypage(info, keyinfo, page, DFLT_INIT_HITS, page_buf))
goto err1;
}
goto ok;
......@@ -783,7 +788,7 @@ int rtree_delete(MI_INFO *info, uint keynr, uchar *key, uint key_length)
goto err1;
}
if (!_mi_fetch_keypage(info, keyinfo, ReinsertList.pages[i].offs,
page_buf, 0))
DFLT_INIT_HITS, page_buf, 0))
goto err1;
nod_flag = mi_test_if_nod(page_buf);
k = rt_PAGE_FIRST_KEY(page_buf, nod_flag);
......@@ -798,7 +803,8 @@ int rtree_delete(MI_INFO *info, uint keynr, uchar *key, uint key_length)
}
}
my_afree((byte*)page_buf);
if (_mi_dispose(info, keyinfo, ReinsertList.pages[i].offs))
if (_mi_dispose(info, keyinfo, ReinsertList.pages[i].offs,
DFLT_INIT_HITS))
goto err1;
}
if (ReinsertList.pages)
......@@ -807,7 +813,8 @@ int rtree_delete(MI_INFO *info, uint keynr, uchar *key, uint key_length)
/* check for redundant root (not leaf, 1 child) and eliminate */
if ((old_root = info->s->state.key_root[keynr]) == HA_OFFSET_ERROR)
goto err1;
if (!_mi_fetch_keypage(info, keyinfo, old_root, info->buff, 0))
if (!_mi_fetch_keypage(info, keyinfo, old_root, DFLT_INIT_HITS,
info->buff, 0))
goto err1;
nod_flag = mi_test_if_nod(info->buff);
page_size = mi_getint(info->buff);
......@@ -816,7 +823,7 @@ int rtree_delete(MI_INFO *info, uint keynr, uchar *key, uint key_length)
{
my_off_t new_root = _mi_kpos(nod_flag,
rt_PAGE_FIRST_KEY(info->buff, nod_flag));
if (_mi_dispose(info, keyinfo, old_root))
if (_mi_dispose(info, keyinfo, old_root, DFLT_INIT_HITS))
goto err1;
info->s->state.key_root[keynr] = new_root;
}
......@@ -863,7 +870,7 @@ ha_rows rtree_estimate(MI_INFO *info, uint keynr, uchar *key,
return HA_POS_ERROR;
if (!(page_buf = (uchar*)my_alloca((uint)keyinfo->block_length)))
return HA_POS_ERROR;
if (!_mi_fetch_keypage(info, keyinfo, root, page_buf, 0))
if (!_mi_fetch_keypage(info, keyinfo, root, DFLT_INIT_HITS, page_buf, 0))
goto err1;
nod_flag = mi_test_if_nod(page_buf);
......
......@@ -88,7 +88,8 @@ int rtree_delete_key(MI_INFO *info, uchar *page_buf, uchar *key,
int rtree_set_key_mbr(MI_INFO *info, MI_KEYDEF *keyinfo, uchar *key,
uint key_length, my_off_t child_page)
{
if (!_mi_fetch_keypage(info, keyinfo, child_page, info->buff, 0))
if (!_mi_fetch_keypage(info, keyinfo, child_page,
DFLT_INIT_HITS, info->buff, 0))
return -1;
return rtree_page_mbr(info, keyinfo->seg, info->buff, key, key_length);
......
......@@ -332,10 +332,12 @@ int rtree_split_page(MI_INFO *info, MI_KEYDEF *keyinfo, uchar *page, uchar *key,
mi_putint(page, 2 + n1 * full_length, nod_flag);
mi_putint(new_page, 2 + n2 * full_length, nod_flag);
if ((*new_page_offs= _mi_new(info, keyinfo)) == HA_OFFSET_ERROR)
if ((*new_page_offs= _mi_new(info, keyinfo, DFLT_INIT_HITS)) ==
HA_OFFSET_ERROR)
err_code= -1;
else
err_code= _mi_write_keypage(info, keyinfo, *new_page_offs, new_page);
err_code= _mi_write_keypage(info, keyinfo, *new_page_offs,
DFLT_INIT_HITS, new_page);
my_afree((byte*)new_page);
......
......@@ -135,12 +135,14 @@ typedef struct st_block_link
uint offset; /* beginning of modified data in the buffer */
uint length; /* end of data in the buffer */
uint status; /* state of the block */
uint hits_left; /* number of hits left until promotion */
ulonglong last_hit_time; /* timestamp of the last hit */
KEYCACHE_CONDVAR *condvar; /* condition variable for 'no readers' event */
} BLOCK_LINK;
KEY_CACHE_VAR dflt_key_cache_var=
{
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
};
KEY_CACHE_HANDLE *dflt_keycache= &dflt_key_cache_var.cache;
......@@ -154,12 +156,16 @@ typedef struct st_key_cache
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 */
ulong min_warm_blocks; /* min number of warm blocks; */
ulong age_threshold; /* age threshold for hot blocks */
ulonglong keycache_time; /* total number of block link operations */
uint hash_entries; /* max number of entries in the hash table */
int hash_links; /* max number of hash links */
int hash_links_used; /* number of hash links currently used */
int disk_blocks; /* max number of blocks in the 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 */
#if defined(KEYCACHE_DEBUG)
long blocks_available; /* number of blocks available in the LRU chain */
#endif
......@@ -169,6 +175,7 @@ typedef struct st_key_cache
BLOCK_LINK *block_root; /* memory for block links */
byte HUGE_PTR *block_mem; /* memory for block buffers */
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 waiting_for_hash_link; /* waiting for a free hash link */
KEYCACHE_WQUEUE waiting_for_block; /* requests waiting for a free block */
......@@ -272,8 +279,27 @@ static uint next_power(uint value)
/*
Initialize the key cache,
return number of blocks in it
Initialize a key cache
SYNOPSIS
init_ky_cache()
pkeycache in/out pointer to the key cache handle
key_cache_block_size size of blocks to keep cached data
use_mem total memory to use for the key cache
env ref to other parameters of the key cache, if any
RETURN VALUE
number of blocks in the key cache, if successful,
0 - otherwise.
NOTES.
If pkeycache points to an undefined handle (NULL), a new KEY_CACHE
data structure is created and a pointer to it is returned as a new
key cache handle, otherwise *pkeycache is considered as a reused
handle for a key cache with new blocks.
It's assumed that no two threads call this function simultaneously
referring to the same key cache handle.
*/
int init_key_cache(KEY_CACHE_HANDLE *pkeycache, uint key_cache_block_size,
......@@ -312,36 +338,46 @@ int init_key_cache(KEY_CACHE_HANDLE *pkeycache, uint key_cache_block_size,
keycache->key_cache_shift= my_bit_log2(key_cache_block_size);
keycache->key_cache_mem_size= use_mem;
keycache->key_cache_block_size= key_cache_block_size;
DBUG_PRINT("info",("key_cache_block_size: %u",
DBUG_PRINT("info", ("key_cache_block_size: %u",
key_cache_block_size));
}
/*
These are safety deallocations: actually we always call the
function after having called end_key_cache that deallocates
these memory itself.
*/
if (keycache->block_mem)
my_free_lock((gptr) keycache->block_mem, MYF(0));
keycache->block_mem= NULL;
if (keycache->block_root)
my_free((gptr) keycache->block_root, MYF(0));
keycache->block_root= NULL;
blocks= (uint) (use_mem/(sizeof(BLOCK_LINK)+2*sizeof(HASH_LINK)+
sizeof(HASH_LINK*)*5/4+key_cache_block_size));
blocks= (uint) (use_mem / (sizeof(BLOCK_LINK) + 2 * sizeof(HASH_LINK) +
sizeof(HASH_LINK*) * 5/4 + key_cache_block_size));
/* It doesn't make sense to have too few blocks (less than 8) */
if (blocks >= 8 && keycache->disk_blocks < 0)
{
for (;;)
for ( ; ; )
{
/* Set my_hash_entries to the next bigger 2 power */
if ((keycache->hash_entries= next_power(blocks)) < blocks*5/4)
if ((keycache->hash_entries= next_power(blocks)) < blocks * 5/4)
keycache->hash_entries<<= 1;
hash_links= 2*blocks;
hash_links= 2 * blocks;
#if defined(MAX_THREADS)
if (hash_links < MAX_THREADS + blocks - 1)
hash_links=MAX_THREADS + blocks - 1;
hash_links= MAX_THREADS + blocks - 1;
#endif
while ((length=(ALIGN_SIZE(blocks*sizeof(BLOCK_LINK))+
ALIGN_SIZE(hash_links*sizeof(HASH_LINK))+
ALIGN_SIZE(sizeof(HASH_LINK*)*keycache->hash_entries)))+
while ((length= (ALIGN_SIZE(blocks * sizeof(BLOCK_LINK)) +
ALIGN_SIZE(hash_links * sizeof(HASH_LINK)) +
ALIGN_SIZE(sizeof(HASH_LINK*) *
keycache->hash_entries))) +
((ulong) blocks << keycache->key_cache_shift) > use_mem)
blocks--;
/* Allocate memory for cache page buffers */
if ((keycache->block_mem=
my_malloc_lock((ulong) blocks*keycache->key_cache_block_size,
my_malloc_lock((ulong) blocks * keycache->key_cache_block_size,
MYF(0))))
{
/*
......@@ -358,7 +394,7 @@ int init_key_cache(KEY_CACHE_HANDLE *pkeycache, uint key_cache_block_size,
my_errno= ENOMEM;
goto err;
}
blocks= blocks/4*3;
blocks= blocks / 4*3;
}
keycache->disk_blocks= (int) blocks;
keycache->hash_links= hash_links;
......@@ -368,11 +404,11 @@ int init_key_cache(KEY_CACHE_HANDLE *pkeycache, uint key_cache_block_size,
ALIGN_SIZE((sizeof(HASH_LINK*) *
keycache->hash_entries)));
bzero((byte*) keycache->block_root,
keycache->disk_blocks*sizeof(BLOCK_LINK));
keycache->disk_blocks * sizeof(BLOCK_LINK));
bzero((byte*) keycache->hash_root,
keycache->hash_entries*sizeof(HASH_LINK*));
keycache->hash_entries * sizeof(HASH_LINK*));
bzero((byte*) keycache->hash_link_root,
keycache->hash_links*sizeof(HASH_LINK));
keycache->hash_links * sizeof(HASH_LINK));
keycache->hash_links_used= 0;
keycache->free_hash_list= NULL;
keycache->blocks_used= keycache->blocks_changed= 0;
......@@ -382,7 +418,16 @@ int init_key_cache(KEY_CACHE_HANDLE *pkeycache, uint key_cache_block_size,
keycache->blocks_available=0;
#endif
/* The LRU chain is empty after initialization */
keycache->used_last=NULL;
keycache->used_last= NULL;
keycache->used_ins= NULL;
keycache->keycache_time= 0;
keycache->warm_blocks= 0;
keycache->min_warm_blocks= env && env->division_limit ?
blocks * env->division_limit / 100 + 1 :
blocks;
keycache->age_threshold= env || env->age_threshold ?
blocks * env->age_threshold / 100 :
blocks;
keycache->waiting_for_hash_link.last_thread= NULL;
keycache->waiting_for_block.last_thread= NULL;
......@@ -394,9 +439,9 @@ int init_key_cache(KEY_CACHE_HANDLE *pkeycache, uint key_cache_block_size,
keycache->hash_links, keycache->hash_link_root));
}
bzero((gptr) keycache->changed_blocks,
sizeof(keycache->changed_blocks[0])*CHANGED_BLOCKS_HASH);
sizeof(keycache->changed_blocks[0]) * CHANGED_BLOCKS_HASH);
bzero((gptr) keycache->file_blocks,
sizeof(keycache->file_blocks[0])*CHANGED_BLOCKS_HASH);
sizeof(keycache->file_blocks[0]) * CHANGED_BLOCKS_HASH);
if (env)
env->blocks= keycache->disk_blocks > 0 ? keycache->disk_blocks : 0;
......@@ -405,25 +450,55 @@ int init_key_cache(KEY_CACHE_HANDLE *pkeycache, uint key_cache_block_size,
err:
error= my_errno;
keycache->disk_blocks= 0;
if (env)
env->blocks= 0;
if (keycache->block_mem)
{
my_free_lock((gptr) keycache->block_mem, MYF(0));
if (keycache->block_mem)
keycache->block_mem= NULL;
}
if (keycache->block_root)
{
my_free((gptr) keycache->block_root, MYF(0));
if (*pkeycache)
my_free((gptr) keycache, MYF(0));
keycache->block_root= NULL;
}
my_errno= error;
DBUG_RETURN(0);
}
/*
Resize the key cache
Resize a key cache
SYNOPSIS
resize_key_cache()
pkeycache in/out pointer to the key cache handle
key_cache_block_size size of blocks to keep cached data
use_mem total memory to use for the new key cache
RETURN VALUE
number of blocks in the key cache, if successful,
0 - otherwise.
NOTES.
The function first compares the memory size and the block size parameters
with the corresponding parameters of the key cache referred by
*pkeycache. If they differ the function free the the memory allocated
for the 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.
*/
int resize_key_cache(KEY_CACHE_HANDLE *pkeycache, ulong use_mem)
int resize_key_cache(KEY_CACHE_HANDLE *pkeycache, uint key_cache_block_size,
ulong use_mem)
{
int blocks;
KEY_CACHE *keycache= *pkeycache;
if (key_cache_block_size == keycache->key_cache_block_size &&
use_mem == keycache->key_cache_mem_size)
return keycache->disk_blocks;
keycache_pthread_mutex_lock(&keycache->cache_lock);
if (flush_all_key_blocks(keycache))
{
......@@ -434,14 +509,59 @@ int resize_key_cache(KEY_CACHE_HANDLE *pkeycache, ulong use_mem)
keycache_pthread_mutex_unlock(&keycache->cache_lock);
end_key_cache(pkeycache, 0);
/* the following will work even if memory is 0 */
blocks=init_key_cache(pkeycache, keycache->key_cache_block_size, use_mem,
blocks=init_key_cache(pkeycache, key_cache_block_size, use_mem,
keycache->env);
return blocks;
}
/*
Change the key cache parameters
SYNOPSIS
change_key_cache_param()
keycache the key cache handle
RETURN VALUE
none
NOTES.
Presently the function resets the key cache parameters
concerning midpoint insertion strategy - division_limit and
age_threshold. It corresponding values are passed through
the keycache->env structure.
*/
void change_key_cache_param(KEY_CACHE_HANDLE keycache)
{
KEY_CACHE_VAR *env= keycache->env;
if (!env)
return;
if (env->division_limit)
keycache->min_warm_blocks= keycache->disk_blocks *
env->division_limit / 100 + 1;
if (env->age_threshold)
keycache->age_threshold= keycache->disk_blocks *
env->age_threshold / 100;
}
/*
Remove key_cache from memory
SYNOPSIS
end_key_cache()
pkeycache in/out pointer to the key cache handle
cleanup <-> the key cache data structure is freed as well
RETURN VALUE
none
NOTES.
If the cleanup parameter is TRUE the data structure with all associated
elements are freed completely and NULL is assigned to *pkeycache.
Otherwise only memory used by the key cache blocks is freed.
*/
void end_key_cache(KEY_CACHE_HANDLE *pkeycache, my_bool cleanup)
......@@ -454,12 +574,14 @@ void end_key_cache(KEY_CACHE_HANDLE *pkeycache, my_bool cleanup)
if (keycache->block_mem)
{
my_free_lock((gptr) keycache->block_mem, MYF(0));
keycache->block_mem= NULL;
my_free((gptr) keycache->block_root, MYF(0));
keycache->block_root= NULL;
}
keycache->disk_blocks= -1;
}
KEYCACHE_DEBUG_CLOSE;
keycache->key_cache_inited=0;
keycache->key_cache_inited= 0;
if (env)
DBUG_PRINT("status",
("used: %d changed: %d w_requests: %ld \
......@@ -470,7 +592,7 @@ void end_key_cache(KEY_CACHE_HANDLE *pkeycache, my_bool cleanup)
if (cleanup)
{
pthread_mutex_destroy(&keycache->cache_lock);
my_free(*pkeycache, MYF(0));
my_free((gptr) *pkeycache, MYF(0));
*pkeycache= NULL;
}
DBUG_VOID_RETURN;
......@@ -478,31 +600,55 @@ void end_key_cache(KEY_CACHE_HANDLE *pkeycache, my_bool cleanup)
/*
Link a thread into double-linked queue of waiting threads
Link a thread into double-linked queue of waiting threads.
SYNOPSIS
link_into_queue()
wqueue pointer to the queue structure
thread pointer to the thread to be added to the queue
RETURN VALUE
none
NOTES.
Queue is represented by a circular list of the thread structures
The list is double-linked of the type (**prev,*next), accessed by
a pointer to the last element.
*/
static inline void link_into_queue(KEYCACHE_WQUEUE *wqueue,
struct st_my_thread_var *thread)
{
struct st_my_thread_var *last;
if (! (last=wqueue->last_thread))
if (! (last= wqueue->last_thread))
{
/* Queue is empty */
thread->next=thread;
thread->prev=&thread->next;
thread->next= thread;
thread->prev= &thread->next;
}
else
{
thread->prev=last->next->prev;
last->next->prev=&thread->next;
thread->next=last->next;
last->next=thread;
thread->prev= last->next->prev;
last->next->prev= &thread->next;
thread->next= last->next;
last->next= thread;
}
wqueue->last_thread=thread;
wqueue->last_thread= thread;
}
/*
Unlink a thread from double-linked queue of waiting threads
SYNOPSIS
unlink_from_queue()
wqueue pointer to the queue structure
thread pointer to the thread to be removed from the queue
RETURN VALUE
none
NOTES.
See NOTES for link_into_queue
*/
static inline void unlink_from_queue(KEYCACHE_WQUEUE *wqueue,
......@@ -511,40 +657,66 @@ static inline void unlink_from_queue(KEYCACHE_WQUEUE *wqueue,
KEYCACHE_DBUG_PRINT("unlink_from_queue", ("thread %ld", thread->id));
if (thread->next == thread)
/* The queue contains only one member */
wqueue->last_thread=NULL;
wqueue->last_thread= NULL;
else
{
thread->next->prev=thread->prev;
thread->next->prev= thread->prev;
*thread->prev=thread->next;
if (wqueue->last_thread == thread)
wqueue->last_thread=STRUCT_PTR(struct st_my_thread_var, next,
wqueue->last_thread= STRUCT_PTR(struct st_my_thread_var, next,
thread->prev);
}
thread->next=NULL;
thread->next= NULL;
}
/*
Add a thread to single-linked queue of waiting threads
SYNOPSIS
add_to_queue()
wqueue pointer to the queue structure
thread pointer to the thread to be added to the queue
RETURN VALUE
none
NOTES.
Queue is represented by a circular list of the thread structures
The list is single-linked of the type (*next), accessed by a pointer
to the last element.
*/
static inline void add_to_queue(KEYCACHE_WQUEUE *wqueue,
struct st_my_thread_var *thread)
{
struct st_my_thread_var *last;
if (! (last=wqueue->last_thread))
thread->next=thread;
if (! (last= wqueue->last_thread))
thread->next= thread;
else
{
thread->next=last->next;
last->next=thread;
thread->next= last->next;
last->next= thread;
}
wqueue->last_thread=thread;
wqueue->last_thread= thread;
}
/*
Remove all threads from queue signaling them to proceed
SYNOPSIS
realease_queue()
wqueue pointer to the queue structure
thread pointer to the thread to be added to the queue
RETURN VALUE
none
NOTES.
See notes for add_to_queue
When removed from the queue each thread is signaled via condition
variable thread->suspend.
*/
static void release_queue(KEYCACHE_WQUEUE *wqueue)
......@@ -558,10 +730,10 @@ static void release_queue(KEYCACHE_WQUEUE *wqueue)
keycache_pthread_cond_signal(&thread->suspend);
KEYCACHE_DBUG_PRINT("release_queue: signal", ("thread %ld", thread->id));
next=thread->next;
thread->next=NULL;
thread->next= NULL;
}
while (thread != last);
wqueue->last_thread=NULL;
wqueue->last_thread= NULL;
}
......@@ -572,8 +744,8 @@ static void release_queue(KEYCACHE_WQUEUE *wqueue)
static inline void unlink_changed(BLOCK_LINK *block)
{
if (block->next_changed)
block->next_changed->prev_changed=block->prev_changed;
*block->prev_changed=block->next_changed;
block->next_changed->prev_changed= block->prev_changed;
*block->prev_changed= block->next_changed;
}
......@@ -583,10 +755,10 @@ static inline void unlink_changed(BLOCK_LINK *block)
static inline void link_changed(BLOCK_LINK *block, BLOCK_LINK **phead)
{
block->prev_changed=phead;
if ((block->next_changed=*phead))
block->prev_changed= phead;
if ((block->next_changed= *phead))
(*phead)->prev_changed= &block->next_changed;
*phead=block;
*phead= block;
}
......@@ -600,10 +772,10 @@ static void link_to_file_list(KEY_CACHE *keycache,
{
if (unlink)
unlink_changed(block);
link_changed(block,&keycache->file_blocks[FILE_HASH(file)]);
link_changed(block, &keycache->file_blocks[FILE_HASH(file)]);
if (block->status & BLOCK_CHANGED)
{
block->status&=~BLOCK_CHANGED;
block->status&= ~BLOCK_CHANGED;
keycache->blocks_changed--;
if (keycache->env)
keycache->env->blocks_changed--;
......@@ -630,14 +802,50 @@ static inline void link_to_changed_list(KEY_CACHE *keycache,
/*
Link a block to the LRU chain at the beginning or at the end
Link a block to the LRU chain at the beginning or at the end of
one of two parts.
SYNOPSIS
link_block()
keycache pointer to a key cache data structure
block pointer to the block to link to the LRU chain
hot <-> to link the block into the hot subchain
at_end <-> to link the block at the end of the subchain
RETURN VALUE
none
NOTES.
The LRU chain is represented by a curcular list of block structures.
The list is double-linked of the type (**prev,*next) type.
The LRU chain is divided into two parts - hot and warm.
There are two pointers to access the last blocks of these two
parts. The beginning of the warm part follows right after the
end of the hot part.
Only blocks of the warm part can be used for replacement.
The first block from the beginning of this subchain is always
taken for eviction (keycache->last_used->next)
LRU chain: +------+ H O T +------+
+----| end |----...<----| beg |----+
| +------+last +------+ |
v<-link in latest hot (new end) |
| link in latest warm (new end)->^
| +------+ W A R M +------+ |
+----| beg |---->...----| end |----+
+------+ +------+ins
first for eviction
*/
static void link_block(KEY_CACHE *keycache, BLOCK_LINK *block, my_bool at_end)
static void link_block(KEY_CACHE *keycache, BLOCK_LINK *block, my_bool hot,
my_bool at_end)
{
BLOCK_LINK *ins;
BLOCK_LINK **pins;
KEYCACHE_DBUG_ASSERT(! (block->hash_link && block->hash_link->requests));
if (keycache->waiting_for_block.last_thread) {
/* Signal that in the LRU chain an available block has appeared */
if (!hot && keycache->waiting_for_block.last_thread) {
/* Signal that in the LRU warm sub-chain an available block has appeared */
struct st_my_thread_var *last_thread=
keycache->waiting_for_block.last_thread;
struct st_my_thread_var *first_thread= last_thread->next;
......@@ -646,8 +854,8 @@ static void link_block(KEY_CACHE *keycache, BLOCK_LINK *block, my_bool at_end)
struct st_my_thread_var *thread;
do
{
thread=next_thread;
next_thread=thread->next;
thread= next_thread;
next_thread= thread->next;
/*
We notify about the event all threads that ask
for the same page as the first thread in the queue
......@@ -670,19 +878,21 @@ static void link_block(KEY_CACHE *keycache, BLOCK_LINK *block, my_bool at_end)
#endif
return;
}
if (keycache->used_last)
pins= hot ? &keycache->used_ins : &keycache->used_last;
ins= *pins;
if (ins)
{
keycache->used_last->next_used->prev_used= &block->next_used;
block->next_used= keycache->used_last->next_used;
block->prev_used= &keycache->used_last->next_used;
keycache->used_last->next_used= block;
ins->next_used->prev_used= &block->next_used;
block->next_used= ins->next_used;
block->prev_used= &ins->next_used;
ins->next_used= block;
if (at_end)
keycache->used_last= block;
*pins= block;
}
else
{
/* The LRU chain is empty */
keycache->used_last=block->next_used= block;
keycache->used_last= keycache->used_ins= block->next_used= block;
block->prev_used= &block->next_used;
}
KEYCACHE_THREAD_TRACE("link_block");
......@@ -690,7 +900,7 @@ static void link_block(KEY_CACHE *keycache, BLOCK_LINK *block, my_bool at_end)
keycache->blocks_available++;
KEYCACHE_DBUG_PRINT("link_block",
("linked block %u:%1u status=%x #requests=%u #available=%u",
BLOCK_NUMBER(block),at_end,block->status,
BLOCK_NUMBER(block), at_end, block->status,
block->requests, keycache->blocks_available));
KEYCACHE_DBUG_ASSERT((ulong) keycache->blocks_available <=
keycache->blocks_used);
......@@ -700,19 +910,32 @@ static void link_block(KEY_CACHE *keycache, BLOCK_LINK *block, my_bool at_end)
/*
Unlink a block from the LRU chain
SYNOPSIS
unlink_block()
keycache pointer to a key cache data structure
block pointer to the block to unlink from the LRU chain
RETURN VALUE
none
NOTES.
See NOTES for link_block
*/
static void unlink_block(KEY_CACHE *keycache, BLOCK_LINK *block)
{
if (block->next_used == block)
/* The list contains only one member */
keycache->used_last= NULL;
keycache->used_last= keycache->used_ins= NULL;
else
{
block->next_used->prev_used= block->prev_used;
*block->prev_used= block->next_used;
if (keycache->used_last == block)
keycache->used_last= STRUCT_PTR(BLOCK_LINK, next_used, block->prev_used);
if (keycache->used_ins == block)
keycache->used_ins=STRUCT_PTR(BLOCK_LINK, next_used, block->prev_used);
}
block->next_used= NULL;
......@@ -721,7 +944,7 @@ static void unlink_block(KEY_CACHE *keycache, BLOCK_LINK *block)
keycache->blocks_available--;
KEYCACHE_DBUG_PRINT("unlink_block",
("unlinked block %u status=%x #requests=%u #available=%u",
BLOCK_NUMBER(block),block->status,
BLOCK_NUMBER(block), block->status,
block->requests, keycache->blocks_available));
KEYCACHE_DBUG_ASSERT(keycache->blocks_available >= 0);
#endif
......@@ -743,13 +966,62 @@ static void reg_requests(KEY_CACHE *keycache, BLOCK_LINK *block, int count)
/*
Unregister request for a block
linking it to the LRU chain if it's the last request
SYNOPSIS
unreg_block()
keycache pointer to a key cache data structure
block pointer to the block to link to the LRU chain
at_end <-> to link the block at the end of the LRU chain
RETURN VALUE
none
NOTES.
Every linking to the LRU chain decrements by one a special block
counter (if it's positive). If the at_end parameter is TRUE the block is
added either at the end of warm sub-chain or at the end of hot sub-chain.
It is added to the hot subchain if its counter is zero and number of
blocks in warm sub-chain is not less than some low limit (determined by
the division_limit parameter). Otherwise the block is added to the warm
sub-chain. If the at_end parameter is FALSE the block is always added
at beginning of the warm sub-chain.
Thus a warm block can be promoted to the hot sub-chain when its counter
becomes zero for the first time.
At the same time the block at the very beginning of the hot subchain
might be moved to the beginning of the warm subchain if it stays untouched
for a too long time (this time is determined by parameter age_threshold).
*/
static inline void unreg_request(KEY_CACHE *keycache,
BLOCK_LINK *block, int at_end)
{
if (! --block->requests)
link_block(keycache, block, (my_bool)at_end);
{
my_bool hot;
if (block->hits_left)
block->hits_left--;
hot= !block->hits_left && at_end &&
keycache->warm_blocks > keycache->min_warm_blocks;
if (hot)
{
keycache->warm_blocks--;
KEYCACHE_DBUG_PRINT("unreg_request", ("#warm_blocks=%u",
keycache->warm_blocks));
}
link_block(keycache, block, hot, (my_bool)at_end);
block->last_hit_time= keycache->keycache_time;
if (++keycache->keycache_time - keycache->used_ins->last_hit_time >
keycache->age_threshold)
{
block= keycache->used_ins;
unlink_block(keycache, block);
link_block(keycache, block, 0, 0);
keycache->warm_blocks++;
KEYCACHE_DBUG_PRINT("unreg_request", ("#warm_blocks=%u",
keycache->warm_blocks));
}
}
}
/*
......@@ -767,14 +1039,15 @@ static inline void remove_reader(BLOCK_LINK *block)
Wait until the last reader of the page in block
signals on its termination
*/
static inline void wait_for_readers(KEY_CACHE *keycache, BLOCK_LINK *block)
{
struct st_my_thread_var *thread=my_thread_var;
while (block->hash_link->requests)
{
block->condvar=&thread->suspend;
block->condvar= &thread->suspend;
keycache_pthread_cond_wait(&thread->suspend, &keycache->cache_lock);
block->condvar=NULL;
block->condvar= NULL;
}
}
......@@ -896,7 +1169,7 @@ static HASH_LINK *get_hash_link(KEY_CACHE *keycache,
if (keycache->free_hash_list)
{
hash_link= keycache->free_hash_list;
keycache->free_hash_list=hash_link->next;
keycache->free_hash_list= hash_link->next;
}
else if (keycache->hash_links_used < keycache->hash_links)
{
......@@ -907,7 +1180,8 @@ static HASH_LINK *get_hash_link(KEY_CACHE *keycache,
/* Wait for a free hash link */
struct st_my_thread_var *thread= my_thread_var;
KEYCACHE_DBUG_PRINT("get_hash_link", ("waiting"));
page.file=file; page.filepos=filepos;
page.file= file;
page.filepos= filepos;
thread->opt_info= (void *) &page;
link_into_queue(&keycache->waiting_for_hash_link, thread);
keycache_pthread_cond_wait(&thread->suspend,
......@@ -929,11 +1203,42 @@ static HASH_LINK *get_hash_link(KEY_CACHE *keycache,
/*
Get a block for the file page requested by a keycache read/write operation;
If the page is not in the cache return a free block, if there is none
return the lru block after saving its buffer if the page is dirty
return the lru block after saving its buffer if the page is dirty.
SYNOPSIS
find_key_block()
keycache pointer to a key cache data structure
file handler for the file to read page from
filepos position of the page in the file
init_hits_left how initialize the block counter for the page
wrmode <-> get for writing
page_st out {PAGE_READ,PAGE_TO_BE_READ,PAGE_WAIT_TO_BE_READ}
RETURN VALUE
Pointer to the found block if successful, 0 - otherwise
NOTES.
For the page from file positioned at filepos the function checks whether
the page is in the key cache specified by the first parameter.
If this is the case it immediately returns the block.
If not, the function first chooses a block for this page. If there is
no not used blocks in the key cache yet, the function takes the block
at the very beginning of the warm sub-chain. It saves the page in that
block if it's dirty before returning the pointer to it.
The function returns in the page_st parameter the following values:
PAGE_READ - if page already in the block,
PAGE_TO_BE_READ - if it is to be read yet by the current thread
WAIT_TO_BE_READ - if it is to be read by another thread
If an error occurs THE BLOCK_ERROR bit is set in the block status.
It might happen that there are no blocks in LRU chain (in warm part) -
all blocks are unlinked for some read/write operations. Then the function
waits until first of this operations links any block back.
*/
static BLOCK_LINK *find_key_block(KEY_CACHE *keycache,
int file, my_off_t filepos,
File file, my_off_t filepos,
int init_hits_left,
int wrmode, int *page_st)
{
HASH_LINK *hash_link;
......@@ -944,9 +1249,9 @@ static BLOCK_LINK *find_key_block(KEY_CACHE *keycache,
DBUG_ENTER("find_key_block");
KEYCACHE_THREAD_TRACE("find_key_block:begin");
DBUG_PRINT("enter", ("file %u, filepos %lu, wrmode %lu",
(uint) file,(ulong) filepos,(uint) wrmode));
(uint) file, (ulong) filepos, (uint) wrmode));
KEYCACHE_DBUG_PRINT("find_key_block", ("file %u, filepos %lu, wrmode %lu",
(uint) file,(ulong) filepos,(uint) wrmode));
(uint) file, (ulong) filepos, (uint) wrmode));
#if !defined(DBUG_OFF) && defined(EXTRA_DEBUG)
DBUG_EXECUTE("check_keycache2",
test_key_cache(keycache, "start of find_key_block", 0););
......@@ -971,7 +1276,7 @@ static BLOCK_LINK *find_key_block(KEY_CACHE *keycache,
all others are to be suspended, then resubmitted
*/
if (!wrmode && !(block->status & BLOCK_REASSIGNED))
reg_requests(keycache, block,1);
reg_requests(keycache, block, 1);
else
{
hash_link->requests--;
......@@ -1016,6 +1321,9 @@ static BLOCK_LINK *find_key_block(KEY_CACHE *keycache,
keycache->blocks_used++;
if (keycache->env)
keycache->env->blocks_used++;
keycache->warm_blocks++;
block->hits_left= init_hits_left;
block->last_hit_time= 0;
link_to_file_list(keycache, block, file, 0);
block->hash_link= hash_link;
page_status= PAGE_TO_BE_READ;
......@@ -1052,6 +1360,8 @@ static BLOCK_LINK *find_key_block(KEY_CACHE *keycache,
unlinking it from the chain
*/
block= keycache->used_last->next_used;
block->hits_left= init_hits_left;
block->last_hit_time= 0;
reg_requests(keycache, block,1);
hash_link->block= block;
}
......@@ -1060,7 +1370,7 @@ static BLOCK_LINK *find_key_block(KEY_CACHE *keycache,
! (block->status & BLOCK_IN_SWITCH) )
{
/* this is a primary request for a new page */
block->status|=BLOCK_IN_SWITCH;
block->status|= BLOCK_IN_SWITCH;
KEYCACHE_DBUG_PRINT("find_key_block",
("got block %u for new page", BLOCK_NUMBER(block)));
......@@ -1148,9 +1458,27 @@ static BLOCK_LINK *find_key_block(KEY_CACHE *keycache,
/*
Read into a key cache block buffer from disk;
do not to report error when the size of successfully read
portion is less than read_length, but not less than min_length
Read into a key cache block buffer from disk.
SYNOPSIS
read_block()
keycache pointer to a key cache data structure
block block to which buffer the data is to be read
read_length size of data to be read
min_length at least so much data must be read
primary <-> the current thread will read the data
RETURN VALUE
None
NOTES.
The function either reads a page data from file to the block buffer,
or waits until another thread reads it. What page to read is determined
by a block parameter - reference to a hash link for this page.
If an error occurs THE BLOCK_ERROR bit is set in the block status.
We do not report error when the size of successfully read
portion is less than read_length, but not less than min_length.
*/
static void read_block(KEY_CACHE *keycache,
......@@ -1199,7 +1527,7 @@ static void read_block(KEY_CACHE *keycache,
KEYCACHE_DBUG_PRINT("read_block",
("secondary request waiting for new page to be read"));
{
struct st_my_thread_var *thread=my_thread_var;
struct st_my_thread_var *thread= my_thread_var;
/* Put the request into a queue and wait until it can be processed */
add_to_queue(&block->wqueue[COND_FOR_REQUESTED], thread);
do
......@@ -1217,22 +1545,42 @@ static void read_block(KEY_CACHE *keycache,
/*
Read a block of data from a cached file into a buffer;
if return_buffer is set then the cache buffer is returned if
it can be used;
filepos must be a multiple of 'block_length', but it doesn't
SYNOPSIS
key_cache_read()
keycache pointer to a key cache data structure
file handler for the file for the block of data to be read
filepos position of the block of data in the file
level determines the weight of the data
buff buffer to where the data must be placed
length length of the buffer
block_length length of the block in the key cache buffer
return_buffer return pointer to the key cache buffer with the data
RETURN VALUE
Returns address from where the data is placed if sucessful, 0 - otherwise.
NOTES.
The function ensures that a block of data of size length from file
positioned at filepos is in the buffers for some key cache blocks.
Then the function either copies the data into the buffer buff, or,
if return_buffer is TRUE, it just returns the pointer to the key cache
buffer with the data.
Filepos must be a multiple of 'block_length', but it doesn't
have to be a multiple of key_cache_block_size;
returns adress from where data is read
*/
byte *key_cache_read(KEY_CACHE_HANDLE keycache,
File file, my_off_t filepos, byte *buff, uint length,
File file, my_off_t filepos, int level,
byte *buff, uint length,
uint block_length __attribute__((unused)),
int return_buffer __attribute__((unused)))
{
int error=0;
DBUG_ENTER("key_cache_read");
DBUG_PRINT("enter", ("file %u, filepos %lu, length %u",
(uint) file,(ulong) filepos,length));
(uint) file, (ulong) filepos, length));
if (keycache->disk_blocks > 0)
{
......@@ -1259,7 +1607,7 @@ byte *key_cache_read(KEY_CACHE_HANDLE keycache,
keycache_pthread_mutex_lock(&keycache->cache_lock);
if (keycache->env)
keycache->env->cache_r_requests++;
block=find_key_block(keycache, file, filepos, 0, &page_st);
block=find_key_block(keycache, file, filepos, level, 0, &page_st);
if (block->status != BLOCK_ERROR && page_st != PAGE_READ)
{
/* The requested page is to be read into the block buffer */
......@@ -1306,7 +1654,7 @@ byte *key_cache_read(KEY_CACHE_HANDLE keycache,
Link the block into the LRU chain
if it's the last submitted request for the block
*/
unreg_request(keycache, block,1);
unreg_request(keycache, block, 1);
keycache_pthread_mutex_unlock(&keycache->cache_lock);
......@@ -1344,18 +1692,22 @@ byte *key_cache_read(KEY_CACHE_HANDLE keycache,
Insert a block of file data from a buffer into key cache
SYNOPSIS
key_cache_insert()
file file descriptor
filepos file offset of the data from the buffer
buff buffer with data to insert into key cache
keycache pointer to a key cache data structure
file handler for the file to insert data from
filepos position of the block of data in the file to insert
level determines the weight of the data
buff buffer to read data from
length length of the data in the buffer
RETURN VALUE
0 if a success, 1 -otherwise.
0 if a success, 1 - otherwise.
*/
int key_cache_insert(KEY_CACHE_HANDLE keycache,
File file, my_off_t filepos, byte *buff, uint length)
File file, my_off_t filepos, int level,
byte *buff, uint length)
{
DBUG_ENTER("key_cache_insert");
DBUG_PRINT("enter", ("file %u, filepos %lu, length %u",
......@@ -1379,7 +1731,7 @@ int key_cache_insert(KEY_CACHE_HANDLE keycache,
keycache_pthread_mutex_lock(&keycache->cache_lock);
if (keycache->env)
keycache->env->cache_r_requests++;
block= find_key_block(keycache, file, filepos, 0, &page_st);
block= find_key_block(keycache, file, filepos, level, 0, &page_st);
if (block->status != BLOCK_ERROR && page_st != PAGE_READ)
{
/* The requested page is to be read into the block buffer */
......@@ -1405,7 +1757,7 @@ int key_cache_insert(KEY_CACHE_HANDLE keycache,
Link the block into the LRU chain
if it's the last submitted request for the block
*/
unreg_request(keycache, block,1);
unreg_request(keycache, block, 1);
keycache_pthread_mutex_unlock(&keycache->cache_lock);
......@@ -1423,15 +1775,35 @@ int key_cache_insert(KEY_CACHE_HANDLE keycache,
/*
Write a buffer into disk;
filepos must be a multiple of 'block_length', but it doesn't
have to be a multiple of key cache block size;
if !dont_write then all dirty pages involved in writing should
have been flushed from key cache before the function starts
Write a buffer into a cached file.
SYNOPSIS
key_cache_write()
keycache pointer to a key cache data structure
file handler for the file to write data to
filepos position in the file to write data to
level determines the weight of the data
buff buffer with the data
length length of the buffer
dont_write if is 0 then all dirty pages involved in writing
should have been flushed from key cache
RETURN VALUE
0 if a success, 1 - otherwise.
NOTES.
The function copies the data of size length from buff into buffers
for key cache blocks that are assigned to contain the portion of
the file starting with position filepos.
It ensures that this data is flushed to the file if dont_write is FALSE.
Filepos must be a multiple of 'block_length', but it doesn't
have to be a multiple of key_cache_block_size;
*/
int key_cache_write(KEY_CACHE_HANDLE keycache,
File file, my_off_t filepos, byte *buff, uint length,
File file, my_off_t filepos, int level,
byte *buff, uint length,
uint block_length __attribute__((unused)),
int dont_write)
{
......@@ -1474,7 +1846,7 @@ int key_cache_write(KEY_CACHE_HANDLE keycache,
keycache_pthread_mutex_lock(&keycache->cache_lock);
if (keycache->env)
keycache->env->cache_w_requests++;
block= find_key_block(keycache, file, filepos, 1, &page_st);
block= find_key_block(keycache, file, filepos, level, 1, &page_st);
if (block->status != BLOCK_ERROR && page_st != PAGE_READ &&
(offset || read_length < keycache->key_cache_block_size))
read_block(keycache, block,
......@@ -1651,7 +2023,17 @@ static int flush_cached_blocks(KEY_CACHE *keycache,
/*
Flush all blocks for a file to disk
*/
SYNOPSIS
flush_key_blocks()
keycache pointer to a key cache data structure
file handler for the file to flush to
flush_type type of the flush
RETURN VALUE
0 if a success, 1 - otherwise.
*/
int flush_key_blocks(KEY_CACHE_HANDLE keycache,
File file, enum flush_type type)
......@@ -1951,7 +2333,7 @@ static void keycache_dump(KEY_CACHE *keycache)
for (j=0 ; j < 2; j++)
{
KEYCACHE_WQUEUE *wqueue=&block->wqueue[j];
thread=last=wqueue->last_thread;
thread= last= wqueue->last_thread;
fprintf(keycache_dump_file, "queue #%d\n", j);
if (thread)
{
......@@ -2003,8 +2385,8 @@ static int keycache_pthread_cond_wait(pthread_cond_t *cond,
/* Get current time */
gettimeofday(&now, &tz);
/* Prepare timeout value */
timeout.tv_sec = now.tv_sec + KEYCACHE_TIMEOUT;
timeout.tv_nsec = now.tv_usec * 1000; /* timeval uses microseconds. */
timeout.tv_sec= now.tv_sec + KEYCACHE_TIMEOUT;
timeout.tv_nsec= now.tv_usec * 1000; /* timeval uses microseconds. */
/* timespec uses nanoseconds. */
/* 1 nanosecond = 1000 micro seconds. */
KEYCACHE_THREAD_TRACE_END("started waiting");
......@@ -2014,7 +2396,7 @@ static int keycache_pthread_cond_wait(pthread_cond_t *cond,
fprintf(keycache_debug_log, "waiting...\n");
fflush(keycache_debug_log);
#endif
rc = pthread_cond_timedwait(cond, mutex, &timeout);
rc= pthread_cond_timedwait(cond, mutex, &timeout);
KEYCACHE_THREAD_TRACE_BEGIN("finished waiting");
#if defined(KEYCACHE_DEBUG)
if (rc == ETIMEDOUT)
......@@ -2042,7 +2424,7 @@ static int keycache_pthread_cond_wait(pthread_cond_t *cond,
{
int rc;
KEYCACHE_THREAD_TRACE_END("started waiting");
rc = pthread_cond_wait(cond, mutex);
rc= pthread_cond_wait(cond, mutex);
KEYCACHE_THREAD_TRACE_BEGIN("finished waiting");
return rc;
}
......@@ -2055,7 +2437,7 @@ static int keycache_pthread_cond_wait(pthread_cond_t *cond,
static int keycache_pthread_mutex_lock(pthread_mutex_t *mutex)
{
int rc;
rc=pthread_mutex_lock(mutex);
rc= pthread_mutex_lock(mutex);
KEYCACHE_THREAD_TRACE_BEGIN("");
return rc;
}
......@@ -2072,7 +2454,7 @@ static int keycache_pthread_cond_signal(pthread_cond_t *cond)
{
int rc;
KEYCACHE_THREAD_TRACE("signal");
rc=pthread_cond_signal(cond);
rc= pthread_cond_signal(cond);
return rc;
}
......@@ -2081,7 +2463,7 @@ static int keycache_pthread_cond_broadcast(pthread_cond_t *cond)
{
int rc;
KEYCACHE_THREAD_TRACE("signal");
rc=pthread_cond_broadcast(cond);
rc= pthread_cond_broadcast(cond);
return rc;
}
......
......@@ -1009,12 +1009,21 @@ int ha_resize_key_cache(KEY_CACHE_VAR *key_cache)
{
if (key_cache->cache)
{
return !resize_key_cache(&key_cache->cache,
return !resize_key_cache(&key_cache->cache, key_cache->block_size,
key_cache->buff_size);
}
return 0;
}
int ha_change_key_cache_param(KEY_CACHE_VAR *key_cache)
{
if (key_cache->cache)
{
change_key_cache_param(key_cache->cache);
}
return 0;
}
int ha_end_key_cache(KEY_CACHE_VAR *key_cache)
{
if (key_cache->cache)
......
......@@ -389,6 +389,7 @@ int ha_delete_table(enum db_type db_type, const char *path);
void ha_drop_database(char* path);
int ha_key_cache(KEY_CACHE_VAR *key_cache);
int ha_resize_key_cache(KEY_CACHE_VAR *key_cache);
int ha_change_key_cache_param(KEY_CACHE_VAR *key_cache);
int ha_end_key_cache(KEY_CACHE_VAR *key_cache);
int ha_start_stmt(THD *thd);
int ha_report_binlog_offset_and_commit(THD *thd, char *log_file_name,
......
......@@ -3441,7 +3441,9 @@ enum options
OPT_FLUSH_TIME, OPT_FT_MIN_WORD_LEN,
OPT_FT_MAX_WORD_LEN, OPT_FT_MAX_WORD_LEN_FOR_SORT, OPT_FT_STOPWORD_FILE,
OPT_INTERACTIVE_TIMEOUT, OPT_JOIN_BUFF_SIZE,
OPT_KEY_BUFFER_SIZE, OPT_KEY_CACHE_BLOCK_SIZE, OPT_LONG_QUERY_TIME,
OPT_KEY_BUFFER_SIZE, OPT_KEY_CACHE_BLOCK_SIZE,
OPT_KEY_CACHE_DIVISION_LIMIT, OPT_KEY_CACHE_AGE_THRESHOLD,
OPT_LONG_QUERY_TIME,
OPT_LOWER_CASE_TABLE_NAMES, OPT_MAX_ALLOWED_PACKET,
OPT_MAX_BINLOG_CACHE_SIZE, OPT_MAX_BINLOG_SIZE,
OPT_MAX_CONNECTIONS, OPT_MAX_CONNECT_ERRORS,
......@@ -4133,6 +4135,16 @@ replicating a LOAD DATA INFILE command.",
(gptr*) &dflt_key_cache_var.block_size,
(gptr*) &dflt_key_cache_var.block_size, 0, GET_ULONG,
REQUIRED_ARG, KEY_CACHE_BLOCK_SIZE , 512, 1024*16, MALLOC_OVERHEAD, 512, 0},
{"key_cache_division_limit", OPT_KEY_CACHE_DIVISION_LIMIT,
"The minimum percentage of warm blocks in key cache",
(gptr*) &dflt_key_cache_var.division_limit,
(gptr*) &dflt_key_cache_var.division_limit, 0, GET_ULONG,
REQUIRED_ARG, 100, 1, 100, 0, 1, 0},
{"key_cache_division_age_threshold", OPT_KEY_CACHE_AGE_THRESHOLD,
"This characterizes the number of hits a hot block has to be untouched until it is considered aged enough to be downgraded to a warm block. This specifies the percentage ratio of that number of hits to the total number of blocks in key cache",
(gptr*) &dflt_key_cache_var.age_threshold,
(gptr*) &dflt_key_cache_var.age_threshold, 0, GET_ULONG,
REQUIRED_ARG, 300, 100, ~0L, 0, 100, 0},
{"long_query_time", OPT_LONG_QUERY_TIME,
"Log all queries that have taken more than long_query_time seconds to execute to file.",
(gptr*) &global_system_variables.long_query_time,
......@@ -5352,18 +5364,23 @@ mysql_getopt_value(const char *keyname, uint key_length,
{
switch (option->id) {
case OPT_KEY_BUFFER_SIZE:
case OPT_KEY_CACHE_BLOCK_SIZE:
case OPT_KEY_CACHE_DIVISION_LIMIT:
case OPT_KEY_CACHE_AGE_THRESHOLD:
{
KEY_CACHE_VAR *key_cache;
if (!(key_cache= get_or_create_key_cache(keyname, key_length)))
exit(1);
switch (option->id) {
case OPT_KEY_BUFFER_SIZE:
return (gptr*) &key_cache->buff_size;
}
case OPT_KEY_CACHE_BLOCK_SIZE:
{
KEY_CACHE_VAR *key_cache;
if (!(key_cache= get_or_create_key_cache(keyname, key_length)))
exit(1);
return (gptr*) &key_cache->block_size;
case OPT_KEY_CACHE_DIVISION_LIMIT:
return (gptr*) &key_cache->division_limit;
case OPT_KEY_CACHE_AGE_THRESHOLD:
return (gptr*) &key_cache->age_threshold;
}
}
}
return option->value;
......@@ -5426,6 +5443,8 @@ static void get_options(int argc,char **argv)
KEY_CACHE_VAR *key_cache= &dflt_key_cache_var;
dflt_key_cache_block_size= key_cache->block_size;
dflt_key_buff_size= key_cache->buff_size;
dflt_key_cache_division_limit= key_cache->division_limit;
dflt_key_cache_age_threshold= key_cache->age_threshold;
}
......
......@@ -59,8 +59,10 @@
#include "ha_innodb.h"
#endif
ulong dflt_key_buff_size;
ulonglong dflt_key_buff_size;
uint dflt_key_cache_block_size;
uint dflt_key_cache_division_limit;
uint dflt_key_cache_age_threshold;
static HASH system_variable_hash;
const char *bool_type_names[]= { "OFF", "ON", NullS };
......@@ -141,6 +143,10 @@ sys_var_thd_ulong sys_join_buffer_size("join_buffer_size",
&SV::join_buff_size);
sys_var_key_buffer_size sys_key_buffer_size("key_buffer_size");
sys_var_key_cache_block_size sys_key_cache_block_size("key_cache_block_size");
sys_var_key_cache_division_limit
sys_key_cache_division_limit("key_cache_division_limit");
sys_var_key_cache_age_threshold
sys_key_cache_age_threshold("key_cache_age_threshold");
sys_var_bool_ptr sys_local_infile("local_infile",
&opt_local_infile);
sys_var_thd_bool sys_log_warnings("log_warnings", &SV::log_warnings);
......@@ -395,6 +401,8 @@ sys_var *sys_variables[]=
&sys_join_buffer_size,
&sys_key_buffer_size,
&sys_key_cache_block_size,
&sys_key_cache_division_limit,
&sys_key_cache_age_threshold,
&sys_last_insert_id,
&sys_local_infile,
&sys_log_binlog,
......@@ -554,6 +562,10 @@ struct show_var_st init_vars[]= {
{sys_key_buffer_size.name, (char*) &sys_key_buffer_size, SHOW_SYS},
{sys_key_cache_block_size.name, (char*) &sys_key_cache_block_size,
SHOW_SYS},
{sys_key_cache_division_limit.name, (char*) &sys_key_cache_division_limit,
SHOW_SYS},
{sys_key_cache_age_threshold.name, (char*) &sys_key_cache_age_threshold,
SHOW_SYS},
{"language", language, SHOW_CHAR},
{"large_files_support", (char*) &opt_large_files, SHOW_BOOL},
{sys_local_infile.name, (char*) &sys_local_infile, SHOW_SYS},
......@@ -1421,7 +1433,7 @@ void sys_var_collation_connection::set_default(THD *thd, enum_var_type type)
static LEX_STRING default_key_cache_base= {(char *) DEFAULT_KEY_CACHE_NAME, 7};
static KEY_CACHE_VAR zero_key_cache= { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
static KEY_CACHE_VAR zero_key_cache= { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
static KEY_CACHE_VAR *get_key_cache(LEX_STRING *cache_name)
{
......@@ -1507,6 +1519,48 @@ bool sys_var_key_cache_block_size::update(THD *thd, set_var *var)
return 0;
}
bool sys_var_key_cache_division_limit::update(THD *thd, set_var *var)
{
ulong tmp= var->value->val_int();
if (!base_name.length)
base_name= default_key_cache_base;
KEY_CACHE_VAR *key_cache= get_key_cache(&base_name);
if (!key_cache && !(key_cache= create_key_cache(base_name.str,
base_name.length)))
return 1;
key_cache->division_limit=
(ulong) getopt_ull_limit_value(tmp, option_limits);
if (key_cache->cache)
/* Do not build a new key cache here */
return (bool) (ha_change_key_cache_param(key_cache));
return 0;
}
bool sys_var_key_cache_age_threshold::update(THD *thd, set_var *var)
{
ulong tmp= var->value->val_int();
if (!base_name.length)
base_name= default_key_cache_base;
KEY_CACHE_VAR *key_cache= get_key_cache(&base_name);
if (!key_cache && !(key_cache= create_key_cache(base_name.str,
base_name.length)))
return 1;
key_cache->division_limit=
(ulong) getopt_ull_limit_value(tmp, option_limits);
if (key_cache->cache)
/* Do not build a new key cache here */
return (bool) (ha_change_key_cache_param(key_cache));
return 0;
}
/*****************************************************************************
Functions to handle SET NAMES and SET CHARACTER SET
......
......@@ -29,8 +29,11 @@ class sys_var;
class set_var;
typedef struct system_variables SV;
extern TYPELIB bool_typelib, delay_key_write_typelib, sql_mode_typelib;
extern ulonglong dflt_key_buff_size;
extern uint dflt_key_cache_block_size;
extern ulong dflt_key_buff_size;
extern uint dflt_key_cache_division_limit;
extern uint dflt_key_cache_age_threshold;
enum enum_var_type
{
......@@ -572,6 +575,34 @@ class sys_var_key_cache_block_size :public sys_var_key_cache_param
bool is_struct() { return 1; }
};
class sys_var_key_cache_division_limit :public sys_var_key_cache_param
{
public:
sys_var_key_cache_division_limit(const char *name_arg)
:sys_var_key_cache_param(name_arg)
{
offset= offsetof(KEY_CACHE_VAR, division_limit);
}
bool update(THD *thd, set_var *var);
SHOW_TYPE type() { return SHOW_LONG; }
bool check_default(enum_var_type type) { return 1; }
bool is_struct() { return 1; }
};
class sys_var_key_cache_age_threshold :public sys_var_key_cache_param
{
public:
sys_var_key_cache_age_threshold(const char *name_arg)
:sys_var_key_cache_param(name_arg)
{
offset= offsetof(KEY_CACHE_VAR, age_threshold);
}
bool update(THD *thd, set_var *var);
SHOW_TYPE type() { return SHOW_LONG; }
bool check_default(enum_var_type type) { return 1; }
bool is_struct() { return 1; }
};
/* Variable that you can only read from */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment