Tower, Episode 3: Readiness
In the previous episode we have seen how to call a Tower service. In particular we have discussed the contract between poll_ready
and call
: callers must first invoke poll_ready
until it returns Poll::Ready
before they invoke call
, else call
might panic.
Of course some services, e.g. the EchoService
described in the first episode, are always ready and therefore invokig call
without poll_ready
works fine.
But generally the internal state or external dependencies, e.g. database connections, can infuence the readiness of a service. Also, composing domain services with middleware, e.g. with a rate limit or a circuit breaker, naturally effect the readiness.
To demonstrate this, let’s take a look at a service which alternates between ready and not ready:
1 | /// AlternatingReady service, responding to a [AlternatingReadyRequest] with an |
The AlternatingReadyService
tracks its readiness via the ready
field. poll_ready
is implemented such that, if the service has been ready before, it sets it as not ready and returns Poll::Pending
; and vice versa. Also, call
takes the ready
field into account such that invoking call
results in panicking if the service is not ready.
Of course this implementation is highly constructed, but it serves well to demonstrate the importance of the contract between poll_ready
and call
:
1 | let mut service = AlternatingReadyService::new(); |
In this example the second call
fails with a panic, because the first one, or rather the ready
invocation, leaves the service as not ready. If you want to check this out yourself, you can find the full code at tower-experiments on GitHub.