use crate::{
model::{
fence_acquisition::{
FenceAcquisitionAcknowledgement, FenceAcquisitionError, FenceAcquisitionPayload,
FenceAcquisitionRequest, original_authority,
},
fence_obligation::FenceObligationRecord,
operation_plan::OperationPlanError,
},
ops::persistence::{
AttemptJournalError, AttemptJournalGuard, ExecutionProgressPersistenceError,
ExecutionStageGuard, ExecutionWorkflowPersistenceError, FenceObligationPersistenceError,
FenceObligationRequirement, read_fence_obligation,
},
policy::fence_acquisition::{FenceAcquisitionAcknowledgementError, validate_acknowledgement},
ports::fence_acquisition::{FenceAcquisitionProvider, FenceAcquisitionProviderError},
};
use thiserror::Error;
pub async fn acquire_fence<E: std::error::Error + 'static>(
stage: &ExecutionStageGuard<'_>,
requirement: FenceObligationRequirement<'_>,
obligation: &FenceObligationRecord,
payload: &FenceAcquisitionPayload,
provider: &mut impl FenceAcquisitionProvider,
admit: impl AsyncFnOnce(&FenceAcquisitionRequest<'_>) -> Result<(), E>,
) -> Result<FenceAcquisitionAcknowledgement, FenceAcquisitionExecutionError<E>> {
let plan = stage.plan();
let layout = stage.layout()?;
read_fence_obligation(layout, plan, requirement, &obligation.digest())?;
let authority = plan.attempt_authority(obligation.acquisition_operation())?;
let mut journal = AttemptJournalGuard::open(layout, &authority)?;
original_authority(plan, obligation, journal.record()?, payload)?;
journal.reserve_planned_mutation(&plan.digest())?;
let request = FenceAcquisitionRequest::new(plan, obligation, journal.record()?, payload)?;
let acknowledgement = super::reserved_effect::submit_once(
&journal,
&request,
async |request| {
admit(request)
.await
.map_err(FenceAcquisitionExecutionError::Admission)
},
|| {
read_fence_obligation(stage.layout()?, plan, requirement, &obligation.digest())?;
Ok(())
},
async |request, record| {
provider
.acquire_fence(request, record)
.await
.map_err(FenceAcquisitionExecutionError::Provider)
},
FenceAcquisitionExecutionError::Journal,
)
.await?;
let layout = match super::reserved_effect::check_returned(
stage,
&journal,
&request,
&acknowledgement,
|request, record, reply| validate_acknowledgement(request, record, reply).map(|_| ()),
) {
Ok(layout) => layout,
Err(error) => {
return Err(match error {
super::reserved_effect::ReturnedError::Journal(source) => {
FenceAcquisitionExecutionError::AfterReplyJournal {
source,
acknowledgement: Box::new(acknowledgement),
}
}
super::reserved_effect::ReturnedError::Association(source) => {
FenceAcquisitionExecutionError::Association {
source,
acknowledgement: Box::new(acknowledgement),
}
}
super::reserved_effect::ReturnedError::Stage(source) => {
FenceAcquisitionExecutionError::AfterReplyStage {
source,
acknowledgement: Box::new(acknowledgement),
}
}
});
}
};
if let Err(source) = read_fence_obligation(layout, plan, requirement, &obligation.digest()) {
return Err(FenceAcquisitionExecutionError::AfterReplyObligation {
source,
acknowledgement: Box::new(acknowledgement),
});
}
Ok(acknowledgement)
}
#[derive(Debug, Error)]
pub enum FenceAcquisitionExecutionError<E: std::error::Error + 'static> {
#[error(transparent)]
Plan(#[from] OperationPlanError),
#[error(transparent)]
Stage(#[from] ExecutionWorkflowPersistenceError),
#[error(transparent)]
Obligation(#[from] FenceObligationPersistenceError),
#[error(transparent)]
Journal(#[from] AttemptJournalError),
#[error(transparent)]
Progress(#[from] ExecutionProgressPersistenceError),
#[error(transparent)]
Request(#[from] FenceAcquisitionError),
#[error("fresh fence acquisition admission failed: {0}")]
Admission(#[source] E),
#[error(transparent)]
Provider(#[from] FenceAcquisitionProviderError),
#[error("fence acquisition acknowledgement association failed: {source}")]
Association {
source: FenceAcquisitionAcknowledgementError,
acknowledgement: Box<FenceAcquisitionAcknowledgement>,
},
#[error("fence acquisition journal changed after reply: {source}")]
AfterReplyJournal {
source: AttemptJournalError,
acknowledgement: Box<FenceAcquisitionAcknowledgement>,
},
#[error("fence acquisition stage changed after reply: {source}")]
AfterReplyStage {
source: ExecutionWorkflowPersistenceError,
acknowledgement: Box<FenceAcquisitionAcknowledgement>,
},
#[error("fence acquisition obligation changed after reply: {source}")]
AfterReplyObligation {
source: FenceObligationPersistenceError,
acknowledgement: Box<FenceAcquisitionAcknowledgement>,
},
}
#[cfg(all(test, unix))]
mod tests;