Skip to content

fix(serializers): type ByteStorage.retrieve failures and collapse duplicated columnar decode (LAB-2736) - #287

Open
27Bslash6 wants to merge 2 commits into
mainfrom
lab-2736-byte-storage-error-typing
Open

27Bslash6 wants to merge 2 commits into
mainfrom
lab-2736-byte-storage-error-typing

Conversation

@27Bslash6

@27Bslash6 27Bslash6 commented Sep 13, 2026

Copy link
Copy Markdown
Contributor

Summary

This PR introduces a distinct exception type for ByteStorage.retrieve() verification failures and consolidates duplicated columnar (DataFrame/Series) decode logic in the AutoSerializer.

Changes

New EnvelopeIntegrityError exception (Rust layer)

  • Adds a new EnvelopeIntegrityError exception (subclass of ValueError) in the Rust bindings, registered on the Python module with a corrected __module__ so it remains picklable across processes.
  • Introduces a retrieve_error_to_py mapping function that translates ByteStorageError variants into the Python exception taxonomy:
    • DeserializationFailed (bytes that were never a ByteStorage envelope) → plain ValueError, preserving the fall-through signal that callers rely on.
    • All other variants (checksum mismatch, decompression bomb/failure, size mismatch, oversized input) → EnvelopeIntegrityError, which must fail closed.

Previously, both "not an envelope" and genuine corruption raised an indistinguishable ValueError, so the deserializer could not reliably tell them apart.

AutoSerializer.deserialize behavior

  • Now catches EnvelopeIntegrityError specifically and re-raises it as SerializationError (fail-closed), rather than falling through to a plain-msgpack/NumPy re-parse of corrupt bytes.
  • Bytes that are genuinely "not an envelope" (e.g. written with integrity checking off) still fall through to the Python-only decode paths.

Collapsed duplicated columnar decode paths (LAB-2736)

  • Merges the separate dataframe and series branches so that, when integrity checking is on, they share a single Rust-envelope retrieve + _decode_columnar path instead of a duplicated copy.
  • Adds a safeguard: when metadata indicates a dataframe/series type but no envelope was used (integrity off) or the envelope wasn't recognized (cross-config read), the bytes are still routed through _decode_columnar to reconstruct the proper DataFrame/Series — preventing a regression where the raw wire dict would be returned instead.
  • Simplifies _deserialize_dataframe and _deserialize_series to accept already-unpacked documents, removing redundant internal msgpack unpacking.

Tests

  • Adds a cross-config regression test (written with integrity off, read with integrity on) verifying columnar reconstruction.
  • Adds tests confirming corrupted payloads surface an "envelope verification" SerializationError, while plain msgpack written with integrity off still falls through and decodes correctly.
  • Updates existing columnar fallback tests to exercise the full serialize/deserialize round-trip (forcing the msgpack-columnar path) instead of calling internal helpers directly.

…licated columnar decode (LAB-2736)

ByteStorage.retrieve() flattened every corruption case (checksum mismatch,
decompression bomb/failure, size mismatch) into the same PyValueError as
"not a ByteStorage envelope at all", so AutoSerializer.deserialize() could
not tell a genuinely corrupted cache entry from bytes that were legitimately
written without an envelope (integrity checking off) - a checksum mismatch
either fell through to a confusing "not decodable" error or, on the
DataFrame/Series path, was duplicated into two near-identical retrieve+decode
blocks with their own ad hoc error text.

Add EnvelopeIntegrityError (rust/src/python_bindings.rs), a ValueError
subclass raised for every ByteStorageError variant except
DeserializationFailed (which stays a plain ValueError - the fall-through
signal deserialize() depends on). AutoSerializer.deserialize() catches it
specifically and re-raises as SerializationError without falling through.
Collapse the DataFrame/Series metadata pre-branch and the verified-envelope
branch onto the single _decode_columnar path, and drop the dead
isinstance(data, dict) branches in _deserialize_dataframe/_deserialize_series
now that no caller passes raw bytes.

Expert-panel review caught a regression the collapse introduced: an entry
written with integrity off and read by an integrity-on reader (same
metadata routing to dataframe/series) fell through to the generic msgpack
fallback and returned the raw wire dict instead of failing closed or
reconstructing - fixed by routing that fallback through _decode_columnar
too, with a regression test.
@coderabbitai

coderabbitai Bot commented Sep 13, 2026

Copy link
Copy Markdown

Warning

Review limit reached

Next included review available in 4 minutes.

Check out review usage here.

View limit details

Limit details: You’ve used all 2 included reviews currently available. Your 86 included PR review attempts over the past 7 days set your current allowance at 2 reviews per hour.

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

Learn how review limits work.

Review configuration:

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Team

Run ID: 395df4ce-50d9-4a07-87a6-736a3d140c11

📥 Commits

Reviewing files that changed from the base of the PR and between 284fa7e and 0eab9eb.

📒 Files selected for processing (5)
  • rust/src/lib.rs
  • rust/src/python_bindings.rs
  • src/cachekit/serializers/auto_serializer.py
  • tests/unit/test_auto_serializer_mutation_and_corruption.py
  • tests/unit/test_auto_serializer_new_types.py

Comment @coderabbitai help to get the list of available commands.

@kodus-27b

kodus-27b Bot commented Sep 13, 2026

Copy link
Copy Markdown

Code Review Completed! 🔥

The code review was successfully completed based on your current configurations.

Kody Guide: Usage and Configuration
Interacting with Kody
  • Request a Review: Ask Kody to review your PR manually by adding a comment with the @kody start-review command at the root of your PR.

  • Validate Business Logic: Ask Kody to validate your code against business rules by adding a comment with the @kody -v business-logic command.

  • Provide Feedback: Help Kody learn and improve by reacting to its comments with a 👍 for helpful suggestions or a 👎 if improvements are needed.

Current Kody Configuration
Review Options

The following review options are enabled or disabled:

Options Enabled
Bug
Performance
Security
Business Logic

Access your configuration settings here.

Comment thread tests/unit/test_auto_serializer_mutation_and_corruption.py
@codecov

codecov Bot commented Sep 13, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ All tests successful. No failed tests found.

📢 Thoughts on this report? Let us know!

@27Bslash6

Copy link
Copy Markdown
Contributor Author

@kody start-review

kodus-27b[bot]
kodus-27b Bot previously approved these changes Sep 13, 2026
Resolve the LAB-3131 (#289) overlap in the columnar decode path:

- src/cachekit/serializers/auto_serializer.py: both sides retired the dead isinstance(data, dict) preamble in _deserialize_dataframe/_deserialize_series; keep main's parameter naming and docstrings. The PR's deserialize() collapse and EnvelopeIntegrityError handling are unchanged.

- tests/unit/test_auto_serializer_new_types.py: both sides adapted the four TestColumnarFallbackExtensionDtypes tests to the decoded-document contract; keep the PR's public serialize()/deserialize() round-trip, which routes through main's _decode_columnar.
@27Bslash6

Copy link
Copy Markdown
Contributor Author

Merged main (284fa7e) into this branch as 0eab9eb. Resolved conflicts in src/cachekit/serializers/auto_serializer.py (kept the _deserialize_dataframe / _deserialize_series signatures and docstrings from #289; this PR's deserialize() changes are intact) and tests/unit/test_auto_serializer_new_types.py (kept this PR's public serialize() / deserialize() round-trip tests). Auto-rebased onto main; CI will re-run.

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.

1 participant