Category Archives: Booko

Posts about booko, some technical, some explaining new features

On Daemons

As you might imagine (depending on just how nerdy and imaginative you are), Booko is a poster child for the concept of long running background tasks. Grabbing prices from 40 online stores isn’t a fast process and you certainly would not want your front end webservers making your users wait as long as the slowest of the 40 stores before responding to a user request.

Over the years, I’ve tried various approaches to running user level daemons. My first attempt was ok – I rolled my own and slowly improved it. It could handle HUP signals, write PID files, die gracefully and it knew if it hadn’t died properly and attempted to kill zombie versions of itself. It had stop / start / restart commands. But it wasn’t all sweetness and light.  What happens when it dies? This is probably the trickiest part of running daemons (Well, having to fork twice and make sure you have detached from the terminal is probably tricker, but still).

So, how do you make sure your daemon is running? Cron immediately springs to mind. So, part two of writing your own daemons is writing something to keep them going.  You may have found yourself in this position and felt a little tickle in the back of your mind when you setup a cron job to solve this problem. My cron job looked at the daemon’s log file’s modified time and if it was more than 5 minutes old, looked for the PID file and sent that process a KILL signal.

It’s an easy, stable solution to the problem at hand – albeit with a 5 minute lag to detect crashed daemons. It’s ok because I run multiple daemons which can take the load if one dies.  But, what happens if you only have a single daemon? Increase the frequency of checking?  Cron’s smallest resolution is 1 minute – that’s not really ok (depending on what your daemon does, it may be fine).  But now you have to make sure that your daemon’s writing to the log at least every minute.  Ugh.

This solution is starting to smell. So, what does everyone else do? Well, I checked out God – but it just doesn’t feel like an elegant solution to this problem. It may solve the problem nicely, but there must be a better way? Hard core nerds would probably move on to daemontools but it’s too much work for me.

That tickle you may have had in the back of your mind earlier was your subconscious telling you the problem is already solved and you already use it for your webserver, mail server, DNS server, ssh server and more. Your operating system can provide this exact service for you. Since I’m using Ubuntu that service is provided by Upstart.

Running your service with Upstart has two very nice consequences. Firstly – you can remove all the code used to manage daemonising. You can now write your code to hang around in the foreground. Leaving your code in the foreground while you’re in development mode is good anyway – you can watch it more closely. If you really want to daemonise in our dev environment, bang up a tiny ruby script with the Ruby Daemon gem which calls your actual script and manages PIDs, signals and a stop/start interface for you.

Setting up a service to run with Upstart requires just a config file – here’s one I prepared earlier:

description "Price Fetcher Upstart script"
author "Dan Milne"

start on startup
stop on shutdown

console output

respawn
instance $FID

script
env RAILS_ENV=production
export RAILS_ENV

exec sudo -u booko RAILS_ENV=production /opt/ruby-enterprise/bin/ruby /var/www/booko.com.au/booko/bin/fetcher.rb $FID
end script

That file gets named “fetcher.conf” and goes in the /etc/init/ directory. This has some nice features; the first of which is that once it’s started, it will keep running. If it dies, it’ll respawn (you can see the option right there in the script).  The fact that it died goes in /var/log/daemons – but what’s even awesomer, you can run multiple instances of the same script, by passing in FID=0 or FID=1 etc when you’re starting it. Finally, it gets the standard init features. You can start it with ‘service fetcher start FID=0’ for example.

The only missing feature that I can see, is that because I need to pass in FID=0 to the script, it doesn’t start at bootup. There appears to be no way of stating “Startup 2 of these at boot time”.

In summary, if you use your OS init services, you get to write simpler code, get respawning at an OS level and you get all the normal daemon control features.

Booko’s moved, features added.

I’ve been working on a beta version of Booko for, like, 7 months now.  I finally upgraded it while moving from Slicehost to Linode.  I’ve made a large number of changes to the way Booko performs long running tasks and how those tasks communicate. But I’ll leave the nerdy stuff for later.

The biggest change from a user point of view is some integration into Freebase.com. You’ll notice extra information appear in book listings now. For example, the Booko page for The Girl with the Dragon Tattoo now tells you:

  • that the book is part of the Millennium Trilogy
  • the other books in the series (The Girl who played with Fire & The Girl who kicked the Hornets’ Nest)
  • the other editions of The Girl with the Dragon Tattoo – hardcover and paperback.

