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

Skip to content

Commit 42e69d8

Browse files
Merge pull request #2013 from phpredis/improved-liveness-detection-rebase
Experimental support to detect unconsumed data
2 parents 3f651d2 + 661b6a3 commit 42e69d8

3 files changed

Lines changed: 97 additions & 4 deletions

File tree

common.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,17 @@ typedef enum {
134134
#define MULTI 1
135135
#define PIPELINE 2
136136

137+
#define PHPREDIS_DEBUG_LOGGING 0
138+
139+
#if PHPREDIS_DEBUG_LOGGING == 1
140+
#define redisDbgFmt(fmt, ...) \
141+
php_printf("%s:%d:%s(): " fmt "\n", __FILE__, __LINE__, __func__, __VA_ARGS__)
142+
#define redisDbgStr(str) phpredisDebugFmt("%s", str)
143+
#else
144+
#define redisDbgFmt(fmt, ...) ((void)0)
145+
#define redisDbgStr(str) ((void)0)
146+
#endif
147+
137148
#define IS_ATOMIC(redis_sock) (redis_sock->mode == ATOMIC)
138149
#define IS_MULTI(redis_sock) (redis_sock->mode & MULTI)
139150
#define IS_PIPELINE(redis_sock) (redis_sock->mode & PIPELINE)

library.c

Lines changed: 84 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2182,6 +2182,82 @@ static int redis_stream_liveness_check(php_stream *stream) {
21822182
SUCCESS : FAILURE;
21832183
}
21842184

2185+
/* Try to get the underlying socket FD for use with poll/select.
2186+
* Returns -1 on failure. */
2187+
static php_socket_t redis_stream_fd_for_select(php_stream *stream) {
2188+
php_socket_t fd;
2189+
int flags;
2190+
2191+
flags = PHP_STREAM_AS_FD_FOR_SELECT | PHP_STREAM_CAST_INTERNAL;
2192+
if (php_stream_cast(stream, flags, (void*)&fd, 1) == FAILURE)
2193+
return -1;
2194+
2195+
return fd;
2196+
}
2197+
2198+
static int redis_detect_dirty_config(void) {
2199+
int val = INI_INT("redis.pconnect.pool_detect_dirty");
2200+
2201+
if (val >= 0 && val <= 2)
2202+
return val;
2203+
else if (val > 2)
2204+
return 2;
2205+
else
2206+
return 0;
2207+
}
2208+
2209+
static int redis_pool_poll_timeout(void) {
2210+
int val = INI_INT("redis.pconnect.pool_poll_timeout");
2211+
if (val >= 0)
2212+
return val;
2213+
2214+
return 0;
2215+
}
2216+
2217+
#define REDIS_POLL_FD_SET(_pfd, _fd, _events) \
2218+
(_pfd).fd = _fd; (_pfd).events = _events; (_pfd).revents = 0
2219+
2220+
/* Try to determine if the socket is out of sync (has unconsumed replies) */
2221+
static int redis_stream_detect_dirty(php_stream *stream) {
2222+
php_socket_t fd;
2223+
php_pollfd pfd;
2224+
int rv, action;
2225+
2226+
/* Short circuit if this is disabled */
2227+
if ((action = redis_detect_dirty_config()) == 0)
2228+
return SUCCESS;
2229+
2230+
/* Seek past unconsumed bytes if we detect them */
2231+
if (stream->readpos < stream->writepos) {
2232+
redisDbgFmt("%s on unconsumed buffer (%ld < %ld)",
2233+
action > 1 ? "Aborting" : "Seeking",
2234+
(long)stream->readpos, (long)stream->writepos);
2235+
2236+
/* Abort if we are configured to immediately fail */
2237+
if (action == 1)
2238+
return FAILURE;
2239+
2240+
/* Seek to the end of buffered data */
2241+
zend_off_t offset = stream->writepos - stream->readpos;
2242+
if (php_stream_seek(stream, offset, SEEK_CUR) == FAILURE)
2243+
return FAILURE;
2244+
}
2245+
2246+
/* Get the underlying FD */
2247+
if ((fd = redis_stream_fd_for_select(stream)) == -1)
2248+
return FAILURE;
2249+
2250+
/* We want to detect a readable socket (it shouln't be) */
2251+
REDIS_POLL_FD_SET(pfd, fd, PHP_POLLREADABLE);
2252+
rv = php_poll2(&pfd, 1, redis_pool_poll_timeout());
2253+
2254+
/* If we detect the socket is readable, it's dirty which is
2255+
* a failure. Otherwise as best we can tell it's good.
2256+
* TODO: We could attempt to consume up to N bytes */
2257+
redisDbgFmt("Detected %s socket", rv > 0 ? "readable" : "unreadable");
2258+
return rv == 0 ? SUCCESS : FAILURE;
2259+
}
2260+
21852261
static int
21862262
redis_sock_check_liveness(RedisSock *redis_sock)
21872263
{
@@ -2190,11 +2266,14 @@ redis_sock_check_liveness(RedisSock *redis_sock)
21902266
smart_string cmd = {0};
21912267
size_t len;
21922268

2193-
/* Short circuit if we detect the stream has gone bad or if the user has
2194-
* configured persistent connection "YOLO mode". */
2195-
if (redis_stream_liveness_check(redis_sock->stream) != SUCCESS) {
2269+
/* Short circuit if PHP detects the stream isn't live */
2270+
if (redis_stream_liveness_check(redis_sock->stream) != SUCCESS)
2271+
goto failure;
2272+
2273+
/* Short circuit if we detect the stream is "dirty", can't or are
2274+
configured not to try and fix it */
2275+
if (redis_stream_detect_dirty(redis_sock->stream) != SUCCESS)
21962276
goto failure;
2197-
}
21982277

21992278
redis_sock->status = REDIS_SOCK_STATUS_CONNECTED;
22002279
if (!INI_INT("redis.pconnect.echo_check_liveness")) {
@@ -2313,6 +2392,7 @@ PHP_REDIS_API int redis_sock_connect(RedisSock *redis_sock)
23132392
if (redis_sock_check_liveness(redis_sock) == SUCCESS) {
23142393
return SUCCESS;
23152394
}
2395+
23162396
p->nb_active--;
23172397
}
23182398

redis.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,8 @@ PHP_INI_BEGIN()
104104
PHP_INI_ENTRY("redis.pconnect.pooling_enabled", "1", PHP_INI_ALL, NULL)
105105
PHP_INI_ENTRY("redis.pconnect.connection_limit", "0", PHP_INI_ALL, NULL)
106106
PHP_INI_ENTRY("redis.pconnect.echo_check_liveness", "1", PHP_INI_ALL, NULL)
107+
PHP_INI_ENTRY("redis.pconnect.pool_detect_dirty", "0", PHP_INI_ALL, NULL)
108+
PHP_INI_ENTRY("redis.pconnect.pool_poll_timeout", "0", PHP_INI_ALL, NULL)
107109
PHP_INI_ENTRY("redis.pconnect.pool_pattern", "", PHP_INI_ALL, NULL)
108110

109111
/* redis session */

0 commit comments

Comments
 (0)