Skip to content

Commit df35f1c

Browse files
authored
fix(shared-error): carry backtrace on the first cast to anyhow error (#1044)
1 parent 89be497 commit df35f1c

File tree

4 files changed

+75
-49
lines changed

4 files changed

+75
-49
lines changed

src/builder/analyzed_flow.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ impl AnalyzedFlow {
4141
}
4242

4343
pub async fn get_execution_plan(&self) -> Result<Arc<plan::ExecutionPlan>> {
44-
let execution_plan = self.execution_plan.clone().await.std_result()?;
44+
let execution_plan = self.execution_plan.clone().await.anyhow_result()?;
4545
Ok(execution_plan)
4646
}
4747
}

src/execution/memoization.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -247,7 +247,7 @@ where
247247
async move { fut.await.map_err(SharedError::new) }
248248
})
249249
.await
250-
.std_result()?,
250+
.anyhow_result()?,
251251
),
252252
None => Cow::Owned(compute().await?),
253253
};

src/execution/source_indexer.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -531,7 +531,7 @@ impl SourceIndexingContext {
531531
pending_update_fut
532532
}
533533
};
534-
pending_update_fut.await.std_result()?;
534+
pending_update_fut.await.anyhow_result()?;
535535
Ok(())
536536
}
537537

src/service/error.rs

Lines changed: 72 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -71,45 +71,91 @@ impl From<ApiError> for PyErr {
7171
}
7272
}
7373

74+
pub struct ResidualErrorData {
75+
message: String,
76+
debug: String,
77+
}
78+
7479
#[derive(Clone)]
75-
pub struct SharedError {
76-
pub err: Arc<anyhow::Error>,
80+
pub struct ResidualError(Arc<ResidualErrorData>);
81+
82+
impl Display for ResidualError {
83+
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
84+
write!(f, "{}", self.0.message)
85+
}
86+
}
87+
88+
impl Debug for ResidualError {
89+
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
90+
write!(f, "{}", self.0.debug)
91+
}
92+
}
93+
94+
impl Error for ResidualError {}
95+
96+
enum SharedErrorState {
97+
Anyhow(anyhow::Error),
98+
ResidualErrorMessage(ResidualError),
7799
}
78100

101+
/// SharedError allows to be cloned.
102+
/// The original `anyhow::Error` can be extracted once, and later it decays to `ResidualError` which preserves the message and debug information.
103+
#[derive(Clone)]
104+
pub struct SharedError(Arc<Mutex<SharedErrorState>>);
105+
79106
impl SharedError {
80107
pub fn new(err: anyhow::Error) -> Self {
81-
Self { err: Arc::new(err) }
108+
Self(Arc::new(Mutex::new(SharedErrorState::Anyhow(err))))
109+
}
110+
111+
fn extract_anyhow_error(&self) -> anyhow::Error {
112+
let mut state = self.0.lock().unwrap();
113+
let mut_state = &mut *state;
114+
115+
let residual_err = match mut_state {
116+
SharedErrorState::ResidualErrorMessage(err) => {
117+
return anyhow::Error::from(err.clone());
118+
}
119+
SharedErrorState::Anyhow(err) => ResidualError(Arc::new(ResidualErrorData {
120+
message: format!("{}", err),
121+
debug: format!("{:?}", err),
122+
})),
123+
};
124+
let orig_state = std::mem::replace(
125+
mut_state,
126+
SharedErrorState::ResidualErrorMessage(residual_err),
127+
);
128+
let SharedErrorState::Anyhow(err) = orig_state else {
129+
panic!("Expected anyhow error");
130+
};
131+
err
82132
}
83133
}
84134
impl Debug for SharedError {
85135
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
86-
Debug::fmt(&self.err, f)
136+
let state = self.0.lock().unwrap();
137+
match &*state {
138+
SharedErrorState::Anyhow(err) => Debug::fmt(err, f),
139+
SharedErrorState::ResidualErrorMessage(err) => Debug::fmt(err, f),
140+
}
87141
}
88142
}
89143

90144
impl Display for SharedError {
91145
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
92-
Display::fmt(&self.err, f)
146+
let state = self.0.lock().unwrap();
147+
match &*state {
148+
SharedErrorState::Anyhow(err) => Display::fmt(err, f),
149+
SharedErrorState::ResidualErrorMessage(err) => Display::fmt(err, f),
150+
}
93151
}
94152
}
95153

96154
impl<E: std::error::Error + Send + Sync + 'static> From<E> for SharedError {
97155
fn from(err: E) -> Self {
98-
Self {
99-
err: Arc::new(anyhow::Error::from(err)),
100-
}
101-
}
102-
}
103-
104-
impl AsRef<dyn std::error::Error> for SharedError {
105-
fn as_ref(&self) -> &(dyn std::error::Error + 'static) {
106-
self.err.as_ref().as_ref()
107-
}
108-
}
109-
110-
impl AsRef<dyn std::error::Error + Send + Sync> for SharedError {
111-
fn as_ref(&self) -> &(dyn std::error::Error + Send + Sync + 'static) {
112-
self.err.as_ref().as_ref()
156+
Self(Arc::new(Mutex::new(SharedErrorState::Anyhow(
157+
anyhow::Error::from(err),
158+
))))
113159
}
114160
}
115161

@@ -119,48 +165,28 @@ pub fn shared_ok<T>(value: T) -> Result<T, SharedError> {
119165

120166
pub type SharedResult<T> = Result<T, SharedError>;
121167

122-
pub struct SharedErrorWrapper(SharedError);
123-
124-
impl Display for SharedErrorWrapper {
125-
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
126-
Display::fmt(&self.0, f)
127-
}
128-
}
129-
130-
impl Debug for SharedErrorWrapper {
131-
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
132-
Debug::fmt(&self.0, f)
133-
}
134-
}
135-
136-
impl Error for SharedErrorWrapper {
137-
fn source(&self) -> Option<&(dyn Error + 'static)> {
138-
self.0.err.as_ref().source()
139-
}
140-
}
141-
142168
pub trait SharedResultExt<T> {
143-
fn std_result(self) -> Result<T, SharedErrorWrapper>;
169+
fn anyhow_result(self) -> Result<T, anyhow::Error>;
144170
}
145171

146172
impl<T> SharedResultExt<T> for Result<T, SharedError> {
147-
fn std_result(self) -> Result<T, SharedErrorWrapper> {
173+
fn anyhow_result(self) -> Result<T, anyhow::Error> {
148174
match self {
149175
Ok(value) => Ok(value),
150-
Err(err) => Err(SharedErrorWrapper(err)),
176+
Err(err) => Err(err.extract_anyhow_error()),
151177
}
152178
}
153179
}
154180

155181
pub trait SharedResultExtRef<'a, T> {
156-
fn std_result(self) -> Result<&'a T, SharedErrorWrapper>;
182+
fn anyhow_result(self) -> Result<&'a T, anyhow::Error>;
157183
}
158184

159185
impl<'a, T> SharedResultExtRef<'a, T> for &'a Result<T, SharedError> {
160-
fn std_result(self) -> Result<&'a T, SharedErrorWrapper> {
186+
fn anyhow_result(self) -> Result<&'a T, anyhow::Error> {
161187
match self {
162188
Ok(value) => Ok(value),
163-
Err(err) => Err(SharedErrorWrapper(err.clone())),
189+
Err(err) => Err(err.extract_anyhow_error()),
164190
}
165191
}
166192
}

0 commit comments

Comments
 (0)