Skip to content

Commit faf84af

Browse files
author
Mitch Hagstrand
committed
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
1 parent 7454211 commit faf84af

File tree

4 files changed

+77
-5
lines changed

4 files changed

+77
-5
lines changed

memcached.ini

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,24 @@ memcached.sess_lock_wait = 150000
1717
; the default value is "memc.sess.key."
1818
memcached.sess_prefix = "memc.sess.key."
1919

20+
21+
; Allow consistent hasing for the session
22+
memcached.sess_consistent_hashing = 1
23+
24+
; Allow failed memcached server to automatically be removed
25+
memcached.sess_remove_failed = 1
26+
27+
; Write data to a number of additional memcached servers
28+
; This is "poor man's HA" as libmemcached calls it.
29+
; If this value is positive and sess_remove_failed is enabled
30+
; when a memcached server fails the session will continue to be available
31+
; from a replica. However, if the failed memcache server
32+
; becomes available again it will read the session from there
33+
; which could have old data or no data at all
34+
memcached.sess_num_replicas = 0;
35+
2036
; memcached session binary mode
37+
; libmemcached replicas only work if binary mode is enabled
2138
memcached.sess_binary = Off
2239

2340
; Set the compression type

php_memcached.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -288,6 +288,9 @@ PHP_INI_BEGIN()
288288
STD_PHP_INI_ENTRY("memcached.sess_binary", "0", PHP_INI_ALL, OnUpdateBool, sess_binary_enabled, zend_php_memcached_globals, php_memcached_globals)
289289
STD_PHP_INI_ENTRY("memcached.sess_lock_wait", "150000", PHP_INI_ALL, OnUpdateLongGEZero,sess_lock_wait, zend_php_memcached_globals, php_memcached_globals)
290290
STD_PHP_INI_ENTRY("memcached.sess_prefix", "memc.sess.key.", PHP_INI_ALL, OnUpdateString, sess_prefix, zend_php_memcached_globals, php_memcached_globals)
291+
STD_PHP_INI_ENTRY("memcached.sess_consistent_hashing", "0", PHP_INI_ALL, OnUpdateBool, sess_consistent_hashing_enabled, zend_php_memcached_globals, php_memcached_globals)
292+
STD_PHP_INI_ENTRY("memcached.sess_num_replicas", "0", PHP_INI_ALL, OnUpdateLong, sess_num_replicas, zend_php_memcached_globals, php_memcached_globals)
293+
STD_PHP_INI_ENTRY("memcached.sess_remove_failed", "0", PHP_INI_ALL, OnUpdateBool, sess_remove_failed_enabled, zend_php_memcached_globals, php_memcached_globals)
291294
#endif
292295
STD_PHP_INI_ENTRY("memcached.compression_type", "fastlz", PHP_INI_ALL, OnUpdateCompressionType, compression_type, zend_php_memcached_globals, php_memcached_globals)
293296
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
30343037
#ifdef HAVE_MEMCACHED_SESSION
30353038
MEMC_G(sess_locking_enabled) = 1;
30363039
MEMC_G(sess_binary_enabled) = 1;
3040+
MEMC_G(sess_consistent_hashing_enabled) = 0;
3041+
MEMC_G(sess_num_replicas) = 0;
3042+
MEMC_G(sess_remove_failed_enabled) = 0;
30373043
MEMC_G(sess_prefix) = NULL;
30383044
MEMC_G(sess_lock_wait) = 0;
30393045
MEMC_G(sess_locked) = 0;

php_memcached.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,9 @@ ZEND_BEGIN_MODULE_GLOBALS(php_memcached)
6666
zend_bool sess_locked;
6767
char* sess_lock_key;
6868
int sess_lock_key_len;
69+
long sess_num_replicas;
70+
zend_bool sess_remove_failed_enabled;
71+
zend_bool sess_consistent_hashing_enabled;
6972
#endif
7073
char *serializer_name;
7174
enum memcached_serializer serializer;

php_memcached_session.c

Lines changed: 51 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ static int php_memc_sess_lock(memcached_st *memc, const char *key TSRMLS_DC)
4848
char *lock_key = NULL;
4949
int lock_key_len = 0;
5050
unsigned long attempts;
51+
long write_retry_attempts = 0;
5152
long lock_maxwait;
5253
long lock_wait = MEMC_G(sess_lock_wait);
5354
time_t expiration;
@@ -64,6 +65,11 @@ static int php_memc_sess_lock(memcached_st *memc, const char *key TSRMLS_DC)
6465
expiration = time(NULL) + lock_maxwait + 1;
6566
attempts = (unsigned long)((1000000.0 / lock_wait) * lock_maxwait);
6667

