@@ -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+
21852261static int
21862262redis_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
0 commit comments