Proper handoff of readers #3286
This commit is contained in:
parent
fbadb89962
commit
46fb44737c
2 changed files with 88 additions and 29 deletions
|
|
@ -243,7 +243,8 @@ private[remote] class ReliableDeliverySupervisor(
|
||||||
case Terminated(_) ⇒
|
case Terminated(_) ⇒
|
||||||
currentHandle = None
|
currentHandle = None
|
||||||
context.become(idle)
|
context.become(idle)
|
||||||
case GotUid(u) ⇒ uid = Some(u)
|
case GotUid(u) ⇒ uid = Some(u)
|
||||||
|
case s: EndpointWriter.StopReading ⇒ writer forward s
|
||||||
}
|
}
|
||||||
|
|
||||||
def gated: Receive = {
|
def gated: Receive = {
|
||||||
|
|
@ -255,7 +256,8 @@ private[remote] class ReliableDeliverySupervisor(
|
||||||
} else context.become(idle)
|
} else context.become(idle)
|
||||||
case s @ Send(msg: SystemMessage, _, _, _) ⇒ tryBuffer(s.copy(seqOpt = Some(nextSeq())))
|
case s @ Send(msg: SystemMessage, _, _, _) ⇒ tryBuffer(s.copy(seqOpt = Some(nextSeq())))
|
||||||
case s: Send ⇒ context.system.deadLetters ! s
|
case s: Send ⇒ context.system.deadLetters ! s
|
||||||
case FlushAndStop ⇒ context.stop(self)
|
case EndpointWriter.FlushAndStop ⇒ context.stop(self)
|
||||||
|
case EndpointWriter.StopReading(w) ⇒ sender ! EndpointWriter.StoppedReading(w)
|
||||||
case _ ⇒ // Ignore
|
case _ ⇒ // Ignore
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -265,7 +267,8 @@ private[remote] class ReliableDeliverySupervisor(
|
||||||
resendAll()
|
resendAll()
|
||||||
handleSend(s)
|
handleSend(s)
|
||||||
context.become(receive)
|
context.become(receive)
|
||||||
case FlushAndStop ⇒ context.stop(self)
|
case EndpointWriter.FlushAndStop ⇒ context.stop(self)
|
||||||
|
case EndpointWriter.StopReading(w) ⇒ sender ! EndpointWriter.StoppedReading(w)
|
||||||
}
|
}
|
||||||
|
|
||||||
def flushWait: Receive = {
|
def flushWait: Receive = {
|
||||||
|
|
@ -362,6 +365,8 @@ private[remote] object EndpointWriter {
|
||||||
case object BackoffTimer
|
case object BackoffTimer
|
||||||
case object FlushAndStop
|
case object FlushAndStop
|
||||||
case object AckIdleCheckTimer
|
case object AckIdleCheckTimer
|
||||||
|
case class StopReading(writer: ActorRef)
|
||||||
|
case class StoppedReading(writer: ActorRef)
|
||||||
|
|
||||||
case class OutboundAck(ack: Ack)
|
case class OutboundAck(ack: Ack)
|
||||||
|
|
||||||
|
|
@ -533,15 +538,23 @@ private[remote] class EndpointWriter(
|
||||||
stash()
|
stash()
|
||||||
stay()
|
stay()
|
||||||
|
|
||||||
|
case _: StopReading ⇒
|
||||||
|
stash()
|
||||||
|
stay()
|
||||||
}
|
}
|
||||||
|
|
||||||
whenUnhandled {
|
whenUnhandled {
|
||||||
case Event(Terminated(r), _) if r == reader.orNull ⇒
|
case Event(Terminated(r), _) if r == reader.orNull ⇒
|
||||||
publishAndThrow(new EndpointDisassociatedException("Disassociated"))
|
publishAndThrow(new EndpointDisassociatedException("Disassociated"))
|
||||||
|
case Event(s: StopReading, _) ⇒
|
||||||
|
reader match {
|
||||||
|
case Some(r) ⇒ r forward s
|
||||||
|
case None ⇒ stash()
|
||||||
|
}
|
||||||
|
stay()
|
||||||
case Event(TakeOver(newHandle), _) ⇒
|
case Event(TakeOver(newHandle), _) ⇒
|
||||||
// Shutdown old reader
|
// Shutdown old reader
|
||||||
handle foreach { _.disassociate() }
|
handle foreach { _.disassociate() }
|
||||||
reader foreach context.stop
|
|
||||||
handle = Some(newHandle)
|
handle = Some(newHandle)
|
||||||
goto(Handoff)
|
goto(Handoff)
|
||||||
case Event(FlushAndStop, _) ⇒
|
case Event(FlushAndStop, _) ⇒
|
||||||
|
|
@ -632,7 +645,7 @@ private[remote] class EndpointReader(
|
||||||
val reliableDeliverySupervisor: Option[ActorRef],
|
val reliableDeliverySupervisor: Option[ActorRef],
|
||||||
val receiveBuffers: ConcurrentHashMap[Link, AckedReceiveBuffer[Message]]) extends EndpointActor(localAddress, remoteAddress, transport, settings, codec) {
|
val receiveBuffers: ConcurrentHashMap[Link, AckedReceiveBuffer[Message]]) extends EndpointActor(localAddress, remoteAddress, transport, settings, codec) {
|
||||||
|
|
||||||
import EndpointWriter.OutboundAck
|
import EndpointWriter.{ OutboundAck, StopReading, StoppedReading }
|
||||||
|
|
||||||
val provider = RARP(context.system).provider
|
val provider = RARP(context.system).provider
|
||||||
var ackedReceiveBuffer = new AckedReceiveBuffer[Message]
|
var ackedReceiveBuffer = new AckedReceiveBuffer[Message]
|
||||||
|
|
@ -646,8 +659,9 @@ private[remote] class EndpointReader(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override def postStop(): Unit = {
|
override def postStop(): Unit = saveState()
|
||||||
|
|
||||||
|
def saveState(): Unit = {
|
||||||
@tailrec
|
@tailrec
|
||||||
def updateSavedState(key: Link, expectedState: AckedReceiveBuffer[Message]): Unit = {
|
def updateSavedState(key: Link, expectedState: AckedReceiveBuffer[Message]): Unit = {
|
||||||
if (expectedState eq null) {
|
if (expectedState eq null) {
|
||||||
|
|
@ -661,7 +675,8 @@ private[remote] class EndpointReader(
|
||||||
}
|
}
|
||||||
|
|
||||||
override def receive: Receive = {
|
override def receive: Receive = {
|
||||||
case Disassociated ⇒ context.stop(self)
|
case Disassociated ⇒
|
||||||
|
context.stop(self)
|
||||||
|
|
||||||
case InboundPayload(p) if p.size <= transport.maximumPayloadBytes ⇒
|
case InboundPayload(p) if p.size <= transport.maximumPayloadBytes ⇒
|
||||||
val (ackOption, msgOption) = tryDecodeMessageAndAck(p)
|
val (ackOption, msgOption) = tryDecodeMessageAndAck(p)
|
||||||
|
|
@ -682,6 +697,24 @@ private[remote] class EndpointReader(
|
||||||
publishError(new OversizedPayloadException(s"Discarding oversized payload received: " +
|
publishError(new OversizedPayloadException(s"Discarding oversized payload received: " +
|
||||||
s"max allowed size [${transport.maximumPayloadBytes}] bytes, actual size [${oversized.size}] bytes."))
|
s"max allowed size [${transport.maximumPayloadBytes}] bytes, actual size [${oversized.size}] bytes."))
|
||||||
|
|
||||||
|
case StopReading(writer) ⇒
|
||||||
|
saveState()
|
||||||
|
context.become(notReading)
|
||||||
|
sender ! StoppedReading(writer)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
def notReading: Receive = {
|
||||||
|
case Disassociated ⇒ context.stop(self)
|
||||||
|
|
||||||
|
case StopReading(newHandle) ⇒
|
||||||
|
sender ! StoppedReading(newHandle)
|
||||||
|
|
||||||
|
case InboundPayload(p) ⇒
|
||||||
|
val (ackOption, _) = tryDecodeMessageAndAck(p)
|
||||||
|
for (ack ← ackOption; reliableDelivery ← reliableDeliverySupervisor) reliableDelivery ! ack
|
||||||
|
|
||||||
|
case _ ⇒
|
||||||
}
|
}
|
||||||
|
|
||||||
private def deliverAndAck(): Unit = {
|
private def deliverAndAck(): Unit = {
|
||||||
|
|
|
||||||
|
|
@ -374,6 +374,8 @@ private[remote] class EndpointManager(conf: Config, log: LoggingAdapter) extends
|
||||||
Some(context.system.scheduler.schedule(pruneInterval, pruneInterval, self, Prune))
|
Some(context.system.scheduler.schedule(pruneInterval, pruneInterval, self, Prune))
|
||||||
else None
|
else None
|
||||||
|
|
||||||
|
var pendingReadHandoffs = Map[ActorRef, AkkaProtocolHandle]()
|
||||||
|
|
||||||
override val supervisorStrategy =
|
override val supervisorStrategy =
|
||||||
OneForOneStrategy(loggingEnabled = false) {
|
OneForOneStrategy(loggingEnabled = false) {
|
||||||
case InvalidAssociation(localAddress, remoteAddress, _) ⇒
|
case InvalidAssociation(localAddress, remoteAddress, _) ⇒
|
||||||
|
|
@ -505,34 +507,42 @@ private[remote] class EndpointManager(conf: Config, log: LoggingAdapter) extends
|
||||||
}
|
}
|
||||||
|
|
||||||
case InboundAssociation(handle: AkkaProtocolHandle) ⇒ endpoints.readOnlyEndpointFor(handle.remoteAddress) match {
|
case InboundAssociation(handle: AkkaProtocolHandle) ⇒ endpoints.readOnlyEndpointFor(handle.remoteAddress) match {
|
||||||
case Some(endpoint) ⇒ endpoint ! EndpointWriter.TakeOver(handle)
|
case Some(endpoint) ⇒
|
||||||
|
endpoint ! EndpointWriter.TakeOver(handle)
|
||||||
case None ⇒
|
case None ⇒
|
||||||
if (endpoints.isQuarantined(handle.remoteAddress, handle.handshakeInfo.uid)) handle.disassociate()
|
if (endpoints.isQuarantined(handle.remoteAddress, handle.handshakeInfo.uid)) handle.disassociate()
|
||||||
else {
|
else endpoints.writableEndpointWithPolicyFor(handle.remoteAddress) match {
|
||||||
val writing = settings.UsePassiveConnections && !endpoints.hasWritableEndpointFor(handle.remoteAddress)
|
case Some(Pass(ep)) ⇒
|
||||||
eventPublisher.notifyListeners(AssociatedEvent(handle.localAddress, handle.remoteAddress, true))
|
pendingReadHandoffs += ep -> handle
|
||||||
val endpoint = createEndpoint(
|
ep ! EndpointWriter.StopReading(ep)
|
||||||
handle.remoteAddress,
|
case _ ⇒
|
||||||
handle.localAddress,
|
val writing = settings.UsePassiveConnections && !endpoints.hasWritableEndpointFor(handle.remoteAddress)
|
||||||
transportMapping(handle.localAddress),
|
eventPublisher.notifyListeners(AssociatedEvent(handle.localAddress, handle.remoteAddress, true))
|
||||||
settings,
|
val endpoint = createEndpoint(
|
||||||
Some(handle),
|
handle.remoteAddress,
|
||||||
writing)
|
handle.localAddress,
|
||||||
if (writing)
|
transportMapping(handle.localAddress),
|
||||||
endpoints.registerWritableEndpoint(handle.remoteAddress, endpoint)
|
settings,
|
||||||
else {
|
Some(handle),
|
||||||
endpoints.registerReadOnlyEndpoint(handle.remoteAddress, endpoint)
|
writing)
|
||||||
endpoints.writableEndpointWithPolicyFor(handle.remoteAddress) match {
|
if (writing)
|
||||||
case Some(Pass(_)) ⇒ // Leave it alone
|
endpoints.registerWritableEndpoint(handle.remoteAddress, endpoint)
|
||||||
case _ ⇒
|
else {
|
||||||
// Since we just communicated with the guy we can lift gate, quarantine, etc. New writer will be
|
endpoints.registerReadOnlyEndpoint(handle.remoteAddress, endpoint)
|
||||||
// opened at first write.
|
endpoints.writableEndpointWithPolicyFor(handle.remoteAddress) match {
|
||||||
endpoints.removePolicy(handle.remoteAddress)
|
case Some(Pass(_)) ⇒ // Leave it alone
|
||||||
|
case _ ⇒
|
||||||
|
// Since we just communicated with the guy we can lift gate, quarantine, etc. New writer will be
|
||||||
|
// opened at first write.
|
||||||
|
endpoints.removePolicy(handle.remoteAddress)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
case EndpointWriter.StoppedReading(endpoint) ⇒
|
||||||
|
acceptPendingReader(takingOverFrom = endpoint)
|
||||||
case Terminated(endpoint) ⇒
|
case Terminated(endpoint) ⇒
|
||||||
|
acceptPendingReader(takingOverFrom = endpoint)
|
||||||
endpoints.unregisterEndpoint(endpoint)
|
endpoints.unregisterEndpoint(endpoint)
|
||||||
case Prune ⇒
|
case Prune ⇒
|
||||||
endpoints.prune()
|
endpoints.prune()
|
||||||
|
|
@ -609,6 +619,22 @@ private[remote] class EndpointManager(conf: Config, log: LoggingAdapter) extends
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private def acceptPendingReader(takingOverFrom: ActorRef): Unit = {
|
||||||
|
if (pendingReadHandoffs.contains(takingOverFrom)) {
|
||||||
|
val handle = pendingReadHandoffs(takingOverFrom)
|
||||||
|
pendingReadHandoffs -= takingOverFrom
|
||||||
|
eventPublisher.notifyListeners(AssociatedEvent(handle.localAddress, handle.remoteAddress, true))
|
||||||
|
val endpoint = createEndpoint(
|
||||||
|
handle.remoteAddress,
|
||||||
|
handle.localAddress,
|
||||||
|
transportMapping(handle.localAddress),
|
||||||
|
settings,
|
||||||
|
Some(handle),
|
||||||
|
writing = false)
|
||||||
|
endpoints.registerReadOnlyEndpoint(handle.remoteAddress, endpoint)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private def createEndpoint(remoteAddress: Address,
|
private def createEndpoint(remoteAddress: Address,
|
||||||
localAddress: Address,
|
localAddress: Address,
|
||||||
transport: Transport,
|
transport: Transport,
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue