Hi there!
I would like to implement Interceptor to control a time of effect evaluation.
I am using ZIO and different server interpreters.
First of all I tried to implement it as RequestIntercepter failing effect in case of timeout. Something like this:
RequestInterceptor.transformResultEffect(new RequestResultEffectTransform[Task] {
override def apply[B](request: ServerRequest, result: Task[RequestResult[B]]): Task[RequestResult[B]] =
result.timeoutFail(new Exception(s"timeout ${timeout.toMillis} ms exceeded"))(
zio.Duration.fromScala(3.seconds)
)
})
Also I have at the very beginning another prepended RequestInterceptor which is aim to recover all errors and render proper response from them. Something like this:
class RecoverInterceptor[F, S[_]] extends RequestInterceptor[Task] {
private val wrapServerError: Throwable => ServerException[_] = {
case se: ServerException[_] => se
case t: Throwable => ServerException.InternalError("Unknown error", t)
}
private def outputM(error: ServerException[_]) =
ValuedEndpointOutput[F](statusCode(StatusCode.InternalServerError) and jsonBody[ServerException[_]], error)
override def apply[R, B](
responder: Responder[Task, B],
requestHandler: EndpointInterceptor[Task] => RequestHandler[Task, R, B]
): RequestHandler[Task, R, B] = {
val next = requestHandler(EndpointInterceptor.noop)
new RequestHandler[Task, R, B] {
override def apply(request: ServerRequest, endpoints: List[ServerEndpoint[R, Task]])(implicit
monad: MonadError[Task]
): Task[RequestResult[B]] =
next(request, endpoints).catchNonFatalOrDie { error =>
for {
output <- outputM(wrapServerError(error))
r <- responder(request, output)
} yield RequestResult.Response(r)
}
}
}
}
But when I started to test it I realized that other Endpoint based Interceptors (for example Metric or Tracing Interceptor) were not correctly processing effect failures from Request Interceptor. As a result, when I get request timeout error no metrics are collected from this request and so on.
If I control effect duration on Endpoint Interceptor level everything will be ok. But I want to control time of all requests on Request Interceptor level.
Other way is to implement Metric and Tracing Interceptor via Request Interceptor. But on this level there is no guaranty to grab endpoint path, because request have not been decoded yet (we have only supposed endpoint list at this point). But endpoint path is one of the main attributes of metric and trace span.
So, my question is what is the proper way to implement request timeout in my case? What do you think the best approach to do it?