fix(http): bound the on-disk response cache - #180
Merged
Conversation
_DiskCache.put() only ever wrote a new file; nothing pruned it, so a key fetched once and never again stayed on disk forever. The in-memory _ResponseCache already has an LRU cap for exactly this reason (cache_max_size); the disk tier had no equivalent. put() now prunes after every write: sweep any expired entry first (so one that is never re-requested does not linger, unlike get()'s lazy expiry which only catches re-requested keys), then, if the directory still exceeds max_size, delete the oldest files by mtime until it does not. Both take a per-call override, mirroring _ResponseCache's own put(key, resp, max_size=None) pattern. Adds ScraperConfig.cache_dir_max_size (default 512, matching cache_max_size) and threads it through both the sync and async _cache_put(), which also now pass cache_ttl so the sweep has something to check against. Fixes mldsveda#166
…d-166 # Conflicts: # tests/test_core/test_http.py
Collaborator
|
Thanks @afonsojanu, clean fix for #166. Right call: get() only reaps re-requested keys, so the disk tier needed prune-on-write. Sweep-then-trim, per-call max_size, best-effort, all match the rest of the cache. Resolved the conflict for you (kept your tests + the new TestObservabilityHooks) and pushed. 583 passed, ruff clean. Merging. |
vedaant00
added a commit
to vedaant00/PyScrappy
that referenced
this pull request
Aug 27, 2026
… cache; bump to 1.6.1 Two features merged after the 1.6.0 release cut had no changelog entry: - mldsveda#174 (mldsveda#161): ScrapeResult.to_parquet()/to_excel() + save() dispatch + the pyscrappy[parquet]/[excel] extras. - mldsveda#180 (mldsveda#166): the on-disk response cache is now pruned to cache_dir_max_size. Add both to CHANGELOG (1.6.1), document the exporters + extras and the disk-cache bound in the README, and bump the version across all four sites.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #166
Summary
_DiskCache.put()only ever wrote a new file — nothing pruned it — so a key fetched once and never again (e.g. a large crawl, or a long-running MCP server withcache_dirset) stayed on disk forever. The in-memory_ResponseCachealready has an LRU cap for exactly this reason (cache_max_size); the disk tier had no equivalent.Fix
put()now prunes after every write:get()'s lazy expiry, which only reaps a key that's actually asked for again.max_size, delete the oldest files by mtime until it doesn't.Both
_DiskCache.__init__andput()take the cap, mirroring_ResponseCache's existingput(key, resp, max_size=None)per-call-override pattern. AddedScraperConfig.cache_dir_max_size(default 512, matchingcache_max_size) and threaded it through both the sync and async_cache_put(), which also now passcache_ttlthrough so the sweep has something to check against.Testing
Four new tests in
TestHttpClientCaching, covering the issue's own three required cases plus the per-call override:test_disk_cache_evicts_oldest_past_max_size— put 20, cap 5 → exactly 5 remain, the 5 most recently written.test_disk_cache_sweeps_an_expired_entry_even_if_never_reread— an entry that expires between twoput()calls to different keys is gone after the second, without ever beingget()'d.test_disk_cache_prune_failure_does_not_raise— the cache dir is removed out from under the cache; the followingput()doesn't raise.test_disk_cache_put_max_size_overrides_the_instance_default— a per-callmax_sizeonput()wins over the instance default.Confirmed all four fail against the pre-fix code (
git stashon the three source files, re-ran): each raisesTypeErrorfor the parameter that doesn't exist yet.mypyreports 6 pre-existing errors inhttp.py/async_http.pyaboutResponse | _StealthResponsetyping at_cache_put()'s existing call sites — identical set, same messages, only shifted line numbers, confirmed by running mypy against the unmodified files. Unrelated to this change.