Hash function transparency, supports CRC32 and FNV-1a

Added INI directive memcache.hash_function = {crc32, fnv}
This commit is contained in:
Mikael Johansson
2007-08-30 19:20:28 +00:00
parent 6feee5a167
commit fb8f6f6109
7 changed files with 82 additions and 38 deletions

View File

@@ -172,6 +172,23 @@ static PHP_INI_MH(OnUpdateHashStrategy) /* {{{ */
}
/* }}} */
static PHP_INI_MH(OnUpdateHashFunction) /* {{{ */
{
if (!strcasecmp(new_value, "crc32")) {
MEMCACHE_G(hash_strategy) = MMC_HASH_CRC32;
}
else if (!strcasecmp(new_value, "fnv")) {
MEMCACHE_G(hash_strategy) = MMC_HASH_FNV1A;
}
else {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "memcache.hash_function must be in set {crc32, fnv} ('%s' given)", new_value);
return FAILURE;
}
return SUCCESS;
}
/* }}} */
/* {{{ PHP_INI */
PHP_INI_BEGIN()
STD_PHP_INI_ENTRY("memcache.allow_failover", "1", PHP_INI_ALL, OnUpdateLong, allow_failover, zend_memcache_globals, memcache_globals)
@@ -179,6 +196,7 @@ PHP_INI_BEGIN()
STD_PHP_INI_ENTRY("memcache.default_port", "11211", PHP_INI_ALL, OnUpdateLong, default_port, zend_memcache_globals, memcache_globals)
STD_PHP_INI_ENTRY("memcache.chunk_size", "8192", PHP_INI_ALL, OnUpdateChunkSize, chunk_size, zend_memcache_globals, memcache_globals)
STD_PHP_INI_ENTRY("memcache.hash_strategy", "standard", PHP_INI_ALL, OnUpdateHashStrategy, hash_strategy, zend_memcache_globals, memcache_globals)
STD_PHP_INI_ENTRY("memcache.hash_function", "crc32", PHP_INI_ALL, OnUpdateHashFunction, hash_function, zend_memcache_globals, memcache_globals)
PHP_INI_END()
/* }}} */

View File

