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): eliminate most RwError usages in common crate #13588

Merged
merged 5 commits into from
Nov 24, 2023

Conversation

BugenZhao
Copy link
Member

@BugenZhao BugenZhao commented Nov 22, 2023

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

What's changed and what's your intention?

Eliminate most RwError usages in the common crate. #4077

To achieve that, one must not hesitate to define per-module error types. Examples:

#[derive(thiserror::Error, Debug, thiserror_ext::ContextInto)]
pub enum JeprofError {
#[error(transparent)]
IoError(#[from] std::io::Error),
#[error("jeprof exit with an error (stdout: {stdout}, stderr: {stderr}): {inner}")]
ExitError {
#[source]
inner: std::process::ExitStatusError,
stdout: String,
stderr: String,
},
}

#[derive(Debug, thiserror::Error)]
#[error("Unsupported oid {0}")]
pub struct UnsupportedOid(i32);


image

The remaining ones are:

Checklist

  • I have written necessary rustdoc comments
  • I have added necessary unit tests and integration tests
  • All checks passed in ./risedev check (or alias, ./risedev c)

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.

Signed-off-by: Bugen Zhao <[email protected]>
Signed-off-by: Bugen Zhao <[email protected]>
Signed-off-by: Bugen Zhao <[email protected]>
Copy link

codecov bot commented Nov 22, 2023

Codecov Report

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

Comparison is base (bf3975f) 68.18% compared to head (b87318c) 68.13%.
Report is 3 commits behind head on main.

Files Patch % Lines
src/common/src/types/mod.rs 27.90% 31 Missing ⚠️
src/common/src/types/interval.rs 56.00% 11 Missing ⚠️
src/common/common_service/src/observer_manager.rs 23.07% 10 Missing ⚠️
src/common/heap_profiling/src/jeprof.rs 0.00% 9 Missing ⚠️
src/common/src/types/to_binary.rs 11.11% 8 Missing ⚠️
src/common/src/types/num256.rs 0.00% 4 Missing ⚠️
...storage/hummock_test/src/bin/replay/replay_impl.rs 0.00% 4 Missing ⚠️
src/common/src/error.rs 0.00% 3 Missing ⚠️
src/common/src/types/datetime.rs 0.00% 3 Missing ⚠️
src/utils/pgwire/src/pg_protocol.rs 25.00% 3 Missing ⚠️
... and 13 more
Additional details and impacted files
@@            Coverage Diff             @@
##             main   #13588      +/-   ##
==========================================
- Coverage   68.18%   68.13%   -0.05%     
==========================================
  Files        1520     1519       -1     
  Lines      261483   261375     -108     
==========================================
- Hits       178289   178097     -192     
- Misses      83194    83278      +84     
Flag Coverage Δ
rust 68.13% <48.20%> (-0.05%) ⬇️

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 requested a review from fuyufjh November 23, 2023 07:51
Copy link
Contributor

@wangrunji0408 wangrunji0408 left a comment

Choose a reason for hiding this comment

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

LGTM!

@BugenZhao
Copy link
Member Author

BugenZhao commented Nov 24, 2023

Update session config error in b87318c. cc @yuhao-su

@BugenZhao
Copy link
Member Author

Update session config error in b87318c.

The error message is much more readable now!

dev=> set rw_implicit_flush to maybe;
ERROR:  Failed to run the query

Caused by these errors (recent errors listed first):
  1: Failed to get/set session config
  2: Invalid value `maybe` for `rw_implicit_flush`
  3: provided string was not `true` or `false`

dev=> set streaming_parallelism to 888888888888888888888888;
ERROR:  Failed to run the query

Caused by these errors (recent errors listed first):
  1: Failed to get/set session config
  2: Invalid value `888888888888888888888888` for `streaming_parallelism`
  3: number too large to fit in target type

dev=> set isolation_level to hello;
ERROR:  Failed to run the query

Caused by these errors (recent errors listed first):
  1: Failed to get/set session config
  2: Unrecognized config entry `isolation_level`

dev=> set transaction_isolation_level to hello;
ERROR:  Failed to run the query

Caused by these errors (recent errors listed first):
  1: Failed to get/set session config
  2: Invalid value `hello` for `transaction_isolation_level`
  3: isolation level is not yet supported

@BugenZhao BugenZhao enabled auto-merge November 24, 2023 06:25
@BugenZhao BugenZhao added this pull request to the merge queue Nov 24, 2023
Copy link
Member

@stdrc stdrc left a comment

Choose a reason for hiding this comment

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

LGTM! Thanks for your elegant work! Could you please write something like an error-handling guide?

Comment on lines +28 to +31
#[source]
inner: std::process::ExitStatusError,
stdout: String,
stderr: String,
Copy link
Member

Choose a reason for hiding this comment

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

#[source] will generate From<ExitStatusError> for JeprofError right? But how are stdout and stderr set?

Does this work because of thiserror_ext::ContextInto which generates into_exit_error which will retain the source error as inner and set the arguments for stdout and stderr?

Copy link
Member Author

Choose a reason for hiding this comment

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

Does this work because of thiserror_ext::ContextInto which generates into_exit_error which will retain the source error as inner and set the arguments for stdout and stderr?

Exactly! I'll refine the documentation of thiserror_ext once it gets more stable.

Copy link
Member Author

Choose a reason for hiding this comment

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

#[source] will generate From<ExitStatusError> for JeprofError right?

This is not accurate. Only #[from] will generate From impl, while #[source] only helps us to correctly maintain the source chain with source() method.

Comment on lines -164 to -181
/// Find `column_desc` in `field_descs` by name.
pub fn field(&self, name: &String) -> crate::error::Result<(ColumnDesc, i32)> {
if let DataType::Struct { .. } = self.data_type {
for (index, col) in self.field_descs.iter().enumerate() {
if col.name == *name {
return Ok((col.clone(), index as i32));
}
}
Err(ErrorCode::ItemNotFound(format!("Invalid field name: {}", name)).into())
} else {
Err(ErrorCode::ItemNotFound(format!(
"Cannot get field from non nested column: {}",
self.name
))
.into())
}
}

Copy link
Member

Choose a reason for hiding this comment

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

This is a dead method?

Copy link
Member Author

Choose a reason for hiding this comment

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

Yes. I don't find any references.

@@ -136,7 +136,7 @@ pub fn cdc_table_name_column_desc() -> ColumnDesc {
/// The local system catalog reader in the frontend node.
#[async_trait]
pub trait SysCatalogReader: Sync + Send + 'static {
async fn read_table(&self, table_id: &TableId) -> Result<Vec<OwnedRow>>;
async fn read_table(&self, table_id: &TableId) -> Result<Vec<OwnedRow>, BoxedError>;
Copy link
Member

Choose a reason for hiding this comment

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

Will BoxedError be abused after removing RwError?

Copy link
Member Author

Choose a reason for hiding this comment

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

Since this is a trait, we have no information about how the users may have implemented it, so does the error type. It seems a common practice to let trait methods return Box<dyn Error>. Similar ideas are applied to sqllogictest::AsyncDB.

@@ -1001,6 +1000,24 @@ impl ToText for crate::types::Interval {
}
}

/// Error type for parsing an [`Interval`].
#[derive(thiserror::Error, Debug, 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.

Is there any big difference between IntervalParseError::Invalid and IntervalParseError::invalid? Given that the former one is also a special function?

Copy link
Member Author

@BugenZhao BugenZhao Nov 24, 2023

Choose a reason for hiding this comment

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

#[automatically_derived]
impl IntervalParseError {
    /// Constructs a [`IntervalParseError::Invalid`] variant.
    pub fn invalid(arg_0: impl Into<String>) -> Self {
        IntervalParseError::Invalid { 0: arg_0.into() }.into()
    }
}

Copy link
Member

@fuyufjh fuyufjh left a comment

Choose a reason for hiding this comment

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

LGTM

Copy link
Contributor

@yuhao-su yuhao-su left a comment

Choose a reason for hiding this comment

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

LGTM🐮

Merged via the queue into main with commit 90ce699 Nov 24, 2023
27 of 28 checks passed
@BugenZhao BugenZhao deleted the bz/error-rw-common branch November 24, 2023 07:14
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.

I often forget why on earth do we want to eliminate big RwError. Do you have a tldr? 🤪

What's the largest benefit?

@BugenZhao
Copy link
Member Author

BugenZhao commented Nov 29, 2023

I often forget why on earth do we want to eliminate big RwError. Do you have a tldr? 🤪

What's the largest benefit?

The worst part of RwError is that it now acts as both the leaf error type (that needs to be frequently and manually created by developers #13627) and the root error type (that returns to pgwire).

These two roles are not that compatible. For example, the leaf error usually contains a message field only, while the root error is likely to be simply some #[from] wrappers with contexts attached. As a result, we get BindError and BindErrorRoot. It's apparent that a better practice should be only keep the Root variant and make BindError a separate error type.

// TODO: use a new type for bind error
#[error("Bind error: {0}")]
BindError(String),
// TODO: only keep this one
#[error("Failed to bind expression: {expr}: {error}")]
BindErrorRoot {
expr: String,
#[source]
#[backtrace]
error: BoxedError,
},

There're also other reasons for removing RwError...

  • With per-module error types, developers are freer to design the types so the categorization can be more accurate.
  • Different modules may expect different boxing or backtrace behavior of its error type.
  • By removing that, it's also a good chance for us to revisit our previous error handling practice. 😄

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.

6 participants