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 [operation] = stage.plan().operations() else {
74 return Err(IcSnapshotMetadataExecutionError::OriginalMismatch);
75 };
76 if operation.operation_sequence() != operation_sequence
77 || operation.request() != payload.digest().hash()
78 {
79 return Err(IcSnapshotMetadataExecutionError::OriginalMismatch);
80 }
81 let (response, settlement) = read_snapshot_with(
82 stage,
83 operation_sequence,
84 IcSnapshotTransferReadPayload::Metadata(payload),
85 provider,
86 admit,
87 async |journal, request, admitted| {
88 settle_metadata(stage, journal, request, admitted, qualify).await
89 },
90 )
91 .await?;
92 super::qualified_receipt::checkpoint_reply(stage, response, settlement, |source, response| {
93 IcSnapshotMetadataExecutionError::AfterReply { source, response }
94 })
95}
96
97async fn settle_metadata<E: std::error::Error + 'static>(
98 stage: &ExecutionStageGuard<'_>,
99 journal: &mut AttemptJournalGuard<'_>,
100 request: &IcSnapshotTransferReadRequest<'_, '_>,
101 admitted: &IcSnapshotTransferReadView<'_, '_>,
102 qualify: impl AsyncFnOnce(
103 &IcSnapshotTransferReadRequest<'_, '_>,
104 &IcSnapshotTransferReadResponse,
105 ) -> Result<MutationReceiptRequest, E>,
106) -> Result<ArtifactChecksumRecord, IcSnapshotMetadataSettlementError<E>> {
107 let receipt = qualify(request, admitted.response())
108 .await
109 .map_err(IcSnapshotMetadataSettlementError::Qualification)?;
110 super::qualified_receipt::record_applied(
111 journal,
112 receipt,
113 request.mutation_attempt(),
114 request.payload().digest().hash(),
115 IcSnapshotMetadataSettlementError::ReceiptRequired,
116 || {
117 let digest = match admitted.reply() {
118 IcSnapshotTransferReadReply::Metadata(metadata) => metadata.digest(),
119 IcSnapshotTransferReadReply::Data(_) => {
120 return Err(IcSnapshotMetadataSettlementError::ReceiptRequired);
121 }
122 };
123 stage.layout()?;
124 Ok(digest)
125 },
126 )
127}
128
129#[derive(Debug, Error)]
131pub enum IcSnapshotMetadataExecutionError<E: std::error::Error + 'static> {
132 #[error("snapshot metadata requires an exact singleton original stage")]
134 OriginalMismatch,
135 #[error(transparent)]
137 Read(#[from] IcSnapshotTransferReadExecutionError<E>),
138 #[error("snapshot metadata reply settlement failed: {source}")]
140 AfterReply {
141 source: IcSnapshotMetadataSettlementError<E>,
143 response: Box<IcSnapshotTransferReadResponse>,
145 },
146}
147
148#[derive(Debug, Error)]
150pub enum IcSnapshotMetadataSettlementError<E: std::error::Error + 'static> {
151 #[error("snapshot metadata qualification failed: {0}")]
153 Qualification(#[source] E),
154 #[error("snapshot metadata requires an explicit original Applied receipt")]
156 ReceiptRequired,
157 #[error(transparent)]
159 Stage(#[from] ExecutionWorkflowPersistenceError),
160 #[error(transparent)]
162 Journal(#[from] AttemptJournalError),
163 #[error(transparent)]
165 Checkpoint(#[from] ExecutionStageCheckpointError),
166}
167
168#[cfg(all(test, unix))]
169mod tests;