Sulu Symfony HttpCache and Doctrine ORM: unresolved ESI surrogate caching decision for concurrent admin writes
0 reputation · 23 Nov 2022, 22:30 UTC
0 reputation · 23 Nov 2022, 22:30 UTC
Sulu 2.4.x integrates Symfony HttpCache with Doctrine ORM in the admin API stack. Under concurrent POST/PUT requests to admin API content endpoints, Symfony's cache lock file and InnoDB row-level locks can serialize writes and produce latency spikes that do not appear in sequential traffic. Sulu supports ESI surrogate caching via the surrogate cache control header, which can reduce perceived latency for fragments, yet enabling it globally remains unresolved because stale fragments in the admin interface could surface outdated entity state.
Because pre-2.0 Sulu used a different lock mechanism and because MySQL isolation levels such as READ COMMITTED versus REPEATABLE READ change contention characteristics, the exact boundary between cache-layer serialization and database transaction queuing is unclear without version-specific profiling. Comparing baseline sequential requests against concurrent load on Sulu 2.4.x with MySQL 8, while measuring average and 95th-percentile response times alongside Symfony lock debug and Doctrine SQL logs, would help isolate the source of added latency.
Under Sulu 2.4.x with MySQL 8, does enabling ESI surrogate caching for admin content fragments introduce stale-content risk under concurrent writes? Which cache invalidation boundaries should be configured to prevent it? Is the observed latency increase primarily attributable to Symfony lock acquisition, Doctrine transaction commits, or both?
33275 reputation · 24 Nov 2022, 00:54 UTC
Does ESI surrogate caching for admin fragments risk stale data under concurrent writes? Yes – if a fragment is cached and a concurrent POST/PUT updates the underlying entity, the cached fragment will continue to be served until it is explicitly purged. Relying on TTL alone is insufficient because the cache key is not automatically tied to the entity’s write cycle.
Which invalidation boundaries should be configured? Invalidate the fragment cache at the aggregate‑root level: on each successful commit of a Doctrine transaction, delete or purge all ESI URLs that depend on the modified entity. Use cache tags if the store supports them, otherwise maintain a lookup table of fragment URLs per entity and purge them in one atomic step.
Is the latency spike mainly due to Symfony lock acquisition or Doctrine transaction commits? The majority of the latency originates from InnoDB row‑level locks that serialize concurrent writes to the same row. Symfony’s lock file is only engaged when a write request concurrently triggers a cache miss on a GET request; it does not serialize the write itself. Therefore, transaction commit time is the primary culprit, with Symfony locks adding a small overhead only for cache‑miss reads.
Confirm cacheability of admin responses. Inspect the Cache‑Control and Surrogate‑Control headers on GET requests. If Cache‑Control: private is present, the fragment is not intended for shared caching and stale risk is moot.
Enable ESI tags or explicit URL purge. In your Symfony configuration, add a cache tag that matches the entity class or ID:
framework:
cache:
pools:
esi_fragment_pool:
adapter: cache.adapter.redis
default_lifetime: 3600
tags: true
Hook into Doctrine lifecycle events. Use an onFlush or postFlush listener to collect all entities that changed and call the cache pool’s deleteTags or clear method with the corresponding tags.
Measure lock contention. Enable doctrine.dbal.logger to log SQL and symfony.http_cache.debug to log lock acquisition. Compare the average and 95th‑percentile times for sequential versus concurrent POST/PUT traces to isolate the contribution of each layer.
Check MySQL isolation level. While REPEATABLE READ can add gap locks, single‑row updates behave similarly to READ COMMITTED. If you observe range contention, consider switching to READ COMMITTED for admin writes.
Cache‑Control: private), disable surrogate caching to avoid unnecessary complexity.To fine‑tune the recommendation, could you confirm whether the admin GET responses currently include a Surrogate‑Control: content="ESI/1.0" header? This determines if the ESI processing step is actually active for the admin API.
Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.