Added Memcached Session Replicas by changes below

1. Allow for automatic removal of failed server to session handler (MEMCACHED_BEHAVIOR_REMOVE_FAILED_SERVERS)
2. Allow X number of replicas (MEMCACHED_BEHAVIOR_NUMBER_OF_REPLICAS)
3. Added option for consistent hasing (MEMCACHED_BEHAVIOR_KETAMA)
4. Added error msg when session handler couldn't obtain a lock
This commit is contained in:
Mitch Hagstrand
2012-10-03 13:04:45 -07:00
parent 74542111f1
commit faf84af789
4 changed files with 77 additions and 5 deletions

View File

@@ -17,7 +17,24 @@ memcached.sess_lock_wait = 150000
; the default value is "memc.sess.key."
memcached.sess_prefix = "memc.sess.key."
; Allow consistent hasing for the session
memcached.sess_consistent_hashing = 1
; Allow failed memcached server to automatically be removed
memcached.sess_remove_failed = 1
; Write data to a number of additional memcached servers
; This is "poor man's HA" as libmemcached calls it.
; If this value is positive and sess_remove_failed is enabled
; when a memcached server fails the session will continue to be available
; from a replica. However, if the failed memcache server
; becomes available again it will read the session from there
; which could have old data or no data at all
memcached.sess_num_replicas = 0;
; memcached session binary mode
; libmemcached replicas only work if binary mode is enabled
memcached.sess_binary = Off
; Set the compression type

View File

