-
-
Notifications
You must be signed in to change notification settings - Fork 9.6k
[HttpKernel] Fix stale-if-error behavior, add tests #35305
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
&& \in_array($response->getStatusCode(), [500, 502, 503, 504]) | ||
&& !$entry->headers->hasCacheControlDirective('no-cache') | ||
&& !$entry->mustRevalidate() | ||
&& !$entry->headers->hasCacheControlDirective('s-maxage') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't get it. If you bypass it when s-maxage is set, that limits the usefulness of the feature drastically.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same for me.
Also see the last sentence in httpwg/http-core#264 (comment) and verify at https://tools.ietf.org/html/rfc7234#section-5.2.2.9.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's revert that part.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You mean only for smax-age
, right? I'm fine with that.
I'll also see if i can get clarifying comments from the RFC editors. Guess the bottom line is that max-age + public != s-maxage
(opposed to what I always thought).
fe0eb52
to
d8bbeb1
Compare
@fabpot I made strict |
Not sure about the switch. In general, I tend to avoid making something configurable when it's not needed. I've one additional question: how does Varnish work? I think doing the same as Varnish is the way to go. WDYT? |
Personally, I have never used Varnish because the |
TL;DR: I may be doing something wrong or misinterpreting the results, but it seems Varnish does not do It always does background validation, so errors show up on the second-next request only. My tests were with Here is the setup to reproduce and/or test it, in case someone wants to tinker with it. You can try arbitrary $ cd $(mktemp -d) $ docker run --rm varnish -V varnishd (varnish-6.3.1 revision 6e96ff048692235e64565211a38c41432a26c055) Copyright (c) 2006 Verdens Gang AS Copyright (c) 2006-2019 Varnish Software AS $ cat <<'EOF' > index.php <?php if (file_exists($_REQUEST['id'])) { header('HTTP/1.1 500 Internal server error'); exit; } touch($_REQUEST['id']); unset($_REQUEST['id']); $cacheControl = 'Cache-Control: public'; foreach ($_REQUEST as $key => $value) { $cacheControl .= ", $key"; if ($value) { $cacheControl .= "=$value"; } } header('HTTP/1.1 200 OK'); header($cacheControl); EOF $ php -S 0.0.0.0:8000 index.php & [1] 8299 PHP 7.2.24-0ubuntu0.18.04.1 Development Server started at Mon Jan 13 07:28:37 2020 Listening on http://0.0.0.0:8000 Document root is /tmp/tmp.PxbY0DOltD Press Ctrl-C to quit. $ cat <<'EOF' > default.vcl vcl 4.0; backend default { .host = "localhost:8000"; } EOF $ docker run -d --rm -v $(pwd)/default.vcl:/etc/varnish/default.vcl:ro --tmpfs /usr/local/var/varnish:exec -p 8001:80 varnish d62ec6ff328d332afb23674bc49bbf29c0a407863c0639cae5f7f18f5389af86 $ export ID=$(date +'%s') # use this to run a fresh set of tests // bust Varnish cache $ curl -IX GET "http://localhost:8001/?id=${ID}&max-age=2&stale-if-error=60" # first hit HTTP/1.1 200 OK Host: localhost:8001 Date: Mon, 13 Jan 2020 07:30:51 GMT Cache-Control: public, max-age=2, stale-if-error=60 Content-type: text/html; charset=UTF-8 X-Varnish: 5 Age: 0 Via: 1.1 varnish (Varnish/6.3) Accept-Ranges: bytes Content-Length: 0 Connection: keep-alive $ curl -IX GET "http://localhost:8001/?id=${ID}&max-age=2&stale-if-error=60" # Varnish seems to always deliver the cached response and do background validation HTTP/1.1 200 OK Host: localhost:8001 Date: Mon, 13 Jan 2020 07:30:51 GMT Cache-Control: public, max-age=2, stale-if-error=60 Content-type: text/html; charset=UTF-8 X-Varnish: 8 6 Age: 11 Via: 1.1 varnish (Varnish/6.3) Accept-Ranges: bytes Content-Length: 0 Connection: keep-alive $ curl -IX GET "http://localhost:8001/?id=${ID}&max-age=2&stale-if-error=60" # Now we see the error HTTP/1.1 500 Internal server error Host: localhost:8001 Date: Mon, 13 Jan 2020 07:31:07 GMT Content-type: text/html; charset=UTF-8 X-Varnish: 32770 Age: 0 Via: 1.1 varnish (Varnish/6.3) Connection: keep-alive Transfer-Encoding: chunked |
@jderusse I’ve seen in Twitter that you’re doing non-trivial use cases with Varnish, so maybe you could kindly answer a few Varnish questions here? First, can you confirm that Varnish does not serve |
@mpdude I never played with staled content in varnish. But, from the documentation varnish handle This custom VCL returns a stale response when status > 400 and use the delay stored in stale-while-revalidate I don't think that's a good model |
@mpdude Let's move on. I would still remove the switch and be non compliant here. |
@fabpot done: Switch removed, but left some comments in case someone reviews this in the future. |
5ec434a
to
ad5f427
Compare
Thank you @mpdude. |
This PR was squashed before being merged into the 3.4 branch (closes #35305). Discussion ---------- [HttpKernel] Fix stale-if-error behavior, add tests | Q | A | ------------- | --- | Branch? | 3.4 | Bug fix? | yes | New feature? | no | Deprecations? | no | Tickets | #24248 | License | MIT | Doc PR | This PR adds the first tests for `stale-if-error` logic in `HttpCache`. It also fixes an observation from #24248: For responses that have been cached as `public` with an `ETag` but without a lifetime, in case of an error the stale response will be served forever (= as long as the error persists), even beyond the configured `stale-if-error` grace period. Furthermore, it tries to improve compliance with RFC 7234: Stale responses must not be sent (under no condition) if one of * `no-cache` * `must-revalidate` * `proxy-revalidate` or * `s-maxage` (sic) is present. This can be found in the corresponding chapters of Section 5.2.2 for these directives, but is also summarized in [Section 4.2.4](https://tools.ietf.org/html/rfc7234#section-4.2.4) as > A cache MUST NOT generate a stale response if it is prohibited by an explicit in-protocol directive (e.g., by a "no-store" or "no-cache" cache directive, a "must-revalidate" cache-response-directive, or an applicable "s-maxage" or "proxy-revalidate" cache-response-directive; see Section 5.2.2). Because disabling of `stale-if-error` for `s-maxage` responses probably has a big impact on the usefulness of that feature in practice, it has to be enabled explicitly with a new config setting `strict_smaxage` (defaulting to `false`). Commits ------- ad5f427 [HttpKernel] Fix stale-if-error behavior, add tests
This PR adds the first tests for
stale-if-error
logic inHttpCache
.It also fixes an observation from #24248: For responses that have been cached as
public
with anETag
but without a lifetime, in case of an error the stale response will be served forever (= as long as the error persists), even beyond the configuredstale-if-error
grace period.Furthermore, it tries to improve compliance with RFC 7234: Stale responses must not be sent (under no condition) if one of
no-cache
must-revalidate
proxy-revalidate
ors-maxage
(sic) is present.This can be found in the corresponding chapters of Section 5.2.2 for these directives, but is also summarized in Section 4.2.4 as
Because disabling of
stale-if-error
fors-maxage
responses probably has a big impact on the usefulness of that feature in practice, it has to be enabled explicitly with a new config settingstrict_smaxage
(defaulting tofalse
).