3

I'm using scrooge + thrift to generate my server and client code. Everything is working just fine so far.

Here's a simplified example of how I use my client:

private lazy val client =
  Thrift.newIface[MyPingService[Future]](s"$host:$port")

def main(args: Array[String]): Unit = {
  logger.info("ping!")
  client.ping().foreach { _ =>
    logger.info("pong!")
    // TODO: close client
    sys.exit(0)
  }
}

Everything is working just fine, but the server complains when the program exits about unclosed connections. I've looked all over but I can't seem to figure out how to close the client instance.

So my question is, how do you close a Finagle thrift client? I feel like I'm missing something obvious.

Chris Eberle
  • 47,994
  • 12
  • 82
  • 119

2 Answers2

7

As far as I know, when you use the automagic Thrift.newIface[Iface] method to create your service, you can't close it, because the only thing that your code knows about the resulting value is that it conforms to Iface. If you need to close it, you can instantiate your client in two steps, creating the Thrift service in one and adapting it to your interface in the other.

Here's how it looks if you're using Scrooge to generate your Thrift interface:

val serviceFactory: ServiceFactory[ThriftClientRequest,Array[Byte]] =
  Thrift.newClient(s"$host:$port")

val client: MyPingService[Future] =
  new MyPingService.FinagledClient(serviceFactory.toService)

doStuff(client).ensure(serviceFactory.close())

I tried this in the repl, and it worked for me. Here's a lightly-edited transcript:

scala> val serviceFactory = Thrift.newClient(...)
serviceFactory: ServiceFactory[ThriftClientRequest,Array[Byte]] = <function1>

scala> val tweetService = new TweetService.FinagledClient(serviceFactory.toService)
tweetService: TweetService.FinagledClient = TweetService$FinagledClient@20ef6b76

scala> Await.result(tweetService.getTweets(GetTweetsRequest(Seq(20))))
res7: Seq[GetTweetResult] = ... "just setting up my twttr" ...

scala> serviceFactory.close
res8: Future[Unit] = ConstFuture(Return(()))

scala> Await.result(tweetService.getTweets(GetTweetsRequest(Seq(20))))
com.twitter.finagle.ServiceClosedException

This is not too bad, but I hope there's a better way that I don't know yet.

j3h
  • 692
  • 5
  • 10
0

I havent used finagle, but according to Finagle documentation

val product = client().flatMap { service =>
  // `service` is checked out from the pool.
  service(QueryRequest("SELECT 5*5 AS `product`")) map {
    case rs: ResultSet => rs.rows.map(processRow)
    case _ => Seq.empty
  } ensure {
    // put `service` back into the pool.
    service.close()
  }
}

couldn’t you adopt similar strategy

client.ping().foreach { service =>
    logger.info("pong!")
    // TODO: close client
    service.close()
    sys.exit(0)
  }
Sajith Silva
  • 823
  • 1
  • 13
  • 24
  • Sorry but your usage of finagle isn't correct. The `service` value above is the return value of ping -- which in this case is `Unit`. So no that wouldn't work. You'd think that maybe `client.close()` would work but there's no such method. – Chris Eberle Jan 16 '16 at 18:22