Unmasking Magento 2's Silent FPC Bug: GraphQL Cache Invalidation Failure
Unmasking Magento 2's Silent FPC Bug: GraphQL Cache Invalidation Failure
As e-commerce migration experts at Shopping Mover, we've identified a critical, yet silently occurring, bug within Magento 2's built-in Full Page Cache (FPC) when handling GraphQL responses. This defect, detailed in GitHub Issue #40823, can lead to stale product or category data being served to customers, severely impacting the user experience and potentially sales for stores using Magento Open Source or Adobe Commerce.
The Problem: Stale GraphQL Data & Broken Invalidation
The issue manifests when using Magento 2.4.7-p9 (and earlier 2.4-develop versions) with built-in FPC and a Redis backend. For cacheable GraphQL GET requests returning product data, the FPC entry should store specific tags like cat_p_ and cat_p. These tags are vital for Magento's cache invalidation, ensuring that product updates in the admin correctly purge cached GraphQL responses.
However, the bug prevents these critical tags from being correctly associated with the FPC entry, resulting in only generic FPC and MAGE tags being saved. Consequently, when a product is updated, Magento's invalidation pipeline correctly emits the necessary cat_p_ tags, but since no FPC entry is registered under them, the invalidation fails silently. Stale product data continues to be served from the FPC until its TTL expires or a manual full FPC flush is performed.
The Deep Dive: A Double-Processing Conundrum
The root cause is a subtle interaction between two stock plugins and the Magento\Framework\App\PageCache\Kernel::process() method. Both Magento\PageCache\Model\Controller\Result\BuiltinPlugin::afterRenderResult and Magento\PageCache\Model\App\FrontController\BuiltinPlugin::aroundDispatch inadvertently call Kernel::process() on the same GraphQL response.
The sequence is critical:
- The first plugin (
Result\BuiltinPlugin) correctly identifies and sets theX-Magento-Tagsheader. - Its call to
Kernel::process()successfully saves the FPC entry with all correct tags. - Crucially, within
Kernel::process(), theX-Magento-Tagsheader is cleared from the response (unless indevelopermode). - The second plugin (
FrontController\BuiltinPlugin) then callsKernel::process()again on the same response. - Because the
X-Magento-Tagsheader was cleared, this secondKernel::process()saves the *same cache identifier* but with an *empty* tag array, effectively "stomping" the correctly tagged entry.
Evidence from the issue logs clearly shows this "tag stomping":
phase=Kernel::process->save
tags=["cat_p","cat_p_1640","FPC"] identifier=...
phase=Kernel::process->save # second call, same identifier
tags=[] # X-Magento-Tags was cleared by the first call
identifier=...
Why This Bug is Hard to Detect
This defect is particularly insidious as it's masked in common development and production scenarios:
- Developer Mode: The
clearHeader('X-Magento-Tags')logic is skipped, making local testing appear correct. - Varnish FPC: Installations using Varnish are unaffected, as the built-in plugins short-circuit, and
Kernel::process()is never called. - Silent Production Symptoms: In production, the cache still serves hits (albeit stale ones). Invalidation logs appear correct, and manual cache flushes temporarily "fix" staleness, obscuring the underlying tag invalidation failure.
Proposed Solution
The suggested fix involves adding a guard to the second Kernel::process() call in Magento\PageCache\Model\App\FrontController\BuiltinPlugin.php. This would prevent reprocessing if the cache has already been handled for the current request:
// vendor/magento/module-page-cache/Model/App/FrontController/BuiltinPlugin.php
// ...
if (!$this->kernel->wasProcessed()) { // new guard
$this->kernel->process($result);
}
// ...
This requires a new wasProcessed() method in Magento\Framework\App\PageCache\Kernel to track processing state per request.
Impact and Recommendation
Classified as S2 severity, this bug forces users to employ workarounds like frequent full cache flushes. For any Magento 2 store relying on built-in FPC and GraphQL, this is a critical issue compromising data integrity and user experience. We urge developers and merchants to be aware of this problem and monitor official Magento updates for a permanent fix to ensure a robust and performant e-commerce platform.