Skip to content

[feat] Add distributed cache trait, type transformation, serialization, compression, encryption#346

Draft
schgoo wants to merge 17 commits intomainfrom
u/schgoo/distributedcache
Draft

[feat] Add distributed cache trait, type transformation, serialization, compression, encryption#346
schgoo wants to merge 17 commits intomainfrom
u/schgoo/distributedcache

Conversation

@schgoo
Copy link
Copy Markdown
Collaborator

@schgoo schgoo commented Mar 27, 2026

No description provided.

schgoo and others added 17 commits March 25, 2026 06:39
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>
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>
@github-actions
Copy link
Copy Markdown

⚠️ Breaking Changes Detected


--- failure enum_variant_added: enum variant added on exhaustive enum ---

Description:
A publicly-visible enum without #[non_exhaustive] has a new variant.
        ref: https://doc.rust-lang.org/cargo/reference/semver.html#enum-variant-new
       impl: https://github.com/obi1kenobi/cargo-semver-checks/tree/v0.46.0/src/lints/enum_variant_added.ron

Failed in:
  variant CacheOp:Len in /home/runner/work/oxidizer/oxidizer/crates/cachet_tier/src/testing.rs:33

If the breaking changes are intentional then everything is fine - this message is merely informative.

Remember to apply a version number bump with the correct severity when publishing a version with breaking changes (1.x.x -> 2.x.x or 0.1.x -> 0.2.x).

impl ZstdCodec {
/// Creates a new Zstd codec with the given compression level.
///
/// Levels typically range from 1 (fastest) to 22 (best compression).
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Should be a newtype constraining the value from 1 to 22.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

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.

Copy link
Copy Markdown
Collaborator Author

@schgoo schgoo Apr 8, 2026

Choose a reason for hiding this comment

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

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();
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

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())
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Does this do a full data copy?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

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:

  1. Add something like BytesView::as_contiguous() -> Cow<[u8]> that returns the slice if the BytesView is single-span, or copies when multi-span
  2. 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)?;
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

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)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

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)?;
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

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();
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I think this isn't right, you need to go through all the slices, right?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants