Search code examples
cachingsymfonysymfony-2.1esi

With Symfony2 why are ESI tags inside cached responses ignored?


I have an ecommerce application that I'm try to set up for caching - initially via the Symfony2 Reverse Proxy, but then ultimately via Varnish in production. I'm using Symfony 2.1.8 on Apache2.

My problem is I cannot get the ESI tags to be re-checked for each request when the primary controller action is cached (important for private content like the basket contents), but I don't understand why.

For example, I cache the homepage with the following code:

public function indexAction(Request $request)
{
    // check cache
    $homepage = $this->getHomepage();

    $response = new Response();
    $response->setPublic();
    $etag = md5('homepage'.$homepage->getUpdated()->getTimestamp());
    $response->setETag($etag);
    $response->setLastModified($homepage->getUpdated());

    if ($response->isNotModified($request))
    {
        // use cached version
        return $response;
    }
    else
    {
        return $this->render(
            'StoreBundle:Store:index.html.twig',
            array(
                'page' => $homepage
            ),
            $response
        );
    }
}

The rendered template extends the base layout template which includes the following ESI to show the basket:

{% render 'PurchaseBundle:Basket:summary' with {}, { 'standalone': true } %}

(Edit: After reading Diego's answer, I have also used the recommended syntax:

{% render url('basket_summary') with {}, {'standalone': true} %}

Unfortunately this had not made any difference.)

I've been playing with the code for the basket summary quite a bit, but this is what I have at present.

public function summaryAction()
{
    $response = new Response();
    $response->setPrivate();
    $response->setVary(array('Accept-Encoding', 'Cookie'));

    if ($this->basket->getId())
    {
        $etag = md5($this->getUniqueEtag());
        $response->setLastModified($this->basket->getUpdated());
    }
    else
    {
        $etag = md5('basket_summary_empty');
    }

    $response->setETag($etag);

    if ($response->isNotModified($this->request))
    {
        // use cached version
        return $response;
    }
    else
    {
        return $this->render(
            'PurchaseBundle:Basket:summary.html.twig',
            array(
                'basket' => $this->basket
            ),
            $response
        );
    }
}

On pages other than the homepage (which are not cached yet) the basket summary caching works just fine, it always displays the correct data. It's only when you return to the homepage that you'd see outdated information. Logging confirms that summaryAction is not called on the homepage unless indexAction actually renders.

Edit

Using error_log($kernel->getLog()) after each page request I get this for a non-cached page:

GET /categories/collections: miss; GET /_internal/secure/PurchaseBundle:Basket:summary/none.html: stale, valid, store; GET /_internal/secure/CatalogBundle:Search:form/none.html: miss; GET /esi/menu/main: fresh

And this for the cached homepage:

GET /: fresh

I must be missing something obvious, but the documentation doesn't appear to cover this, yet it implies it's just the sort of thing ESI is supposed to be used for.


Solution

  • It appears that ESI in Symfony2 doesnt work with the Latmodified/Etag cache validation structure that you used. See here: https://groups.google.com/forum/?fromgroups=#!topic/symfony2/V4BItzpLbOs

    Similar question here: Edge Side Includes and validation cache in Symfony 2

    I've been trying to do the same as you, but can only get ESI to work by using expiration cache.