Blog RSS Feed Subscribe

Jordi Boggiano

Jordi Boggiano Passionate web developer, specialized in web performance and php. Partner at Nelmio, information junkie and speaker.

Categories

ESI - Full page caching with Symfony2

Launched about a month ago, techup.ch runs on the Symfony2 PHP framework, which is still undergoing heavy development but is already a great framework.

Full page caching basics

Don't get me wrong, the framework is fast, pages are rendered by our fairly modest server in 40-50ms on average, so it hardly needs optimization. However I still wanted to try and squeeze more speed out of it, and also get a chance to play with cool stuff, so I decided to implement full page caching with ESI into the application.

The way this works is that you typically install some reverse proxy like Varnish, which will sit between the web and your http server. More complex setups might include another http server in front of varnish to gzip output but I won't go into details on that in this post. The purpose of the reverse proxy is that it will cache the output of your application, for as long as you specify in your Cache-Control header. Once a page is cached, it will just return the output to the clients straight, without ever hitting your http server, php, or your application. Needless to say this is ideal for performance. Symfony2 is a great match for this type of cache because it's supported natively as I'll show, and it also implements a reverse proxy layer in php, that can be used for development or on hostings where you can't have access to Varnish. It acts just the same and is automatically turned off if an ESI-capable proxy is added in front of php.

ESI awesomeness

Of course the issue with caching the entire output is that most sites have areas with dynamic content, especially when users are logged in. This is where ESI comes into play. ESI stands for Edge Side Includes, and is a standard that defines a way to tell reverse proxies how to assemble pages out of smaller bits, that can be cached for various amounts of time, or fully dynamic.

So if you take for example an event page on techup, you have two user-dependent areas, the "login with twitter" button, which turns into "@username" once you're logged in, and the "attend" button is also showing attend or unattend depending on the user viewing the page. Those two areas are ESI includes. What this means for the reverse proxy is that it will first try and fetch the main page content out of its cache, and if found, it will then process the <esi:include src="http://..." /> tags that it finds. Those tags contain the url to a sub-component of the page. So one url will point to an action in one of my controllers that only outputs an attend button, green or red depending on the user viewing it. The rest of the page is still taken out of the cache.

Each of those sub-components have their own Cache-Control header, which means that you can composite a page with various components that expire after various durations.

The way this is done in Symfony2 is pretty straightforward. Your controller actions must always return a response object, and all you need for the reverse proxy cache length is to set the shared max age of the response - beware, max age will apply to the entire page, so you really want to use the shared variant. It's as simple as calling $response->setSharedMaxAge(3600);, 3600 being the length in seconds.

In your templates, if you use Twig, and you really should with Symfony2, it is also quite easy to define an <esi:include /> tag. You call out the controller/action that you want to execute, give it some parameters, and specify it as being standalone which means it's an ESI include, for example {% render 'FooBundle:Default:attendButton' with ['event_id': event.id], ['standalone': true] %}. For more info on how to set that up feel free to go read the Symfony2 docs on the topic.

Invalidation woes

The tricky part, which is also a slightly controversial topic, is invalidation. In theory if you say that a page or sub-component is cache-able for X seconds, you should just live with it and let it be cached, even if the data changed. Now this is an acceptable downside on really high traffic sites, or in cases where only admins publish content and it doesn't really matter if it takes a few seconds/minutes to appear to the end users. But I like to give our users feedback when they add or change data, and I think they should see it straight away, so I decided to invalidate the cached pages in the proxy whenever the data is modified.

I will refer you to the docs as to how to actually setup support for purging (invalidating) caches in your proxy of choice, no point in repeating it all here, but what I want to share is the approach I took on actually managing invalidation. As you may know, invalidation can quickly get very tricky to handle. So what I did is just built centralized methods that contain all the invalidation logic for one domain model. When that model changes, it's passed to the matching method and all the urls that will render it are purged. This at least allows you to keep a good overview of the pages that are affected, and gives you a single point of entry to make adjustments to those invalidation rules.

// src/Application/FooBundle/Controller/FooController.php
protected function invalidateEvent($event)
{
    $args = array('event' => $event->getId(), 'title' => $event->getSlug());
    $this->invalidate('viewEvent', $args);
    $this->invalidate('home');
}

