Getting response times

1k views Asked by At

Is there a simple way to get the response time for a request to a url (other than keeping track of time in the code separately)?

import dispatch._, Defaults._
import scala.util.{Failure, Success}

val svc = dispatch.url(url)
  val response: Future[com.ning.http.client.Response] = Http(svc > (x => x))
  response onComplete {
    case Success(content) => {
      println(s"SUCCESS: ${content.getStatusCode()} for $url")
      //how long did this take??
 }
    case Failure(t) => {
      println(s"ERROR: timeout/failure for $url")
    }
  }
2

There are 2 answers

3
Gábor Bakos On BEST ANSWER

This is a different approach for computing the elapsed time, might need a bit more customization (like handling of failure, NonFatal most probably), but I think this is a bit more idiomatic. It works only with Futures though.

First create a class for the result with time (could be replaced with a tuple too) and one for the failure with time.

import scala.concurrent.Future
import scala.concurrent.ExecutionContext.Implicits.global

case class ResultWithTime[ResultType](val result: ResultType, val time: Long)

class FailureWithTime(failure: Throwable, val time: Long) extends Throwable(failure)

Then create a method that wraps the future to execute and handles the case when it fails (might use Promise if you do not like the Future's map and recoverWith methods (as they require an implicit ExecutionContext)).

object TimerFuture {
  def apply[ResultType](fut: =>Future[ResultType]): Future[ResultWithTime[ResultType]] = {
    val startTime = System.currentTimeMillis
    fut.map(r => ResultWithTime(r, System.currentTimeMillis - startTime))
       .recoverWith{case t:Throwable =>
         Future.failed(new FailureWithTime(t, System.currentTimeMillis - startTime))
         }
  }
}

Here is an example how to use it:

import scala.concurrent.Future
import scala.concurrent.ExecutionContext.Implicits.global
import scala.io.StdIn


object Y extends App {
   TimerFuture(Future{Thread.sleep(200); "Hello"})
     .onSuccess({case ResultWithTime(r, t) => println(s"Took $t ms to get: $r")})
   TimerFuture(Future{Thread.sleep(100); throw new NullPointerException()})
     .onFailure({case f: FailureWithTime => println(s"Took ${f.time} ms to get: ${f.getCause}")})
   StdIn.readLine()
}
3
Gábor Bakos On

You can wrap your code with something like this (not tested):

class Timer[ResultType](computation: =>Future[ResultType]) {
  private val startTime = System.currentTimeMillis
  val result: Future[ResultType] = computation
  private var endTime: Option[Long] = None
  result.onComplete{case _ => endTime = Some(System.currentTimeMillis)}
  def responseTime: Option[Long] = endTime.map(_ - startTime)
  //probably also provide onComplete and other Future methods wrapping result.
}

object Timer{
  def apply[ResultType](computation: =>Future[ResultType]) = {
    new Timer(computation)
  }
}

You can create more specific timers with factory methods in companion objects to help working with the computations you want to measure.

Usage:

val timedComputation = Timer{Http(svc > (x => x))}
timedComputation.result.onComplete{case _ => ???}
//In a completed event:
val responseTimeInMillis: Long = timedComputation.responseTime.get