Commit Graph

114 Commits

Author SHA1 Message Date
Jon Gjengset c1b96616b4
Don't make buffer::Worker block the reactor (#133)
Previously, `tower_buffer::Worker` would continue to loop indefinitely,
even if both the incoming request stream and the service returned
`NotReady`, starving the reactor in the process.
2018-12-06 13:10:10 -05:00
Jon Gjengset 6377702087
Make `Buffer` use a bounded channel (#120)
This change moves `Buffer` from `mpsc::unbounded` to `mpsc::channel`. The primary motivation for this change is that bounded channels provide back-pressure to callers, so that `Balance<Buffer>` for example works as expected. Currently, `Buffer` will accept as many requests as you can make for it without ever stopping down, slowly eating up all your memory.
2018-12-05 11:45:53 -05:00
Sean McArthur 801adb18db tower-buffer: fix Worker closing when service.poll_ready was NotReady 2018-12-03 12:39:11 -08:00
Sean McArthur 6168ba27b5 Adjust retry Budget to overflow less 2018-11-30 13:02:16 -08:00
Sean McArthur ac1586748f move MakeService to tower-util 2018-11-27 12:37:20 -08:00
Sean McArthur 787c10b7c4 move Service::ready() to ServiceExt::ready() (#128) 2018-11-27 12:25:59 -08:00
Jon Gjengset c5cb47d612 Make Buffer::new use DefaultExecutor (#122)
Fixes #121.
2018-11-27 11:14:43 -08:00
Jon Gjengset 72508ff4ba Implement DirectService for ServiceFn (#127) 2018-11-27 11:02:49 -08:00
Jon Gjengset 9bae225918 Add the DirectService trait (#118)
This patch adds the `DirectService` trait, and related implementations
over it in `tower_balance` and `tower_buffer`. `DirectService` is
similar to a `Service`, but must be "driven" through calls to
`poll_service` for the futures returned by `call` to make progress.

The motivation behind adding this trait is that many current `Service`
implementations spawn long-running futures when the service is created,
which then drive the work necessary to turn requests into responses. A
simple example of this is a service that writes requests over a
`TcpStream` and reads responses over that same `TcpStream`. The
underlying stream must be read from to discover new responses, but there
is no single entity to drive that task. The returned futures would share
access to the stream (and worse yet, may get responses out of order),
and then service itself is not guaranteed to see any more calls to it as
the client is waiting for its requests to finish.

`DirectService` solves this by introducing a new method, `poll_service`,
which must be called to make progress on in-progress futures.
Furthermore, like `Future::poll`, `poll_service` must be called whenever
the associated task is notified so that the service can also respect
time-based operations like heartbeats.

The PR includes changes to both `tower_balance::Balance` and
`tower_buffer::Buffer` to add support for wrapping `DirectService`s. For
`Balance` this is straightforward: if the inner service is a `Service`,
the `Balance` also implements `Service`; if the inner service is a
`DirectService`, the `Balance` is itself also a `DirectService`. For
`Buffer`, this is more involved, as a `Buffer` turns any `DirectService`
*into* a `Service`. The `Buffer`'s `Worker` is spawned, and will
therefore drive the wrapped `DirectService`.

One complication arises in that `Buffer<T>` requires that `T: Service`,
but you can safely construct a `Buffer` over a `DirectService` per the
above. `Buffer` works around this by exposing

```rust
impl Service for HandleTo<S> where S: DirectService {}
```

And giving out `Buffer<HandleTo<S>>` when the `new_directed(s: S)`
constructor is invoked. Since `Buffer` never calls any methods on the
service it wraps, `HandleTo`'s implementation just consists of calls to
`unreachable!()`.

Note that `tower_buffer` now also includes a `DirectedService` type,
which is a wrapper around a `Service` that implements `DirectService`.
In theory, we could do away with this by adding a blanket impl:

```rust
impl<T> DirectedService for T where T: Service {}
```

but until we have specialization, this would prevent downstream users
from implementing `DirectService` themselves.

Finally, this also makes `Buffer` use a bounded mpsc channel, which
introduces a new capacity argument to `Buffer::new`.

Fixes #110.
2018-11-19 09:30:45 -08:00
Sean McArthur b8c15904fe Seal the MakeService trait 2018-11-16 10:39:50 -08:00
Sean McArthur f21e3e4df0 refine generics on Reconnect types 2018-11-15 15:40:00 -08:00
Sean McArthur 373d017e0f change NewService to MakeService<Target, Request> 2018-11-08 15:11:59 -08:00
Jon Gjengset 882fe7240b Add Stream adapter for Discover (#117)
This provides a convenient mechanism for taking a `Stream` over
`Service` changes (i.e., `Change`) and using it as a `Discover`.
2018-11-06 10:26:25 -08:00
Jon Gjengset a422ee5680 Make List work with collections (#116)
Previously, you could not use `List<Vec<MyService>>` as a type, because
`List<T>` required `T: Iterator`. Instead, you'd have to write
`List<std::vec::IntoIter<MyService>>`, which gets really bad for
complex iterator types. This change instead makes `List<T>` require
`T: IntoIterator`, and then store `T::IntoIter`. This is a little weird
intuitively, but makes writing out the type for a `List` much more
pleasant.
2018-11-05 10:11:07 -08:00
John Children 17d4d9c65b Removes extra `e` in `tower-timeout` README link (#115)
Link was broken due to typo.
2018-11-02 09:44:48 -07:00
Carl Lerche 75eecc476c
Switch Service request to a generic. (#109)
This changes the Service request type to a generic instead of an associated
type. This is more appropriate as requests are inputs to the service.

This change enables a single implementation of `Service` to accept many
kinds of request types. This also enables requests to be references.

Fixes #99
2018-11-01 12:28:10 -07:00
Sean McArthur b95c8d1030 Adds a retry::Budget (#98) 2018-09-18 10:58:43 -07:00
Nikolay Kim c29f7e97ba Add combinators for working with Services (#104) 2018-09-17 15:04:24 -07:00
Eliza Weisman 4ffe24f485 Update tower-timeout to use new tokio-timer (#101) 2018-09-03 11:03:00 -07:00
Sean McArthur 7dabd34d24
Tower Retry (#96)
- Inspect the request, response, and error when determining to retry
- Return a future if retry is desired
  - This future allows an impl to delay a retry
  - The future yields a new `Policy`, allowing state to be changed
    for later retries.
2018-08-28 11:05:15 -07:00
Carl Lerche aef25abfe9
Add link to gitter (#95) 2018-08-10 11:26:34 -07:00
Corey Farwell 5c20cb8edd Add section for tower-router in README. (#94) 2018-08-10 10:56:31 -07:00
Carl Lerche 57df4b5b4d
Minor documentation improvements (#93) 2018-08-09 10:08:57 -07:00
Corey Farwell 7b6460dff2 Fix broken docs link. (#91) 2018-08-02 13:14:31 -07:00
Markus Jais 09c9419c46 fixed typos in comments (#89) 2018-07-11 15:04:15 -07:00
Oliver Gould 5e0a2c7e2f Introduce tower-watch (#87)
`tower_watch::WatchService` provides a dynamically-bound `Service` that
updates in response to a `Watch`. A `WatchService` is constructed with a
`Watch<T>` and a `Bind<T>` -- `Bind` is a newly introduced trait that
supports instantiating new service instances with a borrowed value, i.e.
from a watch.

This can be used to reconfigure Services from a shared or otherwise
externally-controlled configuration source (for instance, a file
system).
2018-07-11 15:03:20 -07:00
Carl Lerche 20fb04e3e9
in-flight-limit: Add missing task notification. (#85)
Previously, there was no notification when capacity is made available by
requests completing. This patch fixes the bug.

This also switches the tests to use `MockTask` from tokio-test.
2018-06-11 15:29:34 -07:00
Oliver Gould 679dcbe327
balance: Use a default RTT estimate of 1s (#86)
After speaking with @roanta and @adleong, I realized that our
DEFAULT_RTT_ESTIMATE is too optimstic: it gives new endpoints
an _ideal_ RTT. Instead, our intent is to assign a slightly
pessimistic cost to new endpoints so they don't take on more load
than they are due before the EWMA can establish a baseline.
2018-06-08 14:49:11 -07:00
Oliver Gould fcdc9d2777
balance: Implement a Peak-EWMA load metric (#76)
The balancer provides an implementation of two load balancing strategies: RoundRobin and
P2C+LeastLoaded.  The round-robin strategy is extremely simplistic and not sufficient for
most production systems. P2C+LL is a substantial improvement, but relies exclusively on
instantaneous information.

This change introduces P2C+PeakEWMA strategy. P2C+PE improves over P2C+LL by maintaining
an exponentially-weighted moving average of response latencies for each endpoint so that
the recent history directly factors into load balancing decisions. This technique was
pioneered by Finagle for use at Twitter. [Finagle's P2C+PE implementation][finagle] was
referenced heavily while developing this.

The provided demo can be used to illustrate the differences between load balacing
strategies. For example:

```
REQUESTS=50000
CONCURRENCY=50
ENDPOINT_CAPACITY=50
MAX_ENDPOINT_LATENCIES=[1ms, 10ms, 10ms, 10ms, 10ms, 100ms, 100ms, 100ms, 100ms, 1000ms, ]
P2C+PeakEWMA
  wall   15s
  p50     5ms
  p90    56ms
  p95    78ms
  p99    96ms
  p999  105ms
P2C+LeastLoaded
  wall   18s
  p50     5ms
  p90    57ms
  p95    80ms
  p99    98ms
  p999  857ms
RoundRobin
  wall   72s
  p50     9ms
  p90    98ms
  p95   496ms
  p99   906ms
  p999  988ms
````

[numbers]: https://people.eecs.berkeley.edu/~rcs/research/interactive_latency.html
[finagle]: 9cc08d1521/finagle-core/src/main/scala/com/twitter/finagle/loadbalancer/PeakEwma.scala
2018-06-06 23:16:49 -07:00
Chris Pick 3f2932e909 Improve wording in documentation (#83)
Remove redundant wording form documentation.

Port of https://github.com/tokio-rs/tokio-service/pull/30
2018-06-06 14:42:33 -07:00
Oliver Gould 60eb55fdfb
balance: Account for long-lived responses (#74)
tower-balance provides a PendingRequests load metric that counts the number of responses
that have not yet been received. However, especially in the case of HTTP, responses have
bodies that remain active far past the initial receipt of the response. We want load
metrics to be able to take such streams into account.

This change introduces a new utility trait, `load::Measure`, which is used by implementors
of `Load` (like `PendingRequests`) to handle the protocol-specific details of attaching an
_instrument_ with a response message.  _Instruments_ are implemented as RAII-guarded types
that ensure that load calculations are updated as a response completes.  An instrument is
dropped when the load metric no longer needs information from a response.

This all being changed in service of a `PeakEwma` balancer implementation, though this
should benefit the existing load metric as well.
2018-06-06 12:17:28 -07:00
Oliver Gould c0f52bb259
balance: Move constructors under `Balance` (#82)
Previously, `power_of_two_choices` and `round_robin` constructors were
exposed from the crate scope.

These have been replaced by `Balance::p2c`, `Balance::p2c_from_rng`, and
`Balance::round_robin`.
2018-06-05 13:10:00 -07:00
Oliver Gould 5030a4f852
balance: Add debug logging for p2c decisions (#77)
When debugging load balancer behavior, it's convenient to observe the
individual node selection decisions. To that end, this change requires
that `Load::Metric` implement `fmt::Debug` when used by
`PowerOfTwoChoices`.
2018-06-05 07:32:10 -07:00
Oliver Gould 01fd57c053
balance: Update demo (#79)
In preparation for additional load balancing strategies, the demo is
being updated to allow for richer testing in several important ways:

- Adopt the new `tokio` multithreaded runtime.

- Use `tower-buffer` to drive each simulated endpoint on an independent
  task. This fixes a bug where requests appeared active longer than
  intended (while waiting for the SendRequests task process responses).

- A top-level concurrency has been added (by wrapping the balancer in
  `tower-in-flight-limit`) so that `REQUESTS` futures were not created
  immediately. This also caused incorrect load measurements.

- Endpoints are also constrained with `tower-in-flight-limit`. By
  default, the limit is that of the load balancer (so endpoints are
  effectively unlimited).

- The `demo.rs` script has been reorganized to account for the new
  runtime, such that all examples are one task chain.

- New output format:
```
REQUESTS=50000
CONCURRENCY=50
ENDPOINT_CAPACITY=50
MAX_ENDPOINT_LATENCIES=[1ms, 10ms, 10ms, 10ms, 10ms, 100ms, 100ms, 100ms, 100ms, 1000ms, ]
P2C+LeastLoaded
  wall   18s
  p50     5ms
  p90    56ms
  p95    80ms
  p99    98ms
  p999  900ms
RoundRobin
  wall   72s
  p50     9ms
  p90    98ms
  p95   488ms
  p99   898ms
  p999  989ms
```
2018-06-04 17:54:07 -07:00
Oliver Gould 58b8078fd6
balance: Make Balance::is_ready public (#80)
`Balance::is_ready` is erroneously private. This causes `dead_code`
warnings.

Make `is_ready` public and prohibit `dead_code`.
2018-06-04 10:08:21 -07:00
Oliver Gould 9352fc417d
balance: Do not require RNG to build a P2C balancer (#78)
`PowerOfTwoChoices` requires a Random Number Generator. In order for
this randomization source to be configurable (i.e. for tests),
`PowerOfTwoChoices` is generic over its implementation of `rand::Rng`;
however, this leads to needless boilerplate when building P2C balancers.

Because load balancers do not need a cryptographically strong RNG, we
can use `rand::SmallRng` (which is `Send + Sync`). `PowerOfTwoChoices`
exposes constructors that take a `SmallRng`.

In order to do this, the `tower-balance` crate now requires `rand = "0.5"`.
2018-06-04 10:01:38 -07:00
Eliza Weisman 9d49396eb8
tower-balance: Handle duplicate Insert events by overwriting (#75)
When an endpoint's state changes in some way, it may need to be rebound to a 
new service, and reinserted into the load balancer. This PR changes 
`tower-balance` so that, rather than ignoring duplicate `Insert`s, the new
endpoint replaces the old endpoint. The new endpoint is always placed on the
not-ready list; if the replaced endpoint was on the ready list, it is removed
prior to inserting the new endpoint into the not-ready list.

Signed-off-by: Eliza Weisman <eliza@buoyant.io>
2018-05-24 15:58:08 -07:00
Sean McArthur 6cdc8d0ab5
buffer: check for canceled requests before polling inner service (#72) 2018-05-14 10:06:06 -07:00
Carl Lerche 295ae583d4
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".
2018-04-25 14:32:13 -07:00
Carl Lerche 5369879af6
Extract `Service` trait and related into crate. (#67)
This makes the `tower` crate available to be a "batteries included"
facade.
2018-04-25 12:35:52 -07:00
Brian Smith 11b591b6e0 Upgrade indexmap dependency to version 1. (#64)
Signed-off-by: Brian Smith <brian@briansmith.org>
2018-03-15 21:33:20 -07:00
Eliza Weisman 7b6cd0355d Normalize naming scheme (#62) 2018-03-06 11:41:52 -08:00
Eliza Weisman 7caef48bfe
Change Travis test script to `cargo test --all` (#59)
This will ensure that all crates are built & tested, and hopefully prevent errors like the one fixed in #58 from slipping past CI.
2018-02-28 11:06:17 -08:00
Eliza Weisman 434b7e36f8 Fix missing comma in tower-rate-limit Error impl (#58)
This should de-break the build. However, I'm concerned that this made it pass CI --- we should fix the Travis configs.
2018-02-28 10:39:06 -08:00
Eliza Weisman 8acbfa80ee Nested errors no longer print "inner service error:" or similar (#56) 2018-02-27 15:36:44 -08:00
Brian Smith 1efa622b6e Replace ordermap dependency with indexmap. (#55)
indexmap is the new ordermap.

Signed-off-by: Brian Smith <brian@briansmith.org>
2018-02-26 20:43:04 -08:00
Eliza Weisman 59679934c9 Add Error impls for tower-rate-limit and tower-timeout as well (#54) 2018-02-26 09:12:46 -08:00
Feynman Liang c92c2966ac Update github link in README (#53) 2018-02-26 09:12:21 -08:00
Eliza Weisman cc99f32486
Implement std::error::Error for Tower error types (#51)
I've implemented `std::error::Error` for the error types in the `tower-balance`, `tower-buffer`, `tower-in-flight-limit`, and `tower-reconnect` middleware crates.

This is required upstream for runconduit/conduit#442, and also just generally seems like the right thing to do as a library.
2018-02-24 10:48:04 -08:00
Brian Smith e0ca6545bb Upgrade to env_logger 0.5 and log 0.4 so that projects that use those (#52)
versions don't have to build both those versions and the older ones
that h2 is currently using.

Don't enable the regex support in env_logger. Applications that want
the regex support can enable it themselves; this will happen
automatically when they add their env_logger dependency.

Disable the env_logger dependency in quickcheck.

The result of this is that there are fewer dependencies. For example,
regex and its dependencies are no longer required at all, as can be
seen by observing the changes to the Cargo.lock. That said,
env_logger 0.5 does add more dependencies itself; however it seems
applications are going to use env_logger 0.5 anyway so this is still
a net gain.

Submitted on behalf of Buoyant, Inc.

Signed-off-by: Brian Smith <brian@briansmith.org>
2018-02-23 20:24:22 -08:00