mirror of
https://github.com/sbt/sbt.git
synced 2026-09-03 08:34:34 +02:00
**Problem**
InMemoryCacheStore.CacheStoreImpl.read returns cacheStore.read[T]() on a miss
without putting the value in the cache, so the only thing that ever fills the
cache is write. A task whose stored output is already up to date never writes,
so it re-reads and re-deserialises that output on every invocation for the life of
the server, and the cache can never warm up for it.
update is the costly instance: transitiveUpdate runs it once per project in
the dependency closure, and every call goes through
UpdateReportPersistence.readFrom.
**Solution**
Put the value in the cache after a successful disk read. The keying is unchanged
-- an entry is still (path, lastModified), read re-stats the file on every
call, and write still invalidates before rewriting -- so a populated read is
sound for the same reason a populated write is.
Measured on an 81-module workspace, no-op build, real output files (no
cache-restored symlinks), exportJars=true, alternating the two builds over 8
runs each:
update task 398ms across 82 projects -> 30ms across 29
task total 4797ms -> 4623ms
wall (median) 5.15s -> 4.97s
This makes maximumWeight bind where it could not before: reads now admit
entries, so a build whose working set exceeds sbt.file.cache.size will evict
rather than never cache at all. That is the intended behaviour of a bounded cache.
Generated-by: Claude Opus 5