Repository navigation
[6.x] Build Stache indexes in one pass without holding every entry - #15856
Open
DanielDarrenJones wants to merge 3 commits into
Open
DanielDarrenJones wants to merge 3 commits into
DanielDarrenJones wants to merge 3 commits into
Conversation
Warming the Stache made every entry of a collection, kept them all, and then had each index read all of them. Items are now made and cached one at a time, and every index that takes its value from a single item is filled from one pass over them. Entries are let go of once they have been indexed. Entries that need another entry to be indexed (a localization, an entry in a structure, anything whose values go looking for other entries) are indexed in a second pass, read back one at a time. A structure's uri, parent and order indexes are still built the way they were, once the rest are there. Term associations are built from the entries' own indexes rather than by loading every entry that has a term.
A computed value that queries its own collection asks for an index while the entries are still being made. That entry waits for the second pass, but the index was left marked as loaded with nothing in it, so the second pass read it as empty. Warming also filled indexes when updating them was disabled, and a parallel warm's workers didn't know a warm was running.
DanielDarrenJones
marked this pull request as ready for review
November 12, 2026 00:07
This branch has not been deployed
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.
Warming the Stache makes every entry of a collection, keeps all of them in memory, and then has each index read through all of them. Peak memory is the whole collection, parsed, and it grows with the content.
Entries are now made and cached one at a time, and the indexes are filled from a single pass over them. An entry is let go of once it has been indexed, so what's held is the index values rather than the entries.
What changes
Store::paths()makes, caches and moves on, rather than building every item before caching any.Store::fillIndexes()fills every index that takes its value from one item at a time (Indexes\Valueand its subclasses) in one pass.Index::update()goes through it, so the first index a cold request needs fills the rest of that store's indexes in the same pass, instead of each reading every item for itself later.uri,parentandorderindexes are still built the way they were, after the others. Finding an entry's place in a tree queries the collection, which needs its other indexes to exist. Entries of a structured collection are therefore still held, as they are today.An index that overrides
getItems(), or a store that overridesgetItemsFromFiles(), is left to build itself as before.Benchmark
Against
6.41.0, alternating, five rounds each, on an Apple M1 Pro with PHP 8.5 and the file cache store. Each run starts from a cleared Stache.A generated site of 9,780 entries (3,000 of them localizations across three sites, nested page builder content, two taxonomies):
stache:warm, timestache:warm, peak memoryA second generated site of 2,700 files (two sites, a structured pages collection, a collection mounted on a page):
stache:warm5.1 s and 357 MB before, 5.4 s and 87 MB after. One run each, so the times are level and the memory is the result.The first query on a cold Stache is slower. It's a query for twelve books ordered by title. Before, it built the four indexes it used and left the rest for later requests. Now it builds all of that collection's indexes in its one pass. The total work is less, but more of it lands on that first request. A site that runs
stache:warmon deploy doesn't see this.Profiling the warm: 23% of the time on
6.41.0goes on reading entries back out of the cache, against 9% here.Parallel warming
With
STATAMIC_STACHE_PARALLEL_WARMING=trueon the first site, three rounds each:On
6.41.0the worker processes run into PHP's 512 MB memory limit (Allowed memory size of 536870912 bytes exhausted), and the warm logsParallel warming failed, falling back to sequential. Here each worker stays under the limit, so it runs as intended. The indexes and URLs are the same as a sequential warm on6.41.0. It isn't faster than a sequential warm on this site, because one worker still does nearly all of the entries.Checked
6.41.0at every step. Nothing is kept on a store between requests or jobs: the state of a pass is cleared when it ends, and the marker that stops a store being filled twice in one warm only exists whileStache::warm()is running.Not covered
Tests
FillingIndexesTest(new) and three additions toEntriesStoreTest:The existing snapshot tests from #15795 pass unchanged. The full suite passes (11,757 passed, 12 skipped).