Files
intotheeast-com/docs/solutions/integration-issues/grav-deleteall-doesnt-invalidate-page-tree-index.md
T
m038andClaude Opus 4.8 bb2b64bd78 docs(solutions): in-place header edit + APCu cache staleness (Part 3)
Document the trip publish-toggle cache-invalidation finding: an in-place
trip.md `published` edit under cache.check.method: folder + APCu driver stays
stale because the folder checksum is unchanged AND the web APCu store is
unreachable by a CLI clearcache — fixed with apcu_clear_cache() from the web
request. Cross-link the sibling grav-deleteall doc (the create/delete case) as
necessary-but-not-sufficient here, and add the Published/Draft trip status
concept to CONCEPTS.md.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Mpdu3Dt1iVoozHwAMyjrbn
2026-07-08 17:17:22 +02:00

114 lines
5.0 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
title: "cache.deleteAll() doesn't rebuild the page-tree index — a freshly-posted entry 404s when opened for editing"
date: 2026-07-07
category: integration-issues
module: cache-on-save
problem_type: integration_issue
component: plugin
severity: high
symptoms:
- "A just-posted journal entry is written to disk but the API 404s on it (GET /api/v1/pages{route})"
- "Opening the entry you just created for editing shows 'This entry no longer exists — it may have been deleted'"
- "The entry DOES appear in the trip feed, but the edit prefill fetch can't find it until the next unrelated cache bump"
- "Intermittent — only bites when the page-tree index survives the create"
root_cause: incomplete_setup
resolution_type: code_fix
related_components:
- documentation
- development_workflow
tags:
- grav
- cache
- forms
- page-tree
---
# `cache.deleteAll()` doesn't rebuild the page-tree index
## Context — this is BUG-001 Part 2
[BUG-001](../../working/bugs-and-fixes.md) ("new entry not visible after form
submission") was fixed by wiring `$this->grav['cache']->deleteAll()` into the
`cache-on-save` plugin's `onFormProcessed` hook. That made new entries appear in
the trip feed immediately. It was **not the whole story**: `deleteAll()` drops
the Doctrine store (rendered-page cache, feed HTML, etc.) but does **not** force
Grav to rebuild its **regular-pages index**.
The gap only surfaced once the shared `/post` form gained an **edit mode**
(`?edit=<route>`), whose prefill does `GET /api/v1/pages{route}`. On a fresh
create that request would 404 — so the owner opening the entry they had *just*
posted saw "This entry no longer exists."
## Root cause
Grav's regular-pages index is keyed on:
```
md5(dirs + folderHash + config->checksum() + lang) // Pages::buildRegularPages
```
With `cache.check.method: folder` (our setting), the `folderHash` component does
not necessarily change when a new child folder is added inside an existing
tree — so the **index key stays the same** and the stale index (missing the new
entry) is reused. `deleteAll()` clears cache *stores* but does not change any of
the inputs to that key, so the tree is not rebuilt. The new page is on disk and
in the feed (which re-reads children), but the **API lookup by route** resolves
through the cached index and 404s.
## Fix
Add a second invalidation step alongside `deleteAll()`:
```php
use Grav\Common\Cache;
// ...
$this->grav['cache']->deleteAll();
Cache::invalidateCache(); // touch(system.yaml) → bumps config->checksum()
```
`Cache::invalidateCache()` is lightweight and idempotent — it `touch()`es
`system.yaml`, calls `clearstatcache()` and `opcache_reset()` (verified in Grav
core `Cache.php`). Touching `system.yaml` bumps `config->checksum()`, which
changes the index key, so the tree rebuilds on the next request and the new
entry becomes resolvable by route.
### Latch it — the hook fires 4× per submit
`onFormProcessed` fires once per `process:` action, and `post-form.md` has four
(`add_page`, `upload`, `message`, `reset`). Without a guard the
`deleteAll()` + `invalidateCache()` pair runs four times per post (a full store
wipe + `system.yaml` touch each time). Gate it with a once-per-request latch
(`$cacheInvalidated`), the same pattern already used for photo reconciliation
(`$photosReconciled`). See `user/plugins/cache-on-save/cache-on-save.php`.
## How to verify
1. Post a new entry via `/post`.
2. From the trip feed, click the new card's **Edit** link.
3. The form prefills with the entry's title/body — no "no longer exists" banner.
Regression test: `tests/ui/post/edit-mode.spec.js` **ES1** (create → open the
feed card's Edit link → change title + body → Save → assert on disk).
## Residual coverage gap (tracked, not fixed here)
`tests/ui/home/home.spec.js` **H1** and `tests/ui/maps/maps.spec.js` **M8**
require `site.travelling: true` to exercise the active-trip home feed + home GPX
map. The committed local `site.yaml` runs `travelling: false` (owner's testing
config, intentionally not committed as `true`), so both specs **skip loudly**
with a reason rather than fail misleadingly. They validate whenever the site is
in travelling mode. This is a known gap in this environment, not a silent hole —
provisioning `travelling: true` in a dedicated test config would close it.
## Related — Part 3: in-place edits + APCu
The `Cache::invalidateCache()` fix above completes `deleteAll()` for the
**create/delete** case, because a new or removed child folder advances
`folderHash` and the `system.yaml` touch bumps `config->checksum()`. It is
**necessary but not sufficient** for an **in-place frontmatter edit** (e.g. a
trip publish toggle) under `cache.driver: auto` (APCu): the folder structure is
unchanged, and APCu lives in web-server shared memory that a CLI `bin/grav
clearcache` cannot reach. That case additionally requires `apcu_clear_cache()`
called from the web request. See
[`grav-in-place-header-edit-apcu-cache-stale.md`](grav-in-place-header-edit-apcu-cache-stale.md).