protected function invalidate($route, $parameters = array())
{
    $url = $this->router->generate($route, $parameters, true);

    $context = stream_context_create(array('http'=>array('method'=>'PURGE')));
    $stream = fopen($url, 'r', false, $context);
    fclose($stream);
}

This example implementation will do a PURGE request to the site URL. This only scales if you have one single Varnish instance though. I assume you must do a PURGE request on each if you have a redundant setup, but in this case it might become cleaner to use an external job queue like Gearman to execute those outside of php.

There are a few gotchas you should consider, especially if you use the Symfony2 reverse proxy and not Varnish. First of all one thing that is fairly obvious is that you must prevent anyone from purging stuff, otherwise attackers could DDoS you with PURGE requests and make your load skyrocket. The second issue is that if you return a 404 code for "Not purged" a.k.a the page wasn't cached, fopen() will throw a php warning, which is really not that nice. For this reason, and since I don't want to care whether the purge happened or not for now, I chose to just respond always with a 200. It could be handled nicer with curl though, if you really need to have a proper response code to your PURGE requests.

// app/AppCache.php
protected function invalidate(Request $request)
{
    if ($_SERVER['SERVER_ADDR'] !== $request->getClientIp() || 'PURGE' !== $request->getMethod()) {
        return parent::invalidate($request);
    }

    $response = new Response();
    if (!$this->store->purge($request->getUri())) {
        $response->setStatusCode(200, 'Not purged');
    } else {
        $response->setStatusCode(200, 'Purged');
    }

    return $response;
}

The results

It sounds nice and all, but is it actually working?

I used JMeter to benchmark the site with and without reverse proxy. Note that I used the integrated Symfony cache layer and not Varnish, so the results would be even better with Varnish since it's written in C and doesn't have to to hit apache and php on every request.

Before:

/ => 63req/sec
/86/rails-hock => 100req/sec
/api/events/upcoming.json => 70req/sec
/api/event/10.json => 120req/sec

After:

/ => 200req/sec *
/86/rails-hock => 230req/sec
/api/events/upcoming.json => 100req/sec *
/api/event/10.json => 800req/sec

* my 20mbps internet line was the bottleneck for those because they have too large response bodies

In short: Holy crap. Now for the two first pages tested, the improvement is "modest" because they include sub-components which are not cacheable, so they always require some full framework cycles. But the last one which is from the API is just amazing, with 8 times more requests processed per second.

All I can say to conclude is that this is worth playing with, and that Symfony2 really doesn't disappoint with regard to speed. If you have any experience with that kind of setup and want to add anything feel free to do so in the comments, questions are also welcome.

December 09, 2010 // PHP

Post a comment:


Formatting: you may use [code php] [/code] (or other languages) for code blocks, links are automatically linked. <strong>, <em> and <blockquote> html tags are allowed without nesting, the rest will be escaped.

Subscribe to this RSS Feed Comments

2012-01-19 02:13:43

buy kenalog fact

medicare diabetes supplies. autism awareness jewlery http://ordermodafinilonline.com provigil jelly bestellen child and adolescent needs and strengths autism spectrum profile free sample of effexor http://sokalarab.com/market/thread-22989.html kenalog 1 mg. 30 tabs cheapest cost prednisone dose for blocked ears staph infection of the skin.

2012-01-19 02:31:33

pharmacy glucophage glucophage cumwithuscom

Thanks for comments, http://www.sehribayburt.com/entry.php?593-Delivery-Drug-Indometacin-Spondylitis-Arthritis-Swelling-Tendinitis-Inflammati new drug indometacin us licensed pharmacies, 250323,

2012-01-19 03:01:38

ilosone online 56

2, http://level1radio.com/mybb/showthread.php?tid=30186 generic imipramine otc, 96166,

2012-01-19 03:06:36

generic pripsen overnight delivery express delivery

6, http://sokalarab.com/market/thread-19581.html atopica online without prescription, >:-]]],

2012-01-19 03:09:02

buy liv-52 30 day credit

vitamin d deficiency and allergy symptoms. Proscar for baldness. http://ordermodalertonline.com/text/pharmacy-tech-cheap-find-modafinil-lowest-prices-for-at09.html 150 modafinil mg order What is amlodipine besylate used for, Fertility and candidiasis,

2012-01-19 03:13:32

where to buy orapred 2000 mg

