ic_backup/workflow/ic_snapshot_metadata/
mod.rs1use crate::{
4 model::{
5 attempt_journal::{MutationOutcomeRecord, MutationReceiptRequest},
6 execution_workflow::ExecutionStagePredecessorRecord,
7 ic_snapshot_metadata::IcSnapshotMetadataRequest,
8 ic_snapshot_transfer_read::{
9 IcSnapshotTransferReadError, IcSnapshotTransferReadPayload,
10 IcSnapshotTransferReadRequest, IcSnapshotTransferReadResponse,
11 },
12 },
13 ops::persistence::{
14 AttemptJournalError, AttemptJournalGuard, ExecutionStageCheckpointError,
15 ExecutionStageGuard, ExecutionWorkflowPersistenceError,
16 },
17 policy::ic_snapshot_transfer_read::{
18 IcSnapshotTransferReadAssociationError, IcSnapshotTransferReadReply, validate_response,
19 },
20 ports::ic_snapshot_transfer_read::IcSnapshotTransferReadProvider,
21 workflow::ic_snapshot_transfer_read::{IcSnapshotTransferReadExecutionError, read_snapshot},
22};
23use thiserror::Error;
24
25pub fn read_snapshot_metadata<E: std::error::Error + 'static>(
51 stage: &ExecutionStageGuard<'_>,
52 operation_sequence: u64,
53 payload: &IcSnapshotMetadataRequest,
54 provider: &mut impl IcSnapshotTransferReadProvider,
55 admit: impl FnOnce(&IcSnapshotTransferReadRequest<'_, '_>) -> Result<(), E>,
56 qualify: impl FnOnce(
57 &IcSnapshotTransferReadRequest<'_, '_>,
58 &IcSnapshotTransferReadResponse,
59 ) -> Result<MutationReceiptRequest, E>,
60) -> Result<
61 (
62 IcSnapshotTransferReadResponse,
63 ExecutionStagePredecessorRecord,
64 ),
65 IcSnapshotMetadataExecutionError<E>,
66> {
67 let operations = stage.plan().operations();
68 if operations.len() != 1
69 || operations[0].operation_sequence() != operation_sequence
70 || operations[0].request() != payload.digest().hash()
71 {
72 return Err(IcSnapshotMetadataExecutionError::OriginalMismatch);
73 }
74 let response = read_snapshot(
75 stage,
76 operation_sequence,
77 IcSnapshotTransferReadPayload::Metadata(payload),
78 provider,
79 admit,
80 )?;
81 match settle_metadata(stage, operation_sequence, payload, &response, qualify) {
82 Ok(predecessor) => Ok((response, predecessor)),
83 Err(source) => Err(IcSnapshotMetadataExecutionError::AfterReply {
84 source,
85 response: Box::new(response),
86 }),
87 }
88}
89
90fn settle_metadata<E: std::error::Error + 'static>(
91 stage: &ExecutionStageGuard<'_>,
92 sequence: u64,
93 payload: &IcSnapshotMetadataRequest,
94 response: &IcSnapshotTransferReadResponse,
95 qualify: impl FnOnce(
96 &IcSnapshotTransferReadRequest<'_, '_>,
97 &IcSnapshotTransferReadResponse,
98 ) -> Result<MutationReceiptRequest, E>,
99) -> Result<ExecutionStagePredecessorRecord, IcSnapshotMetadataSettlementError<E>> {
100 let plan = stage.plan();
101 let authority = plan
102 .attempt_authority(sequence)
103 .map_err(IcSnapshotTransferReadError::from)?;
104 let mut journal = AttemptJournalGuard::open(stage.layout()?, &authority)?;
105 let request = IcSnapshotTransferReadRequest::new(
106 plan,
107 sequence,
108 journal.record()?,
109 IcSnapshotTransferReadPayload::Metadata(payload),
110 )?;
111 let admitted = validate_response(&request, journal.record()?, response)?;
112 let receipt =
113 qualify(&request, response).map_err(IcSnapshotMetadataSettlementError::Qualification)?;
114 if receipt.outcome != MutationOutcomeRecord::Applied
115 || receipt.attempt != request.mutation_attempt()
116 || receipt.request != payload.digest().hash()
117 {
118 return Err(IcSnapshotMetadataSettlementError::ReceiptRequired);
119 }
120 let IcSnapshotTransferReadReply::Metadata(metadata) = admitted.reply() else {
121 return Err(IcSnapshotMetadataSettlementError::ReceiptRequired);
122 };
123 stage.layout()?;
124 journal.record_mutation(receipt)?;
125 drop(journal);
126 Ok(stage.checkpoint(metadata.digest())?)
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 Request(#[from] IcSnapshotTransferReadError),
166 #[error(transparent)]
168 Association(#[from] IcSnapshotTransferReadAssociationError),
169 #[error(transparent)]
171 Checkpoint(#[from] ExecutionStageCheckpointError),
172}
173
174#[cfg(all(test, unix))]
175mod tests;