models page: stop paying for the scan on every open (v0.2.44)
The page was slow to open on a large configuration. Measuring first, because the obvious suspect was wrong: the disk scans are not the problem — scan_cache_dir over 59 repos is 0.42s and the GGUF walk 0.53s, and _scan_caches() is 0.08s once warm. The server-side stale-while-revalidate cache was already doing its job. What it was, in order of cost: THE CACHE DIED WITH THE PROCESS. It starts empty in every new one, and _cached() recomputes synchronously when it has nothing — so the first read after a restart pays the whole scan while someone sits looking at a spinner. Measured on the real configuration: 3.69s cold, 0.001s once the previous result is read back from disk. It is now persisted (atomically, under <config>/cache/) and seeded on first use. The stored SIGNATURE is deliberately not restored: it is a list of mtimes, and trusting it across a restart would serve a possibly hours-old list without revalidating. Leaving it unset means the stored result is returned instantly AND a refresh starts behind it. A missing, truncated or future-version file just falls back to scanning. THE PAGE WAITED ON ITSELF. First paint ran refreshLoadedStatus, then refreshQuantJobs, then the list — three round trips in a chain — while _loadEngineInfo did two more in another chain and then rendered the list a SECOND time. Inside the list load, /admin/api/cached-models and /admin/api/models also went out one after the other. Now: the two status reads go out together, the two list reads go out together, and concurrent loadCachedModels() calls collapse into one in-flight load, so first paint does one fetch and one render instead of three. A call made after it settles still refetches, which every post-action refresh relies on. IT PAINTED NOTHING UNTIL ALL OF THAT FINISHED. The last response is kept in sessionStorage and painted before any network call, then revalidated in place. It is a listing of what is on disk, so seconds of staleness cost nothing — and a quota error or private mode just means the page behaves as it does today. One thing the tests had to be corrected on: the first version asserted the background rescan had not run, which failed because _cached() starts the refresh thread and then reads state["data"] — with a trivial scan function the thread wins the race. That race is harmless (fresher data), and it was the assertion that was wrong: the property worth holding is that the CALLER is not blocked, so the test now uses a slow scan and asserts the read returns in under 0.2s with the stored value, and that the revalidation really did start. 112 tests. Co-Authored-By:Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0182DhR1eFNPQyGmDrpHbedY
Showing
Please
register
or
sign in
to comment