@@ -288,6 +288,9 @@ PHP_INI_BEGIN()
STD_PHP_INI_ENTRY("memcached.sess_binary", "0", PHP_INI_ALL, OnUpdateBool, sess_binary_enabled, zend_php_memcached_globals, php_memcached_globals)
STD_PHP_INI_ENTRY("memcached.sess_lock_wait", "150000", PHP_INI_ALL, OnUpdateLongGEZero,sess_lock_wait, zend_php_memcached_globals, php_memcached_globals)
STD_PHP_INI_ENTRY("memcached.sess_prefix", "memc.sess.key.", PHP_INI_ALL, OnUpdateString, sess_prefix, zend_php_memcached_globals, php_memcached_globals)
STD_PHP_INI_ENTRY("memcached.sess_consistent_hashing", "0", PHP_INI_ALL, OnUpdateBool, sess_consistent_hashing_enabled, zend_php_memcached_globals, php_memcached_globals)
STD_PHP_INI_ENTRY("memcached.sess_num_replicas", "0", PHP_INI_ALL, OnUpdateLong, sess_num_replicas, zend_php_memcached_globals, php_memcached_globals)
STD_PHP_INI_ENTRY("memcached.sess_remove_failed", "0", PHP_INI_ALL, OnUpdateBool, sess_remove_failed_enabled, zend_php_memcached_globals, php_memcached_globals)
#endif
STD_PHP_INI_ENTRY("memcached.compression_type", "fastlz", PHP_INI_ALL, OnUpdateCompressionType, compression_type, zend_php_memcached_globals, php_memcached_globals)
STD_PHP_INI_ENTRY("memcached.compression_factor", "1.3", PHP_INI_ALL, OnUpdateReal, compression_factor, zend_php_memcached_globals, php_memcached_globals)
@@ -3034,6 +3037,9 @@ static void php_memc_init_globals(zend_php_memcached_globals *php_memcached_glob
#ifdef HAVE_MEMCACHED_SESSION
MEMC_G(sess_locking_enabled) = 1;
MEMC_G(sess_binary_enabled) = 1;
MEMC_G(sess_consistent_hashing_enabled) = 0;
MEMC_G(sess_num_replicas) = 0;
MEMC_G(sess_remove_failed_enabled) = 0;
MEMC_G(sess_prefix) = NULL;
MEMC_G(sess_lock_wait) = 0;
MEMC_G(sess_locked) = 0;

View File

@@ -66,6 +66,9 @@ ZEND_BEGIN_MODULE_GLOBALS(php_memcached)
zend_bool sess_locked;
char* sess_lock_key;
int sess_lock_key_len;
long sess_num_replicas;
zend_bool sess_remove_failed_enabled;
zend_bool sess_consistent_hashing_enabled;
#endif
char *serializer_name;
enum memcached_serializer serializer;

View File

@@ -48,6 +48,7 @@ static int php_memc_sess_lock(memcached_st *memc, const char *key TSRMLS_DC)
char *lock_key = NULL;
int lock_key_len = 0;
unsigned long attempts;
long write_retry_attempts = 0;
long lock_maxwait;
long lock_wait = MEMC_G(sess_lock_wait);
time_t expiration;
@@ -64,6 +65,11 @@ static int php_memc_sess_lock(memcached_st *memc, const char *key TSRMLS_DC)
expiration = time(NULL) + lock_maxwait + 1;
attempts = (unsigned long)((1000000.0 / lock_wait) * lock_maxwait);
/* Set the number of write retry attempts to the number of replicas times the number of attempts to remove a server */
if (MEMC_G(sess_remove_failed_enabled)) {
write_retry_attempts = MEMC_G(sess_num_replicas) * ( memcached_behavior_get(memc, MEMCACHED_BEHAVIOR_SERVER_FAILURE_LIMIT) + 1);
}
lock_key_len = spprintf(&lock_key, 0, "lock.%s", key);
do {
status = memcached_add(memc, lock_key, lock_key_len, "1", sizeof("1")-1, expiration, 0);
@@ -73,6 +79,11 @@ static int php_memc_sess_lock(memcached_st *memc, const char *key TSRMLS_DC)
MEMC_G(sess_lock_key_len) = lock_key_len;
return 0;
} else if (status != MEMCACHED_NOTSTORED && status != MEMCACHED_DATA_EXISTS) {
if (write_retry_attempts > 0) {
write_retry_attempts--;
continue;
}
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Write of lock failed");
break;
}
@@ -195,6 +206,30 @@ success:
}
}
if (MEMC_G(sess_consistent_hashing_enabled)) {
if (memcached_behavior_set(memc_sess->memc_sess, MEMCACHED_BEHAVIOR_KETAMA, (uint64_t) 1) == MEMCACHED_FAILURE) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "failed to set memcached consistent hashing");
return FAILURE;
}
}
/* Allow libmemcached remove failed servers */
if (MEMC_G(sess_remove_failed_enabled)) {
if (memcached_behavior_set(memc_sess->memc_sess, MEMCACHED_BEHAVIOR_REMOVE_FAILED_SERVERS, (uint64_t) 1) == MEMCACHED_FAILURE) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "failed to set: remove failed servers");
return FAILURE;
}
}
/* Allow replicas section */
long num_replicas = MEMC_G(sess_num_replicas);
if (num_replicas > 0) {
/* Set the number of replicas libmemcached will use */
if (memcached_behavior_set(memc_sess->memc_sess, MEMCACHED_BEHAVIOR_NUMBER_OF_REPLICAS, (uint64_t) num_replicas) == MEMCACHED_FAILURE) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "failed to set memcached client replicating");
return FAILURE;
}
}
return SUCCESS;
}
}
@@ -243,6 +278,7 @@ PS_READ_FUNC(memcached)
if (MEMC_G(sess_locking_enabled)) {
if (php_memc_sess_lock(memc_sess->memc_sess, key TSRMLS_CC) < 0) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to clear session lock record");
return FAILURE;
}
}
@@ -263,6 +299,7 @@ PS_WRITE_FUNC(memcached)
{
int key_len = strlen(key);
time_t expiration = 0;
long write_try_attempts = 1;
memcached_return status;
memcached_sess *memc_sess = PS_GET_MOD_DATA();
size_t key_length;
@@ -277,13 +314,22 @@ PS_WRITE_FUNC(memcached)
if (PS(gc_maxlifetime) > 0) {
expiration = PS(gc_maxlifetime);
}
status = memcached_set(memc_sess->memc_sess, key, key_len, val, vallen, expiration, 0);
if (status == MEMCACHED_SUCCESS) {
return SUCCESS;
} else {
return FAILURE;
/* Set the number of write retry attempts to the number of replicas times the number of attempts to remove a server plus the initial write */
if (MEMC_G(sess_remove_failed_enabled)) {
write_try_attempts = 1 + MEMC_G(sess_num_replicas) * ( memcached_behavior_get(memc_sess->memc_sess, MEMCACHED_BEHAVIOR_SERVER_FAILURE_LIMIT) + 1);
}
do {
status = memcached_set(memc_sess->memc_sess, key, key_len, val, vallen, expiration, 0);
if (status == MEMCACHED_SUCCESS) {
return SUCCESS;
} else {
write_try_attempts--;
}
} while (write_try_attempts > 0);
return FAILURE;
}
PS_DESTROY_FUNC(memcached)