ic_backup/workflow/ic_snapshot_metadata/
mod.rs1use crate::{
4 model::{
5 artifacts::ArtifactChecksumRecord,
6 attempt_journal::MutationReceiptRequest,
7 execution_workflow::ExecutionStagePredecessorRecord,
8 ic_snapshot_metadata::IcSnapshotMetadataRequest,
9 ic_snapshot_transfer_read::{
10 IcSnapshotTransferReadError, IcSnapshotTransferReadPayload,
11 IcSnapshotTransferReadRequest, IcSnapshotTransferReadResponse,
12 },
13 },
14 ops::persistence::{
15 AttemptJournalError, AttemptJournalGuard, ExecutionStageCheckpointError,
16 ExecutionStageGuard, ExecutionWorkflowPersistenceError,
17 },
18 policy::ic_snapshot_transfer_read::{
19 IcSnapshotTransferReadAssociationError, IcSnapshotTransferReadReply,
20 IcSnapshotTransferReadView,
21 },
22 ports::ic_snapshot_transfer_read::IcSnapshotTransferReadProvider,
23 workflow::ic_snapshot_transfer_read::{
24 IcSnapshotTransferReadExecutionError, read_snapshot_with,
25 },
26};
27use thiserror::Error;
28
29pub async fn read_snapshot_metadata<E: std::error::Error + 'static>(
60 stage: &ExecutionStageGuard<'_>,
61 operation_sequence: u64,
62 payload: &IcSnapshotMetadataRequest,
63 provider: &mut impl IcSnapshotTransferReadProvider,
64 admit: impl AsyncFnOnce(&IcSnapshotTransferReadRequest<'_, '_>) -> Result<(), E>,
65 qualify: impl AsyncFnOnce(
66 &IcSnapshotTransferReadRequest<'_, '_>,
67 &IcSnapshotTransferReadResponse,
68 ) -> Result<MutationReceiptRequest, E>,
69) -> Result<
70 (
71 IcSnapshotTransferReadResponse,
72 ExecutionStagePredecessorRecord,
73 ),
74 IcSnapshotMetadataExecutionError<E>,
75> {
76 let operations = stage.plan().operations();
77 if operations.len() != 1
78 || operations[0].operation_sequence() != operation_sequence
79 || operations[0].request() != payload.digest().hash()
80 {
81 return Err(IcSnapshotMetadataExecutionError::OriginalMismatch);
82 }
83 let (response, settlement) = read_snapshot_with(
84 stage,
85 operation_sequence,
86 IcSnapshotTransferReadPayload::Metadata(payload),
87 provider,
88 admit,
89 async |journal, request, admitted| {
90 settle_metadata(stage, journal, request, admitted, qualify).await
91 },
92 )
93 .await?;
94 match settlement.and_then(|digest| Ok(stage.checkpoint(digest)?)) {
95 Ok(predecessor) => Ok((response, predecessor)),
96 Err(source) => Err(IcSnapshotMetadataExecutionError::AfterReply {
97 source,
98 response: Box::new(response),
99 }),
100 }
101}
102
103async fn settle_metadata<E: std::error::Error + 'static>(
104 stage: &ExecutionStageGuard<'_>,
105 journal: &mut AttemptJournalGuard<'_>,
106 request: &IcSnapshotTransferReadRequest<'_, '_>,
107 admitted: &IcSnapshotTransferReadView<'_, '_>,
108 qualify: impl AsyncFnOnce(
109 &IcSnapshotTransferReadRequest<'_, '_>,
110 &IcSnapshotTransferReadResponse,
111 ) -> Result<MutationReceiptRequest, E>,
112) -> Result<ArtifactChecksumRecord, IcSnapshotMetadataSettlementError<E>> {
113 super::qualified_receipt::AppliedReceiptRecorder {
114 journal,
115 expected_attempt: request.mutation_attempt(),
116 expected_request: request.payload().digest().hash(),
117 }
118 .record_applied(
119 request,
120 admitted.response(),
121 async |request, reply| qualify(request, reply).await,
122 IcSnapshotMetadataSettlementError::Qualification,
123 || IcSnapshotMetadataSettlementError::ReceiptRequired,
124 || {
125 let digest = match admitted.reply() {
126 IcSnapshotTransferReadReply::Metadata(metadata) => metadata.digest(),
127 IcSnapshotTransferReadReply::Data(_) => {
128 return Err(IcSnapshotMetadataSettlementError::ReceiptRequired);
129 }
130 };
131 stage.layout()?;
132 Ok(digest)
133 },
134 )
135 .await
136}
137
138#[derive(Debug, Error)]
140pub enum IcSnapshotMetadataExecutionError<E: std::error::Error + 'static> {
141 #[error("snapshot metadata requires an exact singleton original stage")]
143 OriginalMismatch,
144 #[error(transparent)]
146 Read(#[from] IcSnapshotTransferReadExecutionError<E>),
147 #[error("snapshot metadata reply settlement failed: {source}")]
149 AfterReply {
150 source: IcSnapshotMetadataSettlementError<E>,
152 response: Box<IcSnapshotTransferReadResponse>,
154 },
155}
156
157#[derive(Debug, Error)]
159pub enum IcSnapshotMetadataSettlementError<E: std::error::Error + 'static> {
160 #[error("snapshot metadata qualification failed: {0}")]
162 Qualification(#[source] E),
163 #[error("snapshot metadata requires an explicit original Applied receipt")]
165 ReceiptRequired,
166 #[error(transparent)]
168 Stage(#[from] ExecutionWorkflowPersistenceError),
169 #[error(transparent)]
171 Journal(#[from] AttemptJournalError),
172 #[error(transparent)]
174 Request(#[from] IcSnapshotTransferReadError),
175 #[error(transparent)]
177 Association(#[from] IcSnapshotTransferReadAssociationError),
178 #[error(transparent)]
180 Checkpoint(#[from] ExecutionStageCheckpointError),
181}
182
183#[cfg(all(test, unix))]
184mod tests;