Skip to content

[6.x] Avoid loading the whole container listing in Asset::exists() - #15609

Open
edalzell wants to merge 8 commits into
statamic:6.xfrom
edalzell:fix/asset-exists-skip-listing
Open

edalzell wants to merge 8 commits into
statamic:6.xfrom
edalzell:fix/asset-exists-skip-listing

Conversation

@edalzell

@edalzell edalzell commented Oct 7, 2026 •

Copy link
Copy Markdown
Contributor

Stacked on #15610; merge that first. Until it lands this diff includes its commits.

Asset::exists() answered via container()->files(), which unserializes the whole cached recursive listing. Asset::find() calls it, so resolving one asset reference on the front end loaded the full listing. On a 197k-file S3 container that is an 80 MB cache entry and OOMs a 1 GB worker.

exists() now uses the in-memory listing only when this request has already loaded it (AssetContainerContents::isLoaded()), and otherwise does a single disk()->exists() call (one S3 HEAD). CP listings load the listing first, so they keep their zero-extra-IO behaviour. The disk answer is memoized per asset path, since meta() calls exists() before its own memo; without that a front-end find() plus augmenting width/height/size/alt was 16 HEADs per asset, now 3.

I skipped the suggested meta-cache shortcut: move() never forgets the old path's meta key, so it would report a moved-away path as existing. A disk check is always right.

Closes #15607

@edalzell
edalzell force-pushed the fix/asset-exists-skip-listing branch from 1dbdb61 to 8cfc47c Compare October 16, 2026 02:44

This branch has not been deployed

No deployments
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.

Asset::exists() loads the entire container listing to check one file

1 participant