Adhearsion Blog by Jay Phillips

Adhearsion, Ruby, VoIP, Entrepreneurship

Adhearsion Just Got Way Fast

without comments

Ahh, it’s such a good feeling when you know you just wrote a really awesome feature. Last Saturday, late at night as I was trying to force myself asleep, I had an epiphany that solved the last hurdle I had with implementing a caching system in Adhearsion. The next morning I sat down and busted out a nineteen line method that took me about twenty minutes to write but another ninety minutes to document!

This cache method now lets Adhearsion totally bypass interpreting the dial plan for incoming calls which have already been cached. Does your dial plan make heavy use of your database? Does it use a really “expensive” helper that’s very resource-intensive? Are you pushing hundreds of calls a minute? So what? You can cache now!

For those that want the nitty-gritty, real documentation, see Adhearsion’s RDocs at http://rdocs.adhearsion.com. Scroll down in the top-right pane until you find cache().

I feel if Rembrandt had written this method, its usage still wouldn’t have come out as beautiful.

cache :for => 1.hour do
    play weather_report('Dallas, Texas')
end

… or, how about this hottie:

cache :per => extension do
    user = User.find_by_extension extension
    speak "Calling #{user.name}"
    dial user, :for => 1.minute
end

… which will store different caches based on the extension the user dialed. Since many IVRs route depending solely on this variable, caching in this case is both smart and fast. The variable per which the caches are stored can be virtually anything.

For the Ruby hackers, here’s the cache method I implemented.

The epiphany I had was to use the return value of Ruby’s handy Kernel#caller method as a Hash key which uniquely identifies the FILE and LINE which invoked cache. Without this, I wouldn’t be able to distinguish between stored caches which occur in the same file or the same context. In the global cache variable ($cache_dir), this Hash of caller()s maps stack traces to another Hash which contains the constraints for the call—that is any use of the :per Hash key argument. This second nested Hash’s constraining keys map to an Array accessed as a queue of Strings which are simply rawr()ed to Asterisk. The time-to-live (specified with the :for Hash key argument) is stored as index 0 of the Array as a Time object. You can see this being stored with the “ttl.from_now” line and accessed with the “queue.first < Time.now” comparison.

For the bolder Adhearsion hackers, I’d like to call to arms anyone who’d be willing to really destroy Adhearsion with stress tests. There’s an open-source SIP stress tester developed by Hewlett Packard called SIPp with which you could pound your Adhearsion-managed Asterisk PBX.

This is the first of many substantial performance improvements coming. I’ve a few other tricks up my sleeves. :)

Written by Jay Phillips

February 14th, 2007 at 7:27 pm