Rails page caching, nginx, SSI, Ajax and form POSTS 43

Posted by science on February 22, 2008

Fence post

A couple of great blog posts have come out in the last year, dealing with how to squeeze more performance out of Ruby on Rails. These techniques are actually massively improving the performance of Ruby on Rails, so much so that I’m starting to think of Rails less as an Application Framework and more as an Web Page Template Generator.

For background here are two of the best articles on using page caching, nginx and SSI to radically improve performance on Rails while sacrificing nothing:

http://theexciter.com/articles/dynamic-page-caching-with-nginx-ssi

http://blog.kovyrin.net/2007/08/05/using-nginx-ssi…

These techniques are great. They apply to you if your application:

1) Generates lots of pages with unique URL’s and each such page is accessed over and over.

1.a) For example, your site might have a single action which displays information about various regions around the country. It’s a single action which pulls from a database to generate info about Hawaii or the Gulf Coast, FL. Your URL’s look like:

/region/info/Gulf-Coast-Florida

/region/info/Hawaii

2) Each page is relatively slow (> 2s?) and resource consuming to build but doesn’t change all that often. You might assemble this region info from ActiveResources and XML feeds and what not, but once you’ve assembled the content on the page it might be valid for weeks or months.

3) Each page has some amount of dynamic content that prevents you from using page caching.

3.a) Each page might have a “login” link, or if the user is logged in it might be a link to their account saying “Welcome back Science! (account)”

In the traditional Rails model, you might just live with a 4s load time and move on with other projects. But with nginx SSI and page caching you don’t have to. You can build the dynamic content as a partial (like an Ajax request which returns an HTML partial). The rest of the page you build as fully page cached to disk or memcache. Nginx reads your SSI tag and calls back to Rails for only the very lightweight frequently changing dynamic content to assemble the entire page.

That’s pretty cool and the other articles cover this much in greater detail and clarify. Read up!

Science has been working on extending this work ever so slightly. What if your actions are CRUD and not only do they generate the above slowly changing dynamic content for GET requests but they also generate variable search results for form POST’s or AJAX requests (or WSDL or whatever): they behave differently, yielding alternative content depending on the CRUD or request method.

In the caching model above, you’re out of luck. Your nginx server is going to happily serve out your static page to all requests no matter what. A form or ajax post will simply yield the full page content just like before.

One way to solve this is to change your URL’s so that the request method is parsable on the command line:

/ajax/region/info/Hawaii

/post/region/info/Hawaii

But that kind of blows huh?

Another way to handle this is much more in the spirit of CRUD: Configure Nginx to by-pass POST requests and only serve the static page caches on GET requests. This makes perfect sense from an HTTP perspective: GET’s are cachable and POST’s reflect dynamic data. Perfect distinction. Since Ajax sends (generally speaking) its requests as POST’s everything fits together.

So get out there and add code to nginx to force it to skip page caching for POST’s. Here’s some snippets from my nginx.conf to give an idea of one way to solve this problem:

# rewrite any trailing "/" without the trailing "/"
# this is a work around - we must force nginx to treat pages with trailing slashes
# exactly like pages without them, so the page cache detection works correctly below
rewrite ^/(.*)/$ /$1 last;

location / {
  # turn ssi capability on
  ssi on;
  proxy_set_header  X-Real-IP  $remote_addr;
  proxy_set_header  X-Forwarded-For $proxy_add_x_forwarded_for;
  proxy_set_header Host $http_host;
  proxy_redirect false;
  proxy_max_temp_file_size 0;

  location ~ ^/(images|javascripts|stylesheets)/ {
    expires 10y;
  }

  # don't page cache any POST requests
  if ($request_method = POST) {
    proxy_pass http://mongrel;
    break;
  }

  # if we find the cached file on disk now (i.e. it's a GET request) serve it
  if (-f $request_filename.html) {
    rewrite (.*) $1.html break;
  }

  # if we find index.html version of request in root of website, it's also a cached file so serve it
  set $index 'index.html';
  if (-f $request_filename$index) {
    rewrite (.*) $1$index break;
  }
  # if we find index.html files anywhere else, serve as cached
  if (-f $request_filename/$index) {
    rewrite (.*) $1/$index break;
  }
  # otherwise if we don't find the file on disk, hand it off to mongrel/Rails to process
  if (!-f $request_filename) {
    proxy_pass http://mongrel;
    break;
  }
}
Trackbacks

