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

Use concrete error types for ipld/* and shared #463

Draft
wants to merge 7 commits into
base: master
Choose a base branch
from

Conversation

dignifiedquire
Copy link
Contributor

This is the first step to be able to remove the usage of downcasting in actors.
It removes the usage of anyhow in lower level parts.

Comment on lines 75 to 82
#[error("cbor input was not canonical (lval 24 with value < 24)")]
HeaderLval24,
#[error("cbor input was not canonical (lval 25 with value <= MaxUint8)")]
HeaderLval25,
#[error("cbor input was not canonical (lval 26 with value <= MaxUint16)")]
HeaderLval26,
#[error("cbor input was not canonical (lval 27 with value <= MaxUint32)")]
HeaderLval27,
Copy link
Contributor

Choose a reason for hiding this comment

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

Without knowing what they are really about, I'd collapse them into HeaderLval(u8).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Probably, I am deferring to figuring out the best thing to @raulk or @Stebalien (not sure who wrote this code)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

fixed

@Stebalien
Copy link
Member

We switched to anyhow because:

  1. These errors aren't useful in practice. We can't actually do anything other than to give the user a nice error message telling them what's wrong.
  2. Converting between and handling errors became a real pain.

@dignifiedquire
Copy link
Contributor Author

dignifiedquire commented Apr 6, 2022

@Stebalien well I would argue they are very useful, because you don't need to downcast anymore, you can actually do type level checks in actors with this

@Stebalien
Copy link
Member

@Stebalien well I would argue they are very useful, because you don't need to downcast anymore, you can actually do type level checks in actors with this

Sorry, I should have been specific. I meant having many detailed errors rather than just a simple "here's what went wrong" error message.

On the other hand, they don't doesn't really hurt, I guess.

But please propagate before merging here. I agree with you in theory, but enumerated errors became a real pain in practice.

  • Anyhow allowed us to just combine arbitrary errors. This makes propagating errors really easy.
  • Error enums means you need to either:
    • Define a different enum for every case.
    • Define a single enum that includes every sub-error in your crate, which is pretty useless (no way to tell which errors can actually happen in any given case).
  • Making the blockstore generic over the error tainted everything with additional generics.

On the other hand, we started with an unholy mix of enumerated errors, string errors, and anyhow. You may have better luck now.

Copy link
Member

@anorth anorth left a comment

Choose a reason for hiding this comment

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

Looks great to me, but please wait for someone else's review too.

@@ -14,7 +14,6 @@ thiserror = "1.0"
once_cell = "1.5"
ahash = { version = "0.7", optional = true }
itertools = "0.10"
anyhow = "1.0.51"
Copy link
Member

Choose a reason for hiding this comment

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

🎉

// TODO: optimize this
let mut modified = false;

// Iterate sorted indices. Sorted to safely optimize later.
for i in sorted(iter) {
let found = self.delete(i)?.is_none();
if strict && found {
return Err(anyhow!("no such index {} in Amt for batch delete", i).into());
return Err(Error::BatchDelteNotFound(i));
Copy link
Member

Choose a reason for hiding this comment

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

Typo in BatchDelteNotFound

Copy link
Contributor Author

Choose a reason for hiding this comment

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

fixed

Self::Dynamic(anyhow::anyhow!(e))
}
#[derive(Debug, Error)]
pub enum EitherError<U, E> {
Copy link
Member

Choose a reason for hiding this comment

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

Please add some comments explaining this. I get that it's unioning two types of errors, but don't understand why or how it should be used. What's the distinction between the two types of errors? If raising, how do I chose which one? If handling, what can I infer from the different types?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

added some docs

#[error("user: {0}")]
User(U),
#[error("hamt: {0}")]
Hamt(#[from] Error<E>),
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 AMT not HAMT

Copy link
Contributor Author

Choose a reason for hiding this comment

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

that’s what I get for copy pasting 😬

Copy link
Contributor Author

Choose a reason for hiding this comment

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

fixed

@dignifiedquire
Copy link
Contributor Author

dignifiedquire commented Apr 6, 2022

once the exit code PR lands on builtin-actors, I will make a matchinh PR, to make sure things work as intended based on this.

#[error("string in cbor input too long")]
StringTooLong,
#[error("Invalid link ({0}) in flushing buffered store")]
InvalidLink(Cid),
Copy link
Member

Choose a reason for hiding this comment

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

MissingBlock?

*s = Default::default();

Ok(())
}
}

#[derive(thiserror::Error, Debug)]
pub enum FlushError {
Copy link
Member

Choose a reason for hiding this comment

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

I'd reduce this to:

  • MissingBlock (can't find a block)
  • IPLD (encoding error)

(how can we even have an IO error that's not a blockstore error?)

If we're super-specific, we'll have to break this error type every time we change some detail (switch to libipld's link enumeration function, support new IPLD codecs, etc.).

@@ -15,26 +14,28 @@ pub use block::*;
///
/// The cgo blockstore adapter implements this trait.
pub trait Blockstore {
type Error: std::error::Error + std::fmt::Debug + Send + Sync + 'static;
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 what we started with, but it was a huge pain.

  1. This is incompatible with anyhow, because anyhow doesn't implement std::error::Error.
  2. Repeating this kind of constraint wherever we want to be generic is really annoying.

What if we just don't constrain this? Really, only applications care about enforcing constraints on this type.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

That is true, the main reason I constrained it is because anyhow::Context requires this, and so it gets painful there, as I can't easily define a where clause on Machine::Blockstore::Error without having to write it in a LOT of places.

Copy link
Member

Choose a reason for hiding this comment

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

Yeah, I guess that's fine. We just need to make sure we have patches for the actors before we merge.

@@ -111,7 +112,7 @@ where

/// Internal method to cleanup children, to ensure consistent tree representation
/// after deletes.
pub(crate) fn clean(&mut self) -> Result<(), Error> {
pub(crate) fn clean<BS: Blockstore>(&mut self) -> Result<(), Error<BS::Error>> {
Copy link
Member

Choose a reason for hiding this comment

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

Can you use ! or convert::Infallable as the blockstore error?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

If I do that, the error propagation stops working when I call it. If you have an idea how to work around it, I am all ears

Copy link
Member

Choose a reason for hiding this comment

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

Oh. I see.

@maciejwitowski
Copy link
Contributor

@Stebalien This is assigned to M2.1 now. Is it correct?

@maciejwitowski maciejwitowski mentioned this pull request Oct 10, 2022
85 tasks
@Stebalien
Copy link
Member

It probably won't land in M2.1. I'd like it to, but I expect we'll handle this in M2.2.

@Stebalien Stebalien modified the milestones: M2.1, M2.2 Oct 13, 2022
@rjan90 rjan90 marked this pull request as draft August 20, 2024 21:12
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