The Ruby 3.4+ socket.rb logic contains a new implementation of Socket.tcp for establishing a TCP connection. The logic uses connect_nonblock to perform a nonblocking connection and then select to wait for it to be ready, but does not appear to make any further calls to connect_nonblock to complete that connection.
Meanwhile, the JDK SocketChannel has been nonblocking connected and is in connection-pending state waiting for us to call finishConnect. As no further connect_nonblock call is made, this call never happens and the socket is left half-connected.
This is the cause of the issue reported by @PikachuEXE in redis/redis-rb#1339.
To reproduce that issue, setup Redis, install the redis gem for Ruby, and run the following script:
require "redis"
Redis::VERSION # "5.4.1"
redis = Redis.new
redis.setex('key', 3600, 'value')
# Socket is not connected - No message available (redis://localhost:6379) (Redis::ConnectionError)
Debugging into the point at which it attempts to write to the Redis client, I can see that the socket is in connection-pending state.
We'll need to figure out why the new logic does not attempt another connect_nonblock to complete the connection, and if that is valid behavior for Ruby sockets, we'll perhaps need to make our IO.select finish the connection so it is truly ready.
The Ruby 3.4+ socket.rb logic contains a new implementation of
Socket.tcpfor establishing a TCP connection. The logic usesconnect_nonblockto perform a nonblocking connection and thenselectto wait for it to be ready, but does not appear to make any further calls toconnect_nonblockto complete that connection.Meanwhile, the JDK
SocketChannelhas been nonblocking connected and is inconnection-pendingstate waiting for us to callfinishConnect. As no furtherconnect_nonblockcall is made, this call never happens and the socket is left half-connected.This is the cause of the issue reported by @PikachuEXE in redis/redis-rb#1339.
To reproduce that issue, setup Redis, install the
redisgem for Ruby, and run the following script:Debugging into the point at which it attempts to write to the Redis client, I can see that the socket is in
connection-pendingstate.We'll need to figure out why the new logic does not attempt another
connect_nonblockto complete the connection, and if that is valid behavior for Ruby sockets, we'll perhaps need to make ourIO.selectfinish the connection so it is truly ready.