make return error type for Batch generic
This commit is contained in:
parent
6cc1627a5d
commit
63ae085945
|
|
@ -1927,6 +1927,7 @@ dependencies = [
|
||||||
name = "tower-batch"
|
name = "tower-batch"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"color-eyre",
|
||||||
"ed25519-zebra",
|
"ed25519-zebra",
|
||||||
"futures",
|
"futures",
|
||||||
"futures-core",
|
"futures-core",
|
||||||
|
|
|
||||||
|
|
@ -21,3 +21,4 @@ tokio = { version = "0.2", features = ["full"]}
|
||||||
tracing-error = "0.1.2"
|
tracing-error = "0.1.2"
|
||||||
tracing-subscriber = "0.2.5"
|
tracing-subscriber = "0.2.5"
|
||||||
tracing = "0.1.15"
|
tracing = "0.1.15"
|
||||||
|
color-eyre = "0.3.4"
|
||||||
|
|
|
||||||
|
|
@ -1,47 +1,12 @@
|
||||||
//! Error types for the `Batch` middleware.
|
//! Error types for the `Batch` middleware.
|
||||||
|
|
||||||
use crate::BoxError;
|
use std::fmt;
|
||||||
use std::{fmt, sync::Arc};
|
|
||||||
|
|
||||||
/// An error produced by a `Service` wrapped by a `Batch`.
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct ServiceError {
|
|
||||||
inner: Arc<BoxError>,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// An error produced when the batch worker closes unexpectedly.
|
/// An error produced when the batch worker closes unexpectedly.
|
||||||
pub struct Closed {
|
pub struct Closed {
|
||||||
_p: (),
|
_p: (),
|
||||||
}
|
}
|
||||||
|
|
||||||
// ===== impl ServiceError =====
|
|
||||||
|
|
||||||
impl ServiceError {
|
|
||||||
pub(crate) fn new(inner: BoxError) -> ServiceError {
|
|
||||||
let inner = Arc::new(inner);
|
|
||||||
ServiceError { inner }
|
|
||||||
}
|
|
||||||
|
|
||||||
// Private to avoid exposing `Clone` trait as part of the public API
|
|
||||||
pub(crate) fn clone(&self) -> ServiceError {
|
|
||||||
ServiceError {
|
|
||||||
inner: self.inner.clone(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl fmt::Display for ServiceError {
|
|
||||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
|
||||||
write!(fmt, "batching service failed: {}", self.inner)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl std::error::Error for ServiceError {
|
|
||||||
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
|
|
||||||
Some(&**self.inner)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ===== impl Closed =====
|
// ===== impl Closed =====
|
||||||
|
|
||||||
impl Closed {
|
impl Closed {
|
||||||
|
|
|
||||||
|
|
@ -4,47 +4,68 @@ use super::{error::Closed, message};
|
||||||
use futures_core::ready;
|
use futures_core::ready;
|
||||||
use pin_project::pin_project;
|
use pin_project::pin_project;
|
||||||
use std::{
|
use std::{
|
||||||
|
fmt::Debug,
|
||||||
future::Future,
|
future::Future,
|
||||||
pin::Pin,
|
pin::Pin,
|
||||||
task::{Context, Poll},
|
task::{Context, Poll},
|
||||||
};
|
};
|
||||||
|
use tower::Service;
|
||||||
|
|
||||||
/// Future that completes when the batch processing is complete.
|
/// Future that completes when the batch processing is complete.
|
||||||
#[pin_project]
|
#[pin_project]
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct ResponseFuture<T> {
|
pub struct ResponseFuture<T, E, R>
|
||||||
|
where
|
||||||
|
T: Service<crate::BatchControl<R>>,
|
||||||
|
{
|
||||||
#[pin]
|
#[pin]
|
||||||
state: ResponseState<T>,
|
state: ResponseState<T, E, R>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[pin_project(project = ResponseStateProj)]
|
#[pin_project(project = ResponseStateProj)]
|
||||||
#[derive(Debug)]
|
enum ResponseState<T, E, R>
|
||||||
enum ResponseState<T> {
|
where
|
||||||
Failed(Option<crate::BoxError>),
|
T: Service<crate::BatchControl<R>>,
|
||||||
Rx(#[pin] message::Rx<T>),
|
{
|
||||||
Poll(#[pin] T),
|
Failed(Option<E>),
|
||||||
|
Rx(#[pin] message::Rx<T::Future, T::Error>),
|
||||||
|
Poll(#[pin] T::Future),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> ResponseFuture<T> {
|
impl<T, E, R> Debug for ResponseState<T, E, R>
|
||||||
pub(crate) fn new(rx: message::Rx<T>) -> Self {
|
where
|
||||||
|
T: Service<crate::BatchControl<R>>,
|
||||||
|
{
|
||||||
|
fn fmt(&self, _f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, E, R> ResponseFuture<T, E, R>
|
||||||
|
where
|
||||||
|
T: Service<crate::BatchControl<R>>,
|
||||||
|
{
|
||||||
|
pub(crate) fn new(rx: message::Rx<T::Future, T::Error>) -> Self {
|
||||||
ResponseFuture {
|
ResponseFuture {
|
||||||
state: ResponseState::Rx(rx),
|
state: ResponseState::Rx(rx),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn failed(err: crate::BoxError) -> Self {
|
pub(crate) fn failed(err: E) -> Self {
|
||||||
ResponseFuture {
|
ResponseFuture {
|
||||||
state: ResponseState::Failed(Some(err)),
|
state: ResponseState::Failed(Some(err)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<F, T, E> Future for ResponseFuture<F>
|
impl<S, E2, R> Future for ResponseFuture<S, E2, R>
|
||||||
where
|
where
|
||||||
F: Future<Output = Result<T, E>>,
|
S: Service<crate::BatchControl<R>>,
|
||||||
E: Into<crate::BoxError>,
|
S::Future: Future<Output = Result<S::Response, S::Error>>,
|
||||||
|
S::Error: Into<E2>,
|
||||||
|
crate::error::Closed: Into<E2>,
|
||||||
{
|
{
|
||||||
type Output = Result<T, crate::BoxError>;
|
type Output = Result<S::Response, E2>;
|
||||||
|
|
||||||
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||||
let mut this = self.project();
|
let mut this = self.project();
|
||||||
|
|
|
||||||
|
|
@ -9,13 +9,14 @@ use tower::Service;
|
||||||
/// which means that this layer can only be used on the Tokio runtime.
|
/// which means that this layer can only be used on the Tokio runtime.
|
||||||
///
|
///
|
||||||
/// See the module documentation for more details.
|
/// See the module documentation for more details.
|
||||||
pub struct BatchLayer<Request> {
|
pub struct BatchLayer<Request, E> {
|
||||||
max_items: usize,
|
max_items: usize,
|
||||||
max_latency: std::time::Duration,
|
max_latency: std::time::Duration,
|
||||||
_p: PhantomData<fn(Request)>,
|
_p: PhantomData<fn(Request)>,
|
||||||
|
_e: PhantomData<E>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Request> BatchLayer<Request> {
|
impl<Request, E> BatchLayer<Request, E> {
|
||||||
/// Creates a new `BatchLayer`.
|
/// Creates a new `BatchLayer`.
|
||||||
///
|
///
|
||||||
/// The wrapper is responsible for telling the inner service when to flush a
|
/// The wrapper is responsible for telling the inner service when to flush a
|
||||||
|
|
@ -28,25 +29,28 @@ impl<Request> BatchLayer<Request> {
|
||||||
max_items,
|
max_items,
|
||||||
max_latency,
|
max_latency,
|
||||||
_p: PhantomData,
|
_p: PhantomData,
|
||||||
|
_e: PhantomData,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<S, Request> Layer<S> for BatchLayer<Request>
|
impl<S, Request, E> Layer<S> for BatchLayer<Request, E>
|
||||||
where
|
where
|
||||||
S: Service<BatchControl<Request>> + Send + 'static,
|
S: Service<BatchControl<Request>> + Send + 'static,
|
||||||
S::Future: Send,
|
S::Future: Send,
|
||||||
S::Error: Into<crate::BoxError> + Send + Sync,
|
S::Error: Clone + Into<E> + Send + Sync,
|
||||||
Request: Send + 'static,
|
Request: Send + 'static,
|
||||||
|
E: Clone + Send + 'static,
|
||||||
|
crate::error::Closed: Into<E>,
|
||||||
{
|
{
|
||||||
type Service = Batch<S, Request>;
|
type Service = Batch<S, Request, E>;
|
||||||
|
|
||||||
fn layer(&self, service: S) -> Self::Service {
|
fn layer(&self, service: S) -> Self::Service {
|
||||||
Batch::new(service, self.max_items, self.max_latency)
|
Batch::new(service, self.max_items, self.max_latency)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Request> fmt::Debug for BatchLayer<Request> {
|
impl<Request, E> fmt::Debug for BatchLayer<Request, E> {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
f.debug_struct("BufferLayer")
|
f.debug_struct("BufferLayer")
|
||||||
.field("max_items", &self.max_items)
|
.field("max_items", &self.max_items)
|
||||||
|
|
|
||||||
|
|
@ -1,16 +1,15 @@
|
||||||
use super::error::ServiceError;
|
|
||||||
use tokio::sync::oneshot;
|
use tokio::sync::oneshot;
|
||||||
|
|
||||||
/// Message sent to the batch worker
|
/// Message sent to the batch worker
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub(crate) struct Message<Request, Fut> {
|
pub(crate) struct Message<Request, Fut, E> {
|
||||||
pub(crate) request: Request,
|
pub(crate) request: Request,
|
||||||
pub(crate) tx: Tx<Fut>,
|
pub(crate) tx: Tx<Fut, E>,
|
||||||
pub(crate) span: tracing::Span,
|
pub(crate) span: tracing::Span,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Response sender
|
/// Response sender
|
||||||
pub(crate) type Tx<Fut> = oneshot::Sender<Result<Fut, ServiceError>>;
|
pub(crate) type Tx<Fut, E> = oneshot::Sender<Result<Fut, E>>;
|
||||||
|
|
||||||
/// Response receiver
|
/// Response receiver
|
||||||
pub(crate) type Rx<Fut> = oneshot::Receiver<Result<Fut, ServiceError>>;
|
pub(crate) type Rx<Fut, E> = oneshot::Receiver<Result<Fut, E>>;
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,10 @@ use super::{
|
||||||
};
|
};
|
||||||
|
|
||||||
use futures_core::ready;
|
use futures_core::ready;
|
||||||
use std::task::{Context, Poll};
|
use std::{
|
||||||
|
marker::PhantomData,
|
||||||
|
task::{Context, Poll},
|
||||||
|
};
|
||||||
use tokio::sync::{mpsc, oneshot};
|
use tokio::sync::{mpsc, oneshot};
|
||||||
use tower::Service;
|
use tower::Service;
|
||||||
|
|
||||||
|
|
@ -14,18 +17,23 @@ use tower::Service;
|
||||||
///
|
///
|
||||||
/// See the module documentation for more details.
|
/// See the module documentation for more details.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Batch<T, Request>
|
pub struct Batch<T, Request, E = crate::BoxError>
|
||||||
where
|
where
|
||||||
T: Service<BatchControl<Request>>,
|
T: Service<BatchControl<Request>>,
|
||||||
{
|
{
|
||||||
tx: mpsc::Sender<Message<Request, T::Future>>,
|
tx: mpsc::Sender<Message<Request, T::Future, T::Error>>,
|
||||||
handle: Handle,
|
handle: Handle<E>,
|
||||||
|
_error_type: PhantomData<E>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T, Request> Batch<T, Request>
|
impl<T, Request, E> Batch<T, Request, E>
|
||||||
where
|
where
|
||||||
T: Service<BatchControl<Request>>,
|
T: Service<BatchControl<Request>>,
|
||||||
T::Error: Into<crate::BoxError>,
|
T::Error: Into<E>,
|
||||||
|
E: Send + 'static,
|
||||||
|
crate::error::Closed: Into<E>,
|
||||||
|
// crate::error::Closed: Into<<Self as Service<Request>>::Error> + Send + Sync + 'static,
|
||||||
|
// crate::error::ServiceError: Into<<Self as Service<Request>>::Error> + Send + Sync + 'static,
|
||||||
{
|
{
|
||||||
/// Creates a new `Batch` wrapping `service`.
|
/// Creates a new `Batch` wrapping `service`.
|
||||||
///
|
///
|
||||||
|
|
@ -41,29 +49,35 @@ where
|
||||||
where
|
where
|
||||||
T: Send + 'static,
|
T: Send + 'static,
|
||||||
T::Future: Send,
|
T::Future: Send,
|
||||||
T::Error: Send + Sync,
|
T::Error: Send + Sync + Clone,
|
||||||
Request: Send + 'static,
|
Request: Send + 'static,
|
||||||
{
|
{
|
||||||
// XXX(hdevalence): is this bound good
|
// XXX(hdevalence): is this bound good
|
||||||
let (tx, rx) = mpsc::channel(1);
|
let (tx, rx) = mpsc::channel(1);
|
||||||
let (handle, worker) = Worker::new(service, rx, max_items, max_latency);
|
let (handle, worker) = Worker::new(service, rx, max_items, max_latency);
|
||||||
tokio::spawn(worker.run());
|
tokio::spawn(worker.run());
|
||||||
Batch { tx, handle }
|
Batch {
|
||||||
|
tx,
|
||||||
|
handle,
|
||||||
|
_error_type: PhantomData,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_worker_error(&self) -> crate::BoxError {
|
fn get_worker_error(&self) -> E {
|
||||||
self.handle.get_error_on_closed()
|
self.handle.get_error_on_closed()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T, Request> Service<Request> for Batch<T, Request>
|
impl<T, Request, E> Service<Request> for Batch<T, Request, E>
|
||||||
where
|
where
|
||||||
T: Service<BatchControl<Request>>,
|
T: Service<BatchControl<Request>>,
|
||||||
T::Error: Into<crate::BoxError>,
|
crate::error::Closed: Into<E>,
|
||||||
|
T::Error: Into<E>,
|
||||||
|
E: Send + 'static,
|
||||||
{
|
{
|
||||||
type Response = T::Response;
|
type Response = T::Response;
|
||||||
type Error = crate::BoxError;
|
type Error = E;
|
||||||
type Future = ResponseFuture<T::Future>;
|
type Future = ResponseFuture<T, E, Request>;
|
||||||
|
|
||||||
fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
||||||
// If the inner service has errored, then we error here.
|
// If the inner service has errored, then we error here.
|
||||||
|
|
@ -113,6 +127,7 @@ where
|
||||||
Self {
|
Self {
|
||||||
tx: self.tx.clone(),
|
tx: self.tx.clone(),
|
||||||
handle: self.handle.clone(),
|
handle: self.handle.clone(),
|
||||||
|
_error_type: PhantomData,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,14 @@
|
||||||
use super::{
|
use super::{
|
||||||
error::{Closed, ServiceError},
|
error::Closed,
|
||||||
message::{self, Message},
|
message::{self, Message},
|
||||||
BatchControl,
|
BatchControl,
|
||||||
};
|
};
|
||||||
use futures::future::TryFutureExt;
|
use futures::future::TryFutureExt;
|
||||||
use pin_project::pin_project;
|
use pin_project::pin_project;
|
||||||
use std::sync::{Arc, Mutex};
|
use std::{
|
||||||
|
marker::PhantomData,
|
||||||
|
sync::{Arc, Mutex},
|
||||||
|
};
|
||||||
use tokio::{
|
use tokio::{
|
||||||
stream::StreamExt,
|
stream::StreamExt,
|
||||||
sync::mpsc,
|
sync::mpsc,
|
||||||
|
|
@ -23,36 +26,37 @@ use tracing_futures::Instrument;
|
||||||
/// implement (only call).
|
/// implement (only call).
|
||||||
#[pin_project]
|
#[pin_project]
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Worker<T, Request>
|
pub struct Worker<T, Request, E>
|
||||||
where
|
where
|
||||||
T: Service<BatchControl<Request>>,
|
T: Service<BatchControl<Request>>,
|
||||||
T::Error: Into<crate::BoxError>,
|
T::Error: Into<E>,
|
||||||
{
|
{
|
||||||
rx: mpsc::Receiver<Message<Request, T::Future>>,
|
rx: mpsc::Receiver<Message<Request, T::Future, T::Error>>,
|
||||||
service: T,
|
service: T,
|
||||||
failed: Option<ServiceError>,
|
failed: Option<T::Error>,
|
||||||
handle: Handle,
|
handle: Handle<E>,
|
||||||
max_items: usize,
|
max_items: usize,
|
||||||
max_latency: std::time::Duration,
|
max_latency: std::time::Duration,
|
||||||
|
_error_type: PhantomData<E>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the error out
|
/// Get the error out
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub(crate) struct Handle {
|
pub(crate) struct Handle<E> {
|
||||||
inner: Arc<Mutex<Option<ServiceError>>>,
|
inner: Arc<Mutex<Option<E>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T, Request> Worker<T, Request>
|
impl<T, Request, E> Worker<T, Request, E>
|
||||||
where
|
where
|
||||||
T: Service<BatchControl<Request>>,
|
T: Service<BatchControl<Request>>,
|
||||||
T::Error: Into<crate::BoxError>,
|
T::Error: Into<E> + Clone,
|
||||||
{
|
{
|
||||||
pub(crate) fn new(
|
pub(crate) fn new(
|
||||||
service: T,
|
service: T,
|
||||||
rx: mpsc::Receiver<Message<Request, T::Future>>,
|
rx: mpsc::Receiver<Message<Request, T::Future, T::Error>>,
|
||||||
max_items: usize,
|
max_items: usize,
|
||||||
max_latency: std::time::Duration,
|
max_latency: std::time::Duration,
|
||||||
) -> (Handle, Worker<T, Request>) {
|
) -> (Handle<E>, Worker<T, Request, E>) {
|
||||||
let handle = Handle {
|
let handle = Handle {
|
||||||
inner: Arc::new(Mutex::new(None)),
|
inner: Arc::new(Mutex::new(None)),
|
||||||
};
|
};
|
||||||
|
|
@ -64,15 +68,16 @@ where
|
||||||
failed: None,
|
failed: None,
|
||||||
max_items,
|
max_items,
|
||||||
max_latency,
|
max_latency,
|
||||||
|
_error_type: PhantomData,
|
||||||
};
|
};
|
||||||
|
|
||||||
(handle, worker)
|
(handle, worker)
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn process_req(&mut self, req: Request, tx: message::Tx<T::Future>) {
|
async fn process_req(&mut self, req: Request, tx: message::Tx<T::Future, T::Error>) {
|
||||||
if let Some(ref failed) = self.failed {
|
if let Some(failed) = self.failed.clone() {
|
||||||
tracing::trace!("notifying caller about worker failure");
|
tracing::trace!("notifying caller about worker failure");
|
||||||
let _ = tx.send(Err(failed.clone()));
|
let _ = tx.send(Err(failed));
|
||||||
} else {
|
} else {
|
||||||
match self.service.ready_and().await {
|
match self.service.ready_and().await {
|
||||||
Ok(svc) => {
|
Ok(svc) => {
|
||||||
|
|
@ -80,12 +85,11 @@ where
|
||||||
let _ = tx.send(Ok(rsp));
|
let _ = tx.send(Ok(rsp));
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
self.failed(e.into());
|
self.failed(e);
|
||||||
let _ = tx.send(Err(self
|
let _ = tx.send(Err(self
|
||||||
.failed
|
.failed
|
||||||
.as_ref()
|
.clone()
|
||||||
.expect("Worker::failed did not set self.failed?")
|
.expect("Worker::failed did not set self.failed?")));
|
||||||
.clone()));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -98,7 +102,7 @@ where
|
||||||
.and_then(|svc| svc.call(BatchControl::Flush))
|
.and_then(|svc| svc.call(BatchControl::Flush))
|
||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
self.failed(e.into());
|
self.failed(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -165,7 +169,7 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn failed(&mut self, error: crate::BoxError) {
|
fn failed(&mut self, error: T::Error) {
|
||||||
// The underlying service failed when we called `poll_ready` on it with the given `error`. We
|
// The underlying service failed when we called `poll_ready` on it with the given `error`. We
|
||||||
// need to communicate this to all the `Buffer` handles. To do so, we wrap up the error in
|
// need to communicate this to all the `Buffer` handles. To do so, we wrap up the error in
|
||||||
// an `Arc`, send that `Arc<E>` to all pending requests, and store it so that subsequent
|
// an `Arc`, send that `Arc<E>` to all pending requests, and store it so that subsequent
|
||||||
|
|
@ -178,7 +182,6 @@ where
|
||||||
// request. We do this by *first* exposing the error, *then* closing the channel used to
|
// request. We do this by *first* exposing the error, *then* closing the channel used to
|
||||||
// send more requests (so the client will see the error when the send fails), and *then*
|
// send more requests (so the client will see the error when the send fails), and *then*
|
||||||
// sending the error to all outstanding requests.
|
// sending the error to all outstanding requests.
|
||||||
let error = ServiceError::new(error);
|
|
||||||
|
|
||||||
let mut inner = self.handle.inner.lock().unwrap();
|
let mut inner = self.handle.inner.lock().unwrap();
|
||||||
|
|
||||||
|
|
@ -187,7 +190,7 @@ where
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
*inner = Some(error.clone());
|
*inner = Some(error.clone().into());
|
||||||
drop(inner);
|
drop(inner);
|
||||||
|
|
||||||
self.rx.close();
|
self.rx.close();
|
||||||
|
|
@ -200,19 +203,21 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Handle {
|
impl<E> Handle<E>
|
||||||
pub(crate) fn get_error_on_closed(&self) -> crate::BoxError {
|
where
|
||||||
|
crate::error::Closed: Into<E>,
|
||||||
|
{
|
||||||
|
pub(crate) fn get_error_on_closed(&self) -> E {
|
||||||
self.inner
|
self.inner
|
||||||
.lock()
|
.lock()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.as_ref()
|
.take()
|
||||||
.map(|svc_err| svc_err.clone().into())
|
|
||||||
.unwrap_or_else(|| Closed::new().into())
|
.unwrap_or_else(|| Closed::new().into())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Clone for Handle {
|
impl<E> Clone for Handle<E> {
|
||||||
fn clone(&self) -> Handle {
|
fn clone(&self) -> Handle<E> {
|
||||||
Handle {
|
Handle {
|
||||||
inner: self.inner.clone(),
|
inner: self.inner.clone(),
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -131,31 +131,31 @@ where
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn batch_flushes_on_max_items() {
|
async fn batch_flushes_on_max_items() -> color_eyre::Result<()> {
|
||||||
use tokio::time::timeout;
|
use tokio::time::timeout;
|
||||||
install_tracing();
|
install_tracing();
|
||||||
|
|
||||||
// Use a very long max_latency and a short timeout to check that
|
// Use a very long max_latency and a short timeout to check that
|
||||||
// flushing is happening based on hitting max_items.
|
// flushing is happening based on hitting max_items.
|
||||||
let verifier = Batch::new(Ed25519Verifier::new(), 10, Duration::from_secs(1000));
|
let verifier = Batch::<_, _, color_eyre::Report>::new(
|
||||||
assert!(
|
Ed25519Verifier::new(),
|
||||||
timeout(Duration::from_secs(1), sign_and_verify(verifier, 100))
|
10,
|
||||||
.await
|
Duration::from_secs(1000),
|
||||||
.is_ok()
|
);
|
||||||
)
|
Ok(timeout(Duration::from_secs(1), sign_and_verify(verifier, 100)).await?)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn batch_flushes_on_max_latency() {
|
async fn batch_flushes_on_max_latency() -> color_eyre::Result<()> {
|
||||||
use tokio::time::timeout;
|
use tokio::time::timeout;
|
||||||
install_tracing();
|
install_tracing();
|
||||||
|
|
||||||
// Use a very high max_items and a short timeout to check that
|
// Use a very high max_items and a short timeout to check that
|
||||||
// flushing is happening based on hitting max_latency.
|
// flushing is happening based on hitting max_latency.
|
||||||
let verifier = Batch::new(Ed25519Verifier::new(), 100, Duration::from_millis(500));
|
let verifier = Batch::<_, _, color_eyre::Report>::new(
|
||||||
assert!(
|
Ed25519Verifier::new(),
|
||||||
timeout(Duration::from_secs(1), sign_and_verify(verifier, 10))
|
100,
|
||||||
.await
|
Duration::from_millis(500),
|
||||||
.is_ok()
|
);
|
||||||
)
|
Ok(timeout(Duration::from_secs(1), sign_and_verify(verifier, 10)).await?)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue