diff --git a/README.markdown b/README.markdown index 98df2278a8..7c8e2fbaca 100644 --- a/README.markdown +++ b/README.markdown @@ -82,7 +82,7 @@ You can install install it using Homebrew: 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: ~~~ session.save_handler = redis -session.save_path = "tcp://host1:6379?weight=1, tcp://host2:6379?weight=2&timeout=2.5, tcp://host3:6379?weight=2" +session.save_path = "tcp://host1:6379?weight=1, tcp://host2:6379?weight=2&timeout=2.5, tcp://host3:6379?weight=2&read_timeout=2.5" ~~~ `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: @@ -202,6 +202,7 @@ _**Description**_: Connects to a Redis instance. *timeout*: float, value in seconds (optional, default is 0 meaning unlimited) *reserved*: should be NULL if retry_interval is specified *retry_interval*: int, value in milliseconds (optional) +*read_timeout*: float, value in seconds (optional, default is 0 meaning unlimited) ##### *Return value* @@ -238,6 +239,7 @@ persistent equivalents. *timeout*: float, value in seconds (optional, default is 0 meaning unlimited) *persistent_id*: string. identity for the requested persistent connection *retry_interval*: int, value in milliseconds (optional) +*read_timeout*: float, value in seconds (optional, default is 0 meaning unlimited) ##### *Return value* diff --git a/arrays.markdown b/arrays.markdown index efc3f05db4..c41ef616cb 100644 --- a/arrays.markdown +++ b/arrays.markdown @@ -55,6 +55,13 @@ The connect_timeout value is a double and is used to specify a timeout in number $ra = new RedisArray(array("host1", "host2:63792", "host2:6380"), array("connect_timeout" => 0.5)); +#### Specifying the "read_timeout" parameter +The read_timeout value is a double and is used to specify a timeout in number of seconds when waiting response from the server. +
+$ra = new RedisArray(array("host1", "host2:63792", "host2:6380"), array("read_timeout" => 0.5));
+
+ + #### Defining arrays in Redis.ini Because php.ini parameters must be pre-defined, Redis Arrays must all share the same .ini settings. diff --git a/cluster.markdown b/cluster.markdown index 2ba3ee0610..336fcc4986 100644 --- a/cluster.markdown +++ b/cluster.markdown @@ -13,15 +13,11 @@ To maintain consistency with the RedisArray class, one can create and connect to $obj_cluster = new RedisCluster(NULL, Array('host:7000', 'host:7001', 'host:7003')); // Connect and specify timeout and read_timeout -$obj_cluster = new RedisCluster( - NULL, Array("host:7000", "host:7001"), 1.5, 1.5 -); +$obj_cluster = new RedisCluster(NULL, Array("host:7000", "host:7001"), 1.5, 1.5); // Connect with read/write timeout as well as specify that phpredis should use // persistent connections to each node. -$obj_cluster = new RedisCluster( - NULL, Array("host:7000", "host:7001"), 1.5, 1.5, true -); +$obj_cluster = new RedisCluster(NULL, Array("host:7000", "host:7001"), 1.5, 1.5, true); diff --git a/cluster_library.c b/cluster_library.c index d37f29ef20..c1efd0db6a 100644 --- a/cluster_library.c +++ b/cluster_library.c @@ -649,7 +649,7 @@ cluster_node_create(redisCluster *c, char *host, size_t host_len, // Attach socket node->sock = redis_sock_create(host, host_len, port, c->timeout, - c->persistent, NULL, 0, 1); + c->read_timeout, c->persistent, NULL, 0, 1); return node; } @@ -916,7 +916,7 @@ cluster_init_seeds(redisCluster *cluster, HashTable *ht_seeds) { // Allocate a structure for this seed redis_sock = redis_sock_create(str, psep-str, (unsigned short)atoi(psep+1), cluster->timeout, - cluster->persistent, NULL, 0, 0); + cluster->read_timeout, cluster->persistent, NULL, 0, 0); // Index this seed by host/port key_len = snprintf(key, sizeof(key), "%s:%u", redis_sock->host, diff --git a/library.c b/library.c index 09cd7d3adc..4f3ac4ed4a 100644 --- a/library.c +++ b/library.c @@ -1467,9 +1467,10 @@ PHP_REDIS_API void redis_debug_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock * redis_sock_create */ PHP_REDIS_API RedisSock* -redis_sock_create(char *host, int host_len, unsigned short port, double timeout, - int persistent, char *persistent_id, long retry_interval, - zend_bool lazy_connect) +redis_sock_create(char *host, int host_len, unsigned short port, + double timeout, double read_timeout, + int persistent, char *persistent_id, + long retry_interval, zend_bool lazy_connect) { RedisSock *redis_sock; @@ -1490,7 +1491,7 @@ redis_sock_create(char *host, int host_len, unsigned short port, double timeout, redis_sock->port = port; redis_sock->timeout = timeout; - redis_sock->read_timeout = timeout; + redis_sock->read_timeout = read_timeout; redis_sock->serializer = REDIS_SERIALIZER_NONE; redis_sock->mode = ATOMIC; diff --git a/library.h b/library.h index 62a38be441..0fa42dfe6d 100644 --- a/library.h +++ b/library.h @@ -33,7 +33,7 @@ PHP_REDIS_API void redis_info_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock * PHP_REDIS_API void redis_parse_info_response(char *response, zval *z_ret); PHP_REDIS_API void redis_parse_client_list_response(char *response, zval *z_ret); PHP_REDIS_API void redis_type_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); -PHP_REDIS_API RedisSock* redis_sock_create(char *host, int host_len, unsigned short port, double timeout, int persistent, char *persistent_id, long retry_interval, zend_bool lazy_connect); +PHP_REDIS_API RedisSock* redis_sock_create(char *host, int host_len, unsigned short port, double timeout, double read_timeout, int persistent, char *persistent_id, long retry_interval, zend_bool lazy_connect); PHP_REDIS_API int redis_sock_connect(RedisSock *redis_sock TSRMLS_DC); PHP_REDIS_API int redis_sock_server_open(RedisSock *redis_sock TSRMLS_DC); PHP_REDIS_API int redis_sock_disconnect(RedisSock *redis_sock TSRMLS_DC); diff --git a/redis.c b/redis.c index 46d99c65f4..c9996bd006 100644 --- a/redis.c +++ b/redis.c @@ -807,7 +807,7 @@ redis_connect(INTERNAL_FUNCTION_PARAMETERS, int persistent) char *host = NULL, *persistent_id = NULL; zend_long port = -1, retry_interval = 0; strlen_t host_len, persistent_id_len; - double timeout = 0.0; + double timeout = 0.0, read_timeout = 0.0; redis_object *redis; #ifdef ZTS @@ -817,10 +817,10 @@ redis_connect(INTERNAL_FUNCTION_PARAMETERS, int persistent) #endif if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), - "Os|ldsl", &object, redis_ce, &host, + "Os|ldsld", &object, redis_ce, &host, &host_len, &port, &timeout, &persistent_id, - &persistent_id_len, &retry_interval) - == FAILURE) + &persistent_id_len, &retry_interval, + &read_timeout) == FAILURE) { return FAILURE; } else if (!persistent) { @@ -828,8 +828,14 @@ redis_connect(INTERNAL_FUNCTION_PARAMETERS, int persistent) } if (timeout < 0L || timeout > INT_MAX) { - zend_throw_exception(redis_exception_ce, "Invalid timeout", - 0 TSRMLS_CC); + zend_throw_exception(redis_exception_ce, + "Invalid connect timeout", 0 TSRMLS_CC); + return FAILURE; + } + + if (read_timeout < 0L || read_timeout > INT_MAX) { + zend_throw_exception(redis_exception_ce, + "Invalid read timeout", 0 TSRMLS_CC); return FAILURE; } @@ -855,7 +861,7 @@ redis_connect(INTERNAL_FUNCTION_PARAMETERS, int persistent) redis_free_socket(redis->sock); } - redis->sock = redis_sock_create(host, host_len, port, timeout, persistent, + redis->sock = redis_sock_create(host, host_len, port, timeout, read_timeout, persistent, persistent_id, retry_interval, 0); if (redis_sock_server_open(redis->sock TSRMLS_CC) < 0) { diff --git a/redis_array.c b/redis_array.c index 038a7e1a11..2cf2303d3b 100644 --- a/redis_array.c +++ b/redis_array.c @@ -230,7 +230,7 @@ PHP_METHOD(RedisArray, __construct) HashTable *hPrev = NULL, *hOpts = NULL; long l_retry_interval = 0; zend_bool b_lazy_connect = 0; - double d_connect_timeout = 0; + double d_connect_timeout = 0, read_timeout = 0.0; redis_array_object *obj; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z|a", &z0, &z_opts) == FAILURE) { @@ -300,6 +300,17 @@ PHP_METHOD(RedisArray, __construct) d_connect_timeout = atof(Z_STRVAL_P(zpData)); } } + + /* extract read_timeout option */ + if ((zpData = zend_hash_str_find(hOpts, "read_timeout", sizeof("read_timeout") - 1)) != NULL) { + if (Z_TYPE_P(zpData) == IS_DOUBLE) { + read_timeout = Z_DVAL_P(zpData); + } else if (Z_TYPE_P(zpData) == IS_LONG) { + read_timeout = Z_LVAL_P(zpData); + } else if (Z_TYPE_P(zpData) == IS_STRING) { + read_timeout = atof(Z_STRVAL_P(zpData)); + } + } } /* extract either name of list of hosts from z0 */ @@ -309,7 +320,7 @@ PHP_METHOD(RedisArray, __construct) break; case IS_ARRAY: - ra = ra_make_array(Z_ARRVAL_P(z0), &z_fun, &z_dist, hPrev, b_index, b_pconnect, l_retry_interval, b_lazy_connect, d_connect_timeout TSRMLS_CC); + ra = ra_make_array(Z_ARRVAL_P(z0), &z_fun, &z_dist, hPrev, b_index, b_pconnect, l_retry_interval, b_lazy_connect, d_connect_timeout, read_timeout TSRMLS_CC); break; default: diff --git a/redis_array.h b/redis_array.h index fe5eefa767..cab0a26e4d 100644 --- a/redis_array.h +++ b/redis_array.h @@ -52,6 +52,7 @@ typedef struct RedisArray_ { zval z_dist; /* key distributor, callable */ zval z_pure_cmds; /* hash table */ double connect_timeout; /* socket connect timeout */ + double read_timeout; /* socket read timeout */ struct RedisArray_ *prev; } RedisArray; diff --git a/redis_array_impl.c b/redis_array_impl.c index bb803c0207..4918d81453 100644 --- a/redis_array_impl.c +++ b/redis_array_impl.c @@ -79,7 +79,7 @@ ra_load_hosts(RedisArray *ra, HashTable *hosts, long retry_interval, zend_bool b #endif /* create socket */ - redis->sock = redis_sock_create(host, host_len, port, ra->connect_timeout, ra->pconnect, NULL, retry_interval, b_lazy_connect); + redis->sock = redis_sock_create(host, host_len, port, ra->connect_timeout, ra->read_timeout, ra->pconnect, NULL, retry_interval, b_lazy_connect); if (!b_lazy_connect) { @@ -173,13 +173,14 @@ RedisArray *ra_load_array(const char *name TSRMLS_DC) { zval z_params_retry_interval; zval z_params_pconnect; zval z_params_connect_timeout; + zval z_params_read_timeout; zval z_params_lazy_connect; RedisArray *ra = NULL; zend_bool b_index = 0, b_autorehash = 0, b_pconnect = 0; long l_retry_interval = 0; zend_bool b_lazy_connect = 0; - double d_connect_timeout = 0; + double d_connect_timeout = 0, read_timeout = 0.0; HashTable *hHosts = NULL, *hPrev = NULL; size_t name_len = strlen(name); char *iptr; @@ -297,9 +298,25 @@ RedisArray *ra_load_array(const char *name TSRMLS_DC) { d_connect_timeout = Z_LVAL_P(z_data); } } + + /* find read timeout option */ + array_init(&z_params_connect_timeout); + if ((iptr = INI_STR("redis.arrays.readtimeout")) != NULL) { + sapi_module.treat_data(PARSE_STRING, estrdup(iptr), &z_params_read_timeout TSRMLS_CC); + } + if ((z_data = zend_hash_str_find(Z_ARRVAL(z_params_read_timeout), name, name_len)) != NULL) { + if (Z_TYPE_P(z_data) == IS_DOUBLE) { + read_timeout = Z_DVAL_P(z_data); + } else if (Z_TYPE_P(z_data) == IS_STRING) { + read_timeout = atof(Z_STRVAL_P(z_data)); + } else if (Z_TYPE_P(z_data) == IS_LONG) { + read_timeout = Z_LVAL_P(z_data); + } + } + /* create RedisArray object */ - ra = ra_make_array(hHosts, &z_fun, &z_dist, hPrev, b_index, b_pconnect, l_retry_interval, b_lazy_connect, d_connect_timeout TSRMLS_CC); + ra = ra_make_array(hHosts, &z_fun, &z_dist, hPrev, b_index, b_pconnect, l_retry_interval, b_lazy_connect, d_connect_timeout, read_timeout TSRMLS_CC); if (ra) { ra->auto_rehash = b_autorehash; if(ra->prev) ra->prev->auto_rehash = b_autorehash; @@ -315,6 +332,7 @@ RedisArray *ra_load_array(const char *name TSRMLS_DC) { zval_dtor(&z_params_retry_interval); zval_dtor(&z_params_pconnect); zval_dtor(&z_params_connect_timeout); + zval_dtor(&z_params_read_timeout); zval_dtor(&z_params_lazy_connect); zval_dtor(&z_dist); zval_dtor(&z_fun); @@ -323,7 +341,7 @@ RedisArray *ra_load_array(const char *name TSRMLS_DC) { } RedisArray * -ra_make_array(HashTable *hosts, zval *z_fun, zval *z_dist, HashTable *hosts_prev, zend_bool b_index, zend_bool b_pconnect, long retry_interval, zend_bool b_lazy_connect, double connect_timeout TSRMLS_DC) { +ra_make_array(HashTable *hosts, zval *z_fun, zval *z_dist, HashTable *hosts_prev, zend_bool b_index, zend_bool b_pconnect, long retry_interval, zend_bool b_lazy_connect, double connect_timeout, double read_timeout TSRMLS_DC) { int i, count; RedisArray *ra; @@ -341,6 +359,7 @@ ra_make_array(HashTable *hosts, zval *z_fun, zval *z_dist, HashTable *hosts_prev ra->auto_rehash = 0; ra->pconnect = b_pconnect; ra->connect_timeout = connect_timeout; + ra->read_timeout = read_timeout; if (ra_load_hosts(ra, hosts, retry_interval, b_lazy_connect TSRMLS_CC) == NULL || !ra->count) { for (i = 0; i < ra->count; ++i) { @@ -352,7 +371,7 @@ ra_make_array(HashTable *hosts, zval *z_fun, zval *z_dist, HashTable *hosts_prev efree(ra); return NULL; } - ra->prev = hosts_prev ? ra_make_array(hosts_prev, z_fun, z_dist, NULL, b_index, b_pconnect, retry_interval, b_lazy_connect, connect_timeout TSRMLS_CC) : NULL; + ra->prev = hosts_prev ? ra_make_array(hosts_prev, z_fun, z_dist, NULL, b_index, b_pconnect, retry_interval, b_lazy_connect, connect_timeout, read_timeout TSRMLS_CC) : NULL; /* init array data structures */ ra_init_function_table(ra); diff --git a/redis_array_impl.h b/redis_array_impl.h index b385986949..082dc17206 100644 --- a/redis_array_impl.h +++ b/redis_array_impl.h @@ -11,7 +11,7 @@ RedisArray *ra_load_hosts(RedisArray *ra, HashTable *hosts, long retry_interval, zend_bool b_lazy_connect TSRMLS_DC); RedisArray *ra_load_array(const char *name TSRMLS_DC); -RedisArray *ra_make_array(HashTable *hosts, zval *z_fun, zval *z_dist, HashTable *hosts_prev, zend_bool b_index, zend_bool b_pconnect, long retry_interval, zend_bool b_lazy_connect, double connect_timeout TSRMLS_DC); +RedisArray *ra_make_array(HashTable *hosts, zval *z_fun, zval *z_dist, HashTable *hosts_prev, zend_bool b_index, zend_bool b_pconnect, long retry_interval, zend_bool b_lazy_connect, double connect_timeout, double read_timeout TSRMLS_DC); zval *ra_find_node_by_name(RedisArray *ra, const char *host, int host_len TSRMLS_DC); zval *ra_find_node(RedisArray *ra, const char *key, int key_len, int *out_pos TSRMLS_DC); void ra_init_function_table(RedisArray *ra); diff --git a/redis_cluster.c b/redis_cluster.c index 869434e473..8508da0651 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -438,6 +438,8 @@ void redis_cluster_load(redisCluster *c, char *name, int name_len TSRMLS_DC) { timeout = atof(Z_STRVAL_P(z_value)); } else if (Z_TYPE_P(z_value) == IS_DOUBLE) { timeout = Z_DVAL_P(z_value); + } else if (Z_TYPE_P(z_value) == IS_LONG) { + timeout = Z_LVAL_P(z_value); } } @@ -451,6 +453,8 @@ void redis_cluster_load(redisCluster *c, char *name, int name_len TSRMLS_DC) { read_timeout = atof(Z_STRVAL_P(z_value)); } else if (Z_TYPE_P(z_value) == IS_DOUBLE) { read_timeout = Z_DVAL_P(z_value); + } else if (Z_TYPE_P(z_value) == IS_LONG) { + timeout = Z_LVAL_P(z_value); } } diff --git a/redis_session.c b/redis_session.c index a753d198e7..607fc8f0ce 100644 --- a/redis_session.c +++ b/redis_session.c @@ -206,7 +206,7 @@ PS_OPEN_FUNC(redis) if (i < j) { int weight = 1; - double timeout = 86400.0; + double timeout = 86400.0, read_timeout = 0.0; int persistent = 0; int database = -1; char *prefix = NULL, *auth = NULL, *persistent_id = NULL; @@ -246,6 +246,9 @@ PS_OPEN_FUNC(redis) if ((param = zend_hash_str_find(Z_ARRVAL(params), "timeout", sizeof("timeout") - 1)) != NULL) { timeout = atof(Z_STRVAL_P(param)); } + if ((param = zend_hash_str_find(Z_ARRVAL(params), "read_timeout", sizeof("read_timeout") - 1)) != NULL) { + read_timeout = atof(Z_STRVAL_P(param)); + } if ((param = zend_hash_str_find(Z_ARRVAL(params), "persistent", sizeof("persistent") - 1)) != NULL) { persistent = (atol(Z_STRVAL_P(param)) == 1 ? 1 : 0); } @@ -280,9 +283,9 @@ PS_OPEN_FUNC(redis) RedisSock *redis_sock; if(url->host) { - redis_sock = redis_sock_create(url->host, strlen(url->host), url->port, timeout, persistent, persistent_id, retry_interval, 0); + redis_sock = redis_sock_create(url->host, strlen(url->host), url->port, timeout, read_timeout, persistent, persistent_id, retry_interval, 0); } else { /* unix */ - redis_sock = redis_sock_create(url->path, strlen(url->path), 0, timeout, persistent, persistent_id, retry_interval, 0); + redis_sock = redis_sock_create(url->path, strlen(url->path), 0, timeout, read_timeout, persistent, persistent_id, retry_interval, 0); } redis_pool_add(pool, redis_sock, weight, database, prefix, auth TSRMLS_CC);