The data at Freebase is a long way from complete, but it’s constantly growing. This should be a very useful feature. I’ll be doing more to integrate Freebase into the search results – for example, so a book shows up only once, listing the different editions in that single search result item.

List management also got an overhaul. That old list manager page was pretty bad. There’s still work to do, but it should be far more usable now. Log in and check it out!

There are still bugs to fix (soooo many missing images for cover art) and features to add (smarter list price calculation, used books).

Poor Australians

Another example of high prices in Australia. KitchenAid Mixer. With a RRP in the US of $349.99 USD or $382.67 AUD, Amazon sell it for $290 USD ~ $317.079. RRP in Aus is…. $729 AUD.

Amazon.com Vs Kitchenware Direct

I have nothing against Kitchenware Direct – I’ve bought stuff from them in the past and will do in the future. Just like high book prices, no doubt this has nothing to do with retailers, but probably with distributers. (Publishers in the case of books).

Summer fixes

Couple of little fixes, the largest of which is the search functionality is no longer powered by AJAX.  This means the Back button will work correctly after you’ve searched for a book, then gone and viewed a book, then clicked back.  Surprisingly, Safari actually did clever stuff to make this work – clicking back in Safari would take you back to the search results – in every other browser you’d be taken an unexpected page – usually the front page.

Secondly, I’ve added HTML5 attributes to various fields as described by Mark Pilgrim. The most obvious will be to Safari and Chrome users (at least the Mac version) where the search box will have round corners and stuff.  Other fields such as the email entry on login and register, and the OpenID field will now be easier to use on the iPhone, with alternative keyboard making it easier to enter that data.

Let me know what you think.

Google / Yahoo user?

Logging into Booko just got easier. If you have a Google or Yahoo account, just hit the appropriate button and you’re in, registration included.

Turns out, this was super easy to add to Booko since it already does vanilla OpenID logins. Basically, Booko just fills in the OpenID URL with “https://www.google.com/accounts/o8/id” or “http://yahoo.com/” and OpenID Directed Identity does the rest. You could also just type those URLs in yourself and it’ll work just the same.  Sweet!

New Booko features

Hey everyone, Booko has some new features.

User accounts – you can now create an account to save your cart.  Booko accepts OpenID also – so you can use your OpenID provider to log in to Booko.

If you have a Booko account, you can now create additional lists for keeping track of books. For example, you could create a wish list, or a scifi list, or a kids’ book list or a list of all Tintin books. Just go to the “Manage Lists” page and go to town.

Finally, you can make lists public and sharable. Once you’ve created a list, just tick the “Public?” checkbox.  Booko will then create a public URL for you to use.   For example, here’s a list I made of all Tintin comics:
http://www.booko.com.au/lists/view/nx9deGGd9Qgy2SE8

You can copy all the items from any public list into your own cart or into any of your lists.

Let me know what you think.

Updated cart implementation

Tonight I introduced a new shopping cart implementation to Booko. This new cart code is in preparation for allowing Booko users to create lists of books and to enable people to save these lists in their user accounts (coming soon).  The shopping cart will be one of these lists.

The new cart includes the ability to increase the number of any particular title without needing to visit that book’s page.

I wasn’t able to migrate existing carts across to the new system, but any book you’ve viewed recently will show up in the  “Your Recently Viewed” section.  Enjoy!

Playing on the Master branch

I’m working on adding a new feature to Booko, but I accidentally started working on the Git Master branch, which I like to keep sync’d with the production version of Booko. So after a few commits I want to be able to add some fixes to Booko, but I’ve polluted my master branch with untested, unfinished changes.  What to do?

After reading up on Stack Overflow, I decided to fix things.  What I want to do, is reset my master branch to the version in production, and take all the subsequent commits and create a new branch with them.  Turns out, it’s easy.

1. Find the commit you want the master branch to be at. You can find the SHA-1 name with “git log”

2. Create a branch from that commit with: git checkout -b new_master <SHA-1 commit name> (hint: this will be the new master )

3. Rename your current master branch to the new name of the feature: git branch -m master branchname

4. Rename the new_master to master: git branch -m new_master master

And, job done. You’re no longer messing up your master.