pgpool2.git / summary / log / commit / refs

commit    bb63f118e01d1e501e73a2b944e6c729ee55af76
Author:   Tatsuo Ishii <ishii at sraoss.co.jp>
Date:     Mon Oct 03 06:22:50 2011 +0000

    Manage shared memory query cache by hash table on shared memory.
    For this, new directive "memqcache_max_num_cache", which specifies
    max number of hash entries is added.


doc/pgpool-en.html | 10 +- doc/pgpool-ja.html | 9 +- main.c | 4 + pgpool.conf.sample | 4 + pgpool.conf.sample-master-slave | 4 + pgpool.conf.sample-replication | 4 + pgpool.conf.sample-stream | 4 + pool_config.c | 17 +- pool_config.h | 1 + pool_config.l | 17 +- pool_memqcache.c | 585 ++++++++++++++++++++++++++++++---------- pool_memqcache.h | 41 ++- pool_process_reporting.c | 5 + 13 files changed, 550 insertions(+), 155 deletions(-) diff --git a/doc/pgpool-en.html b/doc/pgpool-en.html index c8b99059c..38ef8b2ac 100644 --- a/doc/pgpool-en.html +++ b/doc/pgpool-en.html @@ -7,7 +7,7 @@ </head> <!-- hhmts start --> -Last modified: Thu Sep 29 17:27:50 JST 2011 +Last modified: Mon Oct 3 15:14:24 JST 2011 <!-- hhmts end --> <body bgcolor="#ffffff"> @@ -2279,6 +2279,14 @@ Total memory size in bytes for storing memory cache. Mandatory if memqcache_meth memqcache_total_size = 10240 </pre> +<p> +Total number of cache entries. Mandatory if memqcache_method=shmem. +A cache entry consumes 48 bytes. +</p> +<pre> +memqcache_max_num_cache = 100 +</pre> + <p> Memory cache entry life time specified in seconds. Default is 60. </p> diff --git a/doc/pgpool-ja.html b/doc/pgpool-ja.html index 3c1369b91..5a07da4d8 100644 --- a/doc/pgpool-ja.html +++ b/doc/pgpool-ja.html @@ -8,7 +8,7 @@ <body> <!-- hhmts start --> -Last modified: Thu Sep 29 17:30:22 JST 2011 +Last modified: Mon Oct 3 15:16:43 JST 2011 <!-- hhmts end --> <body bgcolor="#ffffff"> @@ -2189,6 +2189,13 @@ memqcache_memcached_port = 11211 memqcache_total_size = 10240 </pre> +<p> +$B6&M-%a%b%j$r;HMQ$9$k>l9g$O!"%-%c%C%7%e$N?t$r;XDj$7$^$9!#%-%c%C%7%e4IM}NN0h$O%-%c%C%7%e(B1$B8D$"$?$j(B48$B%P%$%H$r>CHq$7$^$9!#(B +</p> +<pre> +memqcache_max_num_cache = 100 +</pre> + <p> $B%/%(%j%-%c%C%7%e$N<wL?$O!"0J2<$N$h$&$K@_Dj$7$^$9!#C10L$OIC!"%G%U%)%k%H$O(B60$B$G$9!#(B </p> diff --git a/main.c b/main.c index a9359cc30..f4fa041bb 100644 --- a/main.c +++ b/main.c @@ -417,6 +417,8 @@ int main(int argc, char **argv) /* * Locate pool_passwd + * The default file name "pool_passwd" can be changed by setting + * pgpool.conf's "pool_passwd" directive. */ if (strcmp("", pool_config->pool_passwd)) { @@ -567,6 +569,8 @@ int main(int argc, char **argv) pool_allocate_fsmm_clock_hand(); pool_discard_oid_maps(); + + pool_hash_init(pool_config->memqcache_max_num_cache); } if (pool_config->memory_cache_enabled) diff --git a/pgpool.conf.sample b/pgpool.conf.sample index b1fcf18bf..82e334a9a 100644 --- a/pgpool.conf.sample +++ b/pgpool.conf.sample @@ -454,6 +454,10 @@ memqcache_memcached_port=11211 # 128MB by default. memqcache_total_size=134217728 +# Total number of cache entries. Mandatory if memqcache_method=shmem. +# a cache entry consumes 48 bytes. +memqcache_max_num_cache=131072 + # Memory cache entry life time specified in seconds. 60 by default. memqcache_expire=60 diff --git a/pgpool.conf.sample-master-slave b/pgpool.conf.sample-master-slave index d13ca6c7c..a8e1714d7 100644 --- a/pgpool.conf.sample-master-slave +++ b/pgpool.conf.sample-master-slave @@ -453,6 +453,10 @@ memqcache_memcached_port=11211 # 128MB by default. memqcache_total_size=134217728 +# Total number of cache entries. Mandatory if memqcache_method=shmem. +# a cache entry consumes 48 bytes. +memqcache_max_num_cache=131072 + # Memory cache entry life time specified in seconds. 60 by default. memqcache_expire=60 diff --git a/pgpool.conf.sample-replication b/pgpool.conf.sample-replication index c8fda43ff..ad52a5512 100644 --- a/pgpool.conf.sample-replication +++ b/pgpool.conf.sample-replication @@ -453,6 +453,10 @@ memqcache_memcached_port=11211 # 128MB by default. memqcache_total_size=134217728 +# Total number of cache entries. Mandatory if memqcache_method=shmem. +# a cache entry consumes 48 bytes. +memqcache_max_num_cache=131072 + # Memory cache entry life time specified in seconds. 60 by default. memqcache_expire=60 diff --git a/pgpool.conf.sample-stream b/pgpool.conf.sample-stream index 628c1d6de..194ac585f 100644 --- a/pgpool.conf.sample-stream +++ b/pgpool.conf.sample-stream @@ -453,6 +453,10 @@ memqcache_memcached_port=11211 # 128MB by default. memqcache_total_size=134217728 +# Total number of cache entries. Mandatory if memqcache_method=shmem. +# a cache entry consumes 48 bytes. +memqcache_max_num_cache=131072 + # Memory cache entry life time specified in seconds. 60 by default. memqcache_expire=60 diff --git a/pool_config.c b/pool_config.c index 0380f997d..149305945 100644 --- a/pool_config.c +++ b/pool_config.c @@ -1945,6 +1945,7 @@ int pool_init_config(void) pool_config->memqcache_memcached_host = ""; pool_config->memqcache_memcached_port = 11211; pool_config->memqcache_total_size = 10240; + pool_config->memqcache_max_num_cache = 100; pool_config->memqcache_expire = 60; pool_config->memqcache_maxcache = 512; pool_config->memqcache_cache_block_size = 8192; @@ -3698,7 +3699,19 @@ int pool_get_config(char *confpath, POOL_CONFIG_CONTEXT context) } pool_config->memqcache_total_size = v; } - else if (!strcmp(key, "memqcache_expire") && CHECK_CONTEXT(INIT_CONFIG, context)) + else if (!strcmp(key, "memqcache_total_size") && CHECK_CONTEXT(INIT_CONFIG, context)) + { + int v = atoi(yytext); + + if (token != POOL_INTEGER || v < 0) + { + pool_error("pool_config: %s must be equal or higher than 0 numeric value", key); + fclose(fd); + return(-1); + } + pool_config->memqcache_total_size = v; + } + else if (!strcmp(key, "memqcache_max_num_cache") && CHECK_CONTEXT(INIT_CONFIG, context)) { int v = atoi(yytext); @@ -3708,7 +3721,7 @@ int pool_get_config(char *confpath, POOL_CONFIG_CONTEXT context) fclose(fd); return(-1); } - pool_config->memqcache_expire = v; + pool_config->memqcache_max_num_cache = v; } else if (!strcmp(key, "memqcache_maxcache") && CHECK_CONTEXT(INIT_CONFIG, context)) { diff --git a/pool_config.h b/pool_config.h index 4e55ba1d4..5f659c678 100644 --- a/pool_config.h +++ b/pool_config.h @@ -190,6 +190,7 @@ typedef struct { char *memqcache_memcached_host; /* Memcached host name. Mandatory if memqcache_method=memcached. */ int memqcache_memcached_port; /* Memcached port number. Mondatory if memqcache_method=memcached. */ int memqcache_total_size; /* Total memory size in bytes for storing memory cache. Mandatory if memqcache_method=shmem. */ + int memqcache_max_num_cache; /* Total number of cache entries. Mandatory if memqcache_method=shmem. */ int memqcache_expire; /* Memory cache entry life time specified in seconds. 60 by default. */ int memqcache_maxcache; /* Maximum SELECT result size in bytes. */ int memqcache_cache_block_size; /* Cache block size in bytes. 8192 by default */ diff --git a/pool_config.l b/pool_config.l index 304e0c4d2..d8bec08f8 100644 --- a/pool_config.l +++ b/pool_config.l @@ -220,6 +220,7 @@ int pool_init_config(void) pool_config->memqcache_memcached_host = ""; pool_config->memqcache_memcached_port = 11211; pool_config->memqcache_total_size = 10240; + pool_config->memqcache_max_num_cache = 100; pool_config->memqcache_expire = 60; pool_config->memqcache_maxcache = 512; pool_config->memqcache_cache_block_size = 8192; @@ -1973,7 +1974,19 @@ int pool_get_config(char *confpath, POOL_CONFIG_CONTEXT context) } pool_config->memqcache_total_size = v; } - else if (!strcmp(key, "memqcache_expire") && CHECK_CONTEXT(INIT_CONFIG, context)) + else if (!strcmp(key, "memqcache_total_size") && CHECK_CONTEXT(INIT_CONFIG, context)) + { + int v = atoi(yytext); + + if (token != POOL_INTEGER || v < 0) + { + pool_error("pool_config: %s must be equal or higher than 0 numeric value", key); + fclose(fd); + return(-1); + } + pool_config->memqcache_total_size = v; + } + else if (!strcmp(key, "memqcache_max_num_cache") && CHECK_CONTEXT(INIT_CONFIG, context)) { int v = atoi(yytext); @@ -1983,7 +1996,7 @@ int pool_get_config(char *confpath, POOL_CONFIG_CONTEXT context) fclose(fd); return(-1); } - pool_config->memqcache_expire = v; + pool_config->memqcache_max_num_cache = v; } else if (!strcmp(key, "memqcache_maxcache") && CHECK_CONTEXT(INIT_CONFIG, context)) { diff --git a/pool_memqcache.c b/pool_memqcache.c index 81f6b4b09..d65a75ada 100644 --- a/pool_memqcache.c +++ b/pool_memqcache.c @@ -55,7 +55,7 @@ memcached_st *memc; #endif static char* encode_key(const char *s, char *buf, POOL_CONNECTION_POOL *backend); -#ifdef DEBU +#ifdef DEBUG static void dump_cache_data(const char *data, size_t len); #endif static int pool_commit_cache(POOL_CONNECTION_POOL *backend, char *query, char *data, size_t datalen, int num_oids, int *oids); @@ -97,6 +97,7 @@ static void pool_update_fsmm(POOL_CACHE_BLOCKID blockid, size_t free_space); static POOL_CACHE_BLOCKID pool_get_block(size_t free_space); static POOL_CACHE_ITEM_HEADER *pool_cache_item_header(POOL_CACHEID *cacheid); static int pool_init_cache_block(POOL_CACHE_BLOCKID blockid); +static void pool_wipe_out_cache_block(POOL_CACHE_BLOCKID blockid); static int pool_delete_item_shmem_cache(POOL_CACHEID *cacheid); static char *block_address(int blockid); static POOL_CACHE_ITEM_POINTER *item_pointer(char *block, int i); @@ -106,6 +107,11 @@ static POOL_CACHE_BLOCKID pool_reuse_block(void); static void dump_shmem_cache(POOL_CACHE_BLOCKID blockid); #endif +static int pool_hash_insert(POOL_QUERY_HASH *key, POOL_CACHEID *cacheid, bool update); +static uint32 create_hash_key(POOL_QUERY_HASH *key); +static POOL_HASH_ELEMENT *get_new_hash_element(void); +static void put_back_hash_element(POOL_HASH_ELEMENT *element); + /* * Connect to Memcached */ @@ -209,7 +215,7 @@ static int pool_commit_cache(POOL_CONNECTION_POOL *backend, char *query, char *d } pool_debug("pool_commit_cache: Query=%s", query); -#ifdef SHMEMCACHE_DEBUG +#ifdef DEBUG dump_cache_data(data, datalen); #endif @@ -344,7 +350,7 @@ static int pool_fetch_cache(POOL_CONNECTION_POOL *backend, const char *query, ch } pool_debug("pool_fetch_cache: query=%s len:%zd", query, *len); -#ifdef SHMEMCACHE_DEBUG +#ifdef DEBUG dump_cache_data(p, *len); #endif @@ -1473,7 +1479,6 @@ static POOL_CACHEID *pool_add_item_shmem_cache(POOL_QUERY_HASH *query_hash, char POOL_CACHE_BLOCKID blockid; POOL_CACHE_BLOCK_HEADER *bh; POOL_CACHE_ITEM_POINTER *cip; - POOL_CACHE_ITEM_POINTER *cip2; POOL_CACHE_ITEM ci; POOL_CACHE_ITEM_POINTER cip_body; @@ -1481,12 +1486,15 @@ static POOL_CACHEID *pool_add_item_shmem_cache(POOL_QUERY_HASH *query_hash, char int request_size; char *p; - int i, j; + int i; char *src; char *dst; - int movelen; - int num_deleted = 0; - char *flags; + int num_deleted; + char *dcip; + char *dci; + bool need_pack; + char *work_buffer; + int index; if (query_hash == NULL) { @@ -1525,90 +1533,106 @@ static POOL_CACHEID *pool_add_item_shmem_cache(POOL_QUERY_HASH *query_hash, char pool_init_cache_block(blockid); /* Get block address on shmem */ - p = pool_memory_cache_address() + blockid * pool_config->memqcache_cache_block_size; + p = block_address(blockid); bh = (POOL_CACHE_BLOCK_HEADER *)p; /* * Create contiguous free space. We assume that item bodies are * ordered from bottom to top of the block, and corresponding item * pointers are ordered from the youngest to the oldest in the - * begging of the block. + * beggining of the block. */ - /* First, pack cache items */ + /* + * Optimization. If there's no deleted items, we don't need to + * pack it to create contiguous free space. + */ + need_pack = false; for (i=0;i<bh->num_items;i++) { - int total_length; - cip = item_pointer(p, i); if (POOL_ITEM_DELETED & cip->flags) /* Deleted item? */ { - /* Pack the unsed space */ - src = p + item_pointer(p, bh->num_items-1)->offset; - movelen = cip->offset - item_pointer(p, bh->num_items-1)->offset; - total_length = item_header(p, i)->total_length; - dst = src + total_length; - pool_debug("i:%d src:%lu dst:%lu movelen:%d", i, (unsigned long int)src, (unsigned long int)dst, movelen); - memmove(dst, src, movelen); - - for (j=i+1;j<bh->num_items;j++) - { - /* Rewrite pointer to cache item */ - cip2 = item_pointer(p, j); - cip2->offset += total_length; - } + need_pack = true; + pool_debug("pool_add_item_shmem_cache: start creating contiguous space"); + break; } } - /* Second, pack cache item pointers */ - flags = malloc(bh->num_items); - if (flags == NULL) + if (need_pack) { - pool_error("pool_add_item_shmem_cache: malloc failed"); - return NULL; - } - - for (i=0;i<bh->num_items;i++) - { - flags[i] = item_pointer(p, i)->flags; - } + /* + * Pack and create contiguous free space. + */ + dcip = calloc(1, pool_config->memqcache_cache_block_size); + if (!dcip) + { + pool_error("pool_add_item_shmem_cache: calloc failed"); + return NULL; + } - cip = item_pointer(p, 0); + work_buffer = dcip; + dci = dcip + pool_config->memqcache_cache_block_size; + num_deleted = 0; + index = 0; - for (i=0;i<bh->num_items;i++) - { - if (POOL_ITEM_DELETED & flags[i]) /* Deleted item? */ + for (i=0;i<bh->num_items;i++) { - num_deleted++; + int total_length; + POOL_CACHEID cid; - movelen = sizeof(POOL_CACHE_ITEM_POINTER)*(bh->num_items - i -1); + cip = item_pointer(p, i); - if (movelen <= 0) - break; + if (POOL_ITEM_DELETED & cip->flags) /* Deleted item? */ + { + num_deleted++; + continue; + } - src = (char *)(cip+1); - dst = (char *)cip; - memmove(dst, src, movelen); - pool_debug("i:%d src:%lu dst:%lu movelen:%d num_deleted:%d", i, (unsigned long int)src, (unsigned long int)dst, movelen, num_deleted); - } - movelen -= sizeof(POOL_CACHE_ITEM_POINTER); - cip++; - } + /* Copy item body */ + src = p + cip->offset; + total_length = item_header(p, i)->total_length; + dst = dci - total_length; + cip->offset = dst - work_buffer; + memcpy(dst, src, total_length); - free(flags); + dci -= total_length; - /* All items deleted? */ - if (num_deleted > 0 && num_deleted == bh->num_items) - { - pool_debug("all items deleted num_deleted:%d", num_deleted); - bh->flags = 0; - pool_init_cache_block(blockid); - pool_update_fsmm(blockid, POOL_MAX_FREE_SPACE); - } - else - { - bh->num_items -= num_deleted; + /* Copy item pointer */ + src = (char *)cip; + dst = (char *)item_pointer(dcip, index); + memcpy(dst, src, sizeof(POOL_CACHE_ITEM_POINTER)); + + /* Update hash index */ + cid.blockid = blockid; + cid.itemid = index; + pool_hash_insert(&cip->query_hash, &cid, true); + pool_debug("pool_add_item_shmem_cache: item cid updated. old:%d %d new:%d %d", + blockid, i, blockid, index); + + index++; + } + + /* All items deleted? */ + if (num_deleted > 0 && num_deleted == bh->num_items) + { + pool_debug("pool_add_item_shmem_cache: all items deleted num_deleted:%d", num_deleted); + bh->flags = 0; + pool_init_cache_block(blockid); + pool_update_fsmm(blockid, POOL_MAX_FREE_SPACE); + } + else + { + /* Update number of items */ + bh->num_items -= num_deleted; + + /* Copy back the packed block except block header */ + memcpy(p+sizeof(POOL_CACHE_BLOCK_HEADER), + work_buffer+sizeof(POOL_CACHE_BLOCK_HEADER), + pool_config->memqcache_cache_block_size-sizeof(POOL_CACHE_BLOCK_HEADER)); + } + free(work_buffer); } /* @@ -1667,12 +1691,15 @@ static POOL_CACHEID *pool_add_item_shmem_cache(POOL_QUERY_HASH *query_hash, char cacheid.blockid = blockid; cacheid.itemid = bh->num_items; - pool_debug("pool_add_item_shmem_cache: blockid: %d itemid:%d", + pool_debug("pool_add_item_shmem_cache: new item inseted. blockid: %d itemid:%d", cacheid.blockid, cacheid.itemid); /* Add up number of items */ bh->num_items++; + /* Update hash table */ + pool_hash_insert(query_hash, &cacheid, false); + #ifdef SHMEMCACHE_DEBUG dump_shmem_cache(blockid); #endif @@ -1732,60 +1759,32 @@ static char *pool_get_item_shmem_cache(POOL_QUERY_HASH *query_hash, int *size, i static POOL_CACHEID *pool_find_item_on_shmem_cache(POOL_QUERY_HASH *query_hash) { static POOL_CACHEID cacheid; - POOL_CACHE_BLOCK_HEADER *bh; - POOL_CACHE_ITEM_POINTER *cip; + POOL_CACHEID *c; POOL_CACHE_ITEM_HEADER *cih; time_t now; - int maxblock = pool_get_memqcache_blocks(); - char *p; - int i, j; - /* XXX Sequential scan. Need hash table for faster access */ - for (i=0;i<maxblock;i++) + c = pool_hash_search(query_hash); + if (!c) { - p = block_address(i); - bh = (POOL_CACHE_BLOCK_HEADER *)p; - if (!(bh->flags & POOL_BLOCK_USED)) - { - /* Unused block */ - continue; - } - - for (j=0;j<bh->num_items;j++) - { - cip = item_pointer(p, j); - - if (!(cip->flags & POOL_ITEM_USED) || cip->flags & POOL_ITEM_DELETED) - { - /* Item not used */ - continue; - } + return NULL; + } - if (!memcmp(cip->query_hash.query_hash, query_hash, sizeof(POOL_QUERY_HASH))) - { - cih = item_header(p, j); - pool_debug("pool_find_item_on_shmem_cache: found in cache blockid:%d itemid:%d", - i, j); - /* Check cache expiration */ - now = time(NULL); - if (now > (cih->timestamp + pool_config->memqcache_expire)) - { - pool_debug("pool_find_item_on_shmem_cache: cache expired"); - pool_debug("pool_find_item_on_shmem_cache: now: %ld timestamp: %ld", - now, cih->timestamp + pool_config->memqcache_expire); - cacheid.blockid = i; - cacheid.itemid = j; - pool_delete_item_shmem_cache(&cacheid); - return NULL; - } + cih = item_header(block_address(c->blockid), c->itemid); - cacheid.blockid = i; - cacheid.itemid = j; - return &cacheid; - } - } + /* Check cache expiration */ + now = time(NULL); + if (now > (cih->timestamp + pool_config->memqcache_expire)) + { + pool_debug("pool_find_item_on_shmem_cache: cache expired"); + pool_debug("pool_find_item_on_shmem_cache: now: %ld timestamp: %ld", + now, cih->timestamp + pool_config->memqcache_expire); + pool_delete_item_shmem_cache(c); + return NULL; } - return NULL; + + cacheid.blockid = c->blockid; + cacheid.itemid = c->itemid; + return &cacheid; } /* @@ -1799,6 +1798,7 @@ static int pool_delete_item_shmem_cache(POOL_CACHEID *cacheid) POOL_CACHE_BLOCK_HEADER *bh; POOL_CACHE_ITEM_POINTER *cip; POOL_CACHE_ITEM_HEADER *cih; + POOL_QUERY_HASH key; int size; pool_debug("pool_delete_item_shmem_cache: cacheid:%d itemid:%d", @@ -1845,6 +1845,9 @@ static int pool_delete_item_shmem_cache(POOL_CACHEID *cacheid) return -1; } + /* Save cache key */ + memcpy(&key, &cip->query_hash, sizeof(POOL_QUERY_HASH)); + cih = pool_cache_item_header(cacheid); size = cih->total_length + sizeof(POOL_CACHE_ITEM_POINTER); @@ -1867,6 +1870,9 @@ static int pool_delete_item_shmem_cache(POOL_CACHEID *cacheid) pool_init_cache_block(cacheid->blockid); } + /* Remove hash index */ + pool_hash_delete(&key); + /* Update FSMM */ pool_update_fsmm(cacheid->blockid, bh->free_bytes); @@ -1897,17 +1903,12 @@ static POOL_CACHE_ITEM_HEADER *pool_cache_item_header(POOL_CACHEID *cacheid) } /* - * Initialize the block if neccessary. If no live items are - * remained, we also initialize the block. If there's contiguous - * deleted items, we turn them into free space as well. + * Initialize specified block. */ static int pool_init_cache_block(POOL_CACHE_BLOCKID blockid) { char *p; POOL_CACHE_BLOCK_HEADER *bh; - POOL_CACHE_ITEM_POINTER *cip; - int i; - int contiguous; if (blockid < 0 || blockid >= pool_get_memqcache_blocks()) { @@ -1915,7 +1916,7 @@ static int pool_init_cache_block(POOL_CACHE_BLOCKID blockid) return -1; } - p = pool_memory_cache_address() + blockid * pool_config->memqcache_cache_block_size; + p = block_address(blockid); bh = (POOL_CACHE_BLOCK_HEADER *)p; /* Is this block used? */ @@ -1926,40 +1927,39 @@ static int pool_init_cache_block(POOL_CACHE_BLOCKID blockid) bh->free_bytes = pool_config->memqcache_cache_block_size - sizeof(POOL_CACHE_BLOCK_HEADER); } + return 0; +} + +/* + * Delete all items in the block. + */ +static void pool_wipe_out_cache_block(POOL_CACHE_BLOCKID blockid) +{ + char *p; + POOL_CACHE_BLOCK_HEADER *bh; + POOL_CACHE_ITEM_POINTER *cip; + POOL_CACHEID cacheid; + int i; + + /* Get block address on shmem */ + p = block_address(blockid); + bh = (POOL_CACHE_BLOCK_HEADER *)p; + cacheid.blockid = blockid; - /* Find contiguous deleted items and turn them into free space */ - contiguous = -1; for (i=0;i<bh->num_items;i++) { - cip = (POOL_CACHE_ITEM_POINTER *)(p + sizeof(POOL_CACHE_BLOCK_HEADER) + - sizeof(POOL_CACHE_ITEM_POINTER) * i); + cip = item_pointer(p, i); - if (cip->flags & POOL_ITEM_USED) - { - contiguous = -1; - } - else + if ((POOL_ITEM_DELETED & cip->flags) == 0) /* Not deleted item? */ { - if (contiguous == -1) - { - contiguous = i; - } + cacheid.itemid = i; + pool_delete_item_shmem_cache(&cacheid); } } - if (contiguous >= 0) - { - /* There are contiguous deleted items starting from - * "contiguous". - */ - for (i=contiguous;i<bh->num_items;i++) - { - cip = (POOL_CACHE_ITEM_POINTER *)(p + sizeof(POOL_CACHE_BLOCK_HEADER) + - sizeof(POOL_CACHE_ITEM_POINTER) * i); - memset(cip, 0, sizeof(POOL_CACHE_ITEM_POINTER)); - } - } - return 0; + bh->flags = 0; + pool_init_cache_block(blockid); + pool_update_fsmm(blockid, POOL_MAX_FREE_SPACE); } /* @@ -2679,3 +2679,294 @@ long long int pool_stats_count_up_num_cache_hits(void) pool_semaphore_unlock(QUERY_CACHE_STATS_SEM); return stats->num_cache_hits; } + +/* + * On shared memory hash table implementation. We use sub part of md5 + * hash key as hash function. The experiment has shown that has_any() + * of PostgreSQL is a little bit better than the method using part of + * md5 hash value, but it seems adding some cpu cycles to call + * hash_any() is not worth the trouble. + */ + +static POOL_HASH_HEADER *hash_header; +static POOL_HASH_ELEMENT *hash_elements; +static POOL_HASH_ELEMENT hash_free_body; +static volatile POOL_HASH_ELEMENT *hash_free; + +/* + * Initialize hash table on shared memory "nelements" is max number of + * hash keys. The actual number of hash key is rounded up to power of + * 2. + */ +int pool_hash_init(int nelements) +{ + size_t size; + int nelements2; /* number of rounded up hash keys */ + int shift; + uint32 mask; + POOL_HASH_HEADER hh; + int i; + + if (nelements <= 0) + { + pool_error("pool_hash_init: invalid nelements:%d", nelements); + return -1; + } + + /* Round up to power of 2 */ + shift = 32; + nelements2 = 1; + do + { + nelements2 <<= 1; + shift--; + } while (nelements2 < nelements); + + mask = ~0; + mask >>= shift; + size = (char *)&hh.elements - (char *)&hh + sizeof(POOL_HEADER_ELEMENT)*nelements2; + hash_header = pool_shared_memory_create(size); + if (hash_header == NULL) + { + pool_error("pool_hash_init: failed to allocate shared memory cache for hash header. request size: %zd", size); + return -1; + } + hash_header->nhash = nelements2; + hash_header->mask = mask; + + size = sizeof(POOL_HASH_ELEMENT)*nelements2; + hash_elements = pool_shared_memory_create(size); + if (hash_elements == NULL) + { + pool_error("pool_hash_init: failed to allocate shared memory cache for free list. request size: %zd", size); + return -1; + } + + for (i=0;i<nelements2-1;i++) + { + hash_elements[i].next = &hash_elements[i+1]; + } + hash_elements[nelements2-1].next = NULL; + hash_free = &hash_free_body; + hash_free->next = hash_elements; + + return 0; +} + +/* + * Search cacheid by MD5 hash key string + * If found, returns cache id, otherwise NULL. + */ +POOL_CACHEID *pool_hash_search(POOL_QUERY_HASH *key) +{ + volatile POOL_HASH_ELEMENT *element; + + uint32 hash_key = create_hash_key(key); + + if (hash_key >= hash_header->nhash) + { + pool_error("pool_hash_search: invalid hash key: %uld nhash: %ld", + hash_key, hash_header->nhash); + return NULL; + } + + { + char md5[POOL_MD5_HASHKEYLEN+1]; + memcpy(md5, key->query_hash, POOL_MD5_HASHKEYLEN); + md5[POOL_MD5_HASHKEYLEN] = '\0'; + pool_log("pool_hash_search: hash_key:%d md5:%s", hash_key, md5); + } + + element = hash_header->elements[hash_key].element; + while (element) + { + if (memcmp((const void *)element->hashkey.query_hash, + (const void *)key->query_hash, sizeof(key->query_hash)) == 0) + { + return (POOL_CACHEID *)&element->cacheid; + } + element = element->next; + } + return NULL; +} + +/* + * Insert MD5 key and associated cache id into shmem hash table. If + * "update" is true, replace cacheid associated with the MD5 key, + * rather than throw an error. + */ +static int pool_hash_insert(POOL_QUERY_HASH *key, POOL_CACHEID *cacheid, bool update) +{ + volatile POOL_HASH_ELEMENT *element; + volatile POOL_HASH_ELEMENT *new_element; + volatile POOL_HASH_ELEMENT **insertion_point; + + uint32 hash_key = create_hash_key(key); + + if (hash_key >= hash_header->nhash) + { + pool_error("pool_hash_insert: invalid hash key: %uld nhash: %ld", + hash_key, hash_header->nhash); + return -1; + } + + { + char md5[POOL_MD5_HASHKEYLEN+1]; + memcpy(md5, key->query_hash, POOL_MD5_HASHKEYLEN); + md5[POOL_MD5_HASHKEYLEN] = '\0'; + pool_log("pool_hash_insert: hash_key:%d md5:%s block:%d item:%d", hash_key, md5, cacheid->blockid, cacheid->itemid); + } + + /* + * Look for insert location + */ + insertion_point = (volatile POOL_HASH_ELEMENT **)&(hash_header->elements[hash_key].element); + element = hash_header->elements[hash_key].element; + + while (element) + { + if (memcmp((const void *)element->hashkey.query_hash, + (const void *)key->query_hash, sizeof(key->query_hash)) == 0) + { + /* Hash key found. If "update" is false, just throw an error. */ + char md5[POOL_MD5_HASHKEYLEN+1]; + + if (!update) + { + memcpy(md5, key->query_hash, POOL_MD5_HASHKEYLEN); + md5[POOL_MD5_HASHKEYLEN] = '\0'; + pool_error("pool_hash_insert: the key:==%s== already exists", md5); + return -1; + } + else + { + /* Update cache id */ + memcpy((void *)&element->cacheid, cacheid, sizeof(POOL_CACHEID)); + return 0; + } + } + *insertion_point = (volatile POOL_HASH_ELEMENT *)&element->next; + element = element->next; + } + + /* + * Get new element from free list + */ + new_element = get_new_hash_element(); + if (!new_element) + { + pool_error("pool_hash_insert: could not get new element"); + return -1; + } + + *insertion_point = new_element; + new_element->next = NULL; + memcpy((void *)new_element->hashkey.query_hash, key->query_hash, POOL_MD5_HASHKEYLEN); + memcpy((void *)&new_element->cacheid, cacheid, sizeof(POOL_CACHEID)); + + return 0; +} + +/* + * Delete MD5 key and associated cache id into shmem hash table. + */ +int pool_hash_delete(POOL_QUERY_HASH *key) +{ + POOL_HASH_ELEMENT *element; + POOL_HASH_ELEMENT **delete_point; + bool found; + + uint32 hash_key = create_hash_key(key); + + if (hash_key >= hash_header->nhash) + { + pool_error("pool_hash_delete: invalid hash key: %uld nhash: %ld", + hash_key, hash_header->nhash); + return -1; + } + + /* + * Look for delete location + */ + found = false; + delete_point = &(hash_header->elements[hash_key].element); + element = hash_header->elements[hash_key].element; + + while (element) + { + if (memcmp(element->hashkey.query_hash, key->query_hash, sizeof(key->query_hash)) == 0) + { + found = true; + break; + } + delete_point = &element->next; + element = element->next; + } + + if (!found) + { + char md5[POOL_MD5_HASHKEYLEN+1]; + + memcpy(md5, key->query_hash, POOL_MD5_HASHKEYLEN); + md5[POOL_MD5_HASHKEYLEN] = '\0'; + pool_error("pool_hash_delete: the key:==%s== not found", md5); + return -1; + } + + /* + * Put back the element to free list + */ + *delete_point = element->next; + put_back_hash_element(element); + + return 0; +} + +/* + * Calculate 32bit binary hash key(i.e. location in hash header) from MD5 + * string. We use top most 8 characters of MD5 string for calculation. +*/ +static uint32 create_hash_key(POOL_QUERY_HASH *key) +{ +#define POOL_HASH_NCHARS 8 + + char md5[POOL_HASH_NCHARS+1]; + uint32 mask; + + memcpy(md5, key->query_hash, POOL_HASH_NCHARS); + md5[POOL_HASH_NCHARS] = '\0'; + mask = strtoul(md5, NULL, 16); + mask &= hash_header->mask; + return mask; +} + +/* + * Get new free hash element from free list. + */ +static POOL_HASH_ELEMENT *get_new_hash_element(void) +{ + POOL_HASH_ELEMENT *elm; + + if (!hash_free->next) + { + /* No free element */ + return NULL; + } + + elm = hash_free->next; + hash_free->next = elm->next; + + return elm; +} + +/* + * Put back hash element to free list. + */ +static void put_back_hash_element(POOL_HASH_ELEMENT *element) +{ + POOL_HASH_ELEMENT *elm; + + elm = hash_free->next; + hash_free->next = element; + element->next = elm; +} diff --git a/pool_memqcache.h b/pool_memqcache.h index 91b736b36..509a80e50 100644 --- a/pool_memqcache.h +++ b/pool_memqcache.h @@ -29,6 +29,8 @@ #include "pool.h" #include <sys/time.h> +#define POOL_MD5_HASHKEYLEN 32 /* MD5 hash key length */ + /* * On memory query cache on shmem is devided into fixed length "cache * block". Each block is assigned a "cache block id", which is @@ -60,7 +62,7 @@ typedef struct { } POOL_CACHE_BLOCK_HEADER; typedef struct { - char query_hash[32]; + char query_hash[POOL_MD5_HASHKEYLEN]; } POOL_QUERY_HASH; #define POOL_ITEM_USED 0x0001 /* is this item used? */ @@ -120,7 +122,7 @@ extern void memqcache_register(char kind, POOL_CONNECTION *frontend, char *data, typedef union { POOL_CACHEID cacheid; /* cache key (shmem configuration) */ - char hashkey[32]; /* cache key (memcached configuration) */ + char hashkey[POOL_MD5_HASHKEYLEN]; /* cache key (memcached configuration) */ } POOL_CACHEKEY; /* @@ -167,6 +169,41 @@ typedef struct long long int num_cache_hits; /* number of SELECTs extracted from cache */ } POOL_QUERY_CACHE_STATS; +/*-------------------------------------------------------------------------------- + * On shared memory hsah table implementation + *-------------------------------------------------------------------------------- + */ + +/* Hash element */ +typedef struct POOL_HASH_ELEMENT +{ + struct POOL_HASH_ELEMENT *next; /* link to next entry */ + POOL_QUERY_HASH hashkey; /* MD5 hash key */ + POOL_CACHEID cacheid; /* logical location of this cache element */ +} POOL_HASH_ELEMENT; + +typedef uint32 POOL_HASH_KEY; + +/* Hash header element */ +typedef struct +{ + POOL_HASH_KEY hashkey; /* hash key */ + POOL_HASH_ELEMENT *element; /* hash element */ +} POOL_HEADER_ELEMENT; + +/* Hash header */ +typedef struct +{ + long nhash; /* number of hash keys (power of 2) */ + uint32 mask; /* mask for hash function */ + POOL_HEADER_ELEMENT elements[1]; /* actual hash elements follows */ +} POOL_HASH_HEADER; + +extern int pool_hash_init(int nelements); +extern POOL_CACHEID *pool_hash_search(POOL_QUERY_HASH *key); +extern int pool_hash_delete(POOL_QUERY_HASH *key); +extern uint32 hash_any(unsigned char *k, int keylen); + extern POOL_STATUS pool_fetch_from_memory_cache(POOL_CONNECTION *frontend, POOL_CONNECTION_POOL *backend, char *contents, bool *foundp); diff --git a/pool_process_reporting.c b/pool_process_reporting.c index da73fced8..7be3d5518 100644 --- a/pool_process_reporting.c +++ b/pool_process_reporting.c @@ -559,6 +559,11 @@ POOL_REPORT_CONFIG* get_config(int *nrows) strncpy(status[i].desc, "Total memory size in bytes for storing memory cache. Mandatory if memqcache_method=shmem", POOLCONFIG_MAXDESCLEN); i++; + strncpy(status[i].name, "memqcache_max_num_cache", POOLCONFIG_MAXNAMELEN); + snprintf(status[i].value, POOLCONFIG_MAXVALLEN, "%d", pool_config->memqcache_max_num_cache); + strncpy(status[i].desc, "Total number of cache entries", POOLCONFIG_MAXDESCLEN); + i++; + strncpy(status[i].name, "memqcache_expire", POOLCONFIG_MAXNAMELEN); snprintf(status[i].value, POOLCONFIG_MAXVALLEN, "%d", pool_config->memqcache_expire); strncpy(status[i].desc, "Memory cache entry life time specified in seconds. 60 by default", POOLCONFIG_MAXDESCLEN); [parent: d5930588747f]