Line data Source code
1 : /// Reasons for downloads or listings to fail.
2 : #[derive(Debug)]
3 : pub enum DownloadError {
4 : /// Validation or other error happened due to user input.
5 : BadInput(anyhow::Error),
6 : /// The file was not found in the remote storage.
7 : NotFound,
8 : /// A cancellation token aborted the download, typically during
9 : /// tenant detach or process shutdown.
10 : Cancelled,
11 : /// A timeout happened while executing the request. Possible reasons:
12 : /// - stuck tcp connection
13 : ///
14 : /// Concurrency control is not timed within timeout.
15 : Timeout,
16 : /// The file was found in the remote storage, but the download failed.
17 : Other(anyhow::Error),
18 : }
19 :
20 : impl std::fmt::Display for DownloadError {
21 6 : fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
22 6 : match self {
23 0 : DownloadError::BadInput(e) => {
24 0 : write!(f, "Failed to download a remote file due to user input: {e}")
25 : }
26 0 : DownloadError::NotFound => write!(f, "No file found for the remote object id given"),
27 0 : DownloadError::Cancelled => write!(f, "Cancelled, shutting down"),
28 0 : DownloadError::Timeout => write!(f, "timeout"),
29 6 : DownloadError::Other(e) => write!(f, "Failed to download a remote file: {e:?}"),
30 : }
31 6 : }
32 : }
33 :
34 : impl std::error::Error for DownloadError {}
35 :
36 : impl DownloadError {
37 : /// Returns true if the error should not be retried with backoff
38 42 : pub fn is_permanent(&self) -> bool {
39 : use DownloadError::*;
40 42 : match self {
41 42 : BadInput(_) | NotFound | Cancelled => true,
42 0 : Timeout | Other(_) => false,
43 : }
44 42 : }
45 :
46 0 : pub fn is_cancelled(&self) -> bool {
47 0 : matches!(self, DownloadError::Cancelled)
48 0 : }
49 : }
50 :
51 : impl From<std::io::Error> for DownloadError {
52 8 : fn from(value: std::io::Error) -> Self {
53 8 : let needs_unwrap = value.kind() == std::io::ErrorKind::Other
54 8 : && value
55 8 : .get_ref()
56 8 : .and_then(|x| x.downcast_ref::<DownloadError>())
57 8 : .is_some();
58 :
59 8 : if needs_unwrap {
60 8 : *value
61 8 : .into_inner()
62 8 : .expect("just checked")
63 8 : .downcast::<DownloadError>()
64 8 : .expect("just checked")
65 : } else {
66 0 : DownloadError::Other(value.into())
67 : }
68 8 : }
69 : }
70 :
71 : #[derive(Debug)]
72 : pub enum TimeTravelError {
73 : /// Validation or other error happened due to user input.
74 : BadInput(anyhow::Error),
75 : /// The used remote storage does not have time travel recovery implemented
76 : Unimplemented,
77 : /// The number of versions/deletion markers is above our limit.
78 : TooManyVersions,
79 : /// A cancellation token aborted the process, typically during
80 : /// request closure or process shutdown.
81 : Cancelled,
82 : /// Other errors
83 : Other(anyhow::Error),
84 : }
85 :
86 : impl std::fmt::Display for TimeTravelError {
87 3 : fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
88 3 : match self {
89 0 : TimeTravelError::BadInput(e) => {
90 0 : write!(
91 0 : f,
92 0 : "Failed to time travel recover a prefix due to user input: {e}"
93 0 : )
94 : }
95 0 : TimeTravelError::Unimplemented => write!(
96 0 : f,
97 0 : "time travel recovery is not implemented for the current storage backend"
98 0 : ),
99 0 : TimeTravelError::Cancelled => write!(f, "Cancelled, shutting down"),
100 : TimeTravelError::TooManyVersions => {
101 0 : write!(f, "Number of versions/delete markers above limit")
102 : }
103 3 : TimeTravelError::Other(e) => write!(f, "Failed to time travel recover a prefix: {e:?}"),
104 : }
105 3 : }
106 : }
107 :
108 : impl std::error::Error for TimeTravelError {}
109 :
110 : /// Plain cancelled error.
111 : ///
112 : /// By design this type does not not implement `std::error::Error` so it cannot be put as the root
113 : /// cause of `std::io::Error` or `anyhow::Error`. It should never need to be exposed out of this
114 : /// crate.
115 : ///
116 : /// It exists to implement permit acquiring in `{Download,TimeTravel}Error` and `anyhow::Error` returning
117 : /// operations and ensuring that those get converted to proper versions with just `?`.
118 : #[derive(Debug)]
119 : pub(crate) struct Cancelled;
120 :
121 : impl From<Cancelled> for anyhow::Error {
122 0 : fn from(_: Cancelled) -> Self {
123 0 : anyhow::Error::new(TimeoutOrCancel::Cancel)
124 0 : }
125 : }
126 :
127 : impl From<Cancelled> for TimeTravelError {
128 0 : fn from(_: Cancelled) -> Self {
129 0 : TimeTravelError::Cancelled
130 0 : }
131 : }
132 :
133 : impl From<Cancelled> for TimeoutOrCancel {
134 0 : fn from(_: Cancelled) -> Self {
135 0 : TimeoutOrCancel::Cancel
136 0 : }
137 : }
138 :
139 : impl From<Cancelled> for DownloadError {
140 0 : fn from(_: Cancelled) -> Self {
141 0 : DownloadError::Cancelled
142 0 : }
143 : }
144 :
145 : /// This type is used at as the root cause for timeouts and cancellations with `anyhow::Error` returning
146 : /// RemoteStorage methods.
147 : ///
148 : /// For use with `utils::backoff::retry` and `anyhow::Error` returning operations there is
149 : /// `TimeoutOrCancel::caused_by_cancel` method to query "proper form" errors.
150 : #[derive(Debug)]
151 : pub enum TimeoutOrCancel {
152 : Timeout,
153 : Cancel,
154 : }
155 :
156 : impl std::fmt::Display for TimeoutOrCancel {
157 0 : fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
158 : use TimeoutOrCancel::*;
159 0 : match self {
160 0 : Timeout => write!(f, "timeout"),
161 0 : Cancel => write!(f, "cancel"),
162 : }
163 0 : }
164 : }
165 :
166 : impl std::error::Error for TimeoutOrCancel {}
167 :
168 : impl TimeoutOrCancel {
169 : /// Returns true if the error was caused by [`TimeoutOrCancel::Cancel`].
170 15 : pub fn caused_by_cancel(error: &anyhow::Error) -> bool {
171 15 : error
172 15 : .root_cause()
173 15 : .downcast_ref::<Self>()
174 15 : .is_some_and(Self::is_cancel)
175 15 : }
176 :
177 3 : pub fn is_cancel(&self) -> bool {
178 3 : matches!(self, TimeoutOrCancel::Cancel)
179 3 : }
180 :
181 0 : pub fn is_timeout(&self) -> bool {
182 0 : matches!(self, TimeoutOrCancel::Timeout)
183 0 : }
184 : }
185 :
186 : /// This conversion is used when [`crate::support::DownloadStream`] notices a cancellation or
187 : /// timeout to wrap it in an `std::io::Error`.
188 : impl From<TimeoutOrCancel> for std::io::Error {
189 13 : fn from(value: TimeoutOrCancel) -> Self {
190 13 : let e = DownloadError::from(value);
191 13 : std::io::Error::other(e)
192 13 : }
193 : }
194 :
195 : impl From<TimeoutOrCancel> for DownloadError {
196 13 : fn from(value: TimeoutOrCancel) -> Self {
197 : use TimeoutOrCancel::*;
198 :
199 13 : match value {
200 5 : Timeout => DownloadError::Timeout,
201 8 : Cancel => DownloadError::Cancelled,
202 : }
203 13 : }
204 : }
|