Skip to main content

ic_backup/workflow/fence_acquisition/
mod.rs

1//! One freshly reserved application acquisition under exact original stage/obligation custody.
2
3use 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
22/// Reserve the original acquisition, freshly admit it and invoke one exact application update.
23///
24/// Hold no attempt guards on entry. Retain the full original stage, every journal,
25/// requirement and whole-unit obligation before calling. Restore requires its source
26/// layout/plan and outside-snapshot custody. Exact original payload admission precedes
27/// spending; complete original-plan admission owns the durable mutation reservation.
28/// The selected journal remains locked across admission and the provider await.
29/// Cancellation retains pending spending and denies redispatch through this step.
30///
31/// Mandatory fresh `admit` must qualify actual context, whole-unit membership,
32/// application method/argument purpose, original fence/revisions, permissions,
33/// dependencies, command custody and proof this reservation has never dispatched.
34/// Any remote preflight has its own prior accounting. The configured provider retains
35/// exact signed ingress before its one update and exact reply bytes before returning.
36/// There is no default application, permission or retention callback.
37///
38/// Recheck retained stage/ancestors and requirement/obligation around dispatch.
39/// Success returns passive association only and leaves the mutation pending.
40/// Applications independently qualify attribution and Active custody before explicit
41/// settlement; lost replies use original reconciliation accounting. No receipt, refund,
42/// automatic uncertainty, acquired fence or terminal/fence/reference release follows.
43/// # Errors
44/// Rejects changed/missing originals, binding/spending, fresh admission, provider or
45/// association failures. Every post-reservation error retains consumption; post-reply
46/// rejections retain the returned acknowledgement. No failure permits redispatch.
47pub 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    admit(&request)
64        .await
65        .map_err(FenceAcquisitionExecutionError::Admission)?;
66    read_fence_obligation(stage.layout()?, plan, requirement, &obligation.digest())?;
67    let acknowledgement = provider.acquire_fence(&request, journal.record()?).await?;
68    let association = match journal.record() {
69        Ok(record) => record,
70        Err(source) => {
71            return Err(FenceAcquisitionExecutionError::AfterReplyJournal {
72                source,
73                acknowledgement: Box::new(acknowledgement),
74            });
75        }
76    };
77    if let Err(source) = validate_acknowledgement(&request, association, &acknowledgement) {
78        return Err(FenceAcquisitionExecutionError::Association {
79            source,
80            acknowledgement: Box::new(acknowledgement),
81        });
82    }
83    let layout = match stage.layout() {
84        Ok(layout) => layout,
85        Err(source) => {
86            return Err(FenceAcquisitionExecutionError::AfterReplyStage {
87                source,
88                acknowledgement: Box::new(acknowledgement),
89            });
90        }
91    };
92    if let Err(source) = read_fence_obligation(layout, plan, requirement, &obligation.digest()) {
93        return Err(FenceAcquisitionExecutionError::AfterReplyObligation {
94            source,
95            acknowledgement: Box::new(acknowledgement),
96        });
97    }
98    Ok(acknowledgement)
99}
100
101/// Acquisition-step failure preserving original spending, obligations and passive replies.
102#[derive(Debug, Error)]
103pub enum FenceAcquisitionExecutionError<E: std::error::Error + 'static> {
104    /// Exact original acquisition authority cannot be derived.
105    #[error(transparent)]
106    Plan(#[from] OperationPlanError),
107    /// Original stage, workflow or ancestor evidence differs.
108    #[error(transparent)]
109    Stage(#[from] ExecutionWorkflowPersistenceError),
110    /// Original requirement or obligation cannot be admitted.
111    #[error(transparent)]
112    Obligation(#[from] FenceObligationPersistenceError),
113    /// Original selected journal admission failed.
114    #[error(transparent)]
115    Journal(#[from] AttemptJournalError),
116    /// Complete original journals, prerequisites or durable reservation failed.
117    #[error(transparent)]
118    Progress(#[from] ExecutionProgressPersistenceError),
119    /// Exact original payload or current reservation differs.
120    #[error(transparent)]
121    Request(#[from] FenceAcquisitionError),
122    /// Fresh application admission rejected after reservation.
123    #[error("fresh fence acquisition admission failed: {0}")]
124    Admission(#[source] E),
125    /// Single provider invocation failed; no nonapplication or refund is inferred.
126    #[error(transparent)]
127    Provider(#[from] FenceAcquisitionProviderError),
128    /// Passive original authority/attempt association rejected the reply.
129    #[error("fence acquisition acknowledgement association failed: {source}")]
130    Association {
131        /// Canonical passive association rejection.
132        source: FenceAcquisitionAcknowledgementError,
133        /// Exact returned passive acknowledgement, without acquisition proof.
134        acknowledgement: Box<FenceAcquisitionAcknowledgement>,
135    },
136    /// Selected journal re-admission failed after reply.
137    #[error("fence acquisition journal changed after reply: {source}")]
138    AfterReplyJournal {
139        /// Original journal rejection.
140        source: AttemptJournalError,
141        /// Exact returned passive acknowledgement.
142        acknowledgement: Box<FenceAcquisitionAcknowledgement>,
143    },
144    /// Retained stage/ancestor admission failed after reply.
145    #[error("fence acquisition stage changed after reply: {source}")]
146    AfterReplyStage {
147        /// Original stage/ancestor rejection.
148        source: ExecutionWorkflowPersistenceError,
149        /// Exact returned passive acknowledgement.
150        acknowledgement: Box<FenceAcquisitionAcknowledgement>,
151    },
152    /// Retained requirement/obligation admission failed after reply.
153    #[error("fence acquisition obligation changed after reply: {source}")]
154    AfterReplyObligation {
155        /// Original requirement/obligation rejection.
156        source: FenceObligationPersistenceError,
157        /// Exact returned passive acknowledgement.
158        acknowledgement: Box<FenceAcquisitionAcknowledgement>,
159    },
160}
161
162#[cfg(all(test, unix))]
163mod tests;