Skip to content

Conversation

@987Nabil
Copy link
Contributor

@987Nabil 987Nabil commented Dec 6, 2025

fixes #1996
/claim #1996

@netlify
Copy link

netlify bot commented Dec 6, 2025

Deploy Preview for zio-http ready!

Name Link
🔨 Latest commit eac1b49
🔍 Latest deploy log https://app.netlify.com/projects/zio-http/deploys/6936ebc620986200086b50b6
😎 Deploy Preview https://deploy-preview-3838--zio-http.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.


private[netty] object NettyConnectionPool {

private val HappyEyeballsDelay: Duration = 250.millis
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe add a comment that this is according to spec recommendations

localAddress: Option[InetSocketAddress],
)(implicit trace: Trace): ZIO[Scope, Throwable, JChannel] = {

if (resolvedHosts.isEmpty) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Very minor nit (feel free to ignore) in order to make this a bit more readable, you can pat-mat on resolvedHosts.size instead of using if statements. e.g.,

resolvedHosts.size match {
  case 0 => ...
  case 1 => ...
  case _ => ....
}

queue <- Queue.bounded[Unit](requestedCapacity = 1)
channel <- ZIO.raceAll(
connectToAddress(
addresses.head,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we're going to be using head and tail it might be better for sortAddresses to return a List instead. Creating a list with a ListBuffer is very cheap

} else {
val addresses = sortAddresses(resolvedHosts)
for {
queue <- Queue.bounded[Unit](requestedCapacity = 1)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A Promise seems to be a better fit for this usecase

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, I made a mistake, there is a loc missing. But there can potentially multiple errors, so it need to be fulfilled multiple times. So queue is right

)
},
)
} yield channel
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I feel like there is a race condition / connection leak here. In the case that two attempts connect at the same time (within the netty connection pool), we won't be closing the 2nd connection until the Scope is closed down. We need to guarantee that only the "winner' connection remains open by the end of this operation

connectionTimeout,
localAddress,
).onExit {
case e: Exit.Success[JChannel] => successful.update(channels => channels :+ e.value)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this will work; Interruption might occur right between the moment that we create the channel and the evaluation of the next effect; before we reach this point.

We probably need to pass the ref into connectToAddress itself and add the channel to the list (potentially unsafely) at some uninterruptible point

Comment on lines +248 to +250
channel <- channels.headOption match {
case ch: Some[JChannel] => ZIO.succeed(ch.value)
case None => ZIO.fail(new RuntimeException("All connection attempts failed"))
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't it better to partition by open channels and pick the first one? And then close all other ones?

case ch: Some[JChannel] => ZIO.succeed(ch.value)
case None => ZIO.fail(new RuntimeException("All connection attempts failed"))
}
_ <- ZIO.foreachDiscard(channels.tail)(ch => ZIO.attempt(ch.close()))
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's a potential leak here again in case that closing one of them fails. I think this is better:

Suggested change
_ <- ZIO.foreachDiscard(channels.tail)(ch => ZIO.attempt(ch.close()))
_ <- ZIO.foreachDiscard(channels.tail)(ch => ZIO.ignore(ch.close()))

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add support for Happy Eyeballs

2 participants