28 August 2008

rails performance and msql driver

I'm sure this is documented all over the internet, except on the pages I happened to look at. I never knew until I was looking in my log for some other problem, and spotted this:

WARNING: You're using the Ruby-based MySQL library that ships with Rails.
This library is not suited for production. 
Please install the C-based MySQL library instead (gem install mysql).

At first I thought, what the hell, the ruby-based library "works on my machine", why bother? And I remembered how suckily my apps were performing. And "gem install mysql" - it couldn't really be easier, could it?

It couldn't except for a dependency on libmysqlclient15, to install which I first needed to update my ubuntu system (a 7.10 on Slicehost), and for the mysql gem I also needed to update rubygems itself. "gem install mysql" just refused to work until these two were done. My version of rubygems was hanging - apparently this happens if it hasn't been run in a while and needs to make a big refresh from the repository. Rubygems 1.2 doesn't have this problem, fortunately.

This was the full sequence:

sudo apt-get update
sudo apt-get install libmysqlclient15

wget http://rubyforge.org/frs/download.php/38646/rubygems-1.2.0.tgz
gunzip rubygems-1.2.0.tgz
tar xvf rubygems-1.2.0.tar
cd rubygems-1.2.0
sudo ruby ./setup.rb

sudo gem install mysql

mongrel_cluster_ctl restart

If you're thinking "what the hell, the ruby-based library works on my machine", change your thinking. This could transform your app from barely usable to pleasantly snappy.

27 August 2008

show/hide element using unobtrusive javascript

"Unobtrusive" javascript is all the rage these days, so I had to give it a go. The idea is, instead of sprinkling bits of javascript inside onclick and onmousemove and friends within your HTML, you use a class name or id to identify elements that will need some event handlers attached. Later, your javascript takes care of adding event handlers. Result: cleaner HTML, more control over javascript.

I was about to implement for the n-teenth time a little show/hide link pair when I realised, this is always the same, onclick this and onclick that; there must be a better way.

Enter Unobtrusive Javascript!!

<span style="color:blue" id="show_hidablestuff">show</span>
<span style="color:blue" id="hide_hidablestuff">hide</span>
<p id="hidablestuff" class="hidable">Now you can see this hidable stuff</p>

"Look, ma, no javascript!". I want to hide hidablestuff, so I give it a class name "hidable". There is no CSS associated with this class name, it's just a hook for javascript later on. Its corresponding show/hide links are related via their ids - show_hidablestuff and hide_hidablestuff. The script will calculate these ids in order to find their elements in the document.

The magic is here:

var initialiseShowHideTrigger = function(h) {
  var show_trigger = $("show_" + h.id); // assume the show trigger has the same id
                                        // as the target with "show_" prepended

  var hide_trigger = $("hide_" + h.id); // similarly for hide trigger

  show_trigger.onclick = function() { // add onclick handler to
    h.show();                         // show the target element,
    show_trigger.hide();              // hide the show trigger,
    hide_trigger.show();              // and show the hide trigger
  }

  hide_trigger.onclick = function() { // add onclick handler to
    h.hide();                         // hide the target element,
    show_trigger.show();              // show the show trigger,
    hide_trigger.hide();              // and hide the hide trigger
  }

  hide_trigger.onclick();             // trigger the hide trigger so initially
                                      // the target is hidden
}

// here we search for all elements with classname "hidable", and invoke
// initialiseShowHideTrigger on each. Call this from body.onload(), or
// from a script at the bottom of your page (after everything is loaded)
var initialiseShowHideTriggers = function() {
  $A(document.getElementsByClassName('hidable')).each(function(h) {
    initialiseShowHideTrigger(h);
  });
}

I plan to do a lot more of this.

22 August 2008

Evolution

There's the National Geographic Laurasia dinosaur poster hanging on the wall in front of me, and to the right, outside the window, a little bird is savaging our miniscule window-sill wheat crop. The bird seems perfectly content, as if she's been doing this for millions of years. And I thought, some of the creationist/intelligent-design anti-evolution arguments boil down to this:

4.320432 × 1017 is such a large number. There is no way you could get to that number, starting at zero, and just repeatedly adding 1.

Pyrenees

I took a week in the Pyrenees at the end of July. The advertising campaign launched by Luz St-Sauveur totally seduced me, so I just had to go. How could you resist this:

