Performance Pitfalls
- Web Tier State
- Unnecessary Serialization
- Blocking
- Blocking Badly
Web Tier State
- Play is stateless by default
- State lives in cookies
- Move state to the client or external data stores
Unnecessary Serialization
val foo = Json.obj("name" -> "foo")
val transformer = (__ \ "name").json.put(JsString("bar"))
val newFoo = foo.transform(transformer)
Blocking
- Threads are a precious resource
- No more thread per connection
Blocking Request
def foo = Action {
Ok("foo")
}
Async Request
def foo = Action.async {
Future.successful(Ok("foo"))
}
Reactive Request (Async + Non-Blocking)
def pause(duration: Int) = Action.async {
Promise.timeout(Ok(duration.toString), duration seconds)
}
Reactive WS Client
val f: Future[Response] = WS.url("http://www.foo.com").get
Reactive Composition
def foo = Action.async {
val futureResponse = WS.url("http://www.foo.com").get
futureResponse.map { response =>
Ok(response.body)
}
}
Reactive Composition
def foo = Action.async {
val futureJW = WS.url("http://www.jamesward.com").get
val futureTwitter = WS.url("http://www.twitter.com").get
for {
jw <- futureJW
twitter <- futureTwitter
} yield Ok(jw.response.body + twitter.response.body)
}
Non-Blocking is Better
- Play's WS client lib
- ReactiveMongo
- Redis, Datomic, etc
Blocking Badly
- Some APIs are only blocking (JDBC, Http Client, etc)
- Threads can accommodate spikes
- Push don't Pull (or Poll)
- Use Actors
Actors: Scalability & Resilience
- Event-Driven
- Non-request based lifecycle
- Managed Concurrency
- Isolated Failure Handling (Supervision)
Watcher Pattern
Client A - Request for x
Fetch x
Client B - Request for x
Client B - Watch for x
Receive x
Send x to A & B