Hardened flushing by sending a final "null" message #990

Merged
merged 1 commit into from Jan 3, 2013

Projects

None yet

4 participants

@drewhk
Member
drewhk commented Jan 3, 2013

To ensure proper flushing of the Netty transport a "null" message is written to the channel first, and we proceed with the release of the resources after that operation is finished.

Collaborator
rkuhn commented Jan 3, 2013

LGTM

@patriknw patriknw commented on the diff Jan 3, 2013
...in/scala/akka/remote/transport/netty/TcpSupport.scala
@@ -32,7 +32,8 @@ private[remote] trait TcpHandlers extends CommonHandlers {
}
override def onMessage(ctx: ChannelHandlerContext, e: MessageEvent) {
- notifyListener(e.getChannel, InboundPayload(ByteString(e.getMessage.asInstanceOf[ChannelBuffer].array())))
+ val bytes: Array[Byte] = e.getMessage.asInstanceOf[ChannelBuffer].array()
+ if (bytes.length > 0) notifyListener(e.getChannel, InboundPayload(ByteString(bytes)))
patriknw
patriknw Jan 3, 2013 Owner

Does this imply that application messages can't be 0 bytes? Is that an issue?

viktorklang
viktorklang Jan 3, 2013 Owner

That's not an application message tho? It's the inbound payload (Akka protocol), right?

patriknw
patriknw Jan 3, 2013 Owner

ok, I see

drewhk
drewhk Jan 3, 2013 Member

No, it's even lower layer. This is the payload of UDP packets, or length
encoded TCP frames. No upper layer protocol message can be contained in a
0 byte frame, so this is safe.

Owner
patriknw commented Jan 3, 2013

LGTM

Owner
patriknw commented Jan 3, 2013

I tried this patch in the stress test branch, locally, and it was success. That is a good start, and I would like to do a proper rebase and continue running the stress test on the build server. Please merge.

@drewhk drewhk merged commit f2aa947 into akka:master Jan 3, 2013
@viktorklang viktorklang commented on the diff Jan 3, 2013
...cala/akka/remote/transport/netty/NettyTransport.scala
}
})
+
viktorklang
viktorklang Jan 3, 2013 Owner

I suggest adding the following to NettyFutureBridge:

def apply(nettyFuture: ChannelGroupFuture): Future[ChannelGroup] = {
    val p = Promise[ChannelGroup]()
    nettyFuture.addListener(new ChannelFutureListener {
      def operationComplete(future: ChannelFuture): Unit = p complete Try(
        if (future.isSuccess) future.getGroup
        else if (future.isCancelled) throw new CancellationException
        else throw future.getCause)
    })
    p.future
  }

And then you can write shutdown as something like:

override def shutdown(): Unit = {
  import appropriate ExecutionContext here
  def always(c: ChannelGroupFuture) = NettyFutureBridge(c) recover { case _ => c.getGroup }
  for {
   _ <- always(channelGroup.write(ChannelBuffers.buffer(0)))
   _ <- always({ channelGroup.unbind(); channelGroup.disconnect() })
   _ <- always(channelGroup.close())
   } inboundBootstrap.releaseExternalResources()
}
drewhk
drewhk Jan 3, 2013 Member

Ok, Cool!

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