Skip to content

Honor the RamCache copy flag in LRU and S3-FIFO#13380

Draft
phongn wants to merge 1 commit into
apache:masterfrom
phongn:fix-ramcache-copy-honor
Draft

Honor the RamCache copy flag in LRU and S3-FIFO#13380
phongn wants to merge 1 commit into
apache:masterfrom
phongn:fix-ramcache-copy-honor

Conversation

@phongn

@phongn phongn commented Jul 14, 2026

Copy link
Copy Markdown
Collaborator

Summary

put(..., copy = true) is the caller's contract that buffers may not be shared between the cache and the caller in either direction. CacheVC requests it whenever RAM cache compression is configured (http_copy_hdr, CacheVC.cc), because it unmarshals HTTP headers in place in its own buffer after the put, and again on every RAM-cache hit.

The LRU and S3-FIFO RAM caches ignored the flag and shared buffers both ways. With proxy.config.cache.ram_cache.compress set and algorithm = 1 (the default) or 2:

  1. put stores the caller's buffer, and CacheVC's subsequent in-place unmarshal silently mutates the cached bytes;
  2. the next HTTP RAM hit hands that buffer back and unmarshals it again — HTTPInfo::unmarshal fails on the already-unmarshalled header (ink_assert in debug builds; broken HTTP RAM hits in release).

The http_copy_hdr gate keys off the compress config only, never the algorithm, so this is reachable purely by configuration today. This correctness fix stands alone (compression support for LRU is proposed separately) and is a backport candidate.

Changes

  • The copy contract is implemented once, as RamCache::copy_data_in() / RamCache::copy_data_out() on the base class; CLFUS's three pre-existing open-coded copies of the same logic are replaced with calls to them (no behavior change).
  • RamCacheLRU and RamCacheS3FIFO now honor copy the way CLFUS always has: copy on put, copy on get, and a copy = true put refreshes a resident entry that may still be sharing a caller's buffer from before a configuration change.
  • copy = false behavior is unchanged: zero-copy sharing, pinned by a test.
  • New Catch2 test (test_RamCacheCopy) pins the contract for all three policies: caller mutation after put, caller mutation of the returned buffer, the resident-refresh transition, and copy=false sharing. Before the fix, the three copy cases fail for LRU and S3-FIFO; CLFUS passes.

Notes for reviewers

  • The copied-in buffer is an exact-size xmalloc allocation, so block_size() recovers the data length and neither entry struct needs a new length field.
  • S3-FIFO's ghost-readmit and stale-auxkey paths go through the single insert site, so they inherit the copy handling.
  • The test keeps constructed caches reachable rather than destroying them: the policies have no destructors (entries are pool-allocated), so destroying a cache object would strand its entries under leak checkers.

CacheVC requests copy semantics (put copy=true) whenever RAM cache
compression is configured, because it unmarshals HTTP headers in place
in its own buffer after the put and again on every RAM hit. LRU and
S3-FIFO ignored the flag and shared buffers with the caller in both
directions, so with proxy.config.cache.ram_cache.compress enabled and
algorithm=1 (the default) or 2, the cached bytes were mutated after
insertion and every subsequent HTTP RAM hit re-unmarshalled an
already-unmarshalled header (assert in debug builds, header unmarshal
failure in release). Copy entries now copy on put, copy on get, and a
copy=true put refreshes a resident entry that may still be sharing a
caller's buffer, matching CLFUS.

This is a backport candidate for all release branches.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@phongn phongn force-pushed the fix-ramcache-copy-honor branch from 514d888 to 1b53b1e Compare July 14, 2026 16:24
@phongn

phongn commented Jul 14, 2026

Copy link
Copy Markdown
Collaborator Author

Tracking issue: #13381

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