A DNS name can have a LOT of A records associated but a socket has to pick one. This is a severe limitation.
Rather, it is a feature.
Standards as well as all manual pages and other documentation say that getaddrinfo() should be called via a loop by clients. And results show that almost all clients indeed do that. Therefore, on the server-side, you can do round-robin DNS, randomization (as suggested by the post linked), fall-backs, CDN things, and many other things.
You can hedge your bets and open a connection and send data to _all_ of them, and pick whichever returns faster.
This of course requires you to know about the application-layer protocol.
For HTTP, you need to restrict this strategy to GET methods, for example.
Hence why it can’t be part of the socket interface.
You can always build higher level abstractions on top of sockets if you need them.
This is even used by browsers, this trick even has a slightly creepy name: "Happy Eyeballs".
TCP does not require that the act of connecting must be pure, so you cannot indiscriminately apply Happy Eyeballs at the socket API level.
More concerning is the risk of exhausting server socket resources when probe-based connects don't hang up quickly if they don't want to use a connections. Lots of servers/load balancers aren't well-tuned to force-close connections if the first byte doesn't arrive within a short time.
Instead we converged on a very simple primitive that makes no such assumptions and can be composed into a wider variety of high level abstractions in user space.
TCP connection cookies solved this problem in 90-s! They fell out of use because dedicating a couple MB of RAM to track a few hundred thousand connections is not a big deal anymore.
For non-public networks, sure. For things like TCP-to-RS232 connectors that can only service one connection at a time. Happy Eyeballs needs to be disabled for them, but that's just one socket option.
And if the Happy Eyeballs protocol was standardized earlier, these kinds of apps arguably would have adapted to it.
I say that not out of purity concerns, but because of how DNS and TCP work. DNS multirecord selection (or not) should be up to the user. DNS timeouts should be surfaced granularly and differently from e.g. TCP SYNACK timeouts. Once a TCP stream is open, if a novice user opened it conceptually to a domain rather than an address, there's no intuitively correct answer to what happens if the domain's resolution changes while the socket's connected.
I've used this in networks and it enables amazingly easy endpoint mobility.
You can have sockets without DNS. You can pick whatever strategy you want when there are multiple A records. You can use SRV records instead. And most importantly imo, it mirrors the listening API.
The ones we have suck or are non-standard.
Another option is for sockets to accept multiple addresses and address families in "connect" calls, so that only one connection wins.
War story: many years ago I was developing a service that runs computational tasks inside containers. That was when K8s didn't work well with cloud providers.
The service was written in Go and worked on AWS. Everything worked fine for me and for our production deployment. Then people started using it with a Python client that used the REST API directly, and for some reason some people couldn't reach it when they were working from home.
Reason: I misconfigured IPv6 on AWS by not enabling the default route, so all IPv6 connections were failing. Go has HappyEyeballs enabled by default, so it worked just fine.
But Python did not have it back then. So people with IPv4-only connectivity (including our office) had no problems. But people with IPv6 were getting connection failures.
Cause what you're asking is not just to do the regular DNS lookup inside `connect`, but to have the OS manage roaming and continuously updating DNS while the connection stays up?
I don't know about that. I don't know. That's a lot of complexity deep in the kernel and ossified.
I'm happy with the QUIC solution that allows you to migrate IPs and it's all userspace. It would be very hard to evolve a network protocol that had to be crammed into 3+ different kernels.