Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit e2d2825

Browse files
committed
Implement failover for session GET.
Simply retrying with a failover server for now, clearing the exception flag.
1 parent f1231c9 commit e2d2825

File tree

2 files changed

+78
-31
lines changed

2 files changed

+78
-31
lines changed

README.markdown

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -40,22 +40,23 @@ Taken from [Compiling phpredis on Zend Server CE/OSX ](http://www.tumblr.com/tag
4040

4141
See also: [Install Redis & PHP Extension PHPRedis with Macports](http://www.lecloud.net/post/3378834922/install-redis-php-extension-phpredis-with-macports).
4242

43-
Session handler (new)
44-
==============
43+
Session handler
44+
===============
4545

4646
phpredis can be used to store PHP sessions. To do this, configure `session.save_handler` and `session.save_path` in your php.ini to tell phpredis where to store the sessions:
4747
<pre>
4848
session.save_handler = redis
49-
session.save_path = "tcp://host1:6379?weight=1, tcp://host2:6379?weight=2&timeout=2.5, tcp://host3:6379?weight=2"
49+
session.save_path = "tcp://host1:6379?weight=1, tcp://host2:6379?weight=2&timeout=2.5, tcp://host3:6379?weight=2&failover=host4:6379"
5050
</pre>
5151

5252
`session.save_path` can have a simple `host:port` format too, but you need to provide the `tcp://` scheme if you want to use the parameters. The following parameters are available:
5353

5454
* weight (integer): the weight of a host is used in comparison with the others in order to customize the session distribution on several hosts. If host A has twice the weight of host B, it will get twice the amount of sessions. In the example, *host1* stores 20% of all the sessions (1/(1+2+2)) while *host2* and *host3* each store 40% (2/1+2+2). The target host is determined once and for all at the start of the session, and doesn't change. The default weight is 1.
5555
* timeout (float): the connection timeout to a redis host, expressed in seconds. If the host is unreachable in that amount of time, the session storage will be unavailable for the client. The default timeout is very high (86400 seconds).
56-
* persistent (integer, should be 1 or 0): defines if a persistent connection should be used. **(experimental setting)**
56+
* persistent (integer, should be 1 or 0): defines if a persistent connection should be used.
5757
* prefix (string, defaults to "PHPREDIS_SESSION:"): used as a prefix to the Redis key in which the session is stored. The key is composed of the prefix followed by the session ID.
5858
* auth (string, empty by default): used to authenticate with the Redis server prior to sending commands.
59+
* failover (string, empty by default): identifies a separate host for failover, contacted only if the main host is unreachable.
5960

6061
Sessions have a lifetime expressed in seconds and stored in the INI variable "session.gc_maxlifetime". You can change it with [`ini_set()`](http://php.net/ini_set).
6162
The session handler requires a version of Redis with the `SETEX` command (at least 2.0).

redis_session.c

Lines changed: 73 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,8 @@ typedef struct redis_pool_member_ {
5353
char *auth;
5454
size_t auth_len;
5555

56+
RedisSock *failover_sock;
57+
5658
struct redis_pool_member_ *next;
5759

5860
} redis_pool_member;
@@ -73,7 +75,7 @@ redis_pool_new(TSRMLS_D) {
7375

7476
PHPAPI void
7577
redis_pool_add(redis_pool *pool, RedisSock *redis_sock, int weight,
76-
char *prefix, char *auth TSRMLS_DC) {
78+
char *prefix, char *auth, RedisSock *failover_sock TSRMLS_DC) {
7779

7880
redis_pool_member *rpm = ecalloc(1, sizeof(redis_pool_member));
7981
rpm->redis_sock = redis_sock;
@@ -85,6 +87,8 @@ redis_pool_add(redis_pool *pool, RedisSock *redis_sock, int weight,
8587
rpm->auth = auth;
8688
rpm->auth_len = (auth?strlen(auth):0);
8789

90+
rpm->failover_sock = failover_sock;
91+
8892
rpm->next = pool->head;
8993
pool->head = rpm;
9094

@@ -108,22 +112,32 @@ redis_pool_free(redis_pool *pool TSRMLS_DC) {
108112
efree(pool);
109113
}
110114

115+
static void
116+
redis_auth(RedisSock *sock, char *cmd, int cmd_len TSRMLS_DC) {
117+
118+
char *response;
119+
int response_len;
120+
121+
if(redis_sock_write(sock, cmd, cmd_len TSRMLS_CC) >= 0) {
122+
if ((response = redis_sock_read(sock, &response_len TSRMLS_CC))) {
123+
efree(response);
124+
}
125+
}
126+
}
127+
111128
void
112129
redis_pool_member_auth(redis_pool_member *rpm TSRMLS_DC) {
113-
RedisSock *redis_sock = rpm->redis_sock;
114-
char *response, *cmd;
115-
int response_len, cmd_len;
130+
char *cmd;
131+
int cmd_len;
116132

117133
if(!rpm->auth || !rpm->auth_len) { /* no password given. */
118134
return;
119135
}
120136
cmd_len = redis_cmd_format_static(&cmd, "AUTH", "s", rpm->auth, rpm->auth_len);
121137

122-
if(redis_sock_write(redis_sock, cmd, cmd_len TSRMLS_CC) >= 0) {
123-
if ((response = redis_sock_read(redis_sock, &response_len TSRMLS_CC))) {
124-
efree(response);
125-
}
126-
}
138+
redis_auth(rpm->redis_sock, cmd, cmd_len TSRMLS_CC);
139+
if(rpm->failover_sock)
140+
redis_auth(rpm->failover_sock, cmd, cmd_len TSRMLS_CC);
127141
efree(cmd);
128142
}
129143

@@ -143,6 +157,9 @@ redis_pool_get_sock(redis_pool *pool, const char *key TSRMLS_DC) {
143157
needs_auth = 1;
144158
}
145159
redis_sock_server_open(rpm->redis_sock, 0 TSRMLS_CC);
160+
if(rpm->failover_sock)
161+
redis_sock_server_open(rpm->failover_sock, 0 TSRMLS_CC);
162+
146163
if(needs_auth) {
147164
redis_pool_member_auth(rpm TSRMLS_CC);
148165
}
@@ -181,7 +198,7 @@ PS_OPEN_FUNC(redis)
181198
int weight = 1;
182199
double timeout = 86400.0;
183200
int persistent = 0;
184-
char *prefix = NULL, *auth = NULL, *persistent_id = NULL;
201+
char *prefix = NULL, *auth = NULL, *persistent_id = NULL, *failover = NULL;
185202

186203
/* translate unix: into file: */
187204
if (!strncmp(save_path+i, "unix:", sizeof("unix:")-1)) {
@@ -232,6 +249,9 @@ PS_OPEN_FUNC(redis)
232249
if (zend_hash_find(Z_ARRVAL_P(params), "auth", sizeof("auth"), (void **) &param) != FAILURE) {
233250
auth = estrndup(Z_STRVAL_PP(param), Z_STRLEN_PP(param));
234251
}
252+
if (zend_hash_find(Z_ARRVAL_P(params), "failover", sizeof("failover"), (void **) &param) != FAILURE) {
253+
failover = estrndup(Z_STRVAL_PP(param), Z_STRLEN_PP(param));
254+
}
235255

236256
/* // not supported yet
237257
if (zend_hash_find(Z_ARRVAL_P(params), "retry_interval", sizeof("retry_interval"), (void **) &param) != FAILURE) {
@@ -250,13 +270,25 @@ PS_OPEN_FUNC(redis)
250270
return FAILURE;
251271
}
252272

253-
RedisSock *redis_sock;
273+
RedisSock *redis_sock, *failover_sock = NULL;
254274
if(url->path) { /* unix */
255275
redis_sock = redis_sock_create(url->path, strlen(url->path), 0, timeout, persistent, persistent_id);
256276
} else {
257277
redis_sock = redis_sock_create(url->host, strlen(url->host), url->port, timeout, persistent, persistent_id);
258278
}
259-
redis_pool_add(pool, redis_sock, weight, prefix, auth TSRMLS_CC);
279+
280+
if(failover) {
281+
short port = 6379;
282+
char *p = strchr(failover, ':');
283+
size_t sz = strlen(failover);
284+
if(p) {
285+
port = atoi(p+1);
286+
sz = p - failover;
287+
}
288+
failover_sock = redis_sock_create(failover, sz, port, timeout, persistent, persistent_id);
289+
}
290+
291+
redis_pool_add(pool, redis_sock, weight, prefix, auth, failover_sock TSRMLS_CC);
260292

261293
php_url_free(url);
262294
}
@@ -307,38 +339,52 @@ redis_session_key(redis_pool_member *rpm, const char *key, int key_len, int *ses
307339
return session;
308340
}
309341

310-
311-
/* {{{ PS_READ_FUNC
312-
*/
313-
PS_READ_FUNC(redis)
342+
static int
343+
get_session(redis_pool_member *rpm, RedisSock *sock, const char *key, char **val, int *vallen)
314344
{
315345
char *session, *cmd;
316-
int session_len, cmd_len;
346+
int session_len, cmd_len, ret;
317347

318-
redis_pool *pool = PS_GET_MOD_DATA();
319-
redis_pool_member *rpm = redis_pool_get_sock(pool, key TSRMLS_CC);
320-
RedisSock *redis_sock = rpm?rpm->redis_sock:NULL;
321-
if(!rpm || !redis_sock){
322-
return FAILURE;
323-
}
324-
325-
/* send GET command */
348+
/* send GET command */
326349
session = redis_session_key(rpm, key, strlen(key), &session_len);
327350
cmd_len = redis_cmd_format_static(&cmd, "GET", "s", session, session_len);
328351
efree(session);
329-
if(redis_sock_write(redis_sock, cmd, cmd_len TSRMLS_CC) < 0) {
352+
if(redis_sock_write(sock, cmd, cmd_len TSRMLS_CC) < 0) {
330353
efree(cmd);
331354
return FAILURE;
332355
}
333356
efree(cmd);
334357

335358
/* read response */
336-
if ((*val = redis_sock_read(redis_sock, vallen TSRMLS_CC)) == NULL) {
359+
if ((*val = redis_sock_read(sock, vallen TSRMLS_CC)) == NULL) {
337360
return FAILURE;
338361
}
339362

340363
return SUCCESS;
341364
}
365+
366+
/* {{{ PS_READ_FUNC
367+
*/
368+
PS_READ_FUNC(redis)
369+
{
370+
int ret;
371+
372+
redis_pool *pool = PS_GET_MOD_DATA();
373+
redis_pool_member *rpm = redis_pool_get_sock(pool, key TSRMLS_CC);
374+
RedisSock *redis_sock = rpm?rpm->redis_sock:NULL, *failover_sock;
375+
if(!rpm || !redis_sock){
376+
return FAILURE;
377+
}
378+
379+
/* read session from main server or failover. */
380+
ret = get_session(rpm, redis_sock, key, val, vallen);
381+
if(EG(exception) && rpm->failover_sock) {
382+
zend_clear_exception(TSRMLS_C); /* clear exception flag. */
383+
ret = get_session(rpm, rpm->failover_sock, key, val, vallen);
384+
}
385+
386+
return ret;
387+
}
342388
/* }}} */
343389

344390
/* {{{ PS_WRITE_FUNC

0 commit comments

Comments
 (0)