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

Skip to content

Commit fcea71f

Browse files
Initial commit for cluster-enabled session handling
1 parent 4ce910a commit fcea71f

File tree

5 files changed

+322
-2
lines changed

5 files changed

+322
-2
lines changed

cluster_library.c

+63-1
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ void cluster_free_reply(clusterReply *reply, int free_data) {
9797
case TYPE_ERR:
9898
case TYPE_LINE:
9999
case TYPE_BULK:
100-
if(free_data)
100+
if(free_data && reply->str)
101101
efree(reply->str);
102102
break;
103103
case TYPE_MULTIBULK:
@@ -785,6 +785,68 @@ static RedisSock *cluster_get_asking_sock(redisCluster *c TSRMLS_DC) {
785785
return cluster_get_asking_node(c TSRMLS_CC)->sock;
786786
}
787787

788+
/* Our context seeds will be a hash table with RedisSock* pointers */
789+
static void ht_free_seed(void *data) {
790+
RedisSock *redis_sock = *(RedisSock**)data;
791+
if(redis_sock) redis_free_socket(redis_sock);
792+
}
793+
794+
/* Free redisClusterNode objects we've stored */
795+
static void ht_free_node(void *data) {
796+
redisClusterNode *node = *(redisClusterNode**)data;
797+
cluster_free_node(node);
798+
}
799+
800+
/* Construct a redisCluster object */
801+
PHP_REDIS_API redisCluster *cluster_create(double timeout, double read_timeout,
802+
int failover)
803+
{
804+
redisCluster *c;
805+
806+
/* Actual our actual cluster structure */
807+
c = ecalloc(1, sizeof(redisCluster));
808+
809+
/* Initialize flags and settings */
810+
c->flags = ecalloc(1, sizeof(RedisSock));
811+
c->subscribed_slot = -1;
812+
c->clusterdown = 0;
813+
c->timeout = timeout;
814+
c->read_timeout = read_timeout;
815+
816+
/* Set up our waitms based on timeout */
817+
c->waitms = (long)(1000 * timeout);
818+
819+
/* Allocate our seeds hash table */
820+
ALLOC_HASHTABLE(c->seeds);
821+
zend_hash_init(c->seeds, 0, NULL, ht_free_seed, 0);
822+
823+
/* Allocate our nodes HashTable */
824+
ALLOC_HASHTABLE(c->nodes);
825+
zend_hash_init(c->nodes, 0, NULL, ht_free_node, 0);
826+
827+
return c;
828+
}
829+
830+
PHP_REDIS_API void cluster_free(redisCluster *c) {
831+
/* Free any allocated prefix */
832+
if (c->flags->prefix) efree(c->flags->prefix);
833+
efree(c->flags);
834+
835+
/* Call hash table destructors */
836+
zend_hash_destroy(c->seeds);
837+
zend_hash_destroy(c->nodes);
838+
839+
/* Free hash tables themselves */
840+
efree(c->seeds);
841+
efree(c->nodes);
842+
843+
/* Free any error we've got */
844+
if (c->err) efree(c->err);
845+
846+
/* Free structure itself */
847+
efree(c);
848+
}
849+
788850
/* Initialize seeds */
789851
PHP_REDIS_API int
790852
cluster_init_seeds(redisCluster *cluster, HashTable *ht_seeds) {

cluster_library.h

+3
Original file line numberDiff line numberDiff line change
@@ -358,6 +358,9 @@ PHP_REDIS_API short cluster_find_slot(redisCluster *c, const char *host,
358358
PHP_REDIS_API int cluster_send_slot(redisCluster *c, short slot, char *cmd,
359359
int cmd_len, REDIS_REPLY_TYPE rtype TSRMLS_DC);
360360

361+
PHP_REDIS_API redisCluster *cluster_create(double timeout, double read_timeout,
362+
int failover);
363+
PHP_REDIS_API void cluster_free(redisCluster *c);
361364
PHP_REDIS_API int cluster_init_seeds(redisCluster *c, HashTable *ht_seeds);
362365
PHP_REDIS_API int cluster_map_keyspace(redisCluster *c TSRMLS_DC);
363366
PHP_REDIS_API void cluster_free_node(redisClusterNode *node);

redis.c

+2
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ extern int le_redis_array;
5252

5353
#ifdef PHP_SESSION
5454
extern ps_module ps_mod_redis;
55+
extern ps_module ps_mod_redis_cluster;
5556
#endif
5657

5758
extern zend_class_entry *redis_array_ce;
@@ -541,6 +542,7 @@ static void add_class_constants(zend_class_entry *ce, int is_cluster TSRMLS_DC)
541542

542543
#ifdef PHP_SESSION
543544
php_session_register_module(&ps_mod_redis);
545+
php_session_register_module(&ps_mod_redis_cluster);
544546
#endif
545547
}
546548

redis_session.c

+248-1
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
#include <zend_exceptions.h>
3535

3636
#include "library.h"
37+
#include "cluster_library.h"
3738

3839
#include "php.h"
3940
#include "php_ini.h"
@@ -44,6 +45,9 @@
4445
ps_module ps_mod_redis = {
4546
PS_MOD(redis)
4647
};
48+
ps_module ps_mod_redis_cluster = {
49+
PS_MOD(rediscluster)
50+
};
4751

4852
typedef struct redis_pool_member_ {
4953

@@ -333,7 +337,6 @@ redis_session_key(redis_pool_member *rpm, const char *key, int key_len, int *ses
333337
return session;
334338
}
335339

336-
337340
/* {{{ PS_READ_FUNC
338341
*/
339342
PS_READ_FUNC(redis)
@@ -457,5 +460,249 @@ PS_GC_FUNC(redis)
457460
}
458461
/* }}} */
459462

463+
/**
464+
* Redis Cluster session handler functions
465+
*/
466+
467+
/* Helper to extract timeout values */
468+
static void session_conf_timeout(HashTable *ht_conf, const char *key, int key_len,
469+
double *val)
470+
{
471+
zval **z_val;
472+
473+
if (zend_hash_find(ht_conf, key, key_len, (void**)&z_val) == SUCCESS) {
474+
if (Z_TYPE_PP(z_val) == IS_STRING) {
475+
*val = atof(Z_STRVAL_PP(z_val));
476+
} else {
477+
*val = Z_DVAL_PP(z_val);
478+
}
479+
}
480+
}
481+
482+
/* Prefix a session key */
483+
static char *cluster_session_key(redisCluster *c, const char *key, int keylen,
484+
int *skeylen, short *slot) {
485+
char *skey;
486+
487+
*skeylen = keylen + c->flags->prefix_len;
488+
skey = emalloc(*skeylen);
489+
memcpy(skey, c->flags->prefix, c->flags->prefix_len);
490+
memcpy(skey + c->flags->prefix_len, key, keylen);
491+
492+
*slot = cluster_hash_key(skey, *skeylen);
493+
494+
return skey;
495+
}
496+
497+
PS_OPEN_FUNC(rediscluster) {
498+
redisCluster *c;
499+
zval *z_conf, **z_val;
500+
HashTable *ht_conf, *ht_seeds;
501+
double timeout = 0, read_timeout = 0;
502+
int retval, prefix_len, failover = REDIS_FAILOVER_NONE;
503+
char *prefix;
504+
505+
/* Parse configuration for session handler */
506+
MAKE_STD_ZVAL(z_conf);
507+
array_init(z_conf);
508+
sapi_module.treat_data(PARSE_STRING, estrdup(save_path), z_conf TSRMLS_CC);
509+
510+
/* Sanity check that we're able to parse and have a seeds array */
511+
if (Z_TYPE_P(z_conf) != IS_ARRAY ||
512+
zend_hash_find(Z_ARRVAL_P(z_conf), "seed", sizeof("seed"), (void**)&z_val) == FAILURE ||
513+
Z_TYPE_PP(z_val) != IS_ARRAY)
514+
{
515+
zval_dtor(z_conf);
516+
efree(z_conf);
517+
return FAILURE;
518+
}
519+
520+
/* Grab a copy of our config hash table and keep seeds array */
521+
ht_conf = Z_ARRVAL_P(z_conf);
522+
ht_seeds = Z_ARRVAL_PP(z_val);
523+
524+
/* Grab timeouts if they were specified */
525+
session_conf_timeout(ht_conf, "timeout", sizeof("timeout"), &timeout);
526+
session_conf_timeout(ht_conf, "read_timeout", sizeof("read_timeout"), &read_timeout);
527+
528+
/* Look for a specific prefix */
529+
if (zend_hash_find(ht_conf, "prefix", sizeof("prefix"), (void**)&z_val) == SUCCESS &&
530+
Z_TYPE_PP(z_val) == IS_STRING && Z_STRLEN_PP(z_val) > 0)
531+
{
532+
prefix = Z_STRVAL_PP(z_val);
533+
prefix_len = Z_STRLEN_PP(z_val);
534+
} else {
535+
prefix = "PHPREDIS_CLUSTER_SESSION:";
536+
prefix_len = sizeof("PHPREDIS_CLUSTER_SESSION:")-1;
537+
}
538+
539+
/* Look for a specific failover setting */
540+
if (zend_hash_find(ht_conf, "failover", sizeof("failover"), (void**)&z_val) == SUCCESS &&
541+
Z_TYPE_PP(z_val) == IS_STRING)
542+
{
543+
if (!strcasecmp(Z_STRVAL_PP(z_val), "error")) {
544+
failover = REDIS_FAILOVER_ERROR;
545+
} else if (!strcasecmp(Z_STRVAL_PP(z_val), "distribute")) {
546+
failover = REDIS_FAILOVER_DISTRIBUTE;
547+
}
548+
}
549+
550+
c = cluster_create(timeout, read_timeout, failover);
551+
if (!cluster_init_seeds(c, ht_seeds) && !cluster_map_keyspace(c TSRMLS_CC)) {
552+
/* Set up our prefix */
553+
c->flags->prefix = estrndup(prefix, prefix_len);
554+
c->flags->prefix_len = prefix_len;
555+
556+
PS_SET_MOD_DATA(c);
557+
retval = SUCCESS;
558+
} else {
559+
cluster_free(c);
560+
retval = FAILURE;
561+
}
562+
563+
/* Cleanup */
564+
zval_dtor(z_conf);
565+
efree(z_conf);
566+
567+
return retval;
568+
}
569+
570+
/* {{{ PS_READ_FUNC
571+
*/
572+
PS_READ_FUNC(rediscluster) {
573+
redisCluster *c = PS_GET_MOD_DATA();
574+
clusterReply *reply;
575+
char *cmd, *skey;
576+
int cmdlen, skeylen;
577+
short slot;
578+
579+
/* Set up our command and slot information */
580+
skey = cluster_session_key(c, key, strlen(key), &skeylen, &slot);
581+
cmdlen = redis_cmd_format_static(&cmd, "GET", "s", skey, skeylen);
582+
efree(skey);
583+
584+
/* Attempt to kick off our command */
585+
c->readonly = 1;
586+
if (cluster_send_command(c,slot,cmd,cmdlen TSRMLS_CC)<0 || c->err) {
587+
efree(cmd);
588+
return FAILURE;
589+
}
590+
591+
/* Clean up command */
592+
efree(cmd);
593+
594+
/* Attempt to read reply */
595+
reply = cluster_read_resp(c TSRMLS_CC);
596+
if (!reply || c->err) {
597+
if (reply) cluster_free_reply(reply, 1);
598+
return FAILURE;
599+
}
600+
601+
/* Push reply value to caller */
602+
*val = reply->str;
603+
*vallen = reply->len;
604+
605+
/* Clean up */
606+
cluster_free_reply(reply, 0);
607+
608+
/* Success! */
609+
return SUCCESS;
610+
}
611+
612+
/* {{{ PS_WRITE_FUNC
613+
*/
614+
PS_WRITE_FUNC(rediscluster) {
615+
redisCluster *c = PS_GET_MOD_DATA();
616+
clusterReply *reply;
617+
char *cmd, *skey;
618+
int cmdlen, skeylen;
619+
short slot;
620+
621+
/* Set up command and slot info */
622+
skey = cluster_session_key(c, key, strlen(key), &skeylen, &slot);
623+
cmdlen = redis_cmd_format_static(&cmd, "SETEX", "sds", skey, skeylen,
624+
INI_INT("session.gc_maxlifetime"),
625+
val, vallen);
626+
efree(skey);
627+
628+
/* Attempt to send command */
629+
c->readonly = 0;
630+
if (cluster_send_command(c,slot,cmd,cmdlen TSRMLS_CC)<0 || c->err) {
631+
efree(cmd);
632+
return FAILURE;
633+
}
634+
635+
/* Clean up our command */
636+
efree(cmd);
637+
638+
/* Attempt to read reply */
639+
reply = cluster_read_resp(c TSRMLS_CC);
640+
if (!reply || c->err) {
641+
if (reply) cluster_free_reply(reply, 1);
642+
return FAILURE;
643+
}
644+
645+
/* Clean up*/
646+
cluster_free_reply(reply, 1);
647+
648+
return SUCCESS;
649+
}
650+
651+
/* {{{ PS_DESTROY_FUNC(rediscluster)
652+
*/
653+
PS_DESTROY_FUNC(rediscluster) {
654+
redisCluster *c = PS_GET_MOD_DATA();
655+
clusterReply *reply;
656+
char *cmd, *skey;
657+
int cmdlen, skeylen;
658+
short slot;
659+
660+
/* Set up command and slot info */
661+
skey = cluster_session_key(c, key, strlen(key), &skeylen, &slot);
662+
cmdlen = redis_cmd_format_static(&cmd, "DEL", "s", skey, skeylen);
663+
efree(skey);
664+
665+
/* Attempt to send command */
666+
if (cluster_send_command(c,slot,cmd,cmdlen TSRMLS_CC)<0 || c->err) {
667+
if (reply) cluster_free_reply(reply, 1);
668+
efree(cmd);
669+
return FAILURE;
670+
}
671+
672+
/* Clean up our command */
673+
efree(cmd);
674+
675+
/* Attempt to read reply */
676+
reply = cluster_read_resp(c TSRMLS_CC);
677+
if (!reply || c->err) {
678+
if (reply) cluster_free_reply(reply, 1);
679+
return FAILURE;
680+
}
681+
682+
/* Clean up our reply */
683+
cluster_free_reply(reply, 1);
684+
685+
return SUCCESS;
686+
}
687+
688+
/* {{{ PS_CLOSE_FUNC
689+
*/
690+
PS_CLOSE_FUNC(rediscluster)
691+
{
692+
redisCluster *c = PS_GET_MOD_DATA();
693+
if (c) {
694+
cluster_free(c);
695+
PS_SET_MOD_DATA(NULL);
696+
}
697+
return SUCCESS;
698+
}
699+
700+
/* {{{ PS_GC_FUNC
701+
*/
702+
PS_GC_FUNC(rediscluster) {
703+
return SUCCESS;
704+
}
705+
460706
#endif
707+
461708
/* vim: set tabstop=4 expandtab: */

redis_session.h

+6
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,12 @@ PS_WRITE_FUNC(redis);
1010
PS_DESTROY_FUNC(redis);
1111
PS_GC_FUNC(redis);
1212

13+
PS_OPEN_FUNC(rediscluster);
14+
PS_CLOSE_FUNC(rediscluster);
15+
PS_READ_FUNC(rediscluster);
16+
PS_WRITE_FUNC(rediscluster);
17+
PS_DESTROY_FUNC(rediscluster);
18+
PS_GC_FUNC(rediscluster);
1319

1420
#endif
1521
#endif

0 commit comments

Comments
 (0)