Permalink
Browse files

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
  • Loading branch information...
1 parent 7454211 commit faf84af78919cc5641675aac98c37fbf278d2fe8 Mitch Hagstrand committed Oct 3, 2012
Showing with 77 additions and 5 deletions.
  1. +17 −0 memcached.ini
  2. +6 −0 php_memcached.c
  3. +3 −0 php_memcached.h
  4. +51 −5 php_memcached_session.c
View
17 memcached.ini
@@ -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
6 php_memcached.c
@@ -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
3 php_memcached.h
@@ -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
56 php_memcached_session.c
@@ -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 @@ PS_OPEN_FUNC(memcached)
}
}
+ 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)

0 comments on commit faf84af

Please sign in to comment.