Skip to content

test(perf): extend peak-RSS suite to backend-inclusive File read paths (LAB-350) - #243

Merged
27Bslash6 merged 4 commits into
mainfrom
lab-350-backend-inclusive-peak-rss
Jul 26, 2026
Merged

test(perf): extend peak-RSS suite to backend-inclusive File read paths (LAB-350)#243
27Bslash6 merged 4 commits into
mainfrom
lab-350-backend-inclusive-peak-rss

Conversation

@27Bslash6

@27Bslash6 27Bslash6 commented Jul 24, 2026

Copy link
Copy Markdown
Contributor

Resolves LAB-350 · Addresses the File-backend portion of #169

Problem

The peak-RSS / OOM-fix suite (tests/performance/test_large_object_memory.py) measured only the serializer layer, with the read input allocated before measurement started. Backend read-side copies — FileBackend.get()'s os.read + file_data[14:] slice — were structurally invisible, so a regression reintroducing a full-payload read-side copy passed the suite while the headline low-read-memory claim quietly broke.

What this adds

Three backend-inclusive guards that run the read end-to-end through the real stack (FileBackend → StandardCacheHandler → CacheOperationHandler → unwrap → deserialize), clearly separated from the serializer-only tests so the headline claim maps to the right number:

Test Metric Healthy Regressed (fault-injected) Bound
test_file_backend_read_python_allocations_bounded (mmap fast path, #171) tracemalloc ~1.05x logical ~1.9x (mmap→os.read fallback) < 1.6x
test_file_backend_bytes_read_python_allocations_bounded (default serializer) tracemalloc ~5.0x payload +1x per added copy < 5.7x
test_file_backend_end_to_end_read_peak_rss_bounded subprocess peak RSS (VmHWM) ~2.0x payload 3.0x (added full-payload copy) < 2.6x

Every bound was verified by fault injection in both directions: the healthy path passes with margin, and a simulated full-payload read-side copy fails. The two metrics are deliberately complementary — the mmap→os.read fallback swaps file-backed pages for anonymous heap at the same ~2x RSS total (tracemalloc catches it at 1.9x), while Arrow-pool/Rust/view-coercion copies are invisible to tracemalloc (RSS catches them at 3.0x).

Surfaced (not fixed here — out of scope per the ticket)

The non-mmap default-serializer read costs ~5x payload on the Python heap: os.read + header slice (the #169 copies), bytes(data) re-coercion of the envelope's zero-copy memoryview in StandardSerializer.deserialize (Rust retrieve needs bytes), the decompressed msgpack document, and the unpacked output. The new test pins this at its current level; reducing it is a follow-up ticket.

Measurement-integrity fix

Subprocess peak-RSS now reads VmHWM from /proc/self/status instead of resource.ru_maxrss: ru_maxrss survives fork+exec on Linux (it lives in the signal struct, not the mm replaced by exec), so a child spawned from a fat pytest process inherits the parent's watermark and real regressions hide under it — observed as a 1997 MiB inherited base with a 0.00x measured read cost. The pre-existing ByteStorage store guard had the same silent false-pass fragility and got the same fix (now also explicitly Linux-only; on macOS ru_maxrss is bytes, not KiB, so it was already de-facto Linux-only).

CI budget

All new tests are performance and slow (the memory-invariant CI job). Full selection: 11 passed in ~11s locally (~7s added).

Docs

tests/performance/README.md now catalogs the memory suite and the two measurement scopes; the module docstring explains the serializer-only vs backend-inclusive split. No public docs (docs.cachekit.io, protocol) state measurable read-RSS numbers, so nothing else needed changing.

Summary by CodeRabbit

  • Documentation

    • Updated performance testing documentation layout to add memory regression guard guidance and expanded measurement utilities.
  • Tests

    • Added bounded-regression tests covering File backend read paths, including mmap and non-mmap bytes reads with tight peak Python-allocation assertions.
    • Added an end-to-end peak RSS check for large payload reads with Linux-only VmHWM measurement logic.
    • Reworked an existing memory test to improve Linux memory measurement reliability and fail fast on subprocess hangs.

…s (LAB-350)

The memory suite measured only the serializer layer with the read input
pre-allocated, so backend read-side copies (File os.read + header slice)
were structurally invisible — a regression reintroducing a full-payload
read-side copy passed the suite (cachekit-py#169).

Add three backend-inclusive guards running the read END-TO-END through
FileBackend -> StandardCacheHandler -> CacheOperationHandler:

- mmap fast path (tracemalloc): ~1.1x logical on the Python heap; the
  os.read fallback regression measures ~1.9x (fault-injected) and trips
  the 1.6x bound.
- non-mmap default-serializer path (tracemalloc): pins the measured ~5x
  cost (os.read + slice + bytes() coercion of the zero-copy view +
  envelope decode + output) so one MORE full-payload copy fails;
  reducing the 5x is follow-up work per #169's scope.
- end-to-end peak RSS (subprocess): ~2x payload (mapped pages + df);
  catches full-payload copies tracemalloc cannot see (Arrow pool, Rust,
  view coercion — fault-injected at 3.0x vs the 2.6x bound).

Subprocess peak-RSS now reads VmHWM from /proc/self/status instead of
ru_maxrss: ru_maxrss survives fork+exec on Linux, so a child spawned
from a fat pytest process inherits the parent's watermark and real
regressions hide under it (observed: 2GB inherited base, 0.00x measured
cost). Applied to the existing ByteStorage store guard too, which had
the same silent false-pass fragility.
@27Bslash6 27Bslash6 closed this Jul 24, 2026
@27Bslash6 27Bslash6 changed the title LAB-350: extend peak-RSS test suite to backend-inclusive File read paths test(perf): extend peak-RSS suite to backend-inclusive File read paths (LAB-350) Jul 24, 2026
@27Bslash6 27Bslash6 reopened this Jul 25, 2026
@codecov

codecov Bot commented Jul 25, 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!

…B-350)

Panel finding (fault-verified): to_pandas(self_destruct, split_blocks)
returns numpy VIEWS over Arrow-pool buffers, so the healthy mmap-path
read allocates ~0.001x logical on the Python heap — not the ~1.1x the
docstring claimed. Under the old 1.6x bound a reintroduced bytes()
coercion of the zero-copy view (measures ~0.96x) silently PASSED.
Tighten the bound to 0.5x: both regression classes now trip it
(coercion ~1.0x, os.read fallback ~1.9x), and a future pyarrow that
copies in to_pandas fails loud for conscious recalibration.

Also from the panel: module docstring no longer claims the suite
avoids process RSS (three tests are RSS-based since this PR); the
two-scopes explanation lives once (section banner) with the module
docstring pointing at it; README catalog entry no longer duplicates
multipliers that live in docstrings; RSS-test baseline folded into
the assert message instead of a capture-swallowed print.
@coderabbitai

coderabbitai Bot commented Jul 25, 2026

Copy link
Copy Markdown

Review Change Stack

Important

Review skipped

No new commits to review since the last review.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: f580280a-c8b2-4752-b544-6af5fa93af24

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Walkthrough

Performance documentation and memory regression tests now use Linux VmHWM measurements and cover Python allocation and peak-RSS bounds across serializer-only and File backend-inclusive read paths.

Changes

Memory Regression Guards

Layer / File(s) Summary
Deterministic peak-memory measurement
tests/performance/README.md, tests/performance/test_large_object_memory.py
Documents serializer-only and backend-inclusive scopes, and replaces inherited ru_maxrss measurements with Linux VmHWM sampling with subprocess timeouts.
Backend-inclusive allocation paths
tests/performance/test_large_object_memory.py
Builds reusable File backend read wiring and bounds Python allocations for mmap and bytes-backed reads.
End-to-end RSS regression bound
tests/performance/test_large_object_memory.py
Measures the separate-subprocess RSS delta for reading a large incompressible Arrow payload and enforces a payload-scaled ceiling.

Estimated code review effort: 4 (Complex) | ~40 minutes

Possibly related issues

  • cachekit-io/cachekit-py issue 169 — Adds File backend-inclusive allocation and peak-RSS coverage for previously unmeasured read-side copies.

Possibly related PRs

  • cachekit-io/cachekit-py#187 — Introduces the File backend zero-copy and mmap fast-path behaviour exercised by these memory regression tests.
  • cachekit-io/cachekit-py#152 — Introduces the serialization and Arrow/DataFrame paths covered by the updated large-object memory tests.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Description check ⚠️ Warning The PR description is informative, but it misses the required template sections and checkbox details like Type of Change, Testing, and Backward Compatibility. Reformat the PR body to match the template and add the missing sections: Description, Motivation, Type of Change, Security Checklist, Testing, and Backward Compatibility.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly matches the main change: extending the performance suite to cover backend-inclusive File read paths.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch lab-350-backend-inclusive-peak-rss

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

@kodus-27b

This comment has been minimized.

Comment thread tests/performance/test_large_object_memory.py
Comment thread tests/performance/test_large_object_memory.py
@27Bslash6

Copy link
Copy Markdown
Contributor Author

@kody start-review

@27Bslash6

Copy link
Copy Markdown
Contributor Author

@kody start-review

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@tests/performance/test_large_object_memory.py`:
- Around line 351-356: Add explicit timeouts to both subprocess.run calls in the
write/read execution flow, using an appropriate bounded duration so wedged child
processes fail the test instead of hanging CI. Preserve the existing
trusted-command S603 suppressions and return-code assertions.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: dbfddb52-d538-4393-88f3-c15715da6196

📥 Commits

Reviewing files that changed from the base of the PR and between e67db58 and 3ffccbc.

📒 Files selected for processing (2)
  • tests/performance/README.md
  • tests/performance/test_large_object_memory.py

Comment thread tests/performance/test_large_object_memory.py
CodeRabbit: subprocess.run without timeout lets a wedged child hang the
CI job to its ceiling. All three measurement subprocesses (ByteStorage
store guard + the RSS write/read pair) now share a 300s timeout —
generous for slow runners, and a hang fails the test loudly via
TimeoutExpired instead of stalling the run.

Co-authored-by: multica-agent <github@multica.ai>
@27Bslash6

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 26, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@kodus-27b

kodus-27b Bot commented Jul 26, 2026

Copy link
Copy Markdown

Kody Review Complete

Great news! 🎉
No issues were found that match your current review configurations.

Keep up the excellent work! 🚀

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.

@27Bslash6
27Bslash6 merged commit 41986a7 into main Jul 26, 2026
35 checks passed
@27Bslash6
27Bslash6 deleted the lab-350-backend-inclusive-peak-rss branch July 26, 2026 13:11
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