-
Notifications
You must be signed in to change notification settings - Fork 619
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
fix: do not cache io errors #5856
Conversation
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.
Always a pleasure to review
utils/near-cache/src/lib.rs
Outdated
{ | ||
match self.get_or_try_insert(key, |k| Ok::<V, std::convert::Infallible>(f(k))) { | ||
Ok(it) => it, | ||
Err(never) => match never {}, |
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.
hmm isn't this equivalent to Err(_) => ()
?
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.
not really, the type of ()
is ()
, the type of match never {}
is !
. The result type here is V
, and match never {}
is compatible with that.
For context: Infallible
is an empty enum: enum Infallible {}
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.
Why not use:
diff --git i/utils/near-cache/src/lib.rs w/utils/near-cache/src/lib.rs
index 069662c90..c13416021 100644
--- i/utils/near-cache/src/lib.rs
+++ w/utils/near-cache/src/lib.rs
@@ -26,10 +26,7 @@ where
V: Clone,
F: FnOnce(&K) -> V,
{
- match self.get_or_try_put(key, |k| Ok::<V, std::convert::Infallible>(f(k))) {
- Ok(it) => it,
- Err(never) => match never {},
- }
+ self.get_or_try_put(key, |k| Ok::<V, std::convert::Infallible>(f(k))).unwrap()
}
/// Returns the value of they key in the cache if present, otherwise
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.
.unwrap()
is an operation that can in general fail at runtime. In this case it can't actually fail, but you need to read the context to understand this. In contrast, match never {}
can't fail in a statically-checked way.
Really, thish should be just Result::into_ok
(https://doc.rust-lang.org/stable/std/result/enum.Result.html#method.into_ok) but that needs to wait until !
is stabilized properly.
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.
In previous codebases I used
Result::<_, Infallible>::unwrap(result)
to very signal clearly enough that “yeah this is unwrap, but no it is actually into_ok”. At codegen level LLVM can see easily enough that panic within unwrap
is not reachable panic won't actually materialize.
match never {}
seems to have confused sufficient number of people that I think its probably not worth whatever static benefits it provides.
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.
Excellent suggestion, thanks!
0aed3a5
to
19246cd
Compare
These two pass-through functions were only necessary when we used cache crate, we don't need them now.
19246cd
to
232f1b7
Compare
When we compile & cache the contract, there are two very different kinds of "error" we can have: * IO error due to the database being corrupted and such * Contract failed to compile because it contained invalid WASM The second outcome is normal, expected mode of execution and should be cached. The first error is a genuine exceptional situation, and should not be cached (eg, retrying the operation might succeed).
232f1b7
to
4fe7fb1
Compare
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.
LGTM, though, it would be nice to have a test for it..
That's a very good call. I've written that "we don't have fault injection tests", but of course we can cheaply add them here, as the cache is already polymorphic. And, surely, doing so uncovered a minor preexisting bug where I mixed up read error and write error! |
It's not really tiny, `trivial_contract` is significantly simpler
e07f5c3
to
84ccbb2
Compare
/// Returns the value of they key in the cache if present, otherwise | ||
/// computes the value using the provided closure. | ||
/// | ||
/// If the key is already in the cache, it gets moved to the head of the LDU |
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.
/// If the key is already in the cache, it gets moved to the head of the LDU | |
/// If the key is already in the cache, it gets moved to the head of the LRU |
When we compile & cache the contract, there are two very different kinds
of "errors" we can have:
The second outcome is normal, expected mode of execution and should be
cached. The first error is a genuine exceptional situation, and should
not be cached (eg, retrying the operation might succeed).
Test Plan
Existing tests to verify that the happy path still works. I don't believe we current have fault injection tests to make sure genuine IO-errors are handled correctly.