Problem/Motivation
Since #2920001: Add cacheable HTTP exceptions: Symfony HTTP exceptions + Drupal cacheability metadata, it is possible to make 4xx responses cacheable. This is valuable for e.g. many GET requests to the same JSON API URL with e.g. incorrect filters. Imagine tens of thousands of Rokus or Apple TVs making the same invalid request. Currently, those would all cause Drupal to bootstrap and fully handle the response. Even though Dynamic Page Cache (or even Page Cache in case of anonymous requests) could handle those much faster, leading to better scalability.
#2765959: Make 4xx REST responses cacheable by (Dynamic) Page Cache + comprehensive cacheability test coverage did this for the core REST module.
Proposed resolution
- Wait to do this until JSON API requires Drupal 8.5 or later, because #2920001: Add cacheable HTTP exceptions: Symfony HTTP exceptions + Drupal cacheability metadata was only added in Drupal 8.5.
- Inspect the JSON API codebase and convert where this makes sense.
- Add test coverage.
Remaining tasks
TBD
User interface changes
None.
API changes
None.
Data model changes
None.
| Comment | File | Size | Author |
|---|---|---|---|
| #37 | 2929428-37.patch | 22.35 KB | gabesullice |
| #37 | interdiff.txt | 576 bytes | gabesullice |
| #35 | 2929428-35.patch | 22.35 KB | gabesullice |
| #35 | interdiff.txt | 5.55 KB | gabesullice |
| #34 | 2929428-33.patch | 22.4 KB | gabesullice |
Comments
Comment #2
wim leersAttached is a patch that doesn't work (it's not yet passing the necessary cacheability metadata), but these are examples of places where I think JSON API would benefit. It's not everything for sure.
Comment #3
wim leersThat makes this a v2-only issue.
Comment #4
wim leersComment #5
axle_foley00 commentedHi @wim-leers, I'll try to tackle this.
Comment #6
wim leers👍 Great, thank you! :)
Comment #7
gabesulliceAdded a child issue for one of the exceptions: #2986987: Convert EntityAccessDeniedHttpException into cacheable exception
Comment #8
wim leers#2986987: Convert EntityAccessDeniedHttpException into cacheable exception was committed last week 🎉
Comment #9
axle_foley00 commentedAttached is my first pass at converting references to "throw new *HttpException" into "throw new Cacheable*HttpException" wherever possible.
Comment #10
wim leersComment #11
wim leersJust got back from vacation — you posted that the day after I left for vacation. Queued a test! Thanks so much for working on this!
Comment #13
wim leersThis is probably the only place that you cannot convert to be a cacheable HTTP exception!
Reverting this change.
Comment #15
wim leersActually, this is another place. Reverting these changes too (plus those in its subclasses).
Comment #16
wim leersHere is an example of a concrete change. In this particular instance, we're throwing an exception due to URL query args that are invalid. So this exception depends on the
url.query_argscache context. (See https://www.drupal.org/docs/8/api/cache-api/cache-contexts.)This makes
WorkflowTestpass! So we should see a lower number of test failures now :)Back to you now!
Comment #19
wim leersFrom >100 to 35! 🎉
I'll do just one more.
TermTest::testRelationships()is failing, and the fatal error points toRequestHandler.php on line 137. On that line, cacheability is missing, just like in #16. But in this particular case, a cacheable exception does not even make sense, because it only ever occurs forPOSTandPATCHrequests, and responses for those HTTP methods are never cacheable anyway, because the HTTP spec forbids anybody from caching responses to such requests!So in this case, we simply can make the exception not cacheable.
Remember, the issue summary says: — this is a case where it doesn't make sense.
After fixing that, a similar problem comes up in
\Drupal\jsonapi\Normalizer\RelationshipNormalizer::massageRelationshipInput(), that code is also only called during denormalization and hence during unsafe requests.Now, truly, back to you!
Comment #21
wim leersTaking this on.
First step: rebase, because #19 no longer applied.
Comment #22
wim leersRecent changes (a.o. #2949807: Spec Compliance: Error responses are missing the `jsonapi` top-level member) have made error responses cacheable (yay!), and the changes here result in more accurate cacheability (more yay!).
This demands some assertion updates. This interdiff for example makes
::getIndividual()pass tests.Comment #25
wim leersThis removes most if not all "cacheable" exception conversions from unsafe HTTP method controllers in
EntityResource. This should fix many failures!Comment #27
wim leersGreen? :)
Comment #28
wim leersOops.
Comment #31
wim leersApparently there's quite a bit more here.
Comment #32
wim leersFix CS violations.
Comment #33
gabesulliceRerolled.
Comment #34
gabesulliceWhoops.
Comment #35
gabesullice❤️
Missing the first argument of a
CacheableDependencyInterface.This could be even narrower:
['url.query_args:filter', 'url.query_args:sort']We can at least cache on the URL's path and filter I think.
Let's just cache this based on the
_formatparam alone.I went ahead and made all those changes. RTBC from my perspective.
Comment #37
gabesulliceForgot to update one thing.
Comment #38
wim leersNice!
Good catch — I missed these!
RTBC from mine too!
Comment #39
axle_foley00 commentedSorry for my lack of progress over the past few weeks. Thanks for working on this @gabesullice and @wim-leers, I unfortunately got bogged down with some work commitments.
Comment #40
wim leers@axle_foley00 Don't apologize! :) You got this started, that's what matters most!
Comment #42
wim leers