Looking Towards Performance With YSlow

Recently during one of my trips down the internet rabbit hole I came across YSlow. This sweet little extension to Firebug blew me away. Firebug is already extremely excellent, I would say that it makes web development possible, but YSlow brings it all the way to your server and makes server optimization possible.

We all know that we should be worried about performance. That’s what everyone keeps telling us (but… will it scale?!?). And we’ve heard of all kinds of techniques that sound pretty cool which we should probably look into one day. And we always keep putting off performance optimization, after all it would be premature. Would my user even notice? But, thanks to YSlow I was able to easily identify at least a half a dozen things that I could do within minutes to improve my performance. All in all it took a couple of hours, mostly spent searching for how to enable the features in Rails an nginx that improve performance.

I won’t keep you waiting any longer, here’s how I turned my performance grade from a 48 to an 80:

1. Enable gzip compression

I’m using nginx, I know almost nothing about it (it’s Russian!), but because it is a well designed piece of software it was incredibly simple to accomplish a simple task. First: search google for nginx + gzip. Second: crack open your trusty ol’ nginx.conf. Third:

    gzip            on;
    gzip_comp_level 3;
    gzip_proxied    any;
    gzip_types      text/plain text/html text/css application/x-javascript text/xml application/xml application/xml+rss text/javascript;

Restart nginx and witness the savings.

2. Make Fewer HTTP Requests

I was serving something like 6 js files and 7 CSS files. Keeping them seperate sure makes it easy to navigate during development, but can really multiply your HTTP request count. Thanks to Rails this one is an easy fix too:

Before:

    = javascript_include_tag 'prototype', 'effects', 'dragdrop', 'application', :juggernaut
    = stylesheet_link_tag :all

After:
    = stylesheet_link_tag :all, :cache => true
    = javascript_include_tag 'prototype', 'effects', 'dragdrop', :juggernaut, :cache => 'base'
    = javascript_include_tag 'application'

Rails has caching enabled by default when running in production mode, but in order to take advantage of it you either need to read the docs or google that shit need to tell it what to cache. This gathers your multitude of files into one or two little happy files. They get even little-er when gzipped, though I am unsure what that does to their happiness. I also took this time to put my CSS above my JS. If possible the JS should be loaded nearer the bottom of the page (tell me more…). I’ll count that as a third and fourth thing I did to improve performance.

5. Add an Expires Header

If you’re like me you’re probably thinking: “What would Stevey do? Should I really become a wino? … and btw wtf is an expires header?” Step one. Step two:

        if ($request_uri ~* ".(ico|css|js|gif|jpe?g|png)?[0-9]+$") {
            expires max;
            break;
        }

Blast that into your nginx.conf. I’m not going to explain it. If you are still confused see step 1.

Well as you can see it was not quite half a dozen things, even with my creative counting. I hope this will help you get started on your road to glorious optimization using YSlow to tweak your Rails and nginx configurations. It’s tired in here… *honk-shu*

What a drag: cancelling with onStart

So in this awesome new web application that I am writing I’ve got this totally sweet window/widget/bazfoo system where users can drag stuff around and it will remember the positions. This is all with Ruby on Rails and Prototype and Scriptaculous, and although the specifics are pretty Scriptaculous specific the generalities can apply to you favorite framework, unless it is … well they could probably apply.

Generally the UI doesn’t care what users want to drag when, but sometimes it does care. Say for example players can move stuff around in other players’ houses (to make it look like a ghost was there or something). This is very cool. Problem: what about not dragging things when not in ‘move-stuff-around-like-a-ghost’ mode? Perhaps there should only be one mode and things can always be moved around like a ghost, but even then, based on context, it seems that you might just not want the player to move stuff sometimes. I didn’t. Even though ‘rampant-ghost’ was the only mode I had so far I could envision wanting to drag an area-of-effect-spell-deployment or power meter or anything else without worrying about dragging the furniture.

So I had to sometimes cancel dragging like some companies sometimes cancel bonuses, but unlike those companies I decided to cancel during the onStart callback, not the somewhereInTheMiddleGodKnowsWhy callback. Scriptaculous provides a convenient hook to onStart (but surprisingly lacks one to halfwayThereButSimultaneoslyCutYourPay. They don’t have it? I know, it’s silly!). So lets just throw some code down to keep it real:

function drag_start(draggable, event) {
  draggable.element.should_revert = true; 
 
  if($current_action == null || $current_action.id != "ghost_party_action") {
    draggable.finishDrag(event, false);
  }
}

This is a pretty simple function, really standard, we have all our draggables sign it. First tell the element that it should revert unless it hears otherwise. Then check the $current_action (the $ lets me know I’m using it as a global, just like Ruby). If the current action doesn’t exist or it’s not the one where the ghosts party, then finish it off like Houchen. Great. Except, it still kind of drags and gets all weird. I could have sworn I was using the onStart callback, not the getWeirdAnyway callback… too bad.

So time to dive into Scriptaculous, source code that is! The file is dragdrop.js, the year 2008, film Noir has lost popularity in recent years but is still present in the minds of… The functions of interest are:

  updateDrag: function(event, pointer) {
    if(!this.dragging) this.startDrag(event);
    // ... Lots more omitted
  }
 
  startDrag: function(event) {
    this.dragging = true;
    // ... Lots of setup, initialization ...
 
    // Bingo!
    Draggables.notify('onStart', this, event);
 
    if(this.options.starteffect) this.options.starteffect(this.element);
  },

updateDrag gets called first when the user wiggles that mouse over the element. updateDrag then calls startDrag, which then calls your callback, which then ends the drag, but updateDrag is still in the dark, so let’s enlighten it:

  updateDrag: function(event, pointer) {
    if(!this.dragging) this.startDrag(event);
    // Added part
    if(!this.dragging) {
      Event.stop(event);
      return;
    }
 
    // Lots more stuff stays the same
  }

See what happened there? updateDrag wasn’t expecting the drag to end so soon after it got started! Now it knows, let that be a lesson. This keeps it from going all loosey goosey everywhere.

Now that is how you cancel a drag with onStart. Leave a comment, it will probably be at least as cool as the UK lottery comment. Also subscribe to my RSS and tell a friend, it’s like twice a month that I publish anything and then you can just scroll past it in Google Reader to get back to your 300 unread TechCrunch posts. Peace!