@@ -26,7 +26,6 @@
#include <stdlib.h>
#include "php.h"
#include "ext/standard/crc32.h"
#include "php_memcache.h"
ZEND_EXTERN_MODULE_GLOBALS(memcache)
@@ -42,12 +41,14 @@ typedef struct mmc_consistent_state {
int num_points;
mmc_t *buckets[MMC_CONSISTENT_BUCKETS];
int buckets_populated;
mmc_hash_function hash;
} mmc_consistent_state_t;
void *mmc_consistent_create_state() /* {{{ */
void *mmc_consistent_create_state(mmc_hash_function hash) /* {{{ */
{
mmc_consistent_state_t *state = emalloc(sizeof(mmc_consistent_state_t));
memset(state, 0, sizeof(mmc_consistent_state_t));
state->hash = hash;
return state;
}
/* }}} */
@@ -64,19 +65,6 @@ void mmc_consistent_free_state(void *s) /* {{{ */
}
/* }}} */
static unsigned int mmc_hash(const char *key, int key_len) /* {{{ */
{
unsigned int crc = ~0;
int i;
for (i=0; i<key_len; i++) {
CRC32(crc, key[i]);
}
return ~crc;
}
/* }}} */
static int mmc_consistent_compare(const void *a, const void *b) /* {{{ */
{
if (((mmc_consistent_point_t *)a)->point < ((mmc_consistent_point_t *)b)->point) {
@@ -139,7 +127,7 @@ mmc_t *mmc_consistent_find_server(void *s, const char *key, int key_len TSRMLS_D
if (!state->buckets_populated) {
mmc_consistent_pupulate_buckets(state);
}
return state->buckets[mmc_hash(key, key_len) % MMC_CONSISTENT_BUCKETS];
return state->buckets[state->hash(key, key_len) % MMC_CONSISTENT_BUCKETS];
}
return state->points[0].server;
@@ -163,7 +151,7 @@ void mmc_consistent_add_server(void *s, mmc_t *mmc, unsigned int weight) /* {{{
for (i=0; i<points; i++) {
key_len = spprintf(&key, 0, "%s:%d-%d", mmc->host, mmc->tcp.port, i);
state->points[state->num_points + i].server = mmc;
state->points[state->num_points + i].point = mmc_hash(key, key_len);
state->points[state->num_points + i].point = state->hash(key, key_len);
efree(key);
}

View File

@@ -28,6 +28,7 @@
#include "php.h"
#include "php_network.h"
#include "ext/standard/crc32.h"
#include "ext/standard/php_var.h"
#include "ext/standard/php_string.h"
#include "ext/standard/php_smart_str.h"
@@ -119,6 +120,36 @@ static inline void mmc_queue_remove(mmc_queue_t *queue, void *ptr) {
mmc_queue_free(&original);
}
static unsigned int mmc_hash_crc32(const char *key, int key_len) /*
new style crc32 hash, compatible with other clients {{{ */
{
unsigned int crc = ~0;
int i;
for (i=0; i<key_len; i++) {
CRC32(crc, key[i]);
}
crc = (~crc >> 16) & 0x7fff;
return crc ? crc : 1;
}
/* }}} */
static unsigned int mmc_hash_fnv1a(const char *key, int key_len) /*
FNV-1a hash {{{ */
{
unsigned int hval = FNV_32_INIT;
int i;
for (i=0; i<key_len; i++) {
hval ^= (unsigned int)key[i];
hval *= FNV_32_PRIME;
}
return hval;
}
/* }}} */
static size_t mmc_stream_read_buffered(mmc_stream_t *io, char *buf, size_t count TSRMLS_DC) /*
attempts to reads count bytes from the stream buffer {{{ */
{
@@ -922,6 +953,7 @@ void mmc_server_free(mmc_t *mmc TSRMLS_DC) /* {{{ */
mmc_pool_t *mmc_pool_new(TSRMLS_D) /* {{{ */
{
mmc_hash_function hash;
mmc_pool_t *pool = emalloc(sizeof(mmc_pool_t));
memset(pool, 0, sizeof(*pool));
@@ -932,8 +964,16 @@ mmc_pool_t *mmc_pool_new(TSRMLS_D) /* {{{ */
default:
pool->hash = &mmc_standard_hash;
}
switch (MEMCACHE_G(hash_function)) {
case MMC_HASH_FNV1A:
hash = &mmc_hash_fnv1a;
break;
default:
hash = &mmc_hash_crc32;
}
pool->hash_state = pool->hash->create_state();
pool->hash_state = pool->hash->create_state(hash);
pool->min_compress_savings = MMC_DEFAULT_SAVINGS;
pool->sending = &(pool->_sending1);

View File

@@ -69,6 +69,8 @@
#define MMC_STANDARD_HASH 1
#define MMC_CONSISTENT_HASH 2
#define MMC_HASH_CRC32 1 /* CRC32 hash function */
#define MMC_HASH_FNV1A 2 /* FNV-1a hash function */
#define MMC_CONSISTENT_POINTS 160 /* points per server */
#define MMC_CONSISTENT_BUCKETS 1024 /* number of precomputed buckets, should be power of 2 */
@@ -190,7 +192,8 @@ struct mmc {
};
/* hashing strategy */
typedef void * (*mmc_hash_create_state)();
typedef unsigned int (*mmc_hash_function)(const char *, int);
typedef void * (*mmc_hash_create_state)(mmc_hash_function);
typedef void (*mmc_hash_free_state)(void *);
typedef mmc_t * (*mmc_hash_find_server)(void *, const char *, int TSRMLS_DC);
typedef void (*mmc_hash_add_server)(void *, mmc_t *, unsigned int);
@@ -205,6 +208,10 @@ typedef struct mmc_hash {
extern mmc_hash_t mmc_standard_hash;
extern mmc_hash_t mmc_consistent_hash;
/* 32 bit magic FNV-1a prime and init */
#define FNV_32_PRIME 0x01000193
#define FNV_32_INIT 0x811c9dc5
/* server pool */
struct mmc_pool {
mmc_t **servers;
@@ -269,6 +276,7 @@ ZEND_BEGIN_MODULE_GLOBALS(memcache)
long default_port;
long chunk_size;
long hash_strategy;
long hash_function;
long allow_failover;
long max_failover_attempts;
ZEND_END_MODULE_GLOBALS(memcache)

View File

@@ -33,12 +33,14 @@ typedef struct mmc_standard_state {
int num_servers;
mmc_t **buckets;
int num_buckets;
mmc_hash_function hash;
} mmc_standard_state_t;
void *mmc_standard_create_state() /* {{{ */
void *mmc_standard_create_state(mmc_hash_function hash) /* {{{ */
{
mmc_standard_state_t *state = emalloc(sizeof(mmc_standard_state_t));
memset(state, 0, sizeof(mmc_standard_state_t));
state->hash = hash;
return state;
}
/* }}} */
@@ -55,26 +57,12 @@ void mmc_standard_free_state(void *s) /* {{{ */
}
/* }}} */
static unsigned int mmc_hash(const char *key, int key_len) /* {{{ */
{
unsigned int crc = ~0;
int i;
for (i=0; i<key_len; i++) {
CRC32(crc, key[i]);
}
crc = (~crc >> 16) & 0x7fff;
return crc ? crc : 1;
}
/* }}} */
mmc_t *mmc_standard_find_server(void *s, const char *key, int key_len TSRMLS_DC) /* {{{ */
{
mmc_standard_state_t *state = s;
if (state->num_servers > 1) {
return state->buckets[mmc_hash(key, key_len) % state->num_buckets];
return state->buckets[state->hash(key, key_len) % state->num_buckets];
}
return state->buckets[0];

View File

@@ -22,7 +22,7 @@ $result4 = $memcache->get(array($balanceKey1, $balanceKey2));
var_dump($result1);
var_dump($result2);
var_dump($result3);
var_dump(array_values($result4));
var_dump(is_array($result4) ? array_values($result4) : $result4);
$memcache = new Memcache();
$memcache->addServer($nonExistingHost, $nonExistingPort);

View File

@@ -27,7 +27,8 @@ var_dump($result3);
var_dump($result4);
$result = $memcache->get(array($balanceKey1, $balanceKey2));
sort($result);
if (is_array($result))
sort($result);
var_dump($result);
ini_set('memcache.allow_failover', 0);
@@ -39,7 +40,8 @@ var_dump($result1);
var_dump($result2);
$result = $memcache->get(array($balanceKey1, $balanceKey2));
sort($result);
if (is_array($result))
sort($result);
var_dump($result);
$result = ini_set('memcache.allow_failover', "abc");