Thanks for comments, http://provigilforsale.com/text/how-to-store-modafinil-shop-250-mg-implants-coste9.html price on modafinil 80 mg, >:DDD,

2012-01-19 03:33:46

buy insulin the

Cool post, http://www.sahracing.com/index/forum/entry.php?2203-Clozaril-Oral-Studies.-Schizophrenia-Psychosis-Schizoaffective-Disorder-Cheap-Cl cheapest clozaril besylate 10mg, tynmv,

2012-01-19 04:31:32

buy promethazine compound w/ codiene

5, http://pnwoutdoors.com/blogs/annascott/13174-buy-moduretic-120-high-blood-pressure-diuretic-congestive-heart-failure.html cost of moduretic 80 mg, opdfnx,

2012-01-19 04:38:56

prescription avlocardyl besylate is used for what?

free recipes for high cholesterol Removing oral thrush. http://pnwoutdoors.com/blogs/marylewis/11180-buy-escitalopram-generic-antidepressant-social-anxiety-anxiety-depressi.html escitalopram los angeles mastercard Morgan spurlock vomits. pictures of nipple changes in breast cancer http://www.kubiak.cc/forum/showthread.php?tid=44377 reductil buy in usa allergy cap test. atorvastatin stable temperature

2012-01-19 05:01:29

principen long term buy

herpes initial symptoms breastfeeding a baby with milk allergy http://findslimex.com/text/florida-medical-clinic-landlord-to-cheat-medicare-by-hiv--aids-09.html obetrim online with cod shipping survive the coming economic depression. Black specks in vomit

2012-01-19 05:03:57

tavanic ciudad juarez pharmacies

zanaflex patient experiences Feliz celexa information http://knowyourpet.co.uk/entry.php?820-Mirtazapin-Internet-Pharmacies-Depression-Antidepressant-5mg-Mirtazapin-Buy order mirtazapin pills stroke recovery nutrients herbs Thyroid post surgery treatment.

2012-01-19 05:11:49

canada cheap drug melphalan in prescription

Nice comment, http://forums.posetech.com/entry.php/12910-Cheapest-1mg-Betamethasone-Mastercard.-Seborrheic-Dermatitis-Atopic-Dermatitis-Ps legality purchase betamethasone, dhlsn,

2012-01-19 05:36:11

seretide generic pharmacy

Cool site, http://www.lebay.us/entry.php/70819-Metoclopramide-Online-180.-Nausea-Ulcers-Vomiting-Gastroesophageal-Reflux-Diseas metoclopramide pharmaceutical sales representatives, 8-O,

2012-01-19 06:04:32

ethinyl-estradiol sales in past year

Nice comment, http://modalertbuyonline.com find buy modafinil, byq,

2012-01-19 06:14:32

cheap ivermectin la online

4, http://norwegiangamingzone.com/forum/showthread.php?tid=36927 prescription drug assistance for tramal, cwdv,

2012-01-19 06:38:52

low cost ciprofloxacin

Cool site, http://ordermodalert.com/text/buy-find-provigil-allergy-cold-fastmelts-get-cheap93.html provigil no prescription mastercard, kkhk,

2012-01-19 07:31:28

discount prexum generic called inhibitron

infertility and pregnancy loss support. treating hypertension through color diet. http://forum.zingzu.com/showthread.php?tid=104951 viagra-for-women overnight visa no prescription main symptoms of asthma. great depression era treatment of women http://www.pmz.tv/wp-content/plugins/zingiri-forum/mybb/showthread.php?tid=467239 catapres no rx florida aortic root cozaar tinnitus and aspirin http://neomania.eu/foorum/showthread.php?tid=6746 azulfidine online basal temperature graph femara causing joint pain Lasix eye surgery and reading glasses,

2012-01-19 07:34:08

buy premarin doll

3, http://www.rosteer.com/entry.php?766-Buy-200mcg-Hydrocortisone.-Dermatitis-Nappy-Rash-Eczema-Insect-Bites-Hydrocorti canada pharmacy hydrocortisone thyroid, %)),

2012-01-19 07:36:39

find ventolin for sale on line

4, http://normahhashim.com/text/upside-down-world-of-daylight-saving-time-returnsc0.html generic fluvoxamine cheap, sqlkym,

2012-01-19 07:39:08

price cyclosporine 10mg

