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 async fn acquire_fence<E: std::error::Error + 'static>(
48 stage: &ExecutionStageGuard<'_>,
49 requirement: FenceObligationRequirement<'_>,
50 obligation: &FenceObligationRecord,
51 payload: &FenceAcquisitionPayload,
52 provider: &mut impl FenceAcquisitionProvider,
53 admit: impl AsyncFnOnce(&FenceAcquisitionRequest<'_>) -> Result<(), E>,
54) -> Result<FenceAcquisitionAcknowledgement, FenceAcquisitionExecutionError<E>> {
55 let plan = stage.plan();
56 let layout = stage.layout()?;
57 read_fence_obligation(layout, plan, requirement, &obligation.digest())?;
58 let authority = plan.attempt_authority(obligation.acquisition_operation())?;
59 let mut journal = AttemptJournalGuard::open(layout, &authority)?;
60 original_authority(plan, obligation, journal.record()?, payload)?;
61 journal.reserve_planned_mutation(&plan.digest())?;
62 let request = FenceAcquisitionRequest::new(plan, obligation, journal.record()?, payload)?;
63 let acknowledgement = super::reserved_effect::submit_once(
64 &journal,
65 &request,
66 async |request| {
67 admit(request)
68 .await
69 .map_err(FenceAcquisitionExecutionError::Admission)
70 },
71 || {
72 read_fence_obligation(stage.layout()?, plan, requirement, &obligation.digest())?;
73 Ok(())
74 },
75 async |request, record| {
76 provider
77 .acquire_fence(request, record)
78 .await
79 .map_err(FenceAcquisitionExecutionError::Provider)
80 },
81 FenceAcquisitionExecutionError::Journal,
82 )
83 .await?;
84 let layout = match super::reserved_effect::check_returned(
85 stage,
86 &journal,
87 &request,
88 &acknowledgement,
89 |request, record, reply| validate_acknowledgement(request, record, reply).map(|_| ()),
90 ) {
91 Ok(layout) => layout,
92 Err(error) => {
93 return Err(match error {
94 super::reserved_effect::ReturnedError::Journal(source) => {
95 FenceAcquisitionExecutionError::AfterReplyJournal {
96 source,
97 acknowledgement: Box::new(acknowledgement),
98 }
99 }
100 super::reserved_effect::ReturnedError::Association(source) => {
101 FenceAcquisitionExecutionError::Association {
102 source,
103 acknowledgement: Box::new(acknowledgement),
104 }
105 }
106 super::reserved_effect::ReturnedError::Stage(source) => {
107 FenceAcquisitionExecutionError::AfterReplyStage {
108 source,
109 acknowledgement: Box::new(acknowledgement),
110 }
111 }
112 });
113 }
114 };
115 if let Err(source) = read_fence_obligation(layout, plan, requirement, &obligation.digest()) {
116 return Err(FenceAcquisitionExecutionError::AfterReplyObligation {
117 source,
118 acknowledgement: Box::new(acknowledgement),
119 });
120 }
121 Ok(acknowledgement)
122}
123
124#[derive(Debug, Error)]
126pub enum FenceAcquisitionExecutionError<E: std::error::Error + 'static> {
127 #[error(transparent)]
129 Plan(#[from] OperationPlanError),
130 #[error(transparent)]
132 Stage(#[from] ExecutionWorkflowPersistenceError),
133 #[error(transparent)]
135 Obligation(#[from] FenceObligationPersistenceError),
136 #[error(transparent)]
138 Journal(#[from] AttemptJournalError),
139 #[error(transparent)]
141 Progress(#[from] ExecutionProgressPersistenceError),
142 #[error(transparent)]
144 Request(#[from] FenceAcquisitionError),
145 #[error("fresh fence acquisition admission failed: {0}")]
147 Admission(#[source] E),
148 #[error(transparent)]
150 Provider(#[from] FenceAcquisitionProviderError),
151 #[error("fence acquisition acknowledgement association failed: {source}")]
153 Association {
154 source: FenceAcquisitionAcknowledgementError,
156 acknowledgement: Box<FenceAcquisitionAcknowledgement>,
158 },
159 #[error("fence acquisition journal changed after reply: {source}")]
161 AfterReplyJournal {
162 source: AttemptJournalError,
164 acknowledgement: Box<FenceAcquisitionAcknowledgement>,
166 },
167 #[error("fence acquisition stage changed after reply: {source}")]
169 AfterReplyStage {
170 source: ExecutionWorkflowPersistenceError,
172 acknowledgement: Box<FenceAcquisitionAcknowledgement>,
174 },
175 #[error("fence acquisition obligation changed after reply: {source}")]
177 AfterReplyObligation {
178 source: FenceObligationPersistenceError,
180 acknowledgement: Box<FenceAcquisitionAcknowledgement>,
182 },
183}
184
185#[cfg(all(test, unix))]
186mod tests;