It was a great opportunity to explore the outdoor office work style. For example:

Alternatively,

And in the shade of the trees:

I thought I would try write code high up, but by the time I got up here -

I was exhausted, and it was too windy and there was no comfortable shady spot. I am possibly the first person ever to lug a heavy macbook up 600 metres of mountain with no food or water. But at least on my way up, I caught a glimpse of my obelisk office - I had found a nice shady spot under the tree directly in front of the obelisk, next to the climbing wall. This is the village of Saint-Sauveur, next to the "Thermes", apparently a kind of hot spa.

From time to time, when I was feeling hungry, I treated myself to a terrace:

of which there are many in Luz, three having free internet access.

Since returning to Paris, I've tried these places:

Montparnasse Cemetery: there is a cosy well-shaded bench next to Baudelaire's grave, as long as you don't mind the tourists. A lot of iconfu.com was written here.

The Louvre: I tried the Dutch Renaissance, but there were no comfortable seats; I found a seat next to a window in Objets d'Art, but I didn't stay long, it was too cold. Finally I ended up among the statues - in the glass-roofed courtyard where it was warm, not too bright, and back support for working comfortably.

St Eustache: I had never been inside this cathedral before, despite having lived next to it for two years. I had reservations about working inside a church, but it started to rain, so the outdoor concept lost some of its shine. And once I was in, I realised it was full of tourists, so a guy in sandals with a laptop was not going to deprive the place of any spirituality. A cathedral is one of only a few kinds of place where you can sit indoors without feeling a moral obligation to purchase a glass or two of fine red wine.

The tip of Île de la Cité: my favourite. Watch the boats go by as you sit and code in the shade of the trees.

Of course, eventually my battery runs out. I try to arrange for this to happen near lunchtime, so I can recharge as I eat. Fortunately I have never found a restaurant in Paris where they objected to me plugging in while enjoying their food and drink.

I hope I can keep this up.

11 August 2008

apache 2, mod_proxy, load balancer, mongrel_cluster, rails, and 404

I don't know where the bug is, but the fix had nothing to do with mongrel or rails. For some pages, most of the time I would get a 404 Not Found error. The apache error log showed this:

[Sun Aug 10 20:54:48 2008] [error] [client a.b.c.d] proxy: error reading status line from remote server 127.0.0.1, referer: http://triplex.softify.com/
[Sun Aug 10 20:54:48 2008] [error] [client a.b.c.d] proxy: Error reading from remote server returned by /user/list, referer: http://triplex.softify.com/

After much googling I ended up on the page that says it all, and more besides: https://issues.apache.org/bugzilla/show_bug.cgi?id=37770

Two config modifications stand out: firstly, to add these lines -

SetEnv force-proxy-request-1.0 1
SetEnv proxy-nokeepalive 1

and secondly, to set the log level to debug:

LogLevel debug

I added both and the situation is much improved. The worst offender is a very slow-loading page.

I'm on apache 2.2.6. I'd love to know what the correct fix is. Interestingly, according to the bugzilla page for bug 37770, this has been an issue since 2.2.2.

This is how my mongrel_cluster apache virtual host proxy load-balancer config looks now:

<VirtualHost *:80>
        ServerName triplex.softify.com
        DocumentRoot /u/apps/triplex/current/public
        RewriteEngine On

        <Proxy balancer://mongrelcluster>
         BalancerMember http://127.0.0.1:8000
         BalancerMember http://127.0.0.1:8001
         BalancerMember http://127.0.0.1:8002
        </Proxy>

        # Redirect all non-static requests to Mongrel
        RewriteCond %{DOCUMENT_ROOT}/%{REQUEST_FILENAME} !-f
        RewriteRule ^/(.*)$ balancer://mongrelcluster%{REQUEST_URI} [P,QSA,L]

        ProxyPass / balancer://mongrelcluster/
        ProxyPassReverse / balancer://mongrelcluster/
        ProxyPreserveHost on

        SetEnv force-proxy-request-1.0 1
        SetEnv proxy-nokeepalive 1

        <Proxy *>
         Order deny,allow
         Allow from all
        </Proxy>

        ErrorLog /var/log/apache2/triplex.softify.com.error.log
        CustomLog /var/log/apache2/triplex.softify.com.access.log combined
        LogLevel debug
</VirtualHost>