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