Why we use unicorn
January 20, 2012 § Leave a comment
Recently a colleague asked me why we used unicorn, obviously I answered “because its awesome”, however this didn’t seem to suffice. So rather then explaining I thought I’d write this post:
In the almost 6 years that I have worked at Mint we have ran our rails applications using a variety of software. Back in the
dark early days we used fast_cgi under apache, luckily we moved on quickly to thin, behind nginx, a web server we have since stuck to. However from thin we moved to mongrel when Zed showed us the awesome and then finally to unicorn. I miss out a brief stage where we entertained the idea of using passenger, but fortunately this never made it onto our production servers. Unicorn has been part of our stack for longer than all of the others put together and this in itself is interesting.
So, why unicorn? Well, basically it’s very unix like. Ryan Tomayko goes into great detail about this in this post, you should definitely read this before moving any further. So what does this give us? Well one of the issues that we found with mongrel was that, at least behind nginx, it didn’t queue requests very nicely. Putting haproxy between these and tuning it worked pretty well, but I’d rather not run haproxy just for that reason.
Unicorn leverages the OS kernel to balance connections between backend processes, this is great because unix is pretty well tested and is known to work pretty well. Unicorn is what you call a pre-forking server, in that a parent process listens on one port and forks a given number of children. This master then manages the children and balances requests to them as they are available to work. Using mongrel or any of the servers before, we’d proxy to each process directly, meaning (if using round-robin), if one process was doing a long running task, every X requests would get stuck. As unicorn listens on one port and queues its own requests this issue goes away to a large extent. So what else do we love:
Low memory overheard
Moving to unicorn we found some pretty good memory savings, meaning you can potentially run more processes per VM.
If your version of ruby supports it (MRI doesn’t), unicorn supports COW, which will greatly improve your memory overhead. If you’re not familiar with COW, it stands for Copy On Write and essentially when the child forks, it just uses its parents memory until it needs to change something. This makes forking faster, but also reduces the ram needed to run.
Unicorn uses standard unix signals to allow you to communicate with it. This is great, in the same way as using the kernel to balance requests is – these things work and have been proven to. Normally you send the signal to the parent which will in turn notify the children. For example sending a TTIN signal to the parent will increase the no of workers by one and sending a TTOU will decrease them by one.
I slightly touched on this in signals, but this is a great win if you are doing many deploys – assuming you have no migrations you can update the code without stoping the site! win, win.
If you’re interested in reading more on unix processes in general I’d highly recommend working with unix processes, this will greatly raise your understanding of how they work and touches on unicorn directly. It also mentions resque, the awesome queuing system and how they use unix style programming.
Well there we have it, a fairly quick run through why we use unicorn and love it. As I mentioned we have used this for far longer than any other server and I can’t see that changing in the near future. However things like mongrel2 are starting to be very interesting and are going to become increasingly needed as we need to start supporting long running connections (e.g. websockets), there’s a great post here on that.