68+
/* Set the number of write retry attempts to the number of replicas times the number of attempts to remove a server */
69+
if (MEMC_G(sess_remove_failed_enabled)) {
70+
write_retry_attempts = MEMC_G(sess_num_replicas) * ( memcached_behavior_get(memc, MEMCACHED_BEHAVIOR_SERVER_FAILURE_LIMIT) + 1);
71+
}
72+
6773
lock_key_len = spprintf(&lock_key, 0, "lock.%s", key);
6874
do {
6975
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)
7379
MEMC_G(sess_lock_key_len) = lock_key_len;
7480
return 0;
7581
} else if (status != MEMCACHED_NOTSTORED && status != MEMCACHED_DATA_EXISTS) {
82+
if (write_retry_attempts > 0) {
83+
write_retry_attempts--;
84+
continue;
85+
}
86+
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Write of lock failed");
7687
break;
7788
}
7889

@@ -195,6 +206,30 @@ PS_OPEN_FUNC(memcached)
195206
}
196207
}
197208

209+
if (MEMC_G(sess_consistent_hashing_enabled)) {
210+
if (memcached_behavior_set(memc_sess->memc_sess, MEMCACHED_BEHAVIOR_KETAMA, (uint64_t) 1) == MEMCACHED_FAILURE) {
211+
php_error_docref(NULL TSRMLS_CC, E_WARNING, "failed to set memcached consistent hashing");
212+
return FAILURE;
213+
}
214+
}
215+
216+
/* Allow libmemcached remove failed servers */
217+
if (MEMC_G(sess_remove_failed_enabled)) {
218+
if (memcached_behavior_set(memc_sess->memc_sess, MEMCACHED_BEHAVIOR_REMOVE_FAILED_SERVERS, (uint64_t) 1) == MEMCACHED_FAILURE) {
219+
php_error_docref(NULL TSRMLS_CC, E_WARNING, "failed to set: remove failed servers");
220+
return FAILURE;
221+
}
222+
}
223+
224+
/* Allow replicas section */
225+
long num_replicas = MEMC_G(sess_num_replicas);
226+
if (num_replicas > 0) {
227+
/* Set the number of replicas libmemcached will use */
228+
if (memcached_behavior_set(memc_sess->memc_sess, MEMCACHED_BEHAVIOR_NUMBER_OF_REPLICAS, (uint64_t) num_replicas) == MEMCACHED_FAILURE) {
229+
php_error_docref(NULL TSRMLS_CC, E_WARNING, "failed to set memcached client replicating");
230+
return FAILURE;
231+
}
232+
}
198233
return SUCCESS;
199234
}
200235
}
@@ -243,6 +278,7 @@ PS_READ_FUNC(memcached)
243278

244279
if (MEMC_G(sess_locking_enabled)) {
245280
if (php_memc_sess_lock(memc_sess->memc_sess, key TSRMLS_CC) < 0) {
281+
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to clear session lock record");
246282
return FAILURE;
247283
}
248284
}
@@ -263,6 +299,7 @@ PS_WRITE_FUNC(memcached)
263299
{
264300
int key_len = strlen(key);
265301
time_t expiration = 0;
302+
long write_try_attempts = 1;
266303
memcached_return status;
267304
memcached_sess *memc_sess = PS_GET_MOD_DATA();
268305
size_t key_length;
@@ -277,13 +314,22 @@ PS_WRITE_FUNC(memcached)
277314
if (PS(gc_maxlifetime) > 0) {
278315
expiration = PS(gc_maxlifetime);
279316
}
280-
status = memcached_set(memc_sess->memc_sess, key, key_len, val, vallen, expiration, 0);
281317

282-
if (status == MEMCACHED_SUCCESS) {
283-
return SUCCESS;
284-
} else {
285-
return FAILURE;
318+
/* 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 */
319+
if (MEMC_G(sess_remove_failed_enabled)) {
320+
write_try_attempts = 1 + MEMC_G(sess_num_replicas) * ( memcached_behavior_get(memc_sess->memc_sess, MEMCACHED_BEHAVIOR_SERVER_FAILURE_LIMIT) + 1);
286321
}
322+
323+
do {
324+
status = memcached_set(memc_sess->memc_sess, key, key_len, val, vallen, expiration, 0);
325+
if (status == MEMCACHED_SUCCESS) {
326+
return SUCCESS;
327+
} else {
328+
write_try_attempts--;
329+
}
330+
} while (write_try_attempts > 0);
331+
332+
return FAILURE;
287333
}
288334

289335
PS_DESTROY_FUNC(memcached)

0 commit comments

Comments
 (0)