Skip to content
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

refactor(error): clean-up direct error formatting (part 1) #13763

Merged
merged 23 commits into from
Dec 5, 2023

Conversation

BugenZhao
Copy link
Member

@BugenZhao BugenZhao commented Dec 1, 2023

I hereby agree to the terms of the RisingWave Labs, Inc. Contributor License Agreement.

What's changed and what's your intention?

See backgrounds in #11443. TL;DR: formatting errors with {} or .to_string can cause the loss of the error source. Use thiserror_ext::Report instead.


Empowered by the lints introduced in #13750.

Run cargo install cargo-dylint dylint-link, then add the following entry to .vscode/settings.json to find all places to fix:

"rust-analyzer.check.overrideCommand": [
    "cargo",
    "dylint",
    "--all",
    "--workspace",
    "--",
    "--all-targets",
    "--message-format=json"
]

This PR fixes

common
batch
stream
compute

and some minor and related ones.

The remainings are...

connector
ctl
expr
frontend
jni
meta
storage (hummock, object store)
risedev
rpc client
pgwire

Checklist

  • I have written necessary rustdoc comments
  • I have added necessary unit tests and integration tests
  • I have added test labels as necessary. See details.
  • I have added fuzzing tests or opened an issue to track them. (Optional, recommended for new SQL features Sqlsmith: Sql feature generation #7934).
  • My PR contains breaking changes. (If it deprecates some features, please create a tracking issue to remove them in the future).
  • All checks passed in ./risedev check (or alias, ./risedev c)
  • My PR changes performance-critical code. (Please run macro/micro-benchmarks and show the results.)
  • My PR contains critical fixes that are necessary to be merged into the latest release. (Please check out the details)

Documentation

  • My PR needs documentation updates. (Please use the Release note section below to summarize the impact on users)

Release note

If this PR includes changes that directly affect users or other significant modifications relevant to the community, kindly draft a release note to provide a concise summary of these changes. Please prioritize highlighting the impact these changes will have on users.

Base automatically changed from bz/dylint-checkin to main December 1, 2023 09:12
Signed-off-by: Bugen Zhao <i@bugenzhao.com>
Signed-off-by: Bugen Zhao <i@bugenzhao.com>
Signed-off-by: Bugen Zhao <i@bugenzhao.com>
Signed-off-by: Bugen Zhao <i@bugenzhao.com>
Signed-off-by: Bugen Zhao <i@bugenzhao.com>
Signed-off-by: Bugen Zhao <i@bugenzhao.com>
Signed-off-by: Bugen Zhao <i@bugenzhao.com>
@BugenZhao BugenZhao force-pushed the bz/format-error-lint-fix branch from 4dd80b0 to 30b8172 Compare December 4, 2023 07:18
Signed-off-by: Bugen Zhao <i@bugenzhao.com>
Signed-off-by: Bugen Zhao <i@bugenzhao.com>
Copy link
Member Author

@BugenZhao BugenZhao left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some guides for refactoring

.map_err(|e| ExprError::Internal(anyhow!("failed to encode row, error: {}", e)))?;
.context("failed to encode row")?;
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use Anyhow::Context::context to create a new internal error with the given message, while keeping the error source.

@@ -101,8 +101,11 @@ where
RwConfig::default()
} else {
let config_str = fs::read_to_string(path)
.unwrap_or_else(|e| panic!("failed to open config file '{}': {}", path, e));
toml::from_str(config_str.as_str()).unwrap_or_else(|e| panic!("parse error {}", e))
.with_context(|| format!("failed to open config file at `{path}`"))
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use with_context if evaluating the message is not const.

@@ -231,8 +231,8 @@ impl BoxedExecutorBuilder for InsertExecutor {
.map(|IndexAndExpr { index: i, expr: e }| {
Ok((
i as usize,
build_from_prost(&e.ok_or_else(|| anyhow!("expression is None"))?)
.map_err(|e| anyhow!("failed to build expression: {}", e))?,
build_from_prost(&e.context("expression is None")?)
Copy link
Member Author

@BugenZhao BugenZhao Dec 4, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Besides Result, context is also applicable to Options.

If you only have the error, use anyhow::anyhow!(source).context("context message").

Comment on lines -161 to 169
let mut output = task.get_task_output(&pb_task_output_id).map_err(|e| {
let mut output = task.get_task_output(&pb_task_output_id).inspect_err(|e| {
error!(
"failed to get task output of Task {:?} in local execution mode",
task_id
error = %e.as_report(),
?task_id,
"failed to get task output in local execution mode",
);
e
})?;
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Prefer inspect_err if not going to change the error variant. For example, logging it out.

Comment on lines 149 to 153
error!(
"failed to build executors and trigger execution of Task {:?}: {}",
task_id, e
error = %e.as_report(),
?task_id,
"failed to build executors and trigger execution"
);
Copy link
Member Author

@BugenZhao BugenZhao Dec 4, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To print an error with tracing, specify a field with error = %e.as_report(). Explanations:

  1. Use the Report wrapper for error to visit the source chain when visiting.
  2. Telling tracing to use Display format with %.
  3. Normalize the error field name to error with error = <expr>

@@ -671,7 +672,7 @@ impl<C: BatchTaskContext> BatchTaskExecution<C> {

let error = error.map(Arc::new);
*self.failure.lock() = error.clone();
let err_str = error.as_ref().map(|e| e.to_string());
let err_str = error.as_ref().map(|e| e.to_report_string());
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we really want to eagerly format the error, use to_report_string to replace to_string. This is basically an alias of e.as_report().to_string().

Note that to_string implies Display trait. The behavior of {} {:#} {:?} {:#?} differ, and there're also corresponding to_report_string variants. Check the documentation of Report.

Comment on lines -35 to +38
.map_err(|e| tonic::Status::internal(format!("{}", e)))?;
.map_err(|e| e.to_status(Code::Internal, "compute"))?;
let stream_config = serde_json::to_string(&self.stream_mgr.config().await)
.map_err(|e| tonic::Status::internal(format!("{}", e)))?;
.map_err(|e| e.to_status(Code::Internal, "compute"))?;
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use to_status to convert an error to tonic::Status. If this is not ad-hoc, you may want to directly impl From<..> for tonic::Status.

@@ -145,6 +146,7 @@ impl std::fmt::Display for TonicStatusWrapper {
write!(f, " to {} service", service_name)?;
}
write!(f, " failed: {}: ", self.0.code())?;
#[allow(rw::format_error)] // intentionally format the source itself
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ignore the customized lints just like how we do for clippy.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
#[allow(rw::format_error)] // intentionally format the source itself
#[expect(rw::format_error)] // intentionally format the source itself

Comment on lines +432 to +433
if let Err(_err) = shutdown_sender.send(()) {
tracing::warn!("Failed to send shutdown");
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No need to print the SendError if the value is meaningless, as it does not contain useful information.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note: the value is the data not sent

@BugenZhao BugenZhao marked this pull request as ready for review December 4, 2023 07:56
@BugenZhao BugenZhao requested a review from a team as a code owner December 4, 2023 07:56
Signed-off-by: Bugen Zhao <i@bugenzhao.com>
Signed-off-by: Bugen Zhao <i@bugenzhao.com>
Signed-off-by: Bugen Zhao <i@bugenzhao.com>
Signed-off-by: Bugen Zhao <i@bugenzhao.com>
Signed-off-by: Bugen Zhao <i@bugenzhao.com>
Comment on lines -29 to +36
#[derive(thiserror::Error, Debug, thiserror_ext::Box)]
#[derive(
thiserror::Error,
Debug,
thiserror_ext::Arc,
thiserror_ext::ContextInto,
thiserror_ext::Construct,
)]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why Arc

What is ContextInto

Copy link
Member Author

@BugenZhao BugenZhao Dec 4, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why Arc

The error is cloned here:

&& collect_notifier
.send(Err(err.clone()))
.is_err()

What is ContextInto

It's another kind of constructor derivation! 😄 Basically, we can write

// Attach the actor id to the error.
let err = err.into_unexpected_exit(actor_id);

as a sugar of

let err = StreamError::unexpected_exit(err, actor_id);

It's not that sweet here, but can be useful for transforming the Results as it's also implemented on Results.

output.status.exit_ok().into_exit_error(
String::from_utf8_lossy(&output.stdout),
String::from_utf8_lossy(&output.stderr),
)?;

It's highly inspired by anyhow::Context and snafu::Context. I'll write the rustdoc for it ASAP.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, the context here is always evaluated. 😕 Perhaps I need to introduce a with method.

output.status.exit_ok().into_exit_error(
String::from_utf8_lossy(&output.stdout),
String::from_utf8_lossy(&output.stderr),
)?;

@BugenZhao BugenZhao requested a review from yuhao-su December 4, 2023 08:47
Copy link
Member

@xxchan xxchan left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Others LGTM. 🫡 for the work. But I'd be even happier if the PR can be even smaller.

Signed-off-by: Bugen Zhao <i@bugenzhao.com>
Signed-off-by: Bugen Zhao <i@bugenzhao.com>
Signed-off-by: Bugen Zhao <i@bugenzhao.com>
# uncomment the following two lines to enable `TaskLocalAlloc`

# Register the `rw` tool for referencing the custom lints in attributes like `#[allow(rw::xx)]`.
# This is essentially the same as `#![..]` in each crate root.
Copy link
Member

@xxchan xxchan Dec 4, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, can we use this to enable all rust features? 🫣

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, really interesting! But it seems painful to also override RUSTDOCFLAGS. 😕

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems not to work for doc tests. I'll manually register it. 🤡

Signed-off-by: Bugen Zhao <i@bugenzhao.com>
Signed-off-by: Bugen Zhao <i@bugenzhao.com>
Signed-off-by: Bugen Zhao <i@bugenzhao.com>
Signed-off-by: Bugen Zhao <i@bugenzhao.com>
Signed-off-by: Bugen Zhao <i@bugenzhao.com>
Copy link

codecov bot commented Dec 5, 2023

Codecov Report

Attention: 150 lines in your changes are missing coverage. Please review.

Comparison is base (a73d940) 68.19% compared to head (4a99055) 68.26%.
Report is 16 commits behind head on main.

Files Patch % Lines
src/utils/runtime/src/logger.rs 0.00% 13 Missing ⚠️
src/stream/src/cache/managed_lru.rs 0.00% 10 Missing ⚠️
src/stream/src/task/barrier_manager.rs 9.09% 10 Missing ⚠️
src/common/src/array/proto_reader.rs 67.85% 9 Missing ⚠️
src/batch/src/rpc/service/task_service.rs 0.00% 8 Missing ⚠️
src/common/src/array/arrow.rs 33.33% 6 Missing ⚠️
src/common/src/types/mod.rs 66.66% 6 Missing ⚠️
src/compute/src/rpc/service/stream_service.rs 0.00% 6 Missing ⚠️
...c/stream/src/task/barrier_manager/managed_state.rs 0.00% 6 Missing ⚠️
src/common/src/config.rs 0.00% 5 Missing ⚠️
... and 33 more
Additional details and impacted files
@@            Coverage Diff             @@
##             main   #13763      +/-   ##
==========================================
+ Coverage   68.19%   68.26%   +0.06%     
==========================================
  Files        1524     1524              
  Lines      262232   262275      +43     
==========================================
+ Hits       178829   179033     +204     
+ Misses      83403    83242     -161     
Flag Coverage Δ
rust 68.26% <34.21%> (+0.06%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@BugenZhao BugenZhao enabled auto-merge December 5, 2023 08:46
@BugenZhao BugenZhao added this pull request to the merge queue Dec 5, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants