fix(cache-on-save): latch page-cache invalidation to once per submit

onFormProcessed fires once per process action (add_page/upload/message/
reset), so the deleteAll() + Cache::invalidateCache() pair ran 4x per post.
Gate it behind a $cacheInvalidated latch (same pattern as $photosReconciled)
so the store wipe + system.yaml touch happen exactly once, and log the step.

Code review F1 (perf) + F7 (observability).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Mpdu3Dt1iVoozHwAMyjrbn
This commit is contained in:
2026-07-07 08:31:50 +02:00
co-authored by Claude Opus 4.8
parent a7bda6ed39
commit 8db3ffeafc
+16 -2
View File
@@ -25,6 +25,14 @@ class CacheOnSavePlugin extends Plugin
*/ */
private bool $photosReconciled = false; private bool $photosReconciled = false;
/**
* Same 4x-per-submit firing as $photosReconciled: onFormProcessed runs once
* per process action. Clearing the page-tree cache is idempotent, but doing it
* four times per post is wasted work (a full deleteAll() + system.yaml touch
* each time). Latch it so the invalidation runs exactly once per submission.
*/
private bool $cacheInvalidated = false;
public static function getSubscribedEvents(): array public static function getSubscribedEvents(): array
{ {
return [ return [
@@ -191,16 +199,22 @@ class CacheOnSavePlugin extends Plugin
} }
} }
// Two-part invalidation. deleteAll() drops the Doctrine store (the tracker // Two-part invalidation, latched to run ONCE per submit (see
// feed page cache etc.), but the page-tree INDEX is keyed on // $cacheInvalidated) — the 4 process actions would otherwise repeat it.
// deleteAll() drops the Doctrine store (the tracker feed page cache etc.),
// but the page-tree INDEX is keyed on
// md5(dirs + folderHash + config->checksum() + lang) (Pages::buildRegularPages). // md5(dirs + folderHash + config->checksum() + lang) (Pages::buildRegularPages).
// With cache.check.method:folder that index can survive a create — a fresh // With cache.check.method:folder that index can survive a create — a fresh
// entry then stays invisible to the API (GET /api/v1/pages{route} 404s), so // entry then stays invisible to the API (GET /api/v1/pages{route} 404s), so
// opening the just-posted entry for editing shows "this entry no longer // opening the just-posted entry for editing shows "this entry no longer
// exists". invalidateCache() touches system.yaml, bumping config->checksum() // exists". invalidateCache() touches system.yaml, bumping config->checksum()
// so the index key changes and the tree rebuilds on the next request. // so the index key changes and the tree rebuilds on the next request.
if (!$this->cacheInvalidated) {
$this->cacheInvalidated = true;
$this->grav['cache']->deleteAll(); $this->grav['cache']->deleteAll();
Cache::invalidateCache(); Cache::invalidateCache();
$this->grav['log']->info('cache-on-save: cleared page cache + invalidated page-tree index after new-entry submit');
}
} }
/** /**