Repository navigation
Conversation
jasonvarga
left a comment
There was a problem hiding this comment.
The two-pass warm approach is sound and the perf win is real, but I found two correctness regressions and a test gap that should be addressed before merge. Left inline comments on the specific spots.
No tests added. This touches core stache:warm behavior and adds a fast/cold-path split with non-trivial cache-key assumptions. A test asserting term/entry associations + value indexes match the old single-pass output — covering (a) a max_items: 1 taxonomy field and (b) an on-the-fly/fileless term — would have caught both inline issues below.
|
Thanks, both were real. Rebased on 6.x and addressed:
Added warm tests for a |
9f70dd6 to
476d590
Compare
jasonvarga
left a comment
There was a problem hiding this comment.
Thanks for the rebase — I've re-reviewed at 476d590. Both of my previous Critical findings are properly fixed, and the two new tests cover them well. Arr::wrap() handles the max_items: 1 shape, and the ReflectionMethod discriminator is a much better check than method_exists — I traced it through every index class in core and it classifies all of them correctly (Terms\Site in particular, which resolves to Terms\Value via same-namespace lookup, correctly stays in Pass 2). CI is green and there are no conflicts.
Four things left, all Warnings rather than blockers-in-principle — but I'd like them addressed before this goes in, since two of them are silent failures.
One broader note on the approach: the two-pass split is sound, and I confirmed the perf mechanism actually fires — CollectionEntriesStore::storeIndexes() already merges $collection->taxonomies()->map->handle(), so the taxonomy value indexes are always resolved and always built in Pass 1, even on a cold cache. So the ordering does what the docblocks claim.
Inline comments below.
Co-authored-by: Jason Varga <jason@pixelfear.com>
|
Benchmarked this against #15639 (merged today) on two real sites, locally: full
#15639 is where the win is. This PR adds nothing on top of it and is slightly slower. A per-store breakdown (Passivehouse, pre15639) shows the terms store at 37.6s of a 39.7s warm: the Harness
#!/usr/bin/env bash
# Usage: stache-perf.sh <site-dir> <variant-name> <cms-checkout-path|installed> <runs> <results.csv>
# Times `please stache:warm --exclude=revisions,assets` after a full cache clear, N times,
# with vendor/statamic/cms temporarily symlinked to the given checkout.
set -euo pipefail
SITE=$1; VARIANT=$2; CMS=$3; RUNS=$4; OUT=$5
VENDOR="$SITE/vendor/statamic/cms"
restore() {
if [ -L "$VENDOR" ]; then rm "$VENDOR"; fi
if [ -d "$VENDOR.orig" ] && [ ! -e "$VENDOR" ]; then mv "$VENDOR.orig" "$VENDOR"; fi
}
trap restore EXIT
if [ "$CMS" != "installed" ]; then
[ -L "$VENDOR" ] && rm "$VENDOR"
[ -d "$VENDOR" ] && mv "$VENDOR" "$VENDOR.orig"
ln -s "$CMS" "$VENDOR"
fi
export STATAMIC_STACHE_WATCHER=false STATAMIC_STACHE_FILE_WATCHER=false DEBUGBAR_ENABLED=false
cd "$SITE"
VERSION=$(php -d memory_limit=-1 please --version 2>/dev/null | tail -1)
echo "[$VARIANT] $VERSION src=$(readlink "$VENDOR" || echo installed) blueprint-md5=$(md5 -q "$VENDOR/src/Fields/Blueprint.php")"
for i in $(seq 1 "$RUNS"); do
php artisan cache:clear -q
php -d memory_limit=-1 please stache:clear -q
START=$(python3 -c 'import time;print(time.time())')
/usr/bin/time -l php -d memory_limit=-1 please stache:warm --exclude=revisions,assets 2> "$OUT.time" >/dev/null
END=$(python3 -c 'import time;print(time.time())')
SECS=$(python3 -c "print(round($END-$START,1))")
RSS=$(awk '/maximum resident set size/{printf "%d", $1/1048576}' "$OUT.time")
echo "$(basename "$SITE"),$VARIANT,$i,$SECS,$RSS" | tee -a "$OUT"
done
<?php
// Usage: php -d memory_limit=-1 breakdown.php <site-dir>
// Clears the cache and warms each Stache store one by one, in Stache::stores() order,
// printing the time and memory each one takes. Diagnostic only: stores warmed this way
// don't go through Stache::warm(), so cross-store ordering effects differ from a real warm.
$site = rtrim($argv[1], '/');
chdir($site);
require $site.'/vendor/autoload.php';
$app = require $site.'/bootstrap/app.php';
$app->make(Illuminate\Contracts\Console\Kernel::class)->bootstrap();
use Statamic\Facades\Stache;
Illuminate\Support\Facades\Cache::flush();
Stache::clear();
$total = microtime(true);
printf("%-14s %8s %8s %6s\n", 'store', 'seconds', 'mem_mb', 'files');
foreach (Stache::stores()->except(['revisions', 'assets']) as $key => $store) {
$t = microtime(true);
$store->warm();
printf("%-14s %8.1f %8d %6d\n", $key, microtime(true) - $t, memory_get_peak_usage(true) / 1048576, $store->paths()->count());
}
printf("%-14s %8.1f %8d\n", 'TOTAL', microtime(true) - $total, memory_get_peak_usage(true) / 1048576); |
|
#15639 is faster. This is not needed anymore. |
On our largest site, stache refreshing took 7-8 mins, which is too close to Forge's 10 mins deployment limit.
I proposed a 2 pass approach to Claude to see if it was feasible and this code results in over a 2 min decrease on my machine, from ~7:30 to ~5:15.