use async_trait::async_trait;
use hitbox_core::{CacheKey, Offload};
use std::future::Future;
use super::CompositionWritePolicy;
use crate::{BackendError, composition::CompositionError};
#[derive(Debug, Clone, Copy, Default)]
pub struct OptimisticParallelWritePolicy;
impl OptimisticParallelWritePolicy {
pub fn new() -> Self {
Self
}
}
#[async_trait]
impl CompositionWritePolicy for OptimisticParallelWritePolicy {
#[tracing::instrument(skip(self, key, write_l1, write_l2, _offload), level = "trace")]
async fn execute_with<F1, F2, Fut1, Fut2, O>(
&self,
key: CacheKey,
write_l1: F1,
write_l2: F2,
_offload: &O,
) -> Result<(), BackendError>
where
F1: FnOnce(CacheKey) -> Fut1 + Send,
F2: FnOnce(CacheKey) -> Fut2 + Send,
Fut1: Future<Output = Result<(), BackendError>> + Send + 'static,
Fut2: Future<Output = Result<(), BackendError>> + Send + 'static,
O: Offload<'static>,
{
let (l1_result, l2_result) = futures::join!(write_l1(key.clone()), write_l2(key));
match (l1_result, l2_result) {
(Ok(()), Ok(())) => {
tracing::trace!("Both L1 and L2 writes succeeded");
Ok(())
}
(Ok(()), Err(e)) => {
tracing::warn!(
error = ?e,
"L2 write failed but L1 succeeded - partial success"
);
Ok(()) }
(Err(e), Ok(())) => {
tracing::warn!(
error = ?e,
"L1 write failed but L2 succeeded - partial success"
);
Ok(()) }
(Err(e1), Err(e2)) => {
tracing::error!(
l1_error = ?e1,
l2_error = ?e2,
"Both L1 and L2 writes failed"
);
Err(BackendError::InternalError(Box::new(
CompositionError::BothLayersFailed { l1: e1, l2: e2 },
)))
}
}
}
}