185 lines
5.4 KiB
Rust
185 lines
5.4 KiB
Rust
#![cfg(feature = "hedge")]
|
|
#[path = "../support.rs"]
|
|
mod support;
|
|
|
|
use std::time::Duration;
|
|
use tokio::time;
|
|
use tokio_test::{assert_pending, assert_ready, assert_ready_ok, task};
|
|
use tower::hedge::{Hedge, Policy};
|
|
use tower_test::{assert_request_eq, mock};
|
|
|
|
#[tokio::test(flavor = "current_thread")]
|
|
async fn hedge_orig_completes_first() {
|
|
let _t = support::trace_init();
|
|
time::pause();
|
|
|
|
let (mut service, mut handle) = new_service(TestPolicy);
|
|
|
|
assert_ready_ok!(service.poll_ready());
|
|
let mut fut = task::spawn(service.call("orig"));
|
|
|
|
// Check that orig request has been issued.
|
|
let req = assert_request_eq!(handle, "orig");
|
|
// Check fut is not ready.
|
|
assert_pending!(fut.poll());
|
|
|
|
// Check hedge has not been issued.
|
|
assert_pending!(handle.poll_request());
|
|
time::advance(Duration::from_millis(11)).await;
|
|
// Check fut is not ready.
|
|
assert_pending!(fut.poll());
|
|
// Check that the hedge has been issued.
|
|
let _hedge_req = assert_request_eq!(handle, "orig");
|
|
|
|
req.send_response("orig-done");
|
|
// Check that fut gets orig response.
|
|
assert_eq!(assert_ready_ok!(fut.poll()), "orig-done");
|
|
}
|
|
|
|
#[tokio::test(flavor = "current_thread")]
|
|
async fn hedge_hedge_completes_first() {
|
|
let _t = support::trace_init();
|
|
time::pause();
|
|
|
|
let (mut service, mut handle) = new_service(TestPolicy);
|
|
|
|
assert_ready_ok!(service.poll_ready());
|
|
let mut fut = task::spawn(service.call("orig"));
|
|
|
|
// Check that orig request has been issued.
|
|
let _req = assert_request_eq!(handle, "orig");
|
|
|
|
// Check fut is not ready.
|
|
assert_pending!(fut.poll());
|
|
|
|
// Check hedge has not been issued.
|
|
assert_pending!(handle.poll_request());
|
|
time::advance(Duration::from_millis(11)).await;
|
|
// Check fut is not ready.
|
|
assert_pending!(fut.poll());
|
|
|
|
// Check that the hedge has been issued.
|
|
let hedge_req = assert_request_eq!(handle, "orig");
|
|
hedge_req.send_response("hedge-done");
|
|
// Check that fut gets hedge response.
|
|
assert_eq!(assert_ready_ok!(fut.poll()), "hedge-done");
|
|
}
|
|
|
|
#[tokio::test(flavor = "current_thread")]
|
|
async fn completes_before_hedge() {
|
|
let _t = support::trace_init();
|
|
let (mut service, mut handle) = new_service(TestPolicy);
|
|
|
|
assert_ready_ok!(service.poll_ready());
|
|
let mut fut = task::spawn(service.call("orig"));
|
|
|
|
// Check that orig request has been issued.
|
|
let req = assert_request_eq!(handle, "orig");
|
|
// Check fut is not ready.
|
|
assert_pending!(fut.poll());
|
|
|
|
req.send_response("orig-done");
|
|
// Check hedge has not been issued.
|
|
assert_pending!(handle.poll_request());
|
|
// Check that fut gets orig response.
|
|
assert_eq!(assert_ready_ok!(fut.poll()), "orig-done");
|
|
}
|
|
|
|
#[tokio::test(flavor = "current_thread")]
|
|
async fn request_not_retyable() {
|
|
let _t = support::trace_init();
|
|
time::pause();
|
|
|
|
let (mut service, mut handle) = new_service(TestPolicy);
|
|
|
|
assert_ready_ok!(service.poll_ready());
|
|
let mut fut = task::spawn(service.call(NOT_RETRYABLE));
|
|
|
|
// Check that orig request has been issued.
|
|
let req = assert_request_eq!(handle, NOT_RETRYABLE);
|
|
// Check fut is not ready.
|
|
assert_pending!(fut.poll());
|
|
|
|
// Check hedge has not been issued.
|
|
assert_pending!(handle.poll_request());
|
|
time::advance(Duration::from_millis(10)).await;
|
|
// Check fut is not ready.
|
|
assert_pending!(fut.poll());
|
|
// Check hedge has not been issued.
|
|
assert_pending!(handle.poll_request());
|
|
|
|
req.send_response("orig-done");
|
|
// Check that fut gets orig response.
|
|
assert_eq!(assert_ready_ok!(fut.poll()), "orig-done");
|
|
}
|
|
|
|
#[tokio::test(flavor = "current_thread")]
|
|
async fn request_not_clonable() {
|
|
let _t = support::trace_init();
|
|
time::pause();
|
|
|
|
let (mut service, mut handle) = new_service(TestPolicy);
|
|
|
|
assert_ready_ok!(service.poll_ready());
|
|
let mut fut = task::spawn(service.call(NOT_CLONABLE));
|
|
|
|
// Check that orig request has been issued.
|
|
let req = assert_request_eq!(handle, NOT_CLONABLE);
|
|
// Check fut is not ready.
|
|
assert_pending!(fut.poll());
|
|
|
|
// Check hedge has not been issued.
|
|
assert_pending!(handle.poll_request());
|
|
time::advance(Duration::from_millis(10)).await;
|
|
// Check fut is not ready.
|
|
assert_pending!(fut.poll());
|
|
// Check hedge has not been issued.
|
|
assert_pending!(handle.poll_request());
|
|
|
|
req.send_response("orig-done");
|
|
// Check that fut gets orig response.
|
|
assert_eq!(assert_ready_ok!(fut.poll()), "orig-done");
|
|
}
|
|
|
|
type Req = &'static str;
|
|
type Res = &'static str;
|
|
type Mock = tower_test::mock::Mock<Req, Res>;
|
|
type Handle = tower_test::mock::Handle<Req, Res>;
|
|
|
|
static NOT_RETRYABLE: &str = "NOT_RETRYABLE";
|
|
static NOT_CLONABLE: &str = "NOT_CLONABLE";
|
|
|
|
#[derive(Clone)]
|
|
struct TestPolicy;
|
|
|
|
impl tower::hedge::Policy<Req> for TestPolicy {
|
|
fn can_retry(&self, req: &Req) -> bool {
|
|
*req != NOT_RETRYABLE
|
|
}
|
|
|
|
fn clone_request(&self, req: &Req) -> Option<Req> {
|
|
if *req == NOT_CLONABLE {
|
|
None
|
|
} else {
|
|
Some(req)
|
|
}
|
|
}
|
|
}
|
|
|
|
fn new_service<P: Policy<Req> + Clone>(policy: P) -> (mock::Spawn<Hedge<Mock, P>>, Handle) {
|
|
let (service, handle) = tower_test::mock::pair();
|
|
|
|
let mock_latencies: [u64; 10] = [1, 1, 1, 1, 1, 1, 1, 1, 10, 10];
|
|
|
|
let service = Hedge::new_with_mock_latencies(
|
|
service,
|
|
policy,
|
|
10,
|
|
0.9,
|
|
Duration::from_secs(60),
|
|
&mock_latencies,
|
|
);
|
|
|
|
(mock::Spawn::new(service), handle)
|
|
}
|