Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Play Scala and thread safety

The project is written using Play framework and Scala language. I have implemented compile time dependency. I have followed this example from Play:

https://github.com/playframework/play-scala-compile-di-example

Looking at the MyApplicationLoader.scala:

import play.api._
import play.api.routing.Router

class MyApplicationLoader extends ApplicationLoader {
  private var components: MyComponents = _

  def load(context: ApplicationLoader.Context): Application = {
    components = new MyComponents(context)
    components.application
  }
}

class MyComponents(context: ApplicationLoader.Context) 
  extends BuiltInComponentsFromContext(context)
  with play.filters.HttpFiltersComponents
  with _root_.controllers.AssetsComponents {

  lazy val homeController = new _root_.controllers.HomeController(controllerComponents)

  lazy val router: Router = new _root_.router.Routes(httpErrorHandler, homeController, assets)
}

and the following line of code:

 lazy val homeController = new _root_.controllers.HomeController(controllerComponents)

my understanding is that there is only one instance of HomeController created the first time HomeController is called. And that instance lives as long as the application lives. Are these statements correct?

The HomeController in my application looks like that:

class HomeController{

   val request = // some code here

   val workflowExecutionResult = Workflow.execute(request)

}

So Workflow is of type object and not class.

The Workflow looks like that:

object Workflow {
  def execute(request: Request) = {

    val retrieveCustomersResult = RetrieveCustomers.retrieve() 
    // some code here

    val createRequestResult = CreateRequest.create(request)
    // some code here

    workflowExecutionResult
  }
}

So Workflow calls a few domain services and each domain service is of type object and not class. All values inside the domain services are immutable, I am using vals everywhere.

Is this enough to ensure thread safety?

I am asking as I'm used to writing C# Web APIs where a HomeController would look like that:

class HomeControllerInSeeSharpProject{

    // some code here

    var request = new Request() // more code here
    var workflow = new WorkflowInSeeSharpProject()
    var workflowExecutionResult = workflow.execute(request)
}

and a Workflow would look like that:

public class WorkflowInSeeSharpProject {

  public execute(Request request) {

      var retrieveCustomers = new RetrieveCustomers()
      var retrieveCustomersResult = retrieveCustomers.retrieve()

      // some code here
      var createRequest = new CreateRequest()
      var createRequestResult = createRequest.create(request)

      // some code here
      return workflowExecutionResult
  }
}

So in a C# project every time a HomeControllerInSeeSharpProject is called a new instance of WorkflowInSeeSharpProject is created and all the domain services are also newed-up and then I can be sure that state cannot be shared between separate threads. So I am afraid that because my Scala Workflow and domain services are of type object and not class that there could be a situation where two requests are sent into the HomeController and state is shared between those two threads.

Can this be the case? Is my application not thread safe?

I have read that objects in Scala are not thread safe since there is only single instance of them. However I have also read that although they are not thread safe using vals will make the application thread safe...

Or maybe Play itself has a way to deal with that problem?

like image 387
Marta Avatar asked Nov 08 '22 06:11

Marta


1 Answers

Because your are using compile time dependency injection, you control the number of instances created, and in your case HomeController is created only once. As requests come in, this single instance will be shared between threads so indeed you have to make sure it is thread-safe. All the dependencies of HomeController will also need to be thread-safe, thus object Workflow has to be thread-safe. Currently, Workflow is not publicly exposing any shared state, so it is thread-safe. In general, val definitions within object are thread-safe.

In effect HomeController is behaving like a singleton and avoiding singletons could be safer. For example, by default Play Framework uses Guice dependency injection which creates a new controller instance per request as long as it is not a @Singleton. One motivation is there is less state to worry about regarding concurrency protection as suggested by Nio's answer:

In general, it is probably best to not use @Singleton unless you have a fair understanding of immutability and thread-safety. If you think you have a use case for Singleton though just make sure you are protecting any shared state.

like image 136
Mario Galic Avatar answered Nov 15 '22 09:11

Mario Galic