[Feature #120782] Introduction of Happy Eyeballs Version 2 (RFC8305) in TCPSocket.new (#11653)

* Introduction of Happy Eyeballs Version 2 (RFC8305) in TCPSocket.new

This is an implementation of Happy Eyeballs version 2 (RFC 8305) in `TCPSocket.new`.
See https://github.com/ruby/ruby/pull/11653

1. Background
Prior to this implementation, I implemented Happy Eyeballs Version 2 (HEv2) for `Socket.tcp` in https://github.com/ruby/ruby/pull/9374.
HEv2 is an algorithm defined in [RFC 8305](https://datatracker.ietf.org/doc/html/rfc8305), aimed at improving network connectivity.
For more details on the specific cases that HEv2 helps, please refer to https://bugs.ruby-lang.org/issues/20108.

2. Proposal & Outcome
This proposal implements the same HEv2 algorithm in `TCPSocket.new`.
Since `TCPSocket.new` is used more widely than `Socket.tcp`, this change is expected to broaden the impact of HEv2's benefits.
Like `Socket.tcp`, I have also added `fast_fallback` keyword argument to `TCPSocket.new`.
This option is set to true by default, enabling the HEv2 functionality.
However, users can explicitly set it to false to disable HEv2 and use the previous behavior of `TCPSocket.new`.

It should be noted that HEv2 is enabled only in environments where pthreads are available.
This specification follows the approach taken in https://bugs.ruby-lang.org/issues/19965 , where name resolution can be interrupted.
(In environments where pthreads are not available, the `fast_fallback` option is ignored.)

3. Performance
Below is the benchmark of 100 requests to `www.ruby-lang.org` with the fast_fallback option set to true and false, respectively.
While there is a slight performance degradation when HEv2 is enabled, the degradation is smaller compared to that seen in `Socket.tcp`.

```
~/s/build ❯❯❯ ../install/bin/ruby ../ruby/test.rb
Rehearsal --------------------------------------------------------
fast_fallback: true    0.017588   0.097045   0.114633 (  1.460664)
fast_fallback: false   0.014033   0.078984   0.093017 (  1.413951)
----------------------------------------------- total: 0.207650sec

                           user     system      total        real
fast_fallback: true    0.020891   0.124054   0.144945 (  1.473816)
fast_fallback: false   0.018392   0.110852   0.129244 (  1.466014)
```

* Update debug prints

Co-authored-by: Nobuyoshi Nakada <nobu.nakada@gmail.com>

* Remove debug prints

* misc

* Disable HEv2 in Win

* Raise resolution error with hostname resolution

* Fix to handle errors

* Remove warnings

* Errors that do not need to be handled

* misc

* Improve doc

* Fix bug on cancellation

* Avoid EAI_ADDRFAMILY for resolving IPv6

* Follow upstream

* misc

* Refactor connection_attempt_fds management

- Introduced allocate_connection_attempt_fds and reallocate_connection_attempt_fds for improved memory allocation of connection_attempt_fds
- Added remove_connection_attempt_fd to resize connection_attempt_fds dynamically.
- Simplified the in_progress_fds function to only check the size of connection_attempt_fds.

* Rename do_pthread_create to raddrinfo_pthread_create to avoid conflicting

---------

Co-authored-by: Nobuyoshi Nakada <nobu.nakada@gmail.com>
This commit is contained in:
Misaki Shioi 2024-11-12 10:06:48 +09:00 committed by GitHub
parent 821a5b966f
commit 4c270200db
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
Notes: git 2024-11-12 01:07:04 +00:00
Merged-By: shioimm <shioi.mm@gmail.com>
7 changed files with 1549 additions and 16 deletions

View file

@ -354,7 +354,7 @@ int rsock_socket(int domain, int type, int proto);
int rsock_detect_cloexec(int fd);
VALUE rsock_init_sock(VALUE sock, int fd);
VALUE rsock_sock_s_socketpair(int argc, VALUE *argv, VALUE klass);
VALUE rsock_init_inetsock(VALUE sock, VALUE remote_host, VALUE remote_serv, VALUE local_host, VALUE local_serv, int type, VALUE resolv_timeout, VALUE connect_timeout);
VALUE rsock_init_inetsock(VALUE sock, VALUE remote_host, VALUE remote_serv, VALUE local_host, VALUE local_serv, int type, VALUE resolv_timeout, VALUE connect_timeout, VALUE fast_fallback, VALUE test_mode_settings);
VALUE rsock_init_unixsock(VALUE sock, VALUE path, int server);
struct rsock_send_arg {
@ -413,6 +413,45 @@ ssize_t rsock_recvmsg(int socket, struct msghdr *message, int flags);
void rsock_discard_cmsg_resource(struct msghdr *mh, int msg_peek_p);
#endif
char *host_str(VALUE host, char *hbuf, size_t hbuflen, int *flags_ptr);
char *port_str(VALUE port, char *pbuf, size_t pbuflen, int *flags_ptr);
#ifndef FAST_FALLBACK_INIT_INETSOCK_IMPL
# if !defined(HAVE_PTHREAD_CREATE) || !defined(HAVE_PTHREAD_DETACH) || defined(__MINGW32__) || defined(__MINGW64__)
# define FAST_FALLBACK_INIT_INETSOCK_IMPL 0
# else
# include "ruby/thread_native.h"
# define FAST_FALLBACK_INIT_INETSOCK_IMPL 1
# define IPV6_HOSTNAME_RESOLVED '1'
# define IPV4_HOSTNAME_RESOLVED '2'
# define SELECT_CANCELLED '3'
struct fast_fallback_getaddrinfo_shared
{
int wait, notify, refcount, connection_attempt_fds_size;
int *connection_attempt_fds, *cancelled;
char *node, *service;
rb_nativethread_lock_t *lock;
};
struct fast_fallback_getaddrinfo_entry
{
int family, err, refcount;
struct addrinfo hints;
struct addrinfo *ai;
struct fast_fallback_getaddrinfo_shared *shared;
int has_syserr;
long test_sleep_ms;
int test_ecode;
};
int raddrinfo_pthread_create(pthread_t *th, void *(*start_routine) (void *), void *arg);
void *do_fast_fallback_getaddrinfo(void *ptr);
void free_fast_fallback_getaddrinfo_entry(struct fast_fallback_getaddrinfo_entry **entry);
void free_fast_fallback_getaddrinfo_shared(struct fast_fallback_getaddrinfo_shared **shared);
# endif
#endif
void rsock_init_basicsocket(void);
void rsock_init_ipsocket(void);
void rsock_init_tcpsocket(void);