PlayFramework: Delay a route / response

29 Jul
2019

In an event-sourced environment, you sometimes have to introduce an artificial delay to some actions to make sure read-sides had the time to update themselves. If you’re using the classic routing mechanism with a routes-file, you can add such a delay declaratively, like here:

+ delay800
PUT   /products/:productId          controllers.ProductCRUDController.update(productId: UUID)

This would delay the response of the update-action by 800 Milliseconds, hopefully giving the other consuming services enough time to catch up.

To make this work, you have to add a filter to your application, like this one:

import akka.actor.ActorSystem
import akka.stream.Materializer
import play.api.mvc.{Filter, RequestHeader, Result}
import play.api.routing.Router
import scala.concurrent.duration._
import scala.concurrent.{ExecutionContext, Future}

class DelayFilter(val mat: Materializer, implicit val ec: ExecutionContext, actorSystem: ActorSystem) extends Filter {

  override def apply(nextFilter: RequestHeader => Future[Result])(rh: RequestHeader): Future[Result] = {
    val handler = rh.attrs.get(Router.Attrs.HandlerDef)
    val delay: Option[Int] = handler
      .flatMap(
        _.modifiers
          .find(s => s.startsWith("delay")))
      .map(s => Integer.parseInt(s.substring("delay".length)))
      .filter(_ > 0)

    val result = nextFilter.apply(rh)
    delay match {
      case Some(d) => akka.pattern.after(d.millis, actorSystem.scheduler)(result)
      case None => result
    }
  }
}Code language: Scala (scala)

and of course, enable the filter in your application.conf

play.filters.enabled+=mypackage.filters.DelayFilterCode language: Properties (properties)

Did this help you in your project? Let me know in the comments!

Comment Form

top