mod allocation;
#[cfg(unix)]
mod data;
#[cfg(unix)]
pub use data::{
IcSnapshotDataUploadExecutionError, IcSnapshotDataUploadReplyError, upload_snapshot_data,
};
pub use allocation::{
IcSnapshotAllocationExecutionError, IcSnapshotAllocationSettlementError, allocate_snapshot,
};
use crate::{
model::{
ic_mutation::IcMutationAcknowledgement,
ic_snapshot_upload::{
IcSnapshotUploadAttempt, IcSnapshotUploadAttemptError, IcSnapshotUploadRequest,
original_authority,
},
operation_plan::OperationPlanError,
},
ops::persistence::{
AttemptJournalError, AttemptJournalGuard, ExecutionProgressPersistenceError,
ExecutionStageGuard, ExecutionWorkflowPersistenceError,
},
policy::ic_snapshot_upload::{IcSnapshotUploadAssociationError, validate_acknowledgement},
ports::{ic_mutation::IcMutationProviderError, ic_snapshot_upload::IcSnapshotUploadProvider},
};
use thiserror::Error;
pub async fn upload_snapshot<E: std::error::Error + 'static>(
stage: &ExecutionStageGuard<'_>,
operation_sequence: u64,
payload: &IcSnapshotUploadRequest<'_>,
provider: &mut impl IcSnapshotUploadProvider,
admit: impl AsyncFnOnce(&IcSnapshotUploadAttempt<'_, '_>) -> Result<(), E>,
) -> Result<IcMutationAcknowledgement, IcSnapshotUploadExecutionError<E>> {
let plan = stage.plan();
let authority = plan.attempt_authority(operation_sequence)?;
let mut journal = AttemptJournalGuard::open(stage.layout()?, &authority)?;
original_authority(plan, operation_sequence, journal.record()?, payload)?;
journal.reserve_planned_mutation(&plan.digest())?;
let request =
IcSnapshotUploadAttempt::new(plan, operation_sequence, journal.record()?, payload)?;
let acknowledgement = super::reserved_effect::submit_once(
&journal,
&request,
async |request| {
admit(request)
.await
.map_err(IcSnapshotUploadExecutionError::Admission)
},
|| {
stage
.layout()
.map(|_| ())
.map_err(IcSnapshotUploadExecutionError::Stage)
},
async |request, record| {
provider
.submit_upload(request, record)
.await
.map_err(IcSnapshotUploadExecutionError::Provider)
},
IcSnapshotUploadExecutionError::Journal,
)
.await?;
if let Err(error) = super::reserved_effect::check_returned(
stage,
&journal,
&request,
&acknowledgement,
|request, record, reply| validate_acknowledgement(request, record, reply).map(|_| ()),
) {
return Err(match error {
super::reserved_effect::ReturnedError::Journal(source) => {
IcSnapshotUploadExecutionError::AfterReplyJournal {
source,
acknowledgement: Box::new(acknowledgement),
}
}
super::reserved_effect::ReturnedError::Association(source) => {
IcSnapshotUploadExecutionError::Association {
source,
acknowledgement: Box::new(acknowledgement),
}
}
super::reserved_effect::ReturnedError::Stage(source) => {
IcSnapshotUploadExecutionError::AfterReplyStage {
source,
acknowledgement: Box::new(acknowledgement),
}
}
});
}
Ok(acknowledgement)
}
#[derive(Debug, Error)]
pub enum IcSnapshotUploadExecutionError<E: std::error::Error + 'static> {
#[error(transparent)]
Plan(#[from] OperationPlanError),
#[error(transparent)]
Stage(#[from] ExecutionWorkflowPersistenceError),
#[error(transparent)]
Journal(#[from] AttemptJournalError),
#[error(transparent)]
Progress(#[from] ExecutionProgressPersistenceError),
#[error(transparent)]
Request(#[from] IcSnapshotUploadAttemptError),
#[error("fresh snapshot upload admission failed: {0}")]
Admission(#[source] E),
#[error(transparent)]
Provider(#[from] IcMutationProviderError),
#[error("snapshot upload acknowledgement association failed: {source}")]
Association {
source: IcSnapshotUploadAssociationError,
acknowledgement: Box<IcMutationAcknowledgement>,
},
#[error("snapshot upload journal changed after reply: {source}")]
AfterReplyJournal {
source: AttemptJournalError,
acknowledgement: Box<IcMutationAcknowledgement>,
},
#[error("snapshot upload stage changed after reply: {source}")]
AfterReplyStage {
source: ExecutionWorkflowPersistenceError,
acknowledgement: Box<IcMutationAcknowledgement>,
},
}
#[cfg(all(test, unix))]
mod tests;