Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

play framework slow at first call

I have an issue, with playframework just after startup.

I have this simple controller:

@Singleton
class BomberManController @Inject()(cc: ControllerComponents) extends AbstractController(cc) {

  def index() = Action { implicit request: Request[AnyContent] =>
    Ok("test")
  }
}

On first call, on prod env, the request take 400ms, at the second request it take 2ms.

I don't understand why and how optimise that. On my project the request must take less than 300ms.

Did you have any idea?

PlayVersion: 2.6

like image 563
deblockt Avatar asked Jun 11 '18 16:06

deblockt


1 Answers

One option is to create a dummy Request and apply it directly in the constructor of the controller via index.apply(request). Consider the definition and the call point of warmUp method bellow:

@Singleton 
class BomberManController @Inject()(cc: ControllerComponents) extends AbstractController(cc) {

  warmUp()

  def index() = Action { implicit request: Request[AnyContent] =>
    Ok("test")   
  }

  private def warmUp() = {
    val requestFactory = new DefaultRequestFactory(HttpConfiguration())
    val request =
      requestFactory.createRequest(
        RemoteConnection("127.0.0.1", false, None),
        "GET",
        RequestTarget("/", "/", Map.empty),
        "HTTP/1.1",
        Headers(),
        TypedMap.empty,
        AnyContentAsEmpty
      )
    index.apply(request)   
  } 
}

In production BomberManController is instantiated on application start and thus warmUp will be called which in turn hits index endpoint.

To test this production behaviour locally, set play.http.secret.key in application.conf and start application with

 sbt clean runProd

If you do not wish to pollute your controllers with warmUp utility methods, you could separate this concern into an utility singleton, say WarmUpUtility, and make use of eager singleton binding. For example:

@Singleton 
class WarmUpUtility @Inject()(bomberManController: BomberManController)() {

  warmUp()

  private def warmUp() = {
    val requestFactory = new DefaultRequestFactory(HttpConfiguration())
    val request =
      requestFactory.createRequest(
        RemoteConnection("127.0.0.1", false, None),
        "GET",
        RequestTarget("/", "/", Map.empty),
        "HTTP/1.1",
        Headers(),
        TypedMap.empty,
        AnyContentAsEmpty
      )
    bomberManController.index.apply(request)
  }

}

// Module should be in the root package 
class Module extends AbstractModule {
  override def configure() = {
    bind(classOf[WarmUpUtility]).asEagerSingleton()
  }
}
like image 117
Mario Galic Avatar answered Nov 04 '22 09:11

Mario Galic