Skip to content

perf(cas): LRU eviction instead of arbitrary HashMap iter order#167

Merged
XciD merged 1 commit into
mainfrom
perf/cas-lru-eviction
May 20, 2026
Merged

perf(cas): LRU eviction instead of arbitrary HashMap iter order#167
XciD merged 1 commit into
mainfrom
perf/cas-lru-eviction

Conversation

@XciD

@XciD XciD commented May 20, 2026

Copy link
Copy Markdown
Member

Summary

When the reconstruction cache hits MAX_CACHE_ENTRIES and all remaining entries are full plans (range entries already purged in pass 1), the previous code picked the eviction victim via:

```rust
cache.keys().next().copied()
```

HashMap::keys() returns entries in hash-bucket order, which is unrelated to access patterns. A plan we just fetched and are actively reading from can be the first one returned, while an entry that's been cold for an hour survives.

This PR adds a last_accessed: Instant field to CacheEntry, bumped on every cache hit. The overflow path picks the entry with the oldest last_accessed. Hot plans stay warm even when they were inserted long ago.

Why not reuse inserted_at?

inserted_at is the time the CAS response arrived. Its only consumer is the TTL check — presigned URLs inside terms expire ~1h after the server issued them, not after we last touched the entry. Bumping inserted_at on hit would let stale URLs slip past the TTL.

Tests

New cache_evicts_lru_full_plan_on_overflow:

  1. Fill cache 0..N with full plans.
  2. Re-fetch entry 0 → bumps its last_accessed.
  3. Insert one more entry → overflow.
  4. Assert entry 0 still cached (no extra CAS call).
  5. Assert entry 1 (true LRU) was evicted (extra CAS call on re-fetch).

All 328 lib + 338 nfs tests pass, clippy clean.

When the reconstruction cache overflows and only full plans remain,
the previous code evicted via cache.keys().next() — HashMap iteration
order, which can drop a hot recently-fetched plan and keep a cold one.

Add last_accessed: Instant to CacheEntry, bumped on every cache hit.
Eviction picks min(last_accessed). inserted_at stays for TTL (presigned
URL expiry, which is relative to issue time, not access time).

Adds a test (cache_evicts_lru_full_plan_on_overflow) that fills the
cache, bumps entry 0, overflows, and asserts entry 0 survives while
entry 1 (true LRU) is evicted.
@github-actions

Copy link
Copy Markdown
Contributor

POSIX Compliance (pjdfstest)

============================================================
  pjdfstest POSIX Compliance Results
------------------------------------------------------------
  Files: 130/130 passed    Tests: 832 total (0 subtests failed)
  Result: PASS
------------------------------------------------------------
  Category               Passed    Total   Status
  -------------------- -------- -------- --------
  chflags                     5        5       OK
  chmod                       8        8       OK
  chown                       6        6       OK
  ftruncate                  13       13       OK
  granular                    5        5       OK
  mkdir                       9        9       OK
  open                       19       19       OK
  posix_fallocate             1        1       OK
  rename                     10       10       OK
  rmdir                      11       11       OK
  symlink                    10       10       OK
  truncate                   13       13       OK
  unlink                     11       11       OK
  utimensat                   9        9       OK
============================================================

@github-actions

Copy link
Copy Markdown
Contributor

Benchmark Results

============================================================
  Benchmark — 50MB
------------------------------------------------------------
  Metric                                 FUSE          NFS
  ------------------------------ ------------ ------------
  Sequential read                    268.7 MB/s     210.9 MB/s
  Sequential re-read                2263.3 MB/s    2444.3 MB/s
  Range read (1MB@25MB)                0.5 ms         0.2 ms
  Random reads (100x4KB avg)           0.0 ms         0.0 ms
  Sequential write (FUSE)           1308.7 MB/s
  Close latency (CAS+Hub)            0.101 s
  Write end-to-end                   358.3 MB/s
  Dedup write                       1663.9 MB/s
  Dedup close latency                0.113 s
  Dedup end-to-end                   348.8 MB/s
============================================================
============================================================
  Benchmark — 200MB
------------------------------------------------------------
  Metric                                 FUSE          NFS
  ------------------------------ ------------ ------------
  Sequential read                    757.5 MB/s     771.2 MB/s
  Sequential re-read                2260.6 MB/s    2458.7 MB/s
  Range read (1MB@25MB)                0.2 ms         0.2 ms
  Random reads (100x4KB avg)           0.0 ms         0.0 ms
  Sequential write (FUSE)           1593.3 MB/s
  Close latency (CAS+Hub)            0.183 s
  Write end-to-end                   648.4 MB/s
  Dedup write                       1620.3 MB/s
  Dedup close latency                0.115 s
  Dedup end-to-end                   837.4 MB/s
============================================================
============================================================
  Benchmark — 500MB
------------------------------------------------------------
  Metric                                 FUSE          NFS
  ------------------------------ ------------ ------------
  Sequential read                   1232.5 MB/s    1047.5 MB/s
  Sequential re-read                2260.5 MB/s    2508.2 MB/s
  Range read (1MB@25MB)                0.2 ms         0.2 ms
  Random reads (100x4KB avg)           0.0 ms         0.0 ms
  Sequential write (FUSE)           1512.3 MB/s
  Close latency (CAS+Hub)            0.330 s
  Write end-to-end                   757.2 MB/s
  Dedup write                       1540.1 MB/s
  Dedup close latency                0.154 s
  Dedup end-to-end                  1044.6 MB/s
============================================================
============================================================
  fio Benchmark Results
------------------------------------------------------------
  Job                        FUSE MB/s   NFS MB/s  FUSE IOPS   NFS IOPS
  ------------------------- ---------- ---------- ---------- ----------
  seq-read-100M                  473.9      531.9                      
  seq-reread-100M               2564.1      134.4                      
  rand-read-4k-100M                0.1        0.1         16         18
  seq-read-5x10M                 617.3      806.5                      
  rand-read-10x1M                  0.1        0.1         36         35
  Random Read Latency           FUSE avg      NFS avg
  ------------------------- ------------ ------------
  rand-read-4k-100M           63289.7 us   54491.4 us
  rand-read-10x1M             27946.4 us   28828.3 us
============================================================

@XciD
XciD marked this pull request as ready for review May 20, 2026 19:05
@XciD
XciD merged commit f5623d1 into main May 20, 2026
6 checks passed
@XciD
XciD deleted the perf/cas-lru-eviction branch May 20, 2026 19:07
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