Best site, http://fileserver.hostoi.com/Forum/showthread.php?tid=18024 cheap esomeprazole 2 day shipping, 8525,

2012-01-19 08:01:32

dewalt nano slimex and sales are

2, http://takumivelvet.com/text/health-insurance-wants-to-reward-non-smokers-in-switzerland55.html inexpensive fluconazole online, %P,

2012-01-19 08:31:29

what is the reductil prescription strength

3, http://norwegiangamingzone.com/forum/showthread.php?tid=37888 indometacin purchase with paypal, cwlph,

2012-01-19 08:34:12

ramipril pharmacy prices

Nexium and synthroid, naproxen sodium generic http://prothom-alojobs.com/forum/thread-33900.html order clobetasols overnight c.o.d on saturday Coumadin and dark green leafy vegetables hiv infection rates

2012-01-19 08:39:01

buy modalert d

lamisil and various doses Under armour myspace http://www.amanpalestin.net/english/forum/showthread.php?tid=114902 clozaril pharmacy oversea armour family project causes of frequent mild heartburn. http://www.learnlatvian.info/forum/showthread.php?tid=75809 under furadantin sale diet for gallbladder disease vomiting diarrhea augmentin http://forums.posetech.com/entry.php/12604-No-prescription-Patanol-5ml.-Eye-Allergy-Allergic-Conjunctivitis-Patanol-Perscrip get patanol prescription online Major mood disorders autism vaccines causes, http://bowibowibowi.web.id/entry.php?116-Order-Pill-Modalert.-Narcolepsy-Obstructive-Sleep-Apnea-Shift-Work-Sleep-Disorder modalert d for sale Causes of multinodular goiter, chronic sinus infections

2012-01-19 09:03:58

fexofenadin online om

Nice comment, http://couponcraver.com/forum/showthread.php?tid=203340 buy clarithromycin without being tracked, 8OOO,

2012-01-19 09:13:27

clobetasol brand prescription with out a prescription

Stopping prednisone abruptly. prednisone eye drops on line. http://pnwoutdoors.com/blogs/ericwhite/12881-metronidazole-buy-aspiration-pneumonia-meningitis-endometritis-pneu.html over the counter substitutes for metronidazole Alternative glaucoma treatment trileptal and birth control pills, http://7graphicsets.com/demo/1/forum/showthread.php?tid=24378 cheap female-pink-viagra 32 women under armour. metoprolol and thyroid, http://www.myprojectfree.org/wp-content/plugins/zingiri-forum/mybb/showthread.php?tid=110339 chivalry rimonabant store bone fracture &amp; thyroid. what does a thyroid do

2012-01-19 09:31:29

can betamethasone otc cause black stools

2, http://cheapestslimex.com/text/purchasing-cfc-sibutramine-sale-online-order-on-line-without-pre71.html good coffee cheaper than sibutramine sign, 981,

2012-01-19 09:36:27

slimex online uk american express

Nice comment, http://community.go-get-guys.com/entries/795-Punarnava-Online-Safety.-Swelling-Edema-Kidney-Function-Fluid-Retention-Otc-Pun online punarnava review, :-D,

2012-01-19 09:43:38

no prescription order mirapex online

4, http://zhyizhong.com/text/cipla-hydroxyzine-purchase-sale-from-vanuatu-bill-consolidation-4c.html obtain hydroxyzine prescription wothout a prescription, 8[,

2012-01-19 10:09:13

buy antidepressant prozac

nkf guidelines on the treatment of hypertension in ckd allergies to dog http://hfengineerers.com/showthread.php?tid=20093 india cheapest generic sumycin Does diabetes education work withdrawal symptoms from acyclovir http://www.sehribayburt.com/entry.php?132-Prozac-Price-Lists-Panic-Attacks-Antidepressant-Bulimia-Premenstrual-Dysphoric 2 prozac prescription bacterial meningitis symptoms. instant relief for migraine mercola, http://www.realestatesecrets101.com/entry.php?7703-Cheap-Ovral-g-32.-Menstrual-Irregularities-Endometriosis-Discount-Ovral-g-Discoun ovral-g no prescription cod wat doet reductil order celecoxib online.

First pagePrevious 1 - 2 - [3] - 4 - 5 - 6 - 7 - 8 - 9 Next Last page