mirror of
https://github.com/tower-rs/tower.git
synced 2025-10-02 07:20:52 +00:00
Update tower-load to std::future (#321)
This bumps tower-load to 0.3.0-alpha.1
This commit is contained in:
parent
db116d1937
commit
f8097a60f6
@ -9,7 +9,7 @@ members = [
|
||||
# "tower-hedge",
|
||||
"tower-layer",
|
||||
# "tower-limit",
|
||||
# "tower-load",
|
||||
"tower-load",
|
||||
# "tower-load-shed",
|
||||
# "tower-reconnect",
|
||||
# "tower-retry",
|
||||
|
@ -1,3 +1,7 @@
|
||||
# 0.3.0-alpha.1
|
||||
|
||||
- Move to `std::future`
|
||||
|
||||
# 0.1.0 (unreleased)
|
||||
|
||||
- Initial release
|
||||
|
@ -8,13 +8,13 @@ name = "tower-load"
|
||||
# - README.md
|
||||
# - Update CHANGELOG.md.
|
||||
# - Create "v0.1.x" git tag.
|
||||
version = "0.1.0"
|
||||
version = "0.3.0-alpha.1"
|
||||
authors = ["Tower Maintainers <team@tower-rs.com>"]
|
||||
license = "MIT"
|
||||
readme = "README.md"
|
||||
repository = "https://github.com/tower-rs/tower"
|
||||
homepage = "https://github.com/tower-rs/tower"
|
||||
documentation = "https://docs.rs/tower-load/0.1.0"
|
||||
documentation = "https://docs.rs/tower-load/0.3.0-alpha.1"
|
||||
description = """
|
||||
Strategies for measuring the load of a service
|
||||
"""
|
||||
@ -23,11 +23,13 @@ edition = "2018"
|
||||
publish = false
|
||||
|
||||
[dependencies]
|
||||
futures = "0.1.26"
|
||||
futures-core-preview = "0.3.0-alpha.18"
|
||||
log = "0.4.1"
|
||||
tokio-timer = "0.2.4"
|
||||
tower-service = "0.2.0"
|
||||
tower-discover = "0.1.0"
|
||||
tokio-timer = "0.3.0-alpha.4"
|
||||
tower-service = "0.3.0-alpha.1"
|
||||
tower-discover = { version = "0.3.0-alpha.1", path = "../tower-discover" }
|
||||
pin-project = "0.4.0-alpha.9"
|
||||
|
||||
[dev-dependencies]
|
||||
tokio-executor = "0.1.2"
|
||||
tokio-test = "0.2.0-alpha.4"
|
||||
futures-util-preview = "0.3.0-alpha.18"
|
||||
|
@ -1,12 +1,18 @@
|
||||
//! A constant `Load` implementation. Primarily useful for testing.
|
||||
|
||||
use futures::{try_ready, Async, Poll};
|
||||
use futures_core::ready;
|
||||
use pin_project::pin_project;
|
||||
use std::{
|
||||
pin::Pin,
|
||||
task::{Context, Poll},
|
||||
};
|
||||
use tower_discover::{Change, Discover};
|
||||
use tower_service::Service;
|
||||
|
||||
use crate::Load;
|
||||
|
||||
/// Wraps a type so that `Load::load` returns a constant value.
|
||||
#[pin_project]
|
||||
pub struct Constant<T, M> {
|
||||
inner: T,
|
||||
load: M,
|
||||
@ -38,8 +44,8 @@ where
|
||||
type Error = S::Error;
|
||||
type Future = S::Future;
|
||||
|
||||
fn poll_ready(&mut self) -> Poll<(), Self::Error> {
|
||||
self.inner.poll_ready()
|
||||
fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
||||
self.inner.poll_ready(cx)
|
||||
}
|
||||
|
||||
fn call(&mut self, req: Request) -> Self::Future {
|
||||
@ -48,20 +54,24 @@ where
|
||||
}
|
||||
|
||||
/// Proxies `Discover` such that all changes are wrapped with a constant load.
|
||||
impl<D: Discover, M: Copy> Discover for Constant<D, M> {
|
||||
impl<D: Discover + Unpin, M: Copy> Discover for Constant<D, M> {
|
||||
type Key = D::Key;
|
||||
type Service = Constant<D::Service, M>;
|
||||
type Error = D::Error;
|
||||
|
||||
/// Yields the next discovery change set.
|
||||
fn poll(&mut self) -> Poll<Change<D::Key, Self::Service>, D::Error> {
|
||||
fn poll_discover(
|
||||
mut self: Pin<&mut Self>,
|
||||
cx: &mut Context<'_>,
|
||||
) -> Poll<Result<Change<D::Key, Self::Service>, D::Error>> {
|
||||
use self::Change::*;
|
||||
|
||||
let change = match try_ready!(self.inner.poll()) {
|
||||
Insert(k, svc) => Insert(k, Constant::new(svc, self.load)),
|
||||
let this = self.project();
|
||||
let change = match ready!(Pin::new(this.inner).poll_discover(cx))? {
|
||||
Insert(k, svc) => Insert(k, Constant::new(svc, *this.load)),
|
||||
Remove(k) => Remove(k),
|
||||
};
|
||||
|
||||
Ok(Async::Ready(change))
|
||||
Poll::Ready(Ok(change))
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,10 @@
|
||||
use futures::{try_ready, Future, Poll};
|
||||
use futures_core::ready;
|
||||
use pin_project::pin_project;
|
||||
use std::{
|
||||
future::Future,
|
||||
pin::Pin,
|
||||
task::{Context, Poll},
|
||||
};
|
||||
|
||||
/// Attaches `I`-typed instruments to `V` typed values.
|
||||
///
|
||||
@ -31,12 +37,10 @@ pub trait Instrument<H, V>: Clone {
|
||||
pub struct NoInstrument;
|
||||
|
||||
/// Attaches a `I`-typed instruments to the result of an `F`-typed `Future`.
|
||||
#[pin_project]
|
||||
#[derive(Debug)]
|
||||
pub struct InstrumentFuture<F, I, H>
|
||||
where
|
||||
F: Future,
|
||||
I: Instrument<H, F::Item>,
|
||||
{
|
||||
pub struct InstrumentFuture<F, I, H> {
|
||||
#[pin]
|
||||
future: F,
|
||||
handle: Option<H>,
|
||||
instrument: I,
|
||||
@ -44,11 +48,7 @@ where
|
||||
|
||||
// ===== impl InstrumentFuture =====
|
||||
|
||||
impl<F, I, H> InstrumentFuture<F, I, H>
|
||||
where
|
||||
F: Future,
|
||||
I: Instrument<H, F::Item>,
|
||||
{
|
||||
impl<F, I, H> InstrumentFuture<F, I, H> {
|
||||
/// Wraps a future, instrumenting its value if successful.
|
||||
pub fn new(instrument: I, handle: H, future: F) -> Self {
|
||||
InstrumentFuture {
|
||||
@ -59,18 +59,18 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<F, I, H> Future for InstrumentFuture<F, I, H>
|
||||
impl<F, I, H, T, E> Future for InstrumentFuture<F, I, H>
|
||||
where
|
||||
F: Future,
|
||||
I: Instrument<H, F::Item>,
|
||||
F: Future<Output = Result<T, E>>,
|
||||
I: Instrument<H, T>,
|
||||
{
|
||||
type Item = I::Output;
|
||||
type Error = F::Error;
|
||||
type Output = Result<I::Output, E>;
|
||||
|
||||
fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
|
||||
let rsp = try_ready!(self.future.poll());
|
||||
let h = self.handle.take().expect("handle");
|
||||
Ok(self.instrument.instrument(h, rsp).into())
|
||||
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||
let this = self.project();
|
||||
let rsp = ready!(this.future.poll(cx))?;
|
||||
let h = this.handle.take().expect("handle");
|
||||
Poll::Ready(Ok(this.instrument.instrument(h, rsp)))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
//! Abstractions and utilties for measuring a service's load.
|
||||
|
||||
#![doc(html_root_url = "https://docs.rs/tower-load/0.1.0")]
|
||||
#![doc(html_root_url = "https://docs.rs/tower-load/0.3.0-alpha.1")]
|
||||
#![deny(missing_docs)]
|
||||
#![deny(rust_2018_idioms)]
|
||||
#![deny(warnings)]
|
||||
|
@ -2,8 +2,13 @@
|
||||
|
||||
use super::{Instrument, InstrumentFuture, NoInstrument};
|
||||
use crate::Load;
|
||||
use futures::{try_ready, Async, Poll};
|
||||
use futures_core::ready;
|
||||
use log::trace;
|
||||
use pin_project::pin_project;
|
||||
use std::{
|
||||
pin::Pin,
|
||||
task::{Context, Poll},
|
||||
};
|
||||
use std::{
|
||||
sync::{Arc, Mutex},
|
||||
time::{Duration, Instant},
|
||||
@ -45,7 +50,9 @@ pub struct PeakEwma<S, I = NoInstrument> {
|
||||
}
|
||||
|
||||
/// Wraps a `D`-typed stream of discovery updates with `PeakEwma`.
|
||||
#[pin_project]
|
||||
pub struct PeakEwmaDiscover<D, I = NoInstrument> {
|
||||
#[pin]
|
||||
discover: D,
|
||||
decay_ns: f64,
|
||||
default_rtt: Duration,
|
||||
@ -108,21 +115,25 @@ where
|
||||
type Service = PeakEwma<D::Service, I>;
|
||||
type Error = D::Error;
|
||||
|
||||
fn poll(&mut self) -> Poll<Change<D::Key, Self::Service>, D::Error> {
|
||||
let change = match try_ready!(self.discover.poll()) {
|
||||
fn poll_discover(
|
||||
mut self: Pin<&mut Self>,
|
||||
cx: &mut Context<'_>,
|
||||
) -> Poll<Result<Change<D::Key, Self::Service>, D::Error>> {
|
||||
let this = self.project();
|
||||
let change = match ready!(this.discover.poll_discover(cx))? {
|
||||
Change::Remove(k) => Change::Remove(k),
|
||||
Change::Insert(k, svc) => {
|
||||
let peak_ewma = PeakEwma::new(
|
||||
svc,
|
||||
self.default_rtt,
|
||||
self.decay_ns,
|
||||
self.instrument.clone(),
|
||||
*this.default_rtt,
|
||||
*this.decay_ns,
|
||||
this.instrument.clone(),
|
||||
);
|
||||
Change::Insert(k, peak_ewma)
|
||||
}
|
||||
};
|
||||
|
||||
Ok(Async::Ready(change))
|
||||
Poll::Ready(Ok(change))
|
||||
}
|
||||
}
|
||||
|
||||
@ -156,8 +167,8 @@ where
|
||||
type Error = S::Error;
|
||||
type Future = InstrumentFuture<S::Future, I, Handle>;
|
||||
|
||||
fn poll_ready(&mut self) -> Poll<(), Self::Error> {
|
||||
self.service.poll_ready()
|
||||
fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
||||
self.service.poll_ready(cx)
|
||||
}
|
||||
|
||||
fn call(&mut self, req: Request) -> Self::Future {
|
||||
@ -293,12 +304,16 @@ fn nanos(d: Duration) -> f64 {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use futures::{future, Future, Poll};
|
||||
use futures_util::future;
|
||||
use std::{
|
||||
future::Future,
|
||||
task::{Context, Poll},
|
||||
};
|
||||
use std::{
|
||||
sync::{Arc, Mutex},
|
||||
time::{Duration, Instant},
|
||||
};
|
||||
use tokio_executor::enter;
|
||||
use tokio_test::{assert_ready, assert_ready_ok};
|
||||
use tokio_timer::clock;
|
||||
|
||||
use super::*;
|
||||
@ -307,10 +322,10 @@ mod tests {
|
||||
impl Service<()> for Svc {
|
||||
type Response = ();
|
||||
type Error = ();
|
||||
type Future = future::FutureResult<(), ()>;
|
||||
type Future = future::Ready<Result<(), ()>>;
|
||||
|
||||
fn poll_ready(&mut self) -> Poll<(), ()> {
|
||||
Ok(().into())
|
||||
fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll<Result<(), ()>> {
|
||||
Poll::Ready(Ok(()))
|
||||
}
|
||||
|
||||
fn call(&mut self, (): ()) -> Self::Future {
|
||||
@ -332,8 +347,7 @@ mod tests {
|
||||
let time = Arc::new(Mutex::new(Instant::now()));
|
||||
let clock = clock::Clock::new_with_now(Now(time.clone()));
|
||||
|
||||
let mut enter = enter().expect("enter");
|
||||
clock::with_default(&clock, &mut enter, |_| {
|
||||
clock::with_default(&clock, || {
|
||||
let svc = PeakEwma::new(
|
||||
Svc,
|
||||
Duration::from_millis(10),
|
||||
@ -360,38 +374,39 @@ mod tests {
|
||||
let time = Arc::new(Mutex::new(Instant::now()));
|
||||
let clock = clock::Clock::new_with_now(Now(time.clone()));
|
||||
|
||||
let mut enter = enter().expect("enter");
|
||||
clock::with_default(&clock, &mut enter, |_| {
|
||||
let mut svc = PeakEwma::new(
|
||||
Svc,
|
||||
Duration::from_millis(20),
|
||||
NANOS_PER_MILLI * 1_000.0,
|
||||
NoInstrument,
|
||||
);
|
||||
assert_eq!(svc.load(), Cost(20.0 * NANOS_PER_MILLI));
|
||||
clock::with_default(&clock, || {
|
||||
tokio_test::task::mock(|cx| {
|
||||
let mut svc = PeakEwma::new(
|
||||
Svc,
|
||||
Duration::from_millis(20),
|
||||
NANOS_PER_MILLI * 1_000.0,
|
||||
NoInstrument,
|
||||
);
|
||||
assert_eq!(svc.load(), Cost(20.0 * NANOS_PER_MILLI));
|
||||
|
||||
*time.lock().unwrap() += Duration::from_millis(100);
|
||||
let rsp0 = svc.call(());
|
||||
assert!(svc.load() > Cost(20.0 * NANOS_PER_MILLI));
|
||||
*time.lock().unwrap() += Duration::from_millis(100);
|
||||
let mut rsp0 = svc.call(());
|
||||
assert!(svc.load() > Cost(20.0 * NANOS_PER_MILLI));
|
||||
|
||||
*time.lock().unwrap() += Duration::from_millis(100);
|
||||
let rsp1 = svc.call(());
|
||||
assert!(svc.load() > Cost(40.0 * NANOS_PER_MILLI));
|
||||
*time.lock().unwrap() += Duration::from_millis(100);
|
||||
let mut rsp1 = svc.call(());
|
||||
assert!(svc.load() > Cost(40.0 * NANOS_PER_MILLI));
|
||||
|
||||
*time.lock().unwrap() += Duration::from_millis(100);
|
||||
let () = rsp0.wait().unwrap();
|
||||
assert_eq!(svc.load(), Cost(400_000_000.0));
|
||||
*time.lock().unwrap() += Duration::from_millis(100);
|
||||
let () = assert_ready_ok!(Pin::new(&mut rsp0).poll(cx));
|
||||
assert_eq!(svc.load(), Cost(400_000_000.0));
|
||||
|
||||
*time.lock().unwrap() += Duration::from_millis(100);
|
||||
let () = rsp1.wait().unwrap();
|
||||
assert_eq!(svc.load(), Cost(200_000_000.0));
|
||||
*time.lock().unwrap() += Duration::from_millis(100);
|
||||
let () = assert_ready_ok!(Pin::new(&mut rsp1).poll(cx));
|
||||
assert_eq!(svc.load(), Cost(200_000_000.0));
|
||||
|
||||
// Check that values decay as time elapses
|
||||
*time.lock().unwrap() += Duration::from_secs(1);
|
||||
assert!(svc.load() < Cost(100_000_000.0));
|
||||
// Check that values decay as time elapses
|
||||
*time.lock().unwrap() += Duration::from_secs(1);
|
||||
assert!(svc.load() < Cost(100_000_000.0));
|
||||
|
||||
*time.lock().unwrap() += Duration::from_secs(10);
|
||||
assert!(svc.load() < Cost(100_000.0));
|
||||
*time.lock().unwrap() += Duration::from_secs(10);
|
||||
assert!(svc.load() < Cost(100_000.0));
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -2,8 +2,13 @@
|
||||
|
||||
use super::{Instrument, InstrumentFuture, NoInstrument};
|
||||
use crate::Load;
|
||||
use futures::{try_ready, Async, Poll};
|
||||
use futures_core::ready;
|
||||
use pin_project::pin_project;
|
||||
use std::sync::Arc;
|
||||
use std::{
|
||||
pin::Pin,
|
||||
task::{Context, Poll},
|
||||
};
|
||||
use tower_discover::{Change, Discover};
|
||||
use tower_service::Service;
|
||||
|
||||
@ -21,8 +26,10 @@ pub struct PendingRequests<S, I = NoInstrument> {
|
||||
struct RefCount(Arc<()>);
|
||||
|
||||
/// Wraps `inner`'s services with `PendingRequests`.
|
||||
#[pin_project]
|
||||
#[derive(Debug)]
|
||||
pub struct PendingRequestsDiscover<D, I = NoInstrument> {
|
||||
#[pin]
|
||||
discover: D,
|
||||
instrument: I,
|
||||
}
|
||||
@ -69,8 +76,8 @@ where
|
||||
type Error = S::Error;
|
||||
type Future = InstrumentFuture<S::Future, I, Handle>;
|
||||
|
||||
fn poll_ready(&mut self) -> Poll<(), Self::Error> {
|
||||
self.service.poll_ready()
|
||||
fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
||||
self.service.poll_ready(cx)
|
||||
}
|
||||
|
||||
fn call(&mut self, req: Request) -> Self::Future {
|
||||
@ -109,15 +116,19 @@ where
|
||||
type Error = D::Error;
|
||||
|
||||
/// Yields the next discovery change set.
|
||||
fn poll(&mut self) -> Poll<Change<D::Key, Self::Service>, D::Error> {
|
||||
fn poll_discover(
|
||||
mut self: Pin<&mut Self>,
|
||||
cx: &mut Context<'_>,
|
||||
) -> Poll<Result<Change<D::Key, Self::Service>, D::Error>> {
|
||||
use self::Change::*;
|
||||
|
||||
let change = match try_ready!(self.discover.poll()) {
|
||||
Insert(k, svc) => Insert(k, PendingRequests::new(svc, self.instrument.clone())),
|
||||
let this = self.project();
|
||||
let change = match ready!(this.discover.poll_discover(cx))? {
|
||||
Insert(k, svc) => Insert(k, PendingRequests::new(svc, this.instrument.clone())),
|
||||
Remove(k) => Remove(k),
|
||||
};
|
||||
|
||||
Ok(Async::Ready(change))
|
||||
Poll::Ready(Ok(change))
|
||||
}
|
||||
}
|
||||
|
||||
@ -132,16 +143,17 @@ impl RefCount {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use futures::{future, Future, Poll};
|
||||
use futures_util::future;
|
||||
use std::task::{Context, Poll};
|
||||
|
||||
struct Svc;
|
||||
impl Service<()> for Svc {
|
||||
type Response = ();
|
||||
type Error = ();
|
||||
type Future = future::FutureResult<(), ()>;
|
||||
type Future = future::Ready<Result<(), ()>>;
|
||||
|
||||
fn poll_ready(&mut self) -> Poll<(), ()> {
|
||||
Ok(().into())
|
||||
fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll<Result<(), ()>> {
|
||||
Poll::Ready(Ok(()))
|
||||
}
|
||||
|
||||
fn call(&mut self, (): ()) -> Self::Future {
|
||||
@ -160,10 +172,10 @@ mod tests {
|
||||
let rsp1 = svc.call(());
|
||||
assert_eq!(svc.load(), Count(2));
|
||||
|
||||
let () = rsp0.wait().unwrap();
|
||||
let () = tokio_test::block_on(rsp0).unwrap();
|
||||
assert_eq!(svc.load(), Count(1));
|
||||
|
||||
let () = rsp1.wait().unwrap();
|
||||
let () = tokio_test::block_on(rsp1).unwrap();
|
||||
assert_eq!(svc.load(), Count(0));
|
||||
}
|
||||
|
||||
@ -183,12 +195,12 @@ mod tests {
|
||||
|
||||
let rsp = svc.call(());
|
||||
assert_eq!(svc.load(), Count(1));
|
||||
let i0 = rsp.wait().unwrap();
|
||||
let i0 = tokio_test::block_on(rsp).unwrap();
|
||||
assert_eq!(svc.load(), Count(1));
|
||||
|
||||
let rsp = svc.call(());
|
||||
assert_eq!(svc.load(), Count(2));
|
||||
let i1 = rsp.wait().unwrap();
|
||||
let i1 = tokio_test::block_on(rsp).unwrap();
|
||||
assert_eq!(svc.load(), Count(2));
|
||||
|
||||
drop(i1);
|
||||
|
Loading…
x
Reference in New Issue
Block a user