Update tower-load to std::future (#321)

This bumps tower-load to 0.3.0-alpha.1
This commit is contained in:
Jon Gjengset 2019-09-09 09:22:49 -04:00 committed by GitHub
parent db116d1937
commit f8097a60f6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 137 additions and 94 deletions

View File

@ -9,7 +9,7 @@ members = [
# "tower-hedge", # "tower-hedge",
"tower-layer", "tower-layer",
# "tower-limit", # "tower-limit",
# "tower-load", "tower-load",
# "tower-load-shed", # "tower-load-shed",
# "tower-reconnect", # "tower-reconnect",
# "tower-retry", # "tower-retry",

View File

@ -1,3 +1,7 @@
# 0.3.0-alpha.1
- Move to `std::future`
# 0.1.0 (unreleased) # 0.1.0 (unreleased)
- Initial release - Initial release

View File

@ -8,13 +8,13 @@ name = "tower-load"
# - README.md # - README.md
# - Update CHANGELOG.md. # - Update CHANGELOG.md.
# - Create "v0.1.x" git tag. # - Create "v0.1.x" git tag.
version = "0.1.0" version = "0.3.0-alpha.1"
authors = ["Tower Maintainers <team@tower-rs.com>"] authors = ["Tower Maintainers <team@tower-rs.com>"]
license = "MIT" license = "MIT"
readme = "README.md" readme = "README.md"
repository = "https://github.com/tower-rs/tower" repository = "https://github.com/tower-rs/tower"
homepage = "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 = """ description = """
Strategies for measuring the load of a service Strategies for measuring the load of a service
""" """
@ -23,11 +23,13 @@ edition = "2018"
publish = false publish = false
[dependencies] [dependencies]
futures = "0.1.26" futures-core-preview = "0.3.0-alpha.18"
log = "0.4.1" log = "0.4.1"
tokio-timer = "0.2.4" tokio-timer = "0.3.0-alpha.4"
tower-service = "0.2.0" tower-service = "0.3.0-alpha.1"
tower-discover = "0.1.0" tower-discover = { version = "0.3.0-alpha.1", path = "../tower-discover" }
pin-project = "0.4.0-alpha.9"
[dev-dependencies] [dev-dependencies]
tokio-executor = "0.1.2" tokio-test = "0.2.0-alpha.4"
futures-util-preview = "0.3.0-alpha.18"

View File

@ -1,12 +1,18 @@
//! A constant `Load` implementation. Primarily useful for testing. //! 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_discover::{Change, Discover};
use tower_service::Service; use tower_service::Service;
use crate::Load; use crate::Load;
/// Wraps a type so that `Load::load` returns a constant value. /// Wraps a type so that `Load::load` returns a constant value.
#[pin_project]
pub struct Constant<T, M> { pub struct Constant<T, M> {
inner: T, inner: T,
load: M, load: M,
@ -38,8 +44,8 @@ where
type Error = S::Error; type Error = S::Error;
type Future = S::Future; type Future = S::Future;
fn poll_ready(&mut self) -> Poll<(), Self::Error> { fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
self.inner.poll_ready() self.inner.poll_ready(cx)
} }
fn call(&mut self, req: Request) -> Self::Future { 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. /// 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 Key = D::Key;
type Service = Constant<D::Service, M>; type Service = Constant<D::Service, M>;
type Error = D::Error; type Error = D::Error;
/// Yields the next discovery change set. /// 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::*; use self::Change::*;
let change = match try_ready!(self.inner.poll()) { let this = self.project();
Insert(k, svc) => Insert(k, Constant::new(svc, self.load)), 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), Remove(k) => Remove(k),
}; };
Ok(Async::Ready(change)) Poll::Ready(Ok(change))
} }
} }

View File

@ -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. /// Attaches `I`-typed instruments to `V` typed values.
/// ///
@ -31,12 +37,10 @@ pub trait Instrument<H, V>: Clone {
pub struct NoInstrument; pub struct NoInstrument;
/// Attaches a `I`-typed instruments to the result of an `F`-typed `Future`. /// Attaches a `I`-typed instruments to the result of an `F`-typed `Future`.
#[pin_project]
#[derive(Debug)] #[derive(Debug)]
pub struct InstrumentFuture<F, I, H> pub struct InstrumentFuture<F, I, H> {
where #[pin]
F: Future,
I: Instrument<H, F::Item>,
{
future: F, future: F,
handle: Option<H>, handle: Option<H>,
instrument: I, instrument: I,
@ -44,11 +48,7 @@ where
// ===== impl InstrumentFuture ===== // ===== impl InstrumentFuture =====
impl<F, I, H> InstrumentFuture<F, I, H> impl<F, I, H> InstrumentFuture<F, I, H> {
where
F: Future,
I: Instrument<H, F::Item>,
{
/// Wraps a future, instrumenting its value if successful. /// Wraps a future, instrumenting its value if successful.
pub fn new(instrument: I, handle: H, future: F) -> Self { pub fn new(instrument: I, handle: H, future: F) -> Self {
InstrumentFuture { 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 where
F: Future, F: Future<Output = Result<T, E>>,
I: Instrument<H, F::Item>, I: Instrument<H, T>,
{ {
type Item = I::Output; type Output = Result<I::Output, E>;
type Error = F::Error;
fn poll(&mut self) -> Poll<Self::Item, Self::Error> { fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let rsp = try_ready!(self.future.poll()); let this = self.project();
let h = self.handle.take().expect("handle"); let rsp = ready!(this.future.poll(cx))?;
Ok(self.instrument.instrument(h, rsp).into()) let h = this.handle.take().expect("handle");
Poll::Ready(Ok(this.instrument.instrument(h, rsp)))
} }
} }

View File

@ -1,6 +1,6 @@
//! Abstractions and utilties for measuring a service's load. //! 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(missing_docs)]
#![deny(rust_2018_idioms)] #![deny(rust_2018_idioms)]
#![deny(warnings)] #![deny(warnings)]

View File

@ -2,8 +2,13 @@
use super::{Instrument, InstrumentFuture, NoInstrument}; use super::{Instrument, InstrumentFuture, NoInstrument};
use crate::Load; use crate::Load;
use futures::{try_ready, Async, Poll}; use futures_core::ready;
use log::trace; use log::trace;
use pin_project::pin_project;
use std::{
pin::Pin,
task::{Context, Poll},
};
use std::{ use std::{
sync::{Arc, Mutex}, sync::{Arc, Mutex},
time::{Duration, Instant}, time::{Duration, Instant},
@ -45,7 +50,9 @@ pub struct PeakEwma<S, I = NoInstrument> {
} }
/// Wraps a `D`-typed stream of discovery updates with `PeakEwma`. /// Wraps a `D`-typed stream of discovery updates with `PeakEwma`.
#[pin_project]
pub struct PeakEwmaDiscover<D, I = NoInstrument> { pub struct PeakEwmaDiscover<D, I = NoInstrument> {
#[pin]
discover: D, discover: D,
decay_ns: f64, decay_ns: f64,
default_rtt: Duration, default_rtt: Duration,
@ -108,21 +115,25 @@ where
type Service = PeakEwma<D::Service, I>; type Service = PeakEwma<D::Service, I>;
type Error = D::Error; type Error = D::Error;
fn poll(&mut self) -> Poll<Change<D::Key, Self::Service>, D::Error> { fn poll_discover(
let change = match try_ready!(self.discover.poll()) { 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::Remove(k) => Change::Remove(k),
Change::Insert(k, svc) => { Change::Insert(k, svc) => {
let peak_ewma = PeakEwma::new( let peak_ewma = PeakEwma::new(
svc, svc,
self.default_rtt, *this.default_rtt,
self.decay_ns, *this.decay_ns,
self.instrument.clone(), this.instrument.clone(),
); );
Change::Insert(k, peak_ewma) Change::Insert(k, peak_ewma)
} }
}; };
Ok(Async::Ready(change)) Poll::Ready(Ok(change))
} }
} }
@ -156,8 +167,8 @@ where
type Error = S::Error; type Error = S::Error;
type Future = InstrumentFuture<S::Future, I, Handle>; type Future = InstrumentFuture<S::Future, I, Handle>;
fn poll_ready(&mut self) -> Poll<(), Self::Error> { fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
self.service.poll_ready() self.service.poll_ready(cx)
} }
fn call(&mut self, req: Request) -> Self::Future { fn call(&mut self, req: Request) -> Self::Future {
@ -293,12 +304,16 @@ fn nanos(d: Duration) -> f64 {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use futures::{future, Future, Poll}; use futures_util::future;
use std::{
future::Future,
task::{Context, Poll},
};
use std::{ use std::{
sync::{Arc, Mutex}, sync::{Arc, Mutex},
time::{Duration, Instant}, time::{Duration, Instant},
}; };
use tokio_executor::enter; use tokio_test::{assert_ready, assert_ready_ok};
use tokio_timer::clock; use tokio_timer::clock;
use super::*; use super::*;
@ -307,10 +322,10 @@ mod tests {
impl Service<()> for Svc { impl Service<()> for Svc {
type Response = (); type Response = ();
type Error = (); type Error = ();
type Future = future::FutureResult<(), ()>; type Future = future::Ready<Result<(), ()>>;
fn poll_ready(&mut self) -> Poll<(), ()> { fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll<Result<(), ()>> {
Ok(().into()) Poll::Ready(Ok(()))
} }
fn call(&mut self, (): ()) -> Self::Future { fn call(&mut self, (): ()) -> Self::Future {
@ -332,8 +347,7 @@ mod tests {
let time = Arc::new(Mutex::new(Instant::now())); let time = Arc::new(Mutex::new(Instant::now()));
let clock = clock::Clock::new_with_now(Now(time.clone())); let clock = clock::Clock::new_with_now(Now(time.clone()));
let mut enter = enter().expect("enter"); clock::with_default(&clock, || {
clock::with_default(&clock, &mut enter, |_| {
let svc = PeakEwma::new( let svc = PeakEwma::new(
Svc, Svc,
Duration::from_millis(10), Duration::from_millis(10),
@ -360,38 +374,39 @@ mod tests {
let time = Arc::new(Mutex::new(Instant::now())); let time = Arc::new(Mutex::new(Instant::now()));
let clock = clock::Clock::new_with_now(Now(time.clone())); let clock = clock::Clock::new_with_now(Now(time.clone()));
let mut enter = enter().expect("enter"); clock::with_default(&clock, || {
clock::with_default(&clock, &mut enter, |_| { tokio_test::task::mock(|cx| {
let mut svc = PeakEwma::new( let mut svc = PeakEwma::new(
Svc, Svc,
Duration::from_millis(20), Duration::from_millis(20),
NANOS_PER_MILLI * 1_000.0, NANOS_PER_MILLI * 1_000.0,
NoInstrument, NoInstrument,
); );
assert_eq!(svc.load(), Cost(20.0 * NANOS_PER_MILLI)); assert_eq!(svc.load(), Cost(20.0 * NANOS_PER_MILLI));
*time.lock().unwrap() += Duration::from_millis(100); *time.lock().unwrap() += Duration::from_millis(100);
let rsp0 = svc.call(()); let mut rsp0 = svc.call(());
assert!(svc.load() > Cost(20.0 * NANOS_PER_MILLI)); assert!(svc.load() > Cost(20.0 * NANOS_PER_MILLI));
*time.lock().unwrap() += Duration::from_millis(100); *time.lock().unwrap() += Duration::from_millis(100);
let rsp1 = svc.call(()); let mut rsp1 = svc.call(());
assert!(svc.load() > Cost(40.0 * NANOS_PER_MILLI)); assert!(svc.load() > Cost(40.0 * NANOS_PER_MILLI));
*time.lock().unwrap() += Duration::from_millis(100); *time.lock().unwrap() += Duration::from_millis(100);
let () = rsp0.wait().unwrap(); let () = assert_ready_ok!(Pin::new(&mut rsp0).poll(cx));
assert_eq!(svc.load(), Cost(400_000_000.0)); assert_eq!(svc.load(), Cost(400_000_000.0));
*time.lock().unwrap() += Duration::from_millis(100); *time.lock().unwrap() += Duration::from_millis(100);
let () = rsp1.wait().unwrap(); let () = assert_ready_ok!(Pin::new(&mut rsp1).poll(cx));
assert_eq!(svc.load(), Cost(200_000_000.0)); assert_eq!(svc.load(), Cost(200_000_000.0));
// Check that values decay as time elapses // Check that values decay as time elapses
*time.lock().unwrap() += Duration::from_secs(1); *time.lock().unwrap() += Duration::from_secs(1);
assert!(svc.load() < Cost(100_000_000.0)); assert!(svc.load() < Cost(100_000_000.0));
*time.lock().unwrap() += Duration::from_secs(10); *time.lock().unwrap() += Duration::from_secs(10);
assert!(svc.load() < Cost(100_000.0)); assert!(svc.load() < Cost(100_000.0));
})
}); });
} }

View File

@ -2,8 +2,13 @@
use super::{Instrument, InstrumentFuture, NoInstrument}; use super::{Instrument, InstrumentFuture, NoInstrument};
use crate::Load; use crate::Load;
use futures::{try_ready, Async, Poll}; use futures_core::ready;
use pin_project::pin_project;
use std::sync::Arc; use std::sync::Arc;
use std::{
pin::Pin,
task::{Context, Poll},
};
use tower_discover::{Change, Discover}; use tower_discover::{Change, Discover};
use tower_service::Service; use tower_service::Service;
@ -21,8 +26,10 @@ pub struct PendingRequests<S, I = NoInstrument> {
struct RefCount(Arc<()>); struct RefCount(Arc<()>);
/// Wraps `inner`'s services with `PendingRequests`. /// Wraps `inner`'s services with `PendingRequests`.
#[pin_project]
#[derive(Debug)] #[derive(Debug)]
pub struct PendingRequestsDiscover<D, I = NoInstrument> { pub struct PendingRequestsDiscover<D, I = NoInstrument> {
#[pin]
discover: D, discover: D,
instrument: I, instrument: I,
} }
@ -69,8 +76,8 @@ where
type Error = S::Error; type Error = S::Error;
type Future = InstrumentFuture<S::Future, I, Handle>; type Future = InstrumentFuture<S::Future, I, Handle>;
fn poll_ready(&mut self) -> Poll<(), Self::Error> { fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
self.service.poll_ready() self.service.poll_ready(cx)
} }
fn call(&mut self, req: Request) -> Self::Future { fn call(&mut self, req: Request) -> Self::Future {
@ -109,15 +116,19 @@ where
type Error = D::Error; type Error = D::Error;
/// Yields the next discovery change set. /// 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::*; use self::Change::*;
let change = match try_ready!(self.discover.poll()) { let this = self.project();
Insert(k, svc) => Insert(k, PendingRequests::new(svc, self.instrument.clone())), let change = match ready!(this.discover.poll_discover(cx))? {
Insert(k, svc) => Insert(k, PendingRequests::new(svc, this.instrument.clone())),
Remove(k) => Remove(k), Remove(k) => Remove(k),
}; };
Ok(Async::Ready(change)) Poll::Ready(Ok(change))
} }
} }
@ -132,16 +143,17 @@ impl RefCount {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use futures::{future, Future, Poll}; use futures_util::future;
use std::task::{Context, Poll};
struct Svc; struct Svc;
impl Service<()> for Svc { impl Service<()> for Svc {
type Response = (); type Response = ();
type Error = (); type Error = ();
type Future = future::FutureResult<(), ()>; type Future = future::Ready<Result<(), ()>>;
fn poll_ready(&mut self) -> Poll<(), ()> { fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll<Result<(), ()>> {
Ok(().into()) Poll::Ready(Ok(()))
} }
fn call(&mut self, (): ()) -> Self::Future { fn call(&mut self, (): ()) -> Self::Future {
@ -160,10 +172,10 @@ mod tests {
let rsp1 = svc.call(()); let rsp1 = svc.call(());
assert_eq!(svc.load(), Count(2)); assert_eq!(svc.load(), Count(2));
let () = rsp0.wait().unwrap(); let () = tokio_test::block_on(rsp0).unwrap();
assert_eq!(svc.load(), Count(1)); assert_eq!(svc.load(), Count(1));
let () = rsp1.wait().unwrap(); let () = tokio_test::block_on(rsp1).unwrap();
assert_eq!(svc.load(), Count(0)); assert_eq!(svc.load(), Count(0));
} }
@ -183,12 +195,12 @@ mod tests {
let rsp = svc.call(()); let rsp = svc.call(());
assert_eq!(svc.load(), Count(1)); 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)); assert_eq!(svc.load(), Count(1));
let rsp = svc.call(()); let rsp = svc.call(());
assert_eq!(svc.load(), Count(2)); 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)); assert_eq!(svc.load(), Count(2));
drop(i1); drop(i1);