-
Notifications
You must be signed in to change notification settings - Fork 468
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
doc/dev: document our approach to error handling/printing #18741
Conversation
I added all (currently known to me) TLs, to let you voice concerns and/or learn about this. |
doc/developer/style.md
Outdated
correctly when needed. With [`thiserror`] this will happen automatically when | ||
you use the `#[from]` attribute. | ||
|
||
Generally, the `Display` impl of your error type should _not_ print the chain |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does the Display trait from thiserror follow this guideline? If so, this paragraph should be more obviously linked to the previous one. As a non-expert in rust errors, it's unclear to me what I need to do exactly, even if I use thiserror. I think these 3 paragraphs could be made more clear with a one sentence happy path: "use thiserror
" and then have the other paragraphs maybe in a separate section documenting what to do if you can't use thiserror. That'd make it super clear to a reader what they should probably do.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's very good feedback! I'll try that
#### Printing errors | ||
|
||
As mentioned above, the `Display` impl of an error should not print the chain | ||
of source errors. Whenever you _do_ need to print an error with its chain of |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm confused, because afaik, we only ever trace/log errors or surface them to users. Are there other ways we print errors? When surfacing to users they all get converted into a hopefully structured SQL error, and (almost?) never have their chain included. I've read this paragraph a few times and don't understand when I'd want to use those functions still.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I should have included more context here or in the PR description! This whole thing got started by https://github.com/MaterializeInc/materialize/issues/18490#issuecomment-1495385815.
As to where we print errors and where we use the _with_causes
variants, I'll try and list the different "flavours" we have, with an example. The only real user facing errors should be (as you pointed out) the errors we report back via the SQL client/pgwire and the errors that we report in the error/status history collections.
Errors that we report in mz_source_status_history
(and related friends and versions for sinks):
materialize/src/storage/src/sink/kafka.rs
Line 907 in 2191ed5
error: format!("{}", error.display_with_causes()), |
Structured SQL errors which include a chain of errors in the details, or in the error itself:
materialize/src/adapter/src/error.rs
Line 266 in acc39ea
storage_error.source().map(|source_error| source_error.to_string_with_causes()) |
and
materialize/src/sql/src/plan/error.rs
Line 180 in 86cc780
Self::FetchingCsrSchemaFailed { cause, .. } => Some(cause.to_string_with_causes()), |
A lot of persist errors, which we mostly log internally, or when panick'ing:
err.display_with_causes() |
Top-level error handlers for the processes:
eprintln!("environmentd: {}", err.display_with_causes()); |
Some that might be pathological cases, where we already have the chain of errors in the unstructured error itself:
materialize/src/adapter/src/error.rs
Line 459 in acc39ea
AdapterError::Unstructured(e) => write!(f, "{}", e.display_with_causes()), |
materialize/src/sql/src/plan/error.rs
Line 466 in 86cc780
sql_err!("{}", e.display_with_causes()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This seems reasonable to me.
In the compute controller, we put some effort into defining separate thiserror
-based error types to reflect that different controller methods can return different sets of errors. The idea is that if a method cannot return a given error variant that variant should not appear in the method's error type. The benefit is that callers know exactly what types of errors to expect, which isn't the case when every method returns the same large error enum. The drawback is that Rust demands a bunch of boilerplate to define all the different error enums (roughly one per API method), so I'm not sure if this is something we should make a general recommendation.
2c6e618
to
8427481
Compare
I changed the first paragraph to make it more obvious what you should do in the common case. Please take another look 🙏 |
Motivation
Socialize the changes implemented in #18583 and #18632, and make sure that we have a more coherent strategy going forward.
Checklist
$T ⇔ Proto$T
mapping (possibly in a backwards-incompatible way) and therefore is tagged with aT-proto
label.