Skip to content

Add new isExpirable() method and consider s-maxage=0 cacheable #22035

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

Closed
wants to merge 5 commits into from
Closed

Add new isExpirable() method and consider s-maxage=0 cacheable #22035

wants to merge 5 commits into from

Conversation

mpdude
Copy link
Contributor

@mpdude mpdude commented Mar 17, 2017

Q A
Branch? 2.8
Bug fix? yes
New feature? no
BC breaks? no
Deprecations? no
Tests pass? yes
Fixed tickets
License MIT
Doc PR

As in #22033, I am trying to find an issue with the HttpCache handling responses with s-maxage=0.

Though I cannot find a place in the RFCs that explicitly states it, I have always interpreted a max-age of zero to mean "you may cache this, but you must revalidate it before it is used the next time".

This is useful for example when using Symfony's HttpCache to keep the entire response but revalidate it every time using an ETag or Last-Modified information.

@nicolas-grekas
Copy link
Member

nicolas-grekas commented Mar 17, 2017

Introducing a new method is a new feature, changing a behavior a "bc break". This PR does both but is classified as bug fix. Triggers a red flag to me, needs to be well discussed/justified.

@nicolas-grekas nicolas-grekas added this to the 2.8 milestone Mar 17, 2017
@mpdude
Copy link
Contributor Author

mpdude commented Mar 17, 2017

Regarding the method, it could be private as well.

Regarding changing behavior, every bug fix is a BC break 🙈

@Nek-
Copy link
Contributor

Nek- commented Mar 17, 2017

isCacheable now ignores the ttl. I agree with @nicolas-grekas for the fact that it needs a discussion.

@mpdude
Copy link
Contributor Author

mpdude commented Mar 17, 2017

You're right, I missed that...

But do you agree that a it would be fine to cache a response with s-maxage=0 as long as it will immediately (i.e. on the next request) be revalidated? In other words, even a response that is stale might be worth caching because it might successfully revalidate later on.

In HttpCache, a response fetched from the backend will only be stored when it isCacheable:

https://github.com/symfony/symfony/blob/6b4cfd6/src/Symfony/Component/HttpKernel/HttpCache/HttpCache.php#L450

When looked up in the cache, here's the check whether the response is fresh enough:

https://github.com/symfony/symfony/blob/6b4cfd6/src/Symfony/Component/HttpKernel/HttpCache/HttpCache.php#L347

@xabbuh
Copy link
Member

xabbuh commented Mar 17, 2017

I am not sure if I get the root issue. What exactly is the point of caching a response with s-maxage being 0? Why would you want to cache a response if you need to fetch it again with the next incoming request anyway?

@mpdude
Copy link
Contributor Author

mpdude commented Mar 17, 2017

It may be expensive for your Controller to generate the complete response, but an If-Modified-Since check may be cheap.

By sending s-maxage=0 and using the HttpCache (for example), you can keep the response body around in the cache and get the (cheap) validation on every subsequent request.

@mpdude
Copy link
Contributor Author

mpdude commented Mar 17, 2017

Why's the test failing on PHP 7 😒 ?

@xabbuh
Copy link
Member

xabbuh commented Mar 17, 2017

You would have to tell the HttpKernel component to make use of version ^2.8.19 of the HttpFoundation component.

@mpdude
Copy link
Contributor Author

mpdude commented Mar 18, 2017

I missed that in order to make revalidation possible, the response needs an ETag or Last-Modified header.

If that is present, Response::isCacheable will already return true without even considering the TTL. So, the response will be cached and – as TTL will be expired next time it is looked at – revalidation be performed. 👍

@mpdude mpdude closed this Mar 18, 2017
@mpdude
Copy link
Contributor Author

mpdude commented Mar 18, 2017

In case you'd be interested, here's a test to safeguard this:

namespace Symfony\Component\HttpKernel\Tests\HttpCache;

class HttpCacheTest ...

    public function testServesResponseWhileFreshAndRevalidatesWithLastModifiedInformation()
    {
        $time = \DateTime::createFromFormat('U', time());

        $this->setNextResponse(200, [], 'Hello World', function (Request $request, Response $response) use ($time) {
            $response->setSharedMaxAge(10);
            $response->headers->set('Last-Modified', $time->format(DATE_RFC2822));
        });

        // prime the cache
        $this->request('GET', '/');

        // next request before s-maxage has expired: Serve from cache without hitting the backend
        $this->request('GET', '/');
        $this->assertHttpKernelIsNotCalled();
        $this->assertEquals(200, $this->response->getStatusCode());
        $this->assertEquals('Hello World', $this->response->getContent());
        $this->assertTraceContains('fresh');

        sleep(15); // expire the cache

        $this->setNextResponse(304, [], '', function (Request $request, Response $response) use ($time) {
            $this->assertEquals($time->format(DATE_RFC2822), $request->headers->get('IF_MODIFIED_SINCE'));
        });

        $this->request('GET', '/');
        $this->assertHttpKernelIsCalled();
        $this->assertEquals(200, $this->response->getStatusCode());
        $this->assertEquals('Hello World', $this->response->getContent());
        $this->assertTraceContains('stale');
        $this->assertTraceContains('valid');
    }

@fabpot
Copy link
Member

fabpot commented Mar 20, 2017

@mpdude Can you submit a PR with just this test then?

@mpdude mpdude deleted the response-cacheable-if-has-maxage branch March 21, 2017 12:35
fabpot added a commit that referenced this pull request Mar 21, 2017
…xpired TTL (mpdude)

This PR was squashed before being merged into the 2.7 branch (closes #22099).

Discussion
----------

HttpCache: New test for revalidating responses with an expired TTL

| Q             | A
| ------------- | ---
| Branch?       | 2.7
| Bug fix?      | no
| New feature?  | no
| BC breaks?    | no
| Deprecations? | no
| Tests pass?   | yes
| Fixed tickets |
| License       | MIT
| Doc PR        |

See #22035, in particular [this and the following comments](#22035 (comment)).

Commits
-------

067ab52 HttpCache: New test for revalidating responses with an expired TTL
symfony-splitter pushed a commit to symfony/http-kernel that referenced this pull request Mar 21, 2017
…xpired TTL (mpdude)

This PR was squashed before being merged into the 2.7 branch (closes #22099).

Discussion
----------

HttpCache: New test for revalidating responses with an expired TTL

| Q             | A
| ------------- | ---
| Branch?       | 2.7
| Bug fix?      | no
| New feature?  | no
| BC breaks?    | no
| Deprecations? | no
| Tests pass?   | yes
| Fixed tickets |
| License       | MIT
| Doc PR        |

See #22035, in particular [this and the following comments](symfony/symfony#22035 (comment)).

Commits
-------

067ab52 HttpCache: New test for revalidating responses with an expired TTL
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants