Repository navigation
cache/disk: persist the index's read history across restarts so a rolled L1 does not report every resident byte as unknown - #72
Conversation
…tate, so boot-scanned entries stop inflating evicted_never_read after a restart Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
On shutdown, after the servers have stopped, write each known entry's access state and last read to <dir>/access-state.v1; the next boot scan consumes the file so pre-existing blobs come back as new/touched with their real recency instead of boot-scanned (unknown), and the file is deleted so a stale copy can never be replayed. Blobs created after the file was written, and anything without a record, stay on file timestamps. Steady state is untouched; the cost is one parallel walk of the index at shutdown (~0.3 s / 2M entries, 21 B per entry) and one map load at boot. Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
|
I'll fix CI failures and address comments from users with write access. I'll skip comments containing "(aside)".
|
|
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 8ed21f2. Configure here.
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>



Summary
After a restart the boot scan only has file timestamps, and on our noatime mounts those cannot tell a blob read a minute ago from one nobody ever asked for, so every pre-existing entry re-entered the index as
itemStateBootScannedand the tenant census reported its bytes as unknown (36 h after the last roll, 93% of prod resident bytes were still unknown). This PR carries the in-memory read history over the restart.Stacked on #71 (same access-state semantics); base switches to
patchsetwhen that merges.Shutdown —
main.gocalls the newdisk.Cache.SaveAccessState()right afterservers.Wait()(gRPC/HTTP stopped, index final). It walks the LRU once underc.muand writes<dir>/access-state.v1:Only
itemStateNew/itemStateTouchedentries are written (an untouched boot-scanned entry would come back boot-scanned anyway); 21 B/entry. Key hashing dominates, so the walk feeds batches to one encoder per CPU and a single writer; temp file + fsync + rename. Measured 0.31 s / 40 MB for 2M entries on 8 cores → ~5 s / 670 MB for the 31.7M-entry us-west members, inside the unit'sTimeoutStopSec=30.Boot —
loadExistingFiles→consumeAccessState(dir)reads the file into amap[[16]byte]{lastAccess,state}, deletes it (consumed exactly once; a later crash cannot replay a stale copy), andscanDirapplies it per file:lookuprefuses records for blobs whose mtime is afterwrittenAt(created after the file; not about this blob) and clampslastAccess >= addedAt. Missing/garbage/truncated file → logged, removed, scan proceeds on timestamps as today. The scan's "unexpected file in cache root" check skips the two file names.Steady state is untouched: no per-request work, no new goroutine, no new config. The new code runs only at the two ends of the process.
Tests: real
diskCacherestart (read blob → touched with its last read, unread → new, blob written after the save → boot-scanned; census unknown/never-read match; LRU front is the read blob), corrupt file + leftover temp file ignored and removed,lookupguards.Link to Devin session: https://app.devin.ai/sessions/df6c6655558146b9bafe936b062e49e7
Open in Devin Desktop: https://app.devin.ai/desktop/session/df6c6655558146b9bafe936b062e49e7?variant=devin
Requested by: @shreyas-blacksmith
Need help on this PR? Tag
@codesmith-botwith what you need.