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.

有帮助吗?

解决方案 2

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.

其他提示

Since 2.0.20/2.1.5 Symfony2 takes a fully qualified URL instead of a controller logical path, as per CVE-2012-6431. So, instead of your current ESI:

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

You should create a route and then use the url method on twig:

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

Also note that today (March 1st) a new stable version of Symfony (2.2.0) has been released with major improvements on sub-requests management. With this new version, you can take two approaches (extracted from the master version of the HTTP Cache chapter in The Book):

{# you can use a controller reference #}
{{ render_esi(controller('...:news', { 'max': 5 })) }}

{# ... or a URL #}
{{ render_esi(url('latest_news', { 'max': 5 })) }}

The side notes on the current version of the linked chapter are worth reading as well, as they contain delicious pieces of information and nice tips that may help you finding the actual problem.

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top