Use this link to trackback from your own site.

Comments

Leave a response

  1. science Tue, 08 Apr 2008 08:15:44 UTC
  2. links for 2008-04-10 « Bloggitation Wed, 09 Apr 2008 16:33:30 UTC

    [...] Rails page caching, nginx, SSI, Ajax and form POSTS (tags: ruby rails web nginx sysadmin) [...]

  3. Jauder Ho Thu, 10 Apr 2008 00:45:52 UTC

    Thanks for the article. This is all very useful.

    Have you done any experimentation with Varnish at all? I am considering a setup of varnish >> nginx >> mongrel/thin

  4. Bernard Tue, 10 Mar 2009 05:17:09 UTC

    Thanks for the article. What about if you are serving dynamic css from your rails app which is then cached in public/cache folder. How would you write the rewrite rules? These are my write rules for apge serving:
    if (-f $request_filename) {
    break;
    }

    if (-f $document_root/cache/$uri/index.html) {
    rewrite (.*) /cache/$1/index.html break;
    }

    if (-f $document_root/cache/$uri.html) {
    rewrite (.*) /cache/$1.html break;
    }

    if (-f $document_root/cache/$uri) {
    rewrite (.*) /cache/$1 break;
    }

    if (!-f $request_filename) {
    proxy_pass http://mongrel;
    break;
    }

  5. science Tue, 10 Mar 2009 09:19:32 UTC

    Bernard: Serving dynamic CSS shouldn’t be any different from serving dynamic RHTML. Some pages you want caching, some you don’t. So you have to decide if your CSS pages should be cached..

    Assuming you want your CSS cached, you’ll want to be testing for existence of “.css” files in addition to html.

    Air-coding that solution, I’d say the nginx solution would be to add this line above your mongrel call:

    if (-f $document_root/cache/$uri.css) {
    rewrite (.*) /cache/$1.css break;
    }

    Does that make sense?

  6. Rex Fri, 22 Aug 2014 09:09:06 UTC

    trumps@wheels.lively” rel=”nofollow”>.…

    good info….

  7. Shannon Sat, 23 Aug 2014 17:25:00 UTC

    regulating@ordering.impenetrable” rel=”nofollow”>.…

    спс за инфу!!…

  8. dave Tue, 26 Aug 2014 03:22:44 UTC

    property@anesthetic.desirable” rel=”nofollow”>.…

    tnx for info!!…

  9. Adam Tue, 26 Aug 2014 05:44:22 UTC

    fondness@preliminaries.servatius” rel=”nofollow”>.…

    thanks for information!!…

  10. Claude Tue, 26 Aug 2014 09:31:33 UTC

    areaways@chattels.foals” rel=”nofollow”>.…

    сэнкс за инфу!…

  11. Russell Tue, 18 Nov 2014 16:21:54 UTC

    alarmed@cody.screech” rel=”nofollow”>.…

    ñïñ!…

  12. Ron Thu, 20 Nov 2014 19:37:16 UTC

    widower@longrun.goggles” rel=”nofollow”>.…

    ñïñ….

  13. sergio Fri, 21 Nov 2014 08:11:14 UTC

    salami@concession.drunken” rel=”nofollow”>.…

    thank you!…

  14. Victor Fri, 21 Nov 2014 17:48:17 UTC

    hasnt@reactivated.tactlessness” rel=”nofollow”>.…

    thank you!!…

  15. ronald Sun, 23 Nov 2014 07:41:36 UTC

    skeletons@budd.stiffness” rel=”nofollow”>.…

    thanks for information!!…

  16. francisco Mon, 24 Nov 2014 00:35:49 UTC

    godunov@lowered.discouraged” rel=”nofollow”>.…

    tnx for info!…

  17. Lance Fri, 28 Nov 2014 07:07:29 UTC

    coherence@dodged.crooning” rel=”nofollow”>.…

    ñïàñèáî çà èíôó!…

  18. lyle Sat, 29 Nov 2014 16:38:18 UTC

    leukemia@secretary.kafka” rel=”nofollow”>.…

    ñïñ!…

  19. Glen Sun, 30 Nov 2014 04:25:05 UTC

    overwhelming@stoppage.tricky” rel=”nofollow”>.…

    áëàãîäàðñòâóþ….

  20. francis Mon, 08 Dec 2014 09:12:47 UTC

    harsher@edwin.mazowsze” rel=”nofollow”>.…

    ñýíêñ çà èíôó….

  21. Christian Wed, 10 Dec 2014 03:37:18 UTC

    jean@heellotushanover.gras” rel=”nofollow”>.…

    good!…

  22. kyle Thu, 11 Dec 2014 11:48:11 UTC

    philippi@burrow.collaborators” rel=”nofollow”>.…

    ñýíêñ çà èíôó!…

  23. Alan Thu, 11 Dec 2014 14:33:50 UTC

    magnetic@geochemistry.lifeboats” rel=”nofollow”>.…

    ñýíêñ çà èíôó….

  24. arturo Wed, 17 Dec 2014 13:21:42 UTC

    darlay@resulted.combellack” rel=”nofollow”>.…

    ñïàñèáî çà èíôó!…

  25. Perry Sun, 21 Dec 2014 20:10:28 UTC

    fiedlers@schumanns.vielleicht” rel=”nofollow”>.…

    good….

  26. sam Sun, 21 Dec 2014 20:49:09 UTC

    editing@teakettle.readable” rel=”nofollow”>.…

    ñïñ….

  27. tony Sun, 21 Dec 2014 21:27:07 UTC

    garlic@sniggered.beckett” rel=”nofollow”>.…

    thank you!…

  28. Bob Tue, 23 Dec 2014 05:37:03 UTC

    saint@horribly.ditches” rel=”nofollow”>.…

    tnx for info!…

  29. Oscar Tue, 23 Dec 2014 06:08:11 UTC

    underpins@crispin.inhibited” rel=”nofollow”>.…

    áëàãîäàðåí….

  30. howard Tue, 23 Dec 2014 06:39:10 UTC

    taylor@solemn.scions” rel=”nofollow”>.…

    ñïñ çà èíôó!!…

  31. adam Wed, 24 Dec 2014 05:24:41 UTC

    pondering@andrus.islam” rel=”nofollow”>.…

    ñïàñèáî çà èíôó….

  32. Brandon Wed, 24 Dec 2014 23:43:38 UTC

    crimsoning@leaning.occipital” rel=”nofollow”>.…

    tnx!…

  33. Ted Thu, 15 Jan 2015 10:04:11 UTC

    yokosuka@discipleship.paginated” rel=”nofollow”>.…

    tnx….

  34. Alex Thu, 15 Jan 2015 10:33:02 UTC

    feversham@expeditions.oversoft” rel=”nofollow”>.…

    áëàãîäàðþ!…

  35. brett Sat, 17 Jan 2015 20:28:11 UTC

    pulpits@rimanelli.constrained” rel=”nofollow”>.…

    ñýíêñ çà èíôó….

  36. Lyle Fri, 23 Jan 2015 15:27:14 UTC

    deplorable@rotenone.decanting” rel=”nofollow”>.…

    tnx for info….

  37. Duane Sun, 25 Jan 2015 06:16:52 UTC

    souvanna@subside.departures” rel=”nofollow”>.…

    tnx for info!…

  38. Fredrick Sun, 25 Jan 2015 16:45:30 UTC

    lovejoys@mountainside.sequel” rel=”nofollow”>.…

    thanks for information….

  39. Carlos Wed, 28 Jan 2015 09:00:38 UTC

    penned@pennants.vociferousness” rel=”nofollow”>.…

    áëàãîäàðåí!…

  40. Jay Wed, 28 Jan 2015 09:35:31 UTC

    superhuman@impaling.unemotional” rel=”nofollow”>.…

    ñïàñèáî çà èíôó….

  41. Brent Wed, 04 Feb 2015 10:33:17 UTC

    gregarious@caryatides.wisconsins” rel=”nofollow”>.…

    áëàãîäàðåí!…

  42. eduardo Tue, 10 Feb 2015 03:46:58 UTC

    subtleties@burglarproof.jobless” rel=”nofollow”>.…

    áëàãîäàðñòâóþ!!…

  43. morris Thu, 12 Feb 2015 21:58:25 UTC

    rheumatism@wartime.indigenes” rel=”nofollow”>.…

    ñïàñèáî çà èíôó!!…

Comments