UdpConnectedIntegrationSpec dns fail fix (#28558)
* More generous timeout for dns resolution failure #28133 * Use async-dns as workaround for JDK/glibc/whatever resolution bug * Handle Async DNS lookup failure in UDP and TCP connections
This commit is contained in:
parent
5bb9a7145a
commit
1df2c2d53a
4 changed files with 41 additions and 13 deletions
|
|
@ -6,15 +6,24 @@ package akka.io
|
||||||
|
|
||||||
import java.net.InetSocketAddress
|
import java.net.InetSocketAddress
|
||||||
|
|
||||||
import akka.testkit.{ AkkaSpec, ImplicitSender, TestProbe }
|
|
||||||
import akka.util.ByteString
|
|
||||||
import akka.actor.ActorRef
|
import akka.actor.ActorRef
|
||||||
import akka.testkit.SocketUtil.temporaryServerAddresses
|
import akka.testkit.SocketUtil.temporaryServerAddresses
|
||||||
import akka.testkit.WithLogCapturing
|
import akka.testkit.WithLogCapturing
|
||||||
|
import akka.testkit.AkkaSpec
|
||||||
|
import akka.testkit.ImplicitSender
|
||||||
|
import akka.testkit.TestProbe
|
||||||
|
import akka.util.ByteString
|
||||||
|
|
||||||
import scala.concurrent.duration._
|
import scala.concurrent.duration._
|
||||||
|
|
||||||
class UdpConnectedIntegrationSpec extends AkkaSpec("""
|
class UdpConnectedIntegrationSpec extends AkkaSpec("""
|
||||||
akka.loglevel = DEBUG
|
akka.loglevel = DEBUG
|
||||||
|
akka.actor.debug.lifecycle = on
|
||||||
|
akka.actor.debug.autoreceive = on
|
||||||
|
akka.io.udp-connected.trace-logging = on
|
||||||
|
# issues with dns resolution of non existent host hanging with the
|
||||||
|
# Java native host resolution
|
||||||
|
akka.io.dns.resolver = async-dns
|
||||||
akka.loggers = ["akka.testkit.SilenceAllTestEventListener"]
|
akka.loggers = ["akka.testkit.SilenceAllTestEventListener"]
|
||||||
""") with ImplicitSender with WithLogCapturing {
|
""") with ImplicitSender with WithLogCapturing {
|
||||||
|
|
||||||
|
|
@ -45,7 +54,7 @@ class UdpConnectedIntegrationSpec extends AkkaSpec("""
|
||||||
val handler = TestProbe()
|
val handler = TestProbe()
|
||||||
val command = UdpConnected.Connect(handler.ref, InetSocketAddress.createUnresolved(serverAddress, 1234), None)
|
val command = UdpConnected.Connect(handler.ref, InetSocketAddress.createUnresolved(serverAddress, 1234), None)
|
||||||
commander.send(IO(UdpConnected), command)
|
commander.send(IO(UdpConnected), command)
|
||||||
commander.expectMsg(6.seconds, UdpConnected.CommandFailed(command))
|
commander.expectMsg(10.seconds, UdpConnected.CommandFailed(command))
|
||||||
}
|
}
|
||||||
|
|
||||||
"report error if can not resolve (cached)" in {
|
"report error if can not resolve (cached)" in {
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,8 @@ package akka.io
|
||||||
import java.net.{ ConnectException, InetSocketAddress }
|
import java.net.{ ConnectException, InetSocketAddress }
|
||||||
import java.nio.channels.{ SelectionKey, SocketChannel }
|
import java.nio.channels.{ SelectionKey, SocketChannel }
|
||||||
|
|
||||||
|
import akka.actor.Status.Failure
|
||||||
|
|
||||||
import scala.util.control.{ NoStackTrace, NonFatal }
|
import scala.util.control.{ NoStackTrace, NonFatal }
|
||||||
import scala.concurrent.duration._
|
import scala.concurrent.duration._
|
||||||
import akka.actor.{ ActorRef, ReceiveTimeout }
|
import akka.actor.{ ActorRef, ReceiveTimeout }
|
||||||
|
|
@ -83,6 +85,11 @@ private[io] class TcpOutgoingConnection(
|
||||||
}
|
}
|
||||||
case ReceiveTimeout =>
|
case ReceiveTimeout =>
|
||||||
connectionTimeout()
|
connectionTimeout()
|
||||||
|
case Failure(ex) =>
|
||||||
|
// async-dns responds with a Failure on DNS server lookup failure
|
||||||
|
reportConnectFailure {
|
||||||
|
throw new RuntimeException(ex)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
def register(address: InetSocketAddress, registration: ChannelRegistration): Unit = {
|
def register(address: InetSocketAddress, registration: ChannelRegistration): Unit = {
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,8 @@ import java.nio.ByteBuffer
|
||||||
import java.nio.channels.DatagramChannel
|
import java.nio.channels.DatagramChannel
|
||||||
import java.nio.channels.SelectionKey._
|
import java.nio.channels.SelectionKey._
|
||||||
|
|
||||||
|
import akka.actor.Status.Failure
|
||||||
|
|
||||||
import scala.annotation.tailrec
|
import scala.annotation.tailrec
|
||||||
import scala.util.control.NonFatal
|
import scala.util.control.NonFatal
|
||||||
import akka.actor.{ Actor, ActorLogging, ActorRef }
|
import akka.actor.{ Actor, ActorLogging, ActorRef }
|
||||||
|
|
@ -50,18 +52,24 @@ private[io] class UdpConnection(
|
||||||
context.become(resolving())
|
context.become(resolving())
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
reportConnectFailure {
|
||||||
doConnect(remoteAddress)
|
doConnect(remoteAddress)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
def resolving(): Receive = {
|
def resolving(): Receive = {
|
||||||
case r: DnsProtocol.Resolved =>
|
case r: DnsProtocol.Resolved =>
|
||||||
reportConnectFailure {
|
reportConnectFailure {
|
||||||
doConnect(new InetSocketAddress(r.address(), remoteAddress.getPort))
|
doConnect(new InetSocketAddress(r.address(), remoteAddress.getPort))
|
||||||
}
|
}
|
||||||
|
case Failure(ex) =>
|
||||||
|
// async-dns responds with a Failure on DNS server lookup failure
|
||||||
|
reportConnectFailure {
|
||||||
|
throw new RuntimeException(ex)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
def doConnect(@unused address: InetSocketAddress): Unit = {
|
def doConnect(@unused address: InetSocketAddress): Unit = {
|
||||||
reportConnectFailure {
|
|
||||||
channel = DatagramChannel.open
|
channel = DatagramChannel.open
|
||||||
channel.configureBlocking(false)
|
channel.configureBlocking(false)
|
||||||
val socket = channel.socket
|
val socket = channel.socket
|
||||||
|
|
@ -69,7 +77,7 @@ private[io] class UdpConnection(
|
||||||
localAddress.foreach(socket.bind)
|
localAddress.foreach(socket.bind)
|
||||||
channel.connect(remoteAddress)
|
channel.connect(remoteAddress)
|
||||||
channelRegistry.register(channel, OP_READ)
|
channelRegistry.register(channel, OP_READ)
|
||||||
}
|
|
||||||
log.debug("Successfully connected to [{}]", remoteAddress)
|
log.debug("Successfully connected to [{}]", remoteAddress)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -47,6 +47,10 @@ object DnsProtocol {
|
||||||
*/
|
*/
|
||||||
def srvRequestType(): RequestType = Srv
|
def srvRequestType(): RequestType = Srv
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sending this to the [[AsyncDnsManager]] will either lead to a [[Resolved]] or a [[akka.actor.Status.Failure]] response.
|
||||||
|
* If request type are both, both resolutions must succeed or the response is a failure.
|
||||||
|
*/
|
||||||
final case class Resolve(name: String, requestType: RequestType) extends ConsistentHashable {
|
final case class Resolve(name: String, requestType: RequestType) extends ConsistentHashable {
|
||||||
override def consistentHashKey: Any = name
|
override def consistentHashKey: Any = name
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue