Hi guys,
I've read http://drupal.org/node/147310 with great interest.
I just wanted to "chime in" with a solution I helped create (after 9/11 - working for several large danish sites) which gives a very high performance benefit.
What we did was, to still output the no-cache etc. headers - ensuring that client proxies (proxies at the visitors site of the connection) didn't cache.
Then we told squid (in test - the actual setup was run on what is now known as bluecoat cache servers) to ignore caching headers and cache EVERYTHING INDEFINETELY (as long as the "login cookie" wasn't in the client request) - and we defined a few "dynamic" urls (the page the login POST was sent to etc.).
To update the cached content, the site-code sent PURGE requests with the URL to purge to the client. (they were simply put in a table - and a script was written to pull url's from the purge-table and connect to the relevant caching machines and purge the url's).
squid implements the PURGE command (simply put it instead of GET) and it can be implemented in VCL for varnish - there's an example about that.
This solution has these upsides:
- If the database server (or webserver for that matter) crashes - the site keeps on going - except for the few dynamic URL's.
- optimal caching - as much as possible is cached
- you can scale easily - as it's easy to add more caching machines. easier than adding several webservers - sharing a database etc.
and (as far as I can see) these downsides:
- if a url (f.ex. /node/23234) is updated - you have to figure out,if f.ex. there's a "latest news" block - and if the update changes this block - every page containing that block must also be purge'd.
There is however a fix for that downside - ESI (Edge Side Includes) - which is in Squid 3.0 and Varnish 2.
Edge Side Includes, means that you split your site (the HTML output) into "chunks" which is delivered from different URL's just like frames - except the caching machines assembles the page.
An example could be like this:
/front ->consisting of what is delivered from /front and this extra page /block/latestnews
The caching machine will then assemble the page and cache the result. if /front (or as the earlier example /node/23234) is updated - you simply purge /block/latestnews and /front (or /node/23234) and the caching machine will re-assemble the pages that include /block/latestnews.
IMHO this is the something near an optimal performing solution, with immediate (as close to) updating when content added/updated, for reverse proxy setups.
I hope you like the idea :)
Comments
Comment #1
sdboyer commentedsubscribe
Comment #2
kbahey commentedLooks interesting.
It would be nice if we can develop standard ESIs for Drupal that would flush the appropriate parts of the cache when the underlying entities are changed.
However, Drupal's cache right now does a flush all when a node is changing, so before we can do that we have to make Drupal's cache more granular, and avoid a flush thundering herd.
Comment #3
klavs commentedIt looks like someone is already working on doing ESI for Drupal 6:
http://dc2009.drupalcon.org/session/drupal-edge
Comment #4
mfbsubscribe
Comment #5
rickvug commented@klavs Any update here, perhaps in a different issue? Edge Side Includes would be an excellent update. Sounds like something for Drupal 8 at this point.
Comment #6
klavs commentedLast time I looked, I only found one hit (which I can't seem to find right now) about one big american site, that used ESI and someone with a "talk" for drupalcon (don't think he actually got to do the talk though) about how he created a module to deliver the seperate page parts seperately to do ESI.
I also just found someone here, working on something that could be usable to implement ESI: http://drupal.org/node/495968
I haven't had any time or need to work on this, since I haven't found anyone who wanted to work on ESI instead of just throwing more "iron" and reverse proxyies with cache at the problem :)
I am not that experienced in writing drupal modules, so I probably wouldn't be the best to develop such a thing either - it is definetely a challenge to write ESI support for such a configurable system as Drupal - without it being overly complex to actually use.
Having a module that can deliver parts - and then perhaps just enable content using this module in the theme would be one way, to let people start using ESI - for atleast parts of the pages. (Latest news, Latest blog entries and others that might get heavy to show on every page on a site).
Comment #7
davideads commentedMany of the suggestions here are interesting (ESI could be very useful for Drupal, though I wonder if serving Drupal with nginx + boost module might not have similar results in terms of performance with a lot less hackery), but I'm not entirely convinced the approach in the parent is entirely wise.
Varnish allows for a grace period to serve expired cache objects if you experience failure of your backend(s). Surely, a reasonable TTL plus a well tuned grace period will achieve near-optimal performance for sites with a lot of (basically) static content without the downsides suggested. In high traffic situations, often a few pieces of content will be targeted -- even short TTLs should allow Drupal to serve hundreds or thousands of end user requests with just a few requests made of Drupal, and still be adequately dynamic.
I wonder why you would using a caching proxy and then ask Drupal to do no work at all. Similarly, by sending purge requests, you unnecessarily complicate your Drupal code. Transparent proxies should be, well, transparent. Now your underlying application depends (albeit VERY loosely) on the caching proxy in front of it. Again, that should ring bells that maybe you should be implementing your solution against Drupal itself -- custom caching in Drupal is possible, and can also provide adequate performance and good granularity (since you can write site specific rules with full access to Drupal's guts and state at the time of request).
It seems to be a better way of accomplishing the same end result would be publishing your whole site to static HTML (or parsed XML, or something else -- just something essentially static). I worked on a Plone project which worked like this, where the site was "staged" and then "pushed" to what amounted to static HTML when users updated content. It was a bit of a pain, but the production site was really freakin' fast, and that was using a pretty standard Apache configuration.
Indefinite caching is also not really an adequate failover mechanism -- proxies like Varnish explicitly implement systems like grace periods. I'd rather have a monitoring solution which paged me if httpd failed on my production Drupal site (or some similar failure event occurred) and a grace period to allow me to patch up the problem than the possibility that the wrong content is served indefinitely (and will soon, as a result of my work).
Comment #8
joshk commentedFYI: http://drupal.org/project/varnish
Drupal 7 supports the basic cache-control headers which are necessary to use Varnish as a cache for full pages. Pressflow backports this functionality to version 6.x for those who need it.
I'm definitely interested in working with people to develop/implement ESI solutions.
Comment #9
very_random_man commentedESIs are very useful if you want to cache most of your page content but still need some areas to be dynamically served to all (even anonymous) users.
I worked on a high volume drupal 5 site that used varnish with a few ESIs to populate areas of the site that needed to be dynamic for everyone, most notably a breadcrumb that tracked the user's journey and any other blocks that were dependent on this user journey.
My memory is a bit hazy as I worked on this about a year ago, but i seem to remember the main problem was that each ESI would hit Drupal and cause a full bootstrap. This meant that our site with four dynamic areas would have actually caused a greater load on the db than the non-cached site.
To get around this I created a lightweight service framework that only did a partial bootstrap, enough to get the functionality we wanted working. However, four of these would still have caused too many db hits.
The final solution was this. When the lightweight service page was called via the ESI, it could generate a key for the content that is was asked for and check the cache (memcache in this case) for this content. If it couldn't find any (which it probably wouldn't on the first page hit), it would do a partial bootstrap and hit a drupal service which would then generate and preacache all required dynamic content for the entire page, whilst returning the content for the first request. Each successive ESI request would then never hit drupal as content was already available in the cache.
I seem to remember that the key was unique to the situation so that it was possible that the cache was effective for multiple users if they happened to use the site in the same way.
This seemed to do the trick at the time and people were happy with the performance although I left the company shortly after so i don't know how it progressed from there.
Hope this is of use to someone! :-)
Comment #10
sun.core commentedComment #11
SqyD commentedI've written a module, Purge, that implements the Purge functionality thought http requests. It should work with both Varnish and Squid. Right now it's still in the waiting line for a review (hint,hint:-) here #938548: SqyD [sqyd] but I hope to have it in contrib soon. It requires the expire module that in turn is a splitoff of the boost code to detect urls that need to be flushed from cache. Having that functionality in core would be good to have so different contrib modules (boost, varnish, purge, etc) can be used when appropriate.
Comment #12
sunIf you want this, please help with http://drupal.org/project/issues/search/drupal?issue_tags=WSCCI