Sulu Page Cache vs. Dynamic Rendering: Which Handles Concurrent Requests Better?
0 reputation · 25 Jan 2022, 06:43 UTC
0 reputation · 25 Jan 2022, 06:43 UTC
I'm running a Sulu CMS site (Sulu 2.x on Symfony) that serves mostly public pages, and I need to decide how to handle latency that only shows up when many requests arrive at once. Under light traffic everything is fast, but I'm weighing two documented approaches before load testing.
Option one is Sulu's built-in static page cache, which stores rendered HTML and bypasses the content resolver and template engine, cutting time-to-first-byte. Option two is keeping pages dynamically rendered, which I need for a few sections with per-visitor personalization.
My concern with the page cache is the stampede scenario: if a cached page expires or is invalidated after an admin UI update, many concurrent requests could try to regenerate it simultaneously, hammering the database connection pool. With dynamic rendering, every request hits the resolver, so concurrency costs are constant rather than spiky.
I plan to verify with Apache Benchmark and a Redis-backed Symfony cache provider, watching response headers to distinguish cache hits from misses.
Given this trade-off: does Sulu's page cache offer any documented locking or stampede protection during regeneration, or is that delegated to the Symfony cache provider? Is a hybrid setup (cache for anonymous pages, dynamic for personalized ones) a supported configuration? What metrics best reveal pool exhaustion under concurrency?
34775 reputation · 25 Jan 2022, 11:37 UTC
Sulu’s built-in page cache delegates stampede protection entirely to the underlying Symfony cache adapter. The Redis adapter (recommended for production) implements atomic locking via SETNX with a TTL, serializing regeneration so only one request rebuilds the page while others wait or receive a stale response. Filesystem adapters rely on flock(), which is unreliable on network filesystems; array/null adapters provide no protection. Hybrid caching—caching anonymous pages while keeping personalized sections dynamic—is a supported, documented configuration via cacheLifetime: 0 in page templates or SuluHttpCacheBundle rules. The metrics that best reveal database connection pool exhaustion under load are: x-sulu-cache response headers (HIT/MISS/REVALIDATE), Redis blocked_clients and rejected_connections, and Doctrine DBAL connection wait times or too many connections errors in the database server logs.
flock(); not safe on NFS or when multiple PHP-FPM workers share a mount.Set cacheLifetime: 0 in the page template’s front matter or via the sulu_http_cache configuration to mark a page as non‑cacheable. Anonymous pages keep the default TTL (e.g., 3600 s). Verify with curl -I: cached pages return x-sulu-cache: HIT and an age header; personalized pages return MISS on every request.
| Layer | Metric | How to Collect |
|---|---|---|
| HTTP cache | x-sulu-cache header | curl -I -H 'Accept: text/html' https://example.com/ |
| Redis | blocked_clients, rejected_connections, keyspace hit/miss ratio | redis-cli INFO stats | grep -E 'keyspace_hits|keyspace_misses|blocked_clients|rejected_connections' |
| Doctrine DBAL | Connection wait time, pool.used/pool.max | Enable doctrine.dbal.logging: true and watch var/log/dev.log for waiting for connection |
| Database server | Active vs max_connections | SHOW STATUS LIKE 'Threads_connected'; (MySQL) or SELECT count(*) FROM pg_stat_activity; (PostgreSQL) |
config/packages/cache.yaml for sulu_page_cache: cache.adapter.redis.redis-cli INFO stats before/after to observe lock contention.wrk or k6 (not ab) using a Lua script that mixes cookie‑less (anonymous) and cookie‑bearing (personalized) requests.x-sulu-cache: MISS spikes with Redis blocked_clients and DBAL connection waits.Which cache adapter is configured for sulu_page_cache in config/packages/cache.yaml? The stampede behavior changes completely between Redis, filesystem, and array adapters.
Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.