Skip to content

[2.x] fix: Build the text formatter during cache:clear, not on the next request - #4990

Merged
imorland merged 2 commits into
2.xfrom
im/warm-formatter-cache
Aug 26, 2026
Merged

imorland merged 2 commits into
2.xfrom
im/warm-formatter-cache

Conversation

@imorland

Copy link
Copy Markdown
Member

Changes proposed in this pull request:

cache:clear deletes the compiled text formatter, so the next request that renders a post rebuilds it. Formatter::getComponent() compiles the renderer lazily inside rememberForever('flarum.formatter', …), which only runs when the cache is cold — i.e. inside whichever request first renders after the clear.

On a forum with many extensions that compiled renderer is large (a real report: ~174 KB, with the cold build peaking over a 256 MB web memory_limit and taking ~30s). When it runs in a web request whose SAPI memory limit is tighter than the CLI's, it can hit a PHP memory fatal — which is not an exception (Flarum's error handler never sees it) and not a kernel OOM (nothing in dmesg), so the visitor gets a blank HTTP 500 and nothing is logged. Clearing the cache from the admin panel is the common trigger, and the failure lands on the next visitor, not the admin who cleared it.

This rebuilds the formatter at the end of cache:clear, where the memory limit is usually generous, so the render path finds it already cached:

  • New Formatter::warm() compiles and caches the formatter on demand.
  • CacheClearCommand calls it after clearing, best-effort: wrapped in try/catch so a warm-build failure never fails the clear itself — the cache is already cleared and the render path rebuilds lazily as before. This only ever removes a failure mode, never adds one.
  • The admin-panel clear runs the same command; it warms in the admin's own request, so if a build is going to fail on tight memory it fails there (visible to the admin) rather than silently on the next visitor. Large forums that clear via the CLI get the build entirely off the web SAPI.

Reviewers should focus on:

  • That warm() failing can't fail the clear — cache:clear still returns success and the formatter still rebuilds lazily.
  • That warming is a plain rebuild of the same cache the render path reads, not a second/parallel cache.

Necessity

  • Has the problem that is being solved here been clearly explained? — a cache clear can silently 500 a forum when the cold formatter build outgrows the web memory limit.
  • If applicable, have various options for solving this problem been considered? — building off the web request removes the failure mode regardless of extension count or memory tuning; a docs-only note would leave the silent failure in place.
  • For core PRs, does this need to be in core, or could it be in an extension? — the formatter and cache:clear are core.
  • Are we willing to maintain this for years / potentially forever?

Confirmed

  • Frontend changes: tested on a local Flarum installation. — no frontend changes.
  • Frontend changes: tests are green — n/a.
  • Frontend changes: tests have been added — n/a.
  • Backend changes: tests are green (run composer test).
  • Backend changes: tests have been added, or are not appropriate here. — a test asserting cache:clear leaves the formatter cache warm, and that it still succeeds.
  • Where applicable, changes are suitable for all supported database drivers (MySQL, MariaDB, PostgreSQL, SQLite). — no database involvement.
  • The description above is written by me and describes what this pull request actually does.

Required changes:

  • Related documentation PR: (Remove if irrelevant)

imorland and others added 2 commits August 26, 2026 14:59
…quest

Clearing the cache deletes the compiled formatter, so the next request
that renders a post rebuilds it. On a forum with many extensions that
compile is large, and it runs inside whichever web request happens to
trigger it - where the memory limit is often tighter than on the CLI. If
it exceeds that limit the request dies on a PHP memory fatal, which is
not catchable and not logged: the visitor gets a blank 500 and nothing
explains why.

Rebuild the formatter at the end of cache:clear instead, where the limit
is usually generous, so the render path finds it already cached. It is
best-effort - if the warm build fails the clear still succeeds and the
formatter rebuilds lazily as before, so this only ever removes a failure
mode, never adds one.
@imorland
imorland requested a review from a team as a code owner August 26, 2026 13:59
@imorland imorland added this to the 2.0.0-rc.8 milestone Aug 26, 2026
@imorland imorland changed the title Build the text formatter during cache:clear, not on the next request [2.x] fix: Build the text formatter during cache:clear, not on the next request Aug 26, 2026
@imorland
imorland merged commit b2e6872 into 2.x Aug 26, 2026
29 checks passed
@imorland
imorland deleted the im/warm-formatter-cache branch August 26, 2026 15:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants