Remove `ReadyService` (#68)

The value added by having a separate trait is not obvious. Equivalent
behavior can be provided by a `Service` implementation that is always
"ready".
This commit is contained in:
Carl Lerche 2018-04-25 14:32:13 -07:00 committed by GitHub
parent 5369879af6
commit 295ae583d4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 21 additions and 136 deletions

View File

@ -8,7 +8,6 @@ members = [
"tower-in-flight-limit",
"tower-mock",
"tower-rate-limit",
"tower-ready-service",
"tower-reconnect",
"tower-router",
"tower-service",

View File

@ -7,7 +7,6 @@ publish = false
[dependencies]
futures = "0.1"
tower-service = { version = "0.1", path = "../tower-service" }
tower-ready-service = { version = "0.1", path = "../tower-ready-service" }
[dev-dependencies]
tower-mock = { version = "0.1", path = "../tower-mock" }

View File

@ -2,10 +2,8 @@
//! service.
extern crate futures;
extern crate tower_ready_service;
extern crate tower_service;
use tower_ready_service::ReadyService;
use tower_service::Service;
use futures::{Future, Poll, Async};
@ -79,29 +77,6 @@ impl<T> InFlightLimit<T> {
pub fn into_inner(self) -> T {
self.inner
}
fn call2<F, R>(&mut self, f: F) -> ResponseFuture<R>
where F: FnOnce(&mut Self) -> R,
{
// In this implementation, `poll_ready` is not expected to be called
// first (though, it might have been).
if self.state.reserved {
self.state.reserved = false;
} else {
// Try to reserve
if !self.state.shared.reserve() {
return ResponseFuture {
inner: None,
shared: self.state.shared.clone(),
};
}
}
ResponseFuture {
inner: Some(f(self)),
shared: self.state.shared.clone(),
}
}
}
impl<S> Service for InFlightLimit<S>
@ -131,20 +106,24 @@ where S: Service
}
fn call(&mut self, request: Self::Request) -> Self::Future {
self.call2(|me| me.inner.call(request))
}
}
// In this implementation, `poll_ready` is not expected to be called
// first (though, it might have been).
if self.state.reserved {
self.state.reserved = false;
} else {
// Try to reserve
if !self.state.shared.reserve() {
return ResponseFuture {
inner: None,
shared: self.state.shared.clone(),
};
}
}
impl<S> ReadyService for InFlightLimit<S>
where S: ReadyService
{
type Request = S::Request;
type Response = S::Response;
type Error = Error<S::Error>;
type Future = ResponseFuture<S::Future>;
fn call(&mut self, request: Self::Request) -> Self::Future {
self.call2(|me| me.inner.call(request))
ResponseFuture {
inner: Some(self.inner.call(request)),
shared: self.state.shared.clone(),
}
}
}

View File

@ -1,9 +0,0 @@
[package]
name = "tower-ready-service"
version = "0.1.0"
authors = ["Carl Lerche <me@carllerche.com>"]
publish = false
[dependencies]
futures = "0.1"
tower-service = { version = "0.1", path = "../tower-service" }

View File

@ -1,3 +0,0 @@
extern crate tower_service;
pub use tower_service::ReadyService;

View File

@ -189,47 +189,9 @@ pub trait Service {
/// implementations should take care to not call `poll_ready`. If the
/// service is at capacity and the request is unable to be handled, the
/// returned `Future` should resolve to an error.
fn call(&mut self, req: Self::Request) -> Self::Future;
}
/// An asynchronous function from `Request` to a `Response` that is always ready
/// to process a request.
///
/// `ReadyService` is similar to `Service`, except that it is always able to
/// accept a request. This request may either complete successfully or resolve
/// to an error, i.e., `ReadyService` implementations may handle out of capacity
/// situations by returning a response future that immediately resolves to an
/// error.
///
/// The `Service` trait should be prefered over this one. `ReadyService` should
/// only be used in situations where there is no way to handle back pressure.
/// When usin a `ReadyService` implementation, back pressure needs to be handled
/// via some other strategy, such as limiting the total number of in flight
/// requests.
///
/// One situation in which there is no way to handle back pressure is routing.
/// A router service receives inbound requests and dispatches them to one of N
/// inner services. In this case, one of the inner services may become "not
/// ready" while the others remain ready. It would not be ideal for the router
/// service to flag itself as "not ready" when only one of the inner services is
/// not ready, but there is no way for the router to communicate to the caller
/// which requests will be accepted and which will be rejected. The router
/// service will implement `ReadyService`, indicating to the user that they are
/// responsible for handling back pressure via some other strategy.
pub trait ReadyService {
/// Requests handled by the service.
type Request;
/// Responses returned by the service.
type Response;
/// Errors produced by the service.
type Error;
/// The future response value.
type Future: Future<Item = Self::Response, Error = Self::Error>;
/// Process the request and return the response asynchronously.
///
/// Calling `call` without calling `poll_ready` is permitted. The
/// implementation must be resilient to this fact.
fn call(&mut self, req: Self::Request) -> Self::Future;
}

View File

@ -7,4 +7,3 @@ publish = false
[dependencies]
futures = "0.1"
tower-service = { version = "0.1", path = "../tower-service" }
tower-ready-service = { version = "0.1", path = "../tower-ready-service" }

View File

@ -1,7 +1,6 @@
//! Various utility types and functions that are generally with Tower.
extern crate futures;
extern crate tower_ready_service;
extern crate tower_service;
pub mod either;
@ -11,5 +10,5 @@ mod service_fn;
pub use boxed::BoxService;
pub use either::EitherService;
pub use service_fn::{ServiceFn, NewServiceFn};
pub use service_fn::{NewServiceFn};
pub use option::OptionService;

View File

@ -1,51 +1,11 @@
use futures::IntoFuture;
use tower_ready_service::ReadyService;
use tower_service::{Service, NewService};
use std::marker::PhantomData;
/// A `Service` implemented by a closure.
#[derive(Debug, Clone)]
pub struct ServiceFn<T, R> {
f: T,
// don't impose Sync on R
_ty: PhantomData<fn() -> R>,
}
/// A `NewService` implemented by a closure.
pub struct NewServiceFn<T> {
f: T,
}
// ===== impl ServiceFn =====
impl<T, R, S> ServiceFn<T, R>
where T: FnMut(R) -> S,
S: IntoFuture,
{
/// Create a new `ServiceFn` backed by the given closure
pub fn new(f: T) -> Self {
ServiceFn {
f,
_ty: PhantomData,
}
}
}
impl<T, R, S> ReadyService for ServiceFn<T, R>
where T: FnMut(R) -> S,
S: IntoFuture,
{
type Request = R;
type Response = S::Item;
type Error = S::Error;
type Future = S::Future;
fn call(&mut self, request: Self::Request) -> Self::Future {
(self.f)(request).into_future()
}
}
// ===== impl NewServiceFn =====
impl<T, N> NewServiceFn<T>