Problem/Motivation
We have a Drupal Commerce checkout using an off-site iframe for payment. It pings a callback controller and with some other logic we load a route using a ?refid= query parameter. Despite this value changing, or being present and then not, the page is HIT after first view by dynamic page cache
Data from Chrome dev tools network tab:
First request:
Request URL:https://example.com/checkout/zuora/2804/payment?refid=
Request Method:GET
Status Code:302
cache-control:must-revalidate, no-cache, private
content-language:en
content-type:text/html; charset=UTF-8
date:Wed, 10 May 2017 21:32:35 GMT
expires:Sun, 19 Nov 1978 05:00:00 GMT
location:/checkout/2804/payment
server:nginx
status:302
x-drupal-dynamic-cache:MISS
Second request
Request URL:https://example.com/checkout/zuora/2804/payment?refid=REDACTED_VALUE
Request Method:GET
Status Code:302
cache-control:must-revalidate, no-cache, private
content-language:en
content-type:text/html; charset=UTF-8
date:Wed, 10 May 2017 21:32:35 GMT
expires:Sun, 19 Nov 1978 05:00:00 GMT
location:/checkout/2804/payment
server:nginx
status:302
x-drupal-dynamic-cache:HIT
Since the refid was present, it should have had a different location header.
For now, I have disabled cache on this route. But I had to move this callback out of our checkout plugin and into a custom route with caches disabled.
Proposed resolution
No idea. \Drupal\Core\Cache\Context\RouteCacheContext checks the route parameters. Which is what the $dynamicPageCacheRedirectRenderArray uses. It also checks request format.
Maybe also add url.query_args as render context?
Remaining tasks
User interface changes
API changes
Data model changes
| Comment | File | Size | Author |
|---|---|---|---|
| #12 | 2877498-12.patch | 3.75 KB | wim leers |
| #4 | dynamic_cache_does_not-2877498-4.patch | 1.3 KB | mglaman |
Comments
Comment #2
mglamanComment #3
mglamanI had to add
no_cache: TRUEto the route, also, even though it returns a redirect response like so:Comment #4
mglamanWhile reviewing the test, and wanting to write one, I discovered this:
So this is intended? What if the query adjusts a page's dynamic content. Such as gtm parameters?
Comment #5
mglamanHoping Fabianx or Wim Leers can provide insight why queries (different ones) wouldn't bust cache in regards to personalization, etc.
Comment #7
wim leersA response MUST vary based on route (because a different controller is called for each route) and request format (again, different controller, or at least different post-processing for the same controller's return value).
Any variations beyond that are handled automatically by Dynamic Page Cache. On one condition: your controller specifies the correct cacheability metadata.
Everything in this issue suggests that Drupal Commerce is missing some cacheability metadata: it should specify
in the code building a render array which varies depending on the specified
?refidin the URL.Comment #8
mglamanBut we're not returning a renderable array. See #3. We're returning a HTTP redirect response, because we're handling an off-site iframe response. Adding cache metadata to that response seems to have no effect. Or do I need to merge in
url.query_args:refid(even with max age at zero?)I guess I should write a test for core which properly mimics what we're doing.
Comment #9
wim leersSorry, I missed #3.
So this adds the cache tag for the Order entity. It also sets the cache tags + cache contexts that are bubbled during URL generation. Likely you're getting the
url.sitecache context. But how would$urlknow about thisrefidURL query argument from the incoming request, which is causing this redirect?From what you described, you need to add
Ideally, I would be able to see the entire controller logic, then I can answer this properly.
Comment #10
mglamanThis works as designed,then, as outlined by the test around
Comment #11
wim leersSo that means you were able to get it working as expected, by adding the missing cacheability metadata? :)
Comment #12
wim leersHm, but wait:
That should have ensured the response wasn't cached by Dynamic Page Cache. We even have test coverage for that in
\Drupal\Tests\dynamic_page_cache\Functional\DynamicPageCacheIntegrationTest:If that's not working, then that would be a bug. Test to prove that this indeed should work (existing test coverage should already be sufficient, but since this issue is suggesting it doesn't actually work, adding more explicit test coverage).
Comment #13
wim leersComment #14
mglamanSorry, yes! This has fixed our problems.
Comment #15
wim leersCorrecting the cacheability metadata has fixed your problems?
Comment #16
mglamanIt worked on my non-form GET requests. I have yet to test it with forms, and ensuring forms with the attached metadata are not cached. I can take some time to re-roll #12 to add this test. Just have been busy lately.
Comment #17
wim leersWell, existing test coverage should be sufficient, but I'd love to hear it if that weren't the case.
Marking as postponed on the feedback #16 indicates will be provided in the future :)
Comment #18
drupalninja99 commented@Wim I am trying this within a block that I would think would bubble up to the page? I am using both the getCacheMaxAge() method (setting it to zero) and getCacheContexts() using 'url.query_args' but the page cache is still ignoring the query string. I am using page_manager which should also respect cache contexts but maybe the bug is in page manager (in which case I can log an issue there)?
Comment #19
wim leersNote
page_cache!==dynamic_page_cache. I think it's best if you open a new issue and the actual code (sanitized). Andpage_managerhas been known to have many cacheability bugs, so yes, that's quite likely.Comment #20
jribeiro commentedJust a feedback, I have a pretty similar case described in this issue and adding
$response->getCacheableMetadata()->addCacheContexts(['url.query_args:query'])solved the issue for me, dynamic cache is caching both urls, with and without the query string.Comment #22
wim leersPinged @mglaman yesterday to get feedback per #17.
Comment #23
mglamanSorry for the delay. The
'url.query_argscontext is working properly for any and all use cases.For example I added
And it bubbles up breaking form cache.
Comment #24
wim leersAlright, thanks for confirming! 👍
Comment #25
floriane commentedI had this problem with my controller's module, which took the cache value instead of the url one (i'm using Drupal 10)
I solved the problem with #cache and max-age to 0: