@@ -651,6 +651,9 @@ cluster_node_create(redisCluster *c, char *host, size_t host_len,
651651 node -> slave = slave ;
652652 node -> slaves = NULL ;
653653
654+ /* Initialize our list of slot ranges */
655+ zend_llist_init (& node -> slots , sizeof (redisSlotRange ), NULL , 0 );
656+
654657 // Attach socket
655658 node -> sock = redis_sock_create (host , host_len , port , c -> timeout ,
656659 c -> read_timeout , c -> persistent , NULL , 0 );
@@ -690,10 +693,11 @@ cluster_node_add_slave(redisClusterNode *master, redisClusterNode *slave)
690693
691694/* Use the output of CLUSTER SLOTS to map our nodes */
692695static int cluster_map_slots (redisCluster * c , clusterReply * r ) {
696+ redisClusterNode * pnode , * master , * slave ;
697+ redisSlotRange range ;
693698 int i ,j , hlen , klen ;
694699 short low , high ;
695700 clusterReply * r2 , * r3 ;
696- redisClusterNode * pnode , * master , * slave ;
697701 unsigned short port ;
698702 char * host , key [1024 ];
699703
@@ -746,6 +750,10 @@ static int cluster_map_slots(redisCluster *c, clusterReply *r) {
746750 for (j = low ; j <= high ; j ++ ) {
747751 c -> master [j ] = master ;
748752 }
753+
754+ /* Append to our list of slot ranges */
755+ range .low = low ; range .high = high ;
756+ zend_llist_add_element (& master -> slots , & range );
749757 }
750758
751759 // Success
@@ -758,7 +766,10 @@ PHP_REDIS_API void cluster_free_node(redisClusterNode *node) {
758766 zend_hash_destroy (node -> slaves );
759767 efree (node -> slaves );
760768 }
769+
770+ zend_llist_destroy (& node -> slots );
761771 redis_free_socket (node -> sock );
772+
762773 efree (node );
763774}
764775
@@ -802,6 +813,23 @@ static void ht_free_node(zval *data) {
802813 cluster_free_node (node );
803814}
804815
816+ /* zend_llist of slot ranges -> persistent array */
817+ static redisSlotRange * slot_range_list_clone (zend_llist * src , size_t * count ) {
818+ redisSlotRange * dst , * range ;
819+ size_t i = 0 ;
820+
821+ * count = zend_llist_count (src );
822+ dst = pemalloc (* count * sizeof (* dst ), 1 );
823+
824+ range = zend_llist_get_first (src );
825+ while (range ) {
826+ memcpy (& dst [i ++ ], range , sizeof (* range ));
827+ range = zend_llist_get_next (src );
828+ }
829+
830+ return dst ;
831+ }
832+
805833/* Construct a redisCluster object */
806834PHP_REDIS_API redisCluster * cluster_create (double timeout , double read_timeout ,
807835 int failover , int persistent )
@@ -860,10 +888,49 @@ cluster_free(redisCluster *c, int free_ctx TSRMLS_DC)
860888 /* Free any error we've got */
861889 if (c -> err ) zend_string_release (c -> err );
862890
891+ /* Invalidate our cache if we were redirected during operation */
892+ if (c -> cache_key ) {
893+ if (c -> redirections ) {
894+ zend_hash_del (& EG (persistent_list ), c -> cache_key );
895+ }
896+ zend_string_release (c -> cache_key );
897+ }
898+
863899 /* Free structure itself */
864900 if (free_ctx ) efree (c );
865901}
866902
903+ /* Create a cluster slot cache structure */
904+ PHP_REDIS_API
905+ redisCachedCluster * cluster_cache_create (zend_string * hash , HashTable * nodes ) {
906+ redisCachedCluster * cc ;
907+ redisCachedMaster * cm ;
908+ redisClusterNode * node ;
909+
910+ cc = pecalloc (1 , sizeof (* cc ), 1 );
911+ cc -> hash = zend_string_dup (hash , 1 );
912+
913+ /* Copy nodes */
914+ cc -> master = pecalloc (zend_hash_num_elements (nodes ), sizeof (* cc -> master ), 1 );
915+ ZEND_HASH_FOREACH_PTR (nodes , node ) {
916+ /* Skip slaves */
917+ if (node -> slave ) continue ;
918+
919+ cm = & cc -> master [cc -> count ];
920+
921+ /* Duplicate host/port and clone slot ranges */
922+ cm -> host .addr = zend_string_dup (node -> sock -> host , 1 );
923+ cm -> host .port = node -> sock -> port ;
924+
925+ /* Copy over slot ranges */
926+ cm -> slot = slot_range_list_clone (& node -> slots , & cm -> slots );
927+
928+ cc -> count ++ ;
929+ } ZEND_HASH_FOREACH_END ();
930+
931+ return cc ;
932+ }
933+
867934/* Takes our input hash table and returns a straigt C array with elements,
868935 * which have been randomized. The return value needs to be freed. */
869936static zval * * cluster_shuffle_seeds (HashTable * seeds , int * len ) {
@@ -892,6 +959,107 @@ static zval **cluster_shuffle_seeds(HashTable *seeds, int *len) {
892959 return z_seeds ;
893960}
894961
962+ static void cluster_free_cached_master (redisCachedMaster * cm ) {
963+ size_t i ;
964+
965+ /* Free each slave entry */
966+ for (i = 0 ; i < cm -> slaves ; i ++ ) {
967+ zend_string_release (cm -> slave [i ].addr );
968+ }
969+
970+ /* Free other elements */
971+ zend_string_release (cm -> host .addr );
972+ pefree (cm -> slave , 1 );
973+ pefree (cm -> slot , 1 );
974+ }
975+
976+ static redisClusterNode *
977+ cached_master_clone (redisCluster * c , redisCachedMaster * cm ) {
978+ redisClusterNode * node ;
979+ size_t i ;
980+
981+ node = cluster_node_create (c , ZSTR_VAL (cm -> host .addr ), ZSTR_LEN (cm -> host .addr ),
982+ cm -> host .port , cm -> slot [0 ].low , 0 );
983+
984+ /* Now copy in our slot ranges */
985+ for (i = 0 ; i < cm -> slots ; i ++ ) {
986+ zend_llist_add_element (& node -> slots , & cm -> slot [i ]);
987+ }
988+
989+ return node ;
990+ }
991+
992+ /* Destroy a persistent cached cluster */
993+ PHP_REDIS_API void cluster_cache_free (redisCachedCluster * rcc ) {
994+ size_t i ;
995+
996+ /* Free masters */
997+ for (i = 0 ; i < rcc -> count ; i ++ ) {
998+ cluster_free_cached_master (& rcc -> master [i ]);
999+ }
1000+
1001+ /* Free hash key */
1002+ zend_string_release (rcc -> hash );
1003+ pefree (rcc -> master , 1 );
1004+ pefree (rcc , 1 );
1005+ }
1006+
1007+ /* Initialize cluster from cached slots */
1008+ PHP_REDIS_API
1009+ void cluster_init_cache (redisCluster * c , redisCachedCluster * cc ) {
1010+ RedisSock * sock ;
1011+ redisClusterNode * mnode , * slave ;
1012+ redisCachedMaster * cm ;
1013+ char key [HOST_NAME_MAX ];
1014+ size_t keylen , i , j , s ;
1015+ int * map ;
1016+
1017+ /* Randomize seeds */
1018+ map = emalloc (sizeof (* map ) * cc -> count );
1019+ for (i = 0 ; i < cc -> count ; i ++ ) map [i ] = i ;
1020+ fyshuffle (map , cc -> count );
1021+
1022+ /* Iterate over masters */
1023+ for (i = 0 ; i < cc -> count ; i ++ ) {
1024+ /* Grab the next master */
1025+ cm = & cc -> master [map [i ]];
1026+
1027+ /* Hash our host and port */
1028+ keylen = snprintf (key , sizeof (key ), "%s:%u" , ZSTR_VAL (cm -> host .addr ),
1029+ cm -> host .port );
1030+
1031+ /* Create socket */
1032+ sock = redis_sock_create (ZSTR_VAL (cm -> host .addr ), ZSTR_LEN (cm -> host .addr ), cm -> host .port ,
1033+ c -> timeout , c -> read_timeout , c -> persistent ,
1034+ NULL , 0 );
1035+
1036+ /* Add to seed nodes */
1037+ zend_hash_str_update_ptr (c -> seeds , key , keylen , sock );
1038+
1039+ /* Create master node */
1040+ mnode = cached_master_clone (c , cm );
1041+
1042+ /* Add our master */
1043+ zend_hash_str_update_ptr (c -> nodes , key , keylen , mnode );
1044+
1045+ /* Attach any slaves */
1046+ for (s = 0 ; s < cm -> slaves ; s ++ ) {
1047+ zend_string * host = cm -> slave [s ].addr ;
1048+ slave = cluster_node_create (c , ZSTR_VAL (host ), ZSTR_LEN (host ), cm -> slave [s ].port , 0 , 1 );
1049+ cluster_node_add_slave (mnode , slave );
1050+ }
1051+
1052+ /* Hook up direct slot access */
1053+ for (j = 0 ; j < cm -> slots ; j ++ ) {
1054+ for (s = cm -> slot [j ].low ; s <= cm -> slot [j ].high ; s ++ ) {
1055+ c -> master [s ] = mnode ;
1056+ }
1057+ }
1058+ }
1059+
1060+ efree (map );
1061+ }
1062+
8951063/* Initialize seeds */
8961064PHP_REDIS_API int
8971065cluster_init_seeds (redisCluster * cluster , HashTable * ht_seeds ) {
@@ -908,6 +1076,7 @@ cluster_init_seeds(redisCluster *cluster, HashTable *ht_seeds) {
9081076 if ((z_seed = z_seeds [i ]) == NULL ) continue ;
9091077
9101078 ZVAL_DEREF (z_seed );
1079+
9111080 /* Has to be a string */
9121081 if (Z_TYPE_P (z_seed ) != IS_STRING ) continue ;
9131082
@@ -940,7 +1109,7 @@ cluster_init_seeds(redisCluster *cluster, HashTable *ht_seeds) {
9401109 efree (z_seeds );
9411110
9421111 // Success if at least one seed seems valid
943- return zend_hash_num_elements (cluster -> seeds ) > 0 ? 0 : -1 ;
1112+ return zend_hash_num_elements (cluster -> seeds ) > 0 ? SUCCESS : FAILURE ;
9441113}
9451114
9461115/* Initial mapping of our cluster keyspace */
@@ -977,10 +1146,10 @@ PHP_REDIS_API int cluster_map_keyspace(redisCluster *c TSRMLS_DC) {
9771146 zend_throw_exception (redis_cluster_exception_ce ,
9781147 "Couldn't map cluster keyspace using any provided seed" , 0
9791148 TSRMLS_CC );
980- return -1 ;
1149+ return FAILURE ;
9811150 }
9821151
983- return 0 ;
1152+ return SUCCESS ;
9841153}
9851154
9861155/* Parse the MOVED OR ASK redirection payload when we get such a response
0 commit comments