Skip to main content

ic_backup/ops/persistence/publication_error/
mod.rs

1//! Backup-owned publication failure and recovery evidence.
2
3use std::{error::Error, fmt, io};
4
5/// File publication failure with explicit visibility and retained cleanup evidence.
6///
7/// `AfterPublication` means the new output is visible, but final durability is
8/// uncertain; callers must reconcile it before considering another write.
9#[derive(Debug)]
10pub enum PublicationError {
11    /// The staged producer failed before publication was attempted.
12    Producer {
13        /// Original producer failure.
14        source: io::Error,
15        /// Failure to clean up the still-owned staging entry, if any.
16        cleanup_error: Option<io::Error>,
17    },
18    /// Admission, synchronization, or rename failed before publication.
19    BeforePublication {
20        /// Original filesystem failure.
21        source: io::Error,
22        /// Failure to clean up the still-owned staging entry, if any.
23        cleanup_error: Option<io::Error>,
24    },
25    /// The new output is visible, but cleanup or final directory sync failed.
26    AfterPublication {
27        /// Original completion failure.
28        source: io::Error,
29    },
30}
31
32impl PublicationError {
33    pub(crate) fn from_host(error: ic_host_fs::durable::NamedWriteError<io::Error>) -> Self {
34        use ic_host_fs::durable::NamedWriteError;
35        match error {
36            NamedWriteError::Producer {
37                source,
38                cleanup_error,
39            } => Self::Producer {
40                source,
41                cleanup_error,
42            },
43            NamedWriteError::BeforePublication {
44                source,
45                cleanup_error,
46            } => Self::BeforePublication {
47                source,
48                cleanup_error,
49            },
50            NamedWriteError::AfterPublication { source } => Self::AfterPublication { source },
51        }
52    }
53}
54
55impl fmt::Display for PublicationError {
56    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
57        let (description, source, cleanup_error) = match self {
58            Self::Producer {
59                source,
60                cleanup_error,
61            } => ("staged producer failed", source, cleanup_error.as_ref()),
62            Self::BeforePublication {
63                source,
64                cleanup_error,
65            } => (
66                "publication failed before rename",
67                source,
68                cleanup_error.as_ref(),
69            ),
70            Self::AfterPublication { source } => {
71                ("output published but completion failed", source, None)
72            }
73        };
74        write!(formatter, "{description}: {source}")?;
75        if let Some(cleanup_error) = cleanup_error {
76            write!(formatter, "; staging cleanup failed: {cleanup_error}")?;
77        }
78        Ok(())
79    }
80}
81
82impl Error for PublicationError {
83    fn source(&self) -> Option<&(dyn Error + 'static)> {
84        match self {
85            Self::Producer { source, .. }
86            | Self::BeforePublication { source, .. }
87            | Self::AfterPublication { source } => Some(source),
88        }
89    }
90}
91
92#[cfg(test)]
93mod tests;