ic_backup/workflow/ic_snapshot_upload/allocation/
mod.rs1use super::{IcSnapshotUploadExecutionError, upload_snapshot};
4use crate::{
5 model::{
6 attempt_journal::MutationReceiptRequest,
7 execution_workflow::ExecutionStagePredecessorRecord,
8 ic_mutation::IcMutationAcknowledgement,
9 ic_snapshot_upload::{
10 IcSnapshotUploadAttempt, IcSnapshotUploadAttemptError, IcSnapshotUploadKind,
11 IcSnapshotUploadRequest,
12 },
13 },
14 ops::persistence::{
15 AttemptJournalError, AttemptJournalGuard, ExecutionStageCheckpointError,
16 ExecutionStageGuard, ExecutionWorkflowPersistenceError,
17 },
18 policy::ic_snapshot_upload::{IcSnapshotUploadAssociationError, validate_acknowledgement},
19 ports::ic_snapshot_upload::IcSnapshotUploadProvider,
20};
21use thiserror::Error;
22
23pub async fn allocate_snapshot<E: std::error::Error + 'static>(
52 stage: &ExecutionStageGuard<'_>,
53 operation_sequence: u64,
54 payload: &IcSnapshotUploadRequest<'_>,
55 provider: &mut impl IcSnapshotUploadProvider,
56 admit: impl AsyncFnOnce(&IcSnapshotUploadAttempt<'_, '_>) -> Result<(), E>,
57 qualify: impl AsyncFnOnce(
58 &IcSnapshotUploadAttempt<'_, '_>,
59 &IcMutationAcknowledgement,
60 ) -> Result<MutationReceiptRequest, E>,
61) -> Result<
62 (IcMutationAcknowledgement, ExecutionStagePredecessorRecord),
63 IcSnapshotAllocationExecutionError<E>,
64> {
65 let operations = stage.plan().operations();
66 if !matches!(payload.kind(), IcSnapshotUploadKind::Metadata)
67 || operations.len() != 1
68 || operations[0].operation_sequence() != operation_sequence
69 || operations[0].request() != payload.binding_digest().hash()
70 {
71 return Err(IcSnapshotAllocationExecutionError::OriginalMismatch);
72 }
73 let acknowledgement =
74 upload_snapshot(stage, operation_sequence, payload, provider, admit).await?;
75 match settle_allocation(
76 stage,
77 operation_sequence,
78 payload,
79 &acknowledgement,
80 qualify,
81 )
82 .await
83 {
84 Ok(predecessor) => Ok((acknowledgement, predecessor)),
85 Err(source) => Err(IcSnapshotAllocationExecutionError::AfterReply {
86 source,
87 acknowledgement: Box::new(acknowledgement),
88 }),
89 }
90}
91
92async fn settle_allocation<E: std::error::Error + 'static>(
93 stage: &ExecutionStageGuard<'_>,
94 sequence: u64,
95 payload: &IcSnapshotUploadRequest<'_>,
96 acknowledgement: &IcMutationAcknowledgement,
97 qualify: impl AsyncFnOnce(
98 &IcSnapshotUploadAttempt<'_, '_>,
99 &IcMutationAcknowledgement,
100 ) -> Result<MutationReceiptRequest, E>,
101) -> Result<ExecutionStagePredecessorRecord, IcSnapshotAllocationSettlementError<E>> {
102 let plan = stage.plan();
103 let authority = plan
104 .attempt_authority(sequence)
105 .map_err(IcSnapshotUploadAttemptError::from)?;
106 let mut journal = AttemptJournalGuard::open(stage.layout()?, &authority)?;
107 let request = IcSnapshotUploadAttempt::new(plan, sequence, journal.record()?, payload)?;
108 let admitted = validate_acknowledgement(&request, journal.record()?, acknowledgement)?;
109 super::super::qualified_receipt::AppliedReceiptRecorder {
110 journal: &mut journal,
111 expected_attempt: request.mutation_attempt(),
112 expected_request: payload.binding_digest().hash(),
113 }
114 .record_applied(
115 &request,
116 acknowledgement,
117 async |request, reply| qualify(request, reply).await,
118 IcSnapshotAllocationSettlementError::Qualification,
119 || IcSnapshotAllocationSettlementError::ReceiptRequired,
120 || {
121 stage
122 .layout()
123 .map(|_| ())
124 .map_err(IcSnapshotAllocationSettlementError::Stage)
125 },
126 )
127 .await?;
128 drop(journal);
129 Ok(stage.checkpoint(admitted.reply().digest())?)
130}
131
132#[derive(Debug, Error)]
134pub enum IcSnapshotAllocationExecutionError<E: std::error::Error + 'static> {
135 #[error("snapshot allocation requires an exact singleton original metadata stage")]
137 OriginalMismatch,
138 #[error(transparent)]
140 Upload(#[from] IcSnapshotUploadExecutionError<E>),
141 #[error("snapshot allocation reply settlement failed: {source}")]
143 AfterReply {
144 source: IcSnapshotAllocationSettlementError<E>,
146 acknowledgement: Box<IcMutationAcknowledgement>,
148 },
149}
150
151#[derive(Debug, Error)]
153pub enum IcSnapshotAllocationSettlementError<E: std::error::Error + 'static> {
154 #[error("snapshot allocation qualification failed: {0}")]
156 Qualification(#[source] E),
157 #[error("snapshot allocation requires an explicit original Applied receipt")]
159 ReceiptRequired,
160 #[error(transparent)]
162 Stage(#[from] ExecutionWorkflowPersistenceError),
163 #[error(transparent)]
165 Journal(#[from] AttemptJournalError),
166 #[error(transparent)]
168 Request(#[from] IcSnapshotUploadAttemptError),
169 #[error(transparent)]
171 Association(#[from] IcSnapshotUploadAssociationError),
172 #[error(transparent)]
174 Checkpoint(#[from] ExecutionStageCheckpointError),
175}
176
177#[cfg(all(test, unix))]
178mod tests;