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

Allow set and add methods to take uint types in addition to &str #28

Merged
merged 10 commits into from
Sep 5, 2024

Conversation

mrattle
Copy link
Contributor

@mrattle mrattle commented Aug 30, 2024

Closes: https://github.com/Shopify/caching-platform/issues/694

The original implementation of the set and add methods did not allow integer inputs, which doesn't make sense in the context of arithmetic methods like increment and decrement.

This PR introduces an AsMemcachedValue trait that serializes value inputs of different types into bytes to be used in memcached commands.

@mrattle mrattle added the #gsd:42391 Meta protocol in async-memcached label Aug 30, 2024
@mrattle mrattle self-assigned this Aug 30, 2024
src/parser/mod.rs Outdated Show resolved Hide resolved
@mrattle mrattle changed the title Allow set method to take uint types in addition to &str Allow set and add methods to take uint types in addition to &str Aug 30, 2024
Comment on lines 162 to 171
impl_to_memcached_value_for_uint!(u8);
impl_to_memcached_value_for_uint!(u16);
impl_to_memcached_value_for_uint!(u32);
impl_to_memcached_value_for_uint!(u64);
impl_to_memcached_value_for_uint!(usize);
Copy link
Contributor Author

Choose a reason for hiding this comment

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

I only covered uints up to u64 here because afaik we should not be initializing negative counters, and u64 is the largest size of uint that SFR is expecting.

@mrattle mrattle force-pushed the mrattle/tomemcachedvalue-for-set branch from 8d22dc8 to e2da127 Compare September 3, 2024 12:53
@mrattle mrattle marked this pull request as ready for review September 3, 2024 13:28
@mrattle mrattle requested a review from a team as a code owner September 3, 2024 13:28
@mrattle mrattle requested review from a team, makrisoft and mkcny and removed request for a team September 3, 2024 13:49
@mrattle mrattle force-pushed the mrattle/tomemcachedvalue-for-set branch from 8ce7e23 to e1844e4 Compare September 3, 2024 13:53
@danmayer
Copy link
Contributor

danmayer commented Sep 3, 2024

@mrattle some conflicts need to be resolved

Copy link
Contributor

@danmayer danmayer left a comment

Choose a reason for hiding this comment

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

looks good

@mrattle mrattle force-pushed the mrattle/tomemcachedvalue-for-set branch from e1844e4 to ca43669 Compare September 3, 2024 15:11
@mrattle mrattle force-pushed the mrattle/tomemcachedvalue-for-set branch from ca43669 to 605fc21 Compare September 3, 2024 15:22
@@ -103,6 +109,67 @@ pub struct KeyMetadata {
pub size: u32,
}

/// A trait for parsing multiple types of values to memcached values.
Copy link
Contributor

Choose a reason for hiding this comment

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

Why is this in parser/mod.rs? Isn't this only used for serializing?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

You're right, doesn't quite make sense in there. Chopped it out into it's own file to try to avoid bloat in lib.rs.

Comment on lines 115 to 120
fn length(&self) -> usize;
/// Writes the value to a writer.
fn write_to<W: AsyncWriteExt + Unpin>(
&self,
writer: &mut W,
) -> impl Future<Output = Result<(), crate::Error>>;
Copy link
Contributor

Choose a reason for hiding this comment

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

Could we collapse this into an fn as_bytes(&self) -> &[u8] method? (Or maybe returning std::borrow::Cow<'_, [u8]> if we need the option of returning owned values). This way we could avoid writing the uint types to the buffer twice and do so only once

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thanks for the suggestion, I was actually trying to work out a way to do exactly that so I'll play around with it a bit more.

Copy link
Contributor Author

@mrattle mrattle Sep 5, 2024

Choose a reason for hiding this comment

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

I'm running in to a couple of issues when trying to return bytes (either directly or through a Cow<_>:

  1. for -> &[u8] - trying to return an owned value and there doesn't seem to be a concise way to satisfy the compiler
  2. for -> Cow<'_, [u8]>: Cow is looking for a vector which requires another allocation, but also seems to just return a reference to empty memory since the underlying data is dropped before it's used.

The path of least resistance to satisfy the compiler is to return a Vec<u8> but this is very slow for long strings so it's a non-starter. I agree that parsing as bytes / writing to a buffer more than once is not great if it can be avoided but any performance cost seems to be negligible compared to a lot of the functional alternatives, though I could definitely be missing something.

@@ -103,6 +109,67 @@ pub struct KeyMetadata {
pub size: u32,
}

/// A trait for parsing multiple types of values to memcached values.
pub trait ToMemcachedValue {
Copy link
Contributor

Choose a reason for hiding this comment

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

Is the intention that we want library consumers to be able to implement this on their own types? My preference would be to not allow that unless there is a need, and to prevent that with a private supertrait as described here.

Something like:

mod private {
  pub(super) trait ToMemcachedValue {
    ...
  }
}

pub trait ToMemcachedValue: private::ToMemcachedValue {
  ...
}

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thanks, I've refactored this with the sealed trait pattern.

) -> impl Future<Output = Result<(), crate::Error>>;
}

impl ToMemcachedValue for &[u8] {
Copy link
Contributor

Choose a reason for hiding this comment

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

Can we do this so that we can continue to support all types that we did previously

Suggested change
impl ToMemcachedValue for &[u8] {
impl<T: AsRef<[u8]>> ToMemcachedValue for T {

Copy link
Contributor Author

Choose a reason for hiding this comment

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

When going down this route I end up having to create a standalone wrapper struct for u64 inputs which sort of defeats the intention behind this change, since it ends up making it more awkward to use a uint value.

I've included String and &String implementations for ToMemcachedValue which I think covers everything from AsRef that we would realistically see as input for the set and add functions here. For reference, this rust-memcache crate is where we picked up this pattern from and they have implemented on the same set of types that I have updated this PR to include.

@mrattle mrattle force-pushed the mrattle/tomemcachedvalue-for-set branch from 17af8e7 to c4c9eb0 Compare September 5, 2024 20:37
@mrattle mrattle merged commit fad4077 into main Sep 5, 2024
4 checks passed
@mrattle mrattle deleted the mrattle/tomemcachedvalue-for-set branch September 5, 2024 21:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
#gsd:42391 Meta protocol in async-memcached
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants