Skip to content

[1.x] perf: add MemoryCacheSettingsRepository layer to eliminate per-call Redis fetches#20

Merged
imorland merged 1 commit into
1.xfrom
im/settings-memory-cache
Mar 6, 2026
Merged

[1.x] perf: add MemoryCacheSettingsRepository layer to eliminate per-call Redis fetches#20
imorland merged 1 commit into
1.xfrom
im/settings-memory-cache

Conversation

@imorland

@imorland imorland commented Mar 6, 2026

Copy link
Copy Markdown
Member

Problem

RedisCacheSettingsRepository::get() calls all() on every invocation, which calls cache->remember() every time — a Redis GET of the full flarum:settings blob on each call.

On a typical forum page, settings are read 60–100 times (core serializers, frontend JS variable injection, middleware, extensions). Each call fetches the entire settings blob from Redis — on production this is ~154 KB. That's up to ~15 MB of Redis egress per page load from settings reads alone, which at scale accounts for the majority of observed Redis outbound bandwidth (~300 Mbps).

Flarum core's SettingsServiceProvider uses MemoryCacheSettingsRepository to provide a per-request in-process cache — one load, then array lookups. fof/redis's Settings provider replaces the entire binding without restoring this layer.

Fix

Wrap RedisCacheSettingsRepository with MemoryCacheSettingsRepository, restoring the three-layer chain:

MemoryCacheSettingsRepository   ← per-request in-process cache (zero network cost after first read)
  └── RedisCacheSettingsRepository  ← cross-request Redis cache (one Redis GET per request, 1h TTL)
        └── DatabaseSettingsRepository  ← source of truth (MySQL, hit only on Redis miss)

After this change, Redis is hit at most once per request for settings instead of once per settings->get() call.

Impact

Expected Redis egress reduction: ~95%+ of settings-related bandwidth eliminated. On production (Nothing Community), this should bring Redis outbound bandwidth from ~300 Mbps down to ~5–15 Mbps.

No behaviour change — invalidation on ClearingCache, Enabled, Disabled events is unchanged. The memory cache is request-scoped (PHP-FPM worker lifetime), so settings changes propagate on the next request after Redis invalidation.

RedisCacheSettingsRepository::get() calls all() on every invocation,
which calls cache->remember() on every call — a Redis GET each time.
With ~60–100 settings reads per page request, each returning the
154 KB flarum:settings blob, this alone accounts for the majority
of Redis egress bandwidth.

Wrapping with MemoryCacheSettingsRepository restores the per-request
in-process cache that Flarum core's SettingsServiceProvider provides
by default. After the first settings read per request, all subsequent
reads within the same request are local array lookups with zero network
cost. Redis is hit at most once per request instead of once per
settings->get() call.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@imorland imorland requested a review from a team as a code owner March 6, 2026 23:39
@imorland imorland changed the title perf: add MemoryCacheSettingsRepository layer to eliminate per-call Redis fetches [1.x] perf: add MemoryCacheSettingsRepository layer to eliminate per-call Redis fetches Mar 6, 2026
@imorland imorland merged commit 3778578 into 1.x Mar 6, 2026
27 checks passed
@imorland imorland deleted the im/settings-memory-cache branch March 6, 2026 23:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant