[feat] Add distributed cache trait, type transformation, serialization, compression, encryption#346
[feat] Add distributed cache trait, type transformation, serialization, compression, encryption#346
Conversation
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…chgoo/distributedcache
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…s/encrypt pipeline Switch the cachet crate's serialize, compress, and encrypt codecs from Vec<u8> to BytesView (from the bytesbuf workspace crate). This makes cloning in the fallback/promotion path O(1) (refcount bump) instead of O(n) (full byte copy). Changes: - Add bytesbuf dependency to cachet - Update BincodeEncoder/BincodeCodec to produce/consume BytesView - Update ZstdCodec to compress/decompress BytesView - Update AesGcmCodec to encrypt/decrypt BytesView - Update builder transform.rs serialize/compress/encrypt methods - Update serialize_pipeline example to use BytesView - Re-export BytesView from cachet::lib - Add Hash impl for BytesView in bytesbuf crate - Add bytesbuf::view::BytesView to allowed_external_types Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
| impl ZstdCodec { | ||
| /// Creates a new Zstd codec with the given compression level. | ||
| /// | ||
| /// Levels typically range from 1 (fastest) to 22 (best compression). |
There was a problem hiding this comment.
Should be a newtype constraining the value from 1 to 22.
There was a problem hiding this comment.
Thanks for the feedback, @geeknoid! This draft PR is just a bytesbuf sanity check/reference and I will close it once I get the relevant code checked in in phases. You're definitely right on a lot of it. The bytesbuf portions were AI generated, and I haven't dug through them deeply yet. I am starting the work on the serialization feature today, so I'll incorporate your comments there.
There was a problem hiding this comment.
The ultimate goal is zero-copy from serialization-I/O. Compression and Encryption do write new output, but maybe we can avoid full copy of the input.
| dctx.reset(zstd::zstd_safe::ResetDirective::SessionOnly) | ||
| .map_err(|code| Error::from_message(format!("failed to reset zstd decompression context: error code {code}")))?; | ||
| let mut decoder = zstd::stream::read::Decoder::with_context(value.clone(), dctx); | ||
| let mut output = Vec::new(); |
There was a problem hiding this comment.
This should be a BytesBuf, right? This would avoid temp allocations.
| .cipher | ||
| .decrypt(nonce, ciphertext) | ||
| .map_err(|e| Error::from_message(format!("AES-GCM decryption failed: {e}")))?; | ||
| Ok(plaintext.into()) |
There was a problem hiding this comment.
I think the issue here is that some encryption algorithms require contiguous input, whereas BytesView/BytesBuf is multi-span. So basically we have two options here:
- Add something like
BytesView::as_contiguous() -> Cow<[u8]>that returns the slice if the BytesView is single-span, or copies when multi-span - Use something like https://github.com/RustCrypto/AEADs/tree/master/aead-stream to implement chunked encryption. This has security risks, and this particular crate is not very widely used and has no tests.
|
|
||
| impl<T: serde::Serialize + Send + Sync> Encoder<T, BytesView> for BincodeEncoder { | ||
| fn encode(&self, value: &T) -> Result<BytesView, Error> { | ||
| let bytes = bincode::serialize(value).map_err(Error::from_source)?; |
There was a problem hiding this comment.
Can we serialize into a BytesBuf to avoid an alloc?
|
|
||
| impl<T: serde::Serialize + serde::de::DeserializeOwned + Send + Sync> Codec<T, BytesView> for BincodeCodec { | ||
| fn decode(&self, bytes: &BytesView) -> Result<T, Error> { | ||
| bincode::deserialize(bytes.first_slice()).map_err(Error::from_source) |
There was a problem hiding this comment.
Is this right? Don't you need to go through all the slices?
|
|
||
| impl Encoder<BytesView, BytesView> for ZstdCodec { | ||
| fn encode(&self, value: &BytesView) -> Result<BytesView, Error> { | ||
| let compressed = zstd::encode_all(value.clone(), self.level).map_err(Error::from_source)?; |
There was a problem hiding this comment.
Can we decompress into a BytesBuf to avoid a copy?
|
|
||
| impl Codec<BytesView, BytesView> for AesGcmCodec { | ||
| fn decode(&self, value: &BytesView) -> Result<BytesView, Error> { | ||
| let slice = value.first_slice(); |
There was a problem hiding this comment.
I think this isn't right, you need to go through all the slices, right?
No description provided.