[ruby-core:104583] [Ruby master Bug#17664] Behavior of sockets changed in Ruby 3.0 to non-blocking
From:
samuel@...
Date:
2021-07-12 01:03:25 UTC
List:
ruby-core #104583
Issue #17664 has been updated by ioquatix (Samuel Williams).
Here is `DirectScheduler`:
```
Running 30s test @ http://localhost:9090
4 threads and 128 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 3.55ms 658.12us 14.96ms 64.38%
Req/Sec 8.92k 0.99k 11.92k 72.81%
1063854 requests in 30.09s, 46.67MB read
Requests/sec: 35361.57
Transfer/sec: 1.55MB
% time seconds usecs/call calls errors syscall
------ ----------- ----------- --------- --------- ------------------
37.77 0.797226 6 120032 io_uring_enter
29.02 0.612538 5 120277 close
19.45 0.410488 3 120041 4 accept4
13.24 0.279448 2 120429 9 newfstatat
0.31 0.006596 3 2079 mprotect
```
I guess it's at least 20-30% slower. My gut feeling is, greedy `read` which avoids fiber context switch improves throughput.
Based on this result, I'm still unconvinced we should change sockets back to blocking.
However, we do want to enable `DirectScheduler` to work efficiently with both.
I propose the following changes:
- `DirectScheduler` makes sense for general IO including blocking IO. `IO#read` and `IO#write` should invoke the fiber scheduler `io_read` and `io_write` respectively.
- `Socket#read` and `Socket#write` should be implemented via a different scheduler hook, maybe `socket_read` and `socket_write`, to go along with what will eventually include` socket_recvmsg` and `socket_sendmsg` etc.
- The implementation of `socket_read` and `socket_write` **could** be the same as `io_read` and `io_write`, but for performance reasons should use polling instead.
@circona what do you think?
----------------------------------------
Bug #17664: Behavior of sockets changed in Ruby 3.0 to non-blocking
https://bugs.ruby-lang.org/issues/17664#change-92866
* Author: ciconia (Sharon Rosner)
* Status: Assigned
* Priority: Normal
* Assignee: ioquatix (Samuel Williams)
* ruby -v: 3.0.0
* Backport: 2.5: UNKNOWN, 2.6: UNKNOWN, 2.7: UNKNOWN, 3.0: UNKNOWN
----------------------------------------
I'm not sure this is a bug, but apparently a change was introduced in Ruby 3.0 that makes sockets non-blocking by default. This change was apparently introduced as part of the work on the [FiberScheduler interface](https://github.com/ruby/ruby/blame/78f188524f551c97b1a7a44ae13514729f1a21c7/ext/socket/init.c#L411-L434). This change of behaviour is not discussed in the Ruby 3.0.0 release notes.
This change complicates the implementation of an io_uring-based fiber scheduler, since io_uring SQE's on fd's with `O_NONBLOCK` can return `EAGAIN` just like normal syscalls. Using io_uring with non-blocking fd's defeats the whole purpose of using io_uring in the first place.
A workaround I have put in place in the Polyphony [io_uring backend](https://github.com/digital-fabric/polyphony/blob/d3c9cf3ddc1f414387948fa40e5f6a24f68bf045/ext/polyphony/backend_io_uring.c#L28-L47) is to make sure `O_NONBLOCK` is not set before attempting I/O operations on any fd.
--
https://bugs.ruby-lang.org/
Unsubscribe: <mailto:ruby-core-request@ruby-lang.org?subject=unsubscribe>
<http://lists.ruby-lang.org/cgi-bin/mailman/options/ruby-core>