-
Notifications
You must be signed in to change notification settings - Fork 538
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
Add FromRedisValue::from_owned_redis_value
to reduce copies while parsing responses
#1030
Add FromRedisValue::from_owned_redis_value
to reduce copies while parsing responses
#1030
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.
Great work! I believe this will be a real boon for perf-oriented users.
redis/src/types.rs
Outdated
match v { | ||
// All binary data except u8 will try to parse into a single element vector. | ||
// u8 has its own implementation of from_byte_vec. | ||
Value::Data(bytes) => match FromRedisValue::from_byte_vec(&bytes) { |
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 copies again. from_byte_vec
should take an actual vec instead of a slice reference. we probably need an owned version there, too, even though the function should've been called from_byte_slice
:(
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.
Yeah, this is one of the limitations of the current design. The hard part here was preserving the error message- if there's a decoding error, it's supposed to show the original bytes, but if we pass them in to this/an equivalent function by value, we lose them.
It might be possible by reworking the signature for this function, but then this would become a breaking change for implementors of FromRedisValue
outside of this crate.
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've added a from_owned_byte_vec
- it will slightly change the error message in the event of a failed decode, but I think there will still be enough context to figure out what's going on.
assert_eq!(v, Ok(vec![1_u16].into_boxed_slice())); | ||
|
||
assert_eq!( | ||
for parse_mode in [RedisParseMode::Owned, RedisParseMode::Ref] { |
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.
minor:
instead of taking
let v = FromRedisValue::from_redis_value(<something>);
assert_eq!(v, <expected value>);
and wrapping it in a for loop through all the tests, maybe just add a fun
assert_parse<T>(v: Value, expected: T) {
for parse_mode in [RedisParseMode::Owned, RedisParseMode::Ref] {
let v = parse_mode.parse_redis_value(value);
assert_eq!(v, expected)
}
}
and then the replacement is simpler:
```rs
let v = FromRedisValue::from_redis_value(<something>);
assert_parse(v, <expected value>);
I believe it will make the diff simpler throughout this file.
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 considered doing it this way, but at least one of the types didn't implement PartialEq
(I think it was the InfoDict
?), so there needs to be at least some duplication or this loop thing.
If you compare with whitespace hidden, the diff in this file is a lot more manageable.
LGTM @jaymell, what do you think? |
0a99f65
to
f175151
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.
Thanks for the PR. This is pretty awesome!
Co-authored-by: James Lucas <jaymell@users.noreply.github.com>
@Nathan-Fenner thanks for the contribution! |
This PR adds new methods,
from_owned_redis_value
andfrom_owned_redis_values
to complement the existingfrom_redis_value
andfrom_redis_values
.A typical call to
FromRedisValue::from_redis_value()
looked like:This call-site is typical, in that we produce a temporary (
self.read_response().await?
) and then pass a reference to that temporary.This means that even if we want to just "take" the data stored in the
redis::Value
, we are forced to clone it, including any reallocations. For example, if I want to get out aVec<u8>
, but the value is aredis::Value::Data(Vec<u8>)
, I have to copy the slice into a new vector instead of just returning it as-is.Slightly abbreviated, the change to
FromRedisValue
looks like:I replaced the existing calls of
from_redis_value
with calls tofrom_owned_redis_value
where the argument was a reference to a temporary.I also updated most of the existing implementations of
FromRedisValue
to have a more-efficientfrom_owned_redis_value
function when it makes sense.To test the change, I updated the tests in
test_types.rs
. A demonstrative example:Each test loops over the two parse modes,
RedisParseMode::Owned
andRedisParseMode::Ref
. The latter callsfrom_redis_value(v)
while the former callsfrom_owned_redis_value(v.clone())
, thus ensuring both methods get coverage.