ic_backup/workflow/fence_acquisition/
mod.rs1use crate::{
4 model::{
5 fence_acquisition::{
6 FenceAcquisitionAcknowledgement, FenceAcquisitionError, FenceAcquisitionPayload,
7 FenceAcquisitionRequest, original_authority,
8 },
9 fence_obligation::FenceObligationRecord,
10 operation_plan::OperationPlanError,
11 },
12 ops::persistence::{
13 AttemptJournalError, AttemptJournalGuard, ExecutionProgressPersistenceError,
14 ExecutionStageGuard, ExecutionWorkflowPersistenceError, FenceObligationPersistenceError,
15 FenceObligationRequirement, read_fence_obligation,
16 },
17 policy::fence_acquisition::{FenceAcquisitionAcknowledgementError, validate_acknowledgement},
18 ports::fence_acquisition::{FenceAcquisitionProvider, FenceAcquisitionProviderError},
19};
20use thiserror::Error;
21
22pub(super) fn open_original<'a, E>(
24 stage: &'a ExecutionStageGuard<'_>,
25 requirement: FenceObligationRequirement<'_>,
26 obligation: &FenceObligationRecord,
27 payload: &FenceAcquisitionPayload,
28) -> Result<AttemptJournalGuard<'a>, E>
29where
30 E: From<ExecutionWorkflowPersistenceError>
31 + From<FenceObligationPersistenceError>
32 + From<OperationPlanError>
33 + From<AttemptJournalError>
34 + From<FenceAcquisitionError>,
35{
36 let plan = stage.plan();
37 let layout = stage.layout()?;
38 read_fence_obligation(layout, plan, requirement, &obligation.digest())?;
39 let authority = plan.attempt_authority(obligation.acquisition_operation())?;
40 let journal = AttemptJournalGuard::open(layout, &authority)?;
41 original_authority(plan, obligation, journal.record()?, payload)?;
42 Ok(journal)
43}
44
45pub async fn acquire_fence<E: std::error::Error + 'static>(
71 stage: &ExecutionStageGuard<'_>,
72 requirement: FenceObligationRequirement<'_>,
73 obligation: &FenceObligationRecord,
74 payload: &FenceAcquisitionPayload,
75 provider: &mut impl FenceAcquisitionProvider,
76 admit: impl AsyncFnOnce(&FenceAcquisitionRequest<'_>) -> Result<(), E>,
77) -> Result<FenceAcquisitionAcknowledgement, FenceAcquisitionExecutionError<E>> {
78 let plan = stage.plan();
79 let mut journal = super::fence_acquisition::open_original::<FenceAcquisitionExecutionError<E>>(
80 stage,
81 requirement,
82 obligation,
83 payload,
84 )?;
85 journal.reserve_planned_mutation(&plan.digest())?;
86 let request = FenceAcquisitionRequest::new(plan, obligation, journal.record()?, payload)?;
87 admit(&request)
88 .await
89 .map_err(FenceAcquisitionExecutionError::Admission)?;
90 read_fence_obligation(stage.layout()?, plan, requirement, &obligation.digest())?;
91 let acknowledgement = provider.acquire_fence(&request, journal.record()?).await?;
92 let admit_reply = || -> Result<(), FenceAcquisitionReplyError> {
93 validate_acknowledgement(&request, journal.record()?, &acknowledgement)?;
94 read_fence_obligation(stage.layout()?, plan, requirement, &obligation.digest())?;
95 Ok(())
96 };
97 let admitted = admit_reply();
98 super::qualified_receipt::retain_reply(acknowledgement, admitted, |source, acknowledgement| {
99 FenceAcquisitionExecutionError::AfterReply {
100 source,
101 acknowledgement,
102 }
103 })
104 .map(|(reply, ())| reply)
105}
106
107#[derive(Debug, Error)]
109pub enum FenceAcquisitionExecutionError<E: std::error::Error + 'static> {
110 #[error(transparent)]
112 Plan(#[from] OperationPlanError),
113 #[error(transparent)]
115 Stage(#[from] ExecutionWorkflowPersistenceError),
116 #[error(transparent)]
118 Obligation(#[from] FenceObligationPersistenceError),
119 #[error(transparent)]
121 Journal(#[from] AttemptJournalError),
122 #[error(transparent)]
124 Progress(#[from] ExecutionProgressPersistenceError),
125 #[error(transparent)]
127 Request(#[from] FenceAcquisitionError),
128 #[error("fresh fence acquisition admission failed: {0}")]
130 Admission(#[source] E),
131 #[error(transparent)]
133 Provider(#[from] FenceAcquisitionProviderError),
134 #[error("fence acquisition reply rejected: {source}")]
136 AfterReply {
137 source: FenceAcquisitionReplyError,
139 acknowledgement: Box<FenceAcquisitionAcknowledgement>,
141 },
142}
143
144#[derive(Debug, Error)]
146pub enum FenceAcquisitionReplyError {
147 #[error(transparent)]
149 Journal(#[from] AttemptJournalError),
150 #[error(transparent)]
152 Association(#[from] FenceAcquisitionAcknowledgementError),
153 #[error(transparent)]
155 Stage(#[from] ExecutionWorkflowPersistenceError),
156 #[error(transparent)]
158 Obligation(#[from] FenceObligationPersistenceError),
159}
160
161#[cfg(all(test, unix))]
162mod tests;