-
Notifications
You must be signed in to change notification settings - Fork 280
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
decide the future of tower::filter
#502
Comments
Actually, after thinking about it, this is only possible if We could have a wrapper fn for turning an async predicate into a sync predicate, but that seems like "just have two separate APIs" but with more steps... |
Doesn't |
Yes, it's always possible to satisfy an async interface with a purely synchronous function using a future that doesn't await, and it's never possible to satisfy a purely synchronous interface with an async function. However, that solution introduces some additional complexity (and overhead) in the case of That's why I think it's better to have two interfaces, so that synchronous filtering doesn't need to introduce a |
Yeah, I agree, I wonder if we could use a trait maybe to get around it? but that sounds hard. We will just have to bike shed the names :( |
## Motivation It was pointed out that there is currently some overlap between the `try_with` `Service` combinator and `tower::filter` middleware (see #499 (comment) ). `try_with` synchronously maps from a `Request` -> `Result<DifferentRequest, Error>`, while `tower::filter` _asynchronously_ maps from a `&Request` to a `Result<(), Error>`. The key differences are: - `try_with` takes a request by value, and allows the predicate to return a *different* request value - `try_with` also permits changing the _type_ of the request - `try_with` is synchronous, while `tower::filter` is asynchronous - `tower::filter` has a `Predicate` trait, which can be implemented by more than just functions. For example, a struct with a `HashSet` could implement `Predicate` by failing requests that match the values in the hashset. It definitely seems like there's demand for both synchronous and asynchronous request filtering. However, the APIs we have currently differ pretty significantly. It would be nice to make them more consistent with each other. As an aside, `tower::filter` [does not seem all that widely used][1]. Meanwhile, `linkerd2-proxy` defines its own `RequestFilter` middleware, using a [predicate trait][2] that's essentially in between `tower::filter` and `ServiceExt::try_with`: - it's synchronous, like `try_with` - it allows modifying the type of the request, like `try_with` - it uses a trait for predicates, rather than a `Fn`, like `tower::filter` - it uses a similar naming scheme to `tower::filter` ("filtering" rather than "with"/"map"). [1]: https://github.com/search?l=&p=1&q=%22tower%3A%3Afilter%22+extension%3Ars&ref=advsearch&type=Code [2]: https://github.com/linkerd/linkerd2-proxy/blob/24bee8cbc5413b4587a14bea1e2714ce1f1f919a/linkerd/stack/src/request_filter.rs#L8-L12 ## Solution This branch rewrites `tower::filter` to make the following changes: * Predicates are synchronous by default. A separate `AsyncFilter` type and an `AsyncPredicate` trait are available for predicates returning futures. * Predicates may now return a new `Request` type, allowing `Filter` and `AsyncFilter` to subsume `try_map_request`. * Predicates may now return any error type, and errors are now converted into `BoxError`s. Closes #502 Signed-off-by: Eliza Weisman <[email protected]>
Motivation
It was pointed out that there is currently some overlap between the
try_with
Service
combinator andtower::filter
middleware (see #499 (comment)).try_with
synchronously maps from aRequest
->Result<DifferentRequest, Error>
, whiletower::filter
asynchronously maps from a&Request
to aResult<(), Error>
. The key differences are:try_with
takes a request by value, and allows the predicate to return a different request valuetry_with
also permits changing the type of the requesttry_with
is synchronous, whiletower::filter
is asynchronoustower::filter
has aPredicate
trait, which can be implemented by more than just functions. For example, a struct with aHashSet
could implementPredicate
by failing requests that match the values in the hashset.It definitely seems like there's demand for both synchronous and asynchronous request filtering. However, the APIs we have currently differ pretty significantly. It would be nice to make them more consistent with each other.
As an aside,
tower::filter
does not seem all that widely used.Meanwhile,
linkerd2-proxy
defines its ownRequestFilter
middleware, using a predicate trait that's essentially in betweentower::filter
andServiceExt::try_with
:try_with
try_with
Fn
, liketower::filter
tower::filter
("filtering" rather than "with"/"map")Proposal
I think we should try to unify these APIs into something with a more consistent interface. I suggest the following:
Request
types by value rather than by reference, and allow returning a new request typeIt's theoretically possible to have one API for both sync and async filtering, but this would require a trait like
IntoFuture
that can be implemented by bothFuture<Output = Result<T, E>
andResult<T, E>
. A predicate trait could return something implementing that trait, andResult
could have an impl that turns itself into a ready future. This would let us have one API for both sync and async predicates. However, it introduces some complexity in the type signatures, and requires a slight added overhead (polling the immediately ready future when the predicate is synchronous). The added complexity of using anIntoFuture
-like trait might be less of an issue when the standard library stabilizesIntoFuture
...On the other hand, we could have separate sync and async filter APIs. The main disadvantage of that approach is that we'd then need to bikeshed a consistent naming scheme that makes both the distinction and the similarity similar. Historically, we've not been great at naming things efficiently ;)
cc @LucioFranco @seanmonstar @olix0r @hlb8122 --- what do you all think?
https://github.com/linkerd/linkerd2-proxy/blob/main/linkerd/stack/src/request_filter.rs
The text was updated successfully, but these errors were encountered: