use crate::{ClientError, KeyedClient};
#[derive(Debug, Clone)]
pub struct KeyedBatchStep {
pub key: Vec<u8>,
pub payload: Vec<u8>,
}
#[derive(Debug, thiserror::Error)]
pub enum BatchError {
#[error("step {step} failed after {completed} successful step(s): {source}")]
Partial {
step: usize,
completed: usize,
#[source]
source: ClientError,
},
}
pub async fn propose_keyed_batch<C: KeyedClient>(
client: &C,
steps: &[KeyedBatchStep],
) -> Result<Vec<Vec<u8>>, BatchError> {
let mut out = Vec::with_capacity(steps.len());
for (step, item) in steps.iter().enumerate() {
match client
.propose_keyed(item.key.clone(), item.payload.clone())
.await
{
Ok(bytes) => out.push(bytes),
Err(source) => {
return Err(BatchError::Partial {
step,
completed: step,
source,
});
}
}
}
Ok(out)
}
#[cfg(test)]
mod tests {
use std::sync::{Arc, Mutex};
use crafty_net::{Route, Transport, TransportError, encode_body};
use crafty_proto::{ClientResponse, NodeId};
use super::*;
use crate::{RemoteClient, RetryPolicy};
struct ScriptTransport {
ok: Mutex<u32>,
}
impl Transport for ScriptTransport {
fn send(
&self,
_peer: NodeId,
_route: Route,
_body: crafty_net::transport::Body,
) -> crafty_net::transport::BoxFuture<
'static,
Result<crafty_net::transport::Body, TransportError>,
> {
let remaining = {
let mut n = self.ok.lock().expect("lock");
if *n == 0 {
return Box::pin(async move { Err(TransportError::Unreachable(NodeId(1))) });
}
*n -= 1;
*n
};
let _ = remaining;
Box::pin(async move {
encode_body(&ClientResponse::Ok(b"ok".to_vec())).map_err(TransportError::Wire)
})
}
}
#[tokio::test]
async fn batch_stops_on_first_failure_and_reports_partial_progress() {
let client =
RemoteClient::new(Arc::new(ScriptTransport { ok: Mutex::new(1) }), [NodeId(1)])
.with_retry(RetryPolicy {
max_attempts: 1,
..RetryPolicy::default()
});
let err = propose_keyed_batch(
&client,
&[
KeyedBatchStep {
key: b"a".to_vec(),
payload: vec![1],
},
KeyedBatchStep {
key: b"b".to_vec(),
payload: vec![2],
},
],
)
.await
.unwrap_err();
assert!(matches!(
err,
BatchError::Partial {
step: 1,
completed: 1,
..
}
));
}
}