Frivol - Frivolously simple temporary storage backed by Redis

A really simple Redis-backed temporary storage mechanism intended to be used with ActiveRecord, but will work with other ORM’s or any classes really.

I developed Frivol secifically for use in Mad Mimi (madmimi.com) to help with caching of data which requires fairly long running (multi-second) database queries, and also to help with communication of status from background tasks running in Resque on workers to the front end web servers. Redis was chosen because we already had Resque, which is Redis-backed. Also, unlike memcached, Redis persists it’s data to disk, meaning there is far less warmup required when a hot system is restarted. Frivol’s design is such that it solves our problem, but I believe it is generic enough to be used in many Rails web projects and even in other types of projects altogether.

Usage

Configure Frivol in your configuration, for example in an initializer or in environment.rb

REDIS_CONFIG = {
  :host => "localhost", 
  :port => 6379
}
Frivol::Config.redis_config = REDIS_CONFIG

Now include Frivol in whichever classes you’d like to make use of temporary storage. You can optionally call the storage_expires_in(time) class method to set a default expiry. In your methods you can now call the store(keys_and_values) and retrieve(keys_and_defaults) methods.

Defaults in the retrieve method can be symbols, in which case Frivol will check if the class respond_to? a method by that name to get the default.

The expire_storage(time) method can be used to set the expiry time in seconds of the temporary storage. The default is not to expire the storage, in which case it will live for as long as Redis keeps it.

Frivol uses the storage_key method to create a base key for storage in Redis. The current implementation uses "#{self.class.name}-#{id}" so you’ll want to override that method if you have classes that don’t respond to id.

Frivol also extends Time to allow it to be (de)serialized to JSON, which currently used to store data in Redis.

Example

class BigComplexCalcer
  include Frivol
  storage_expires_in 600 # temporary storage expires in 10 minutes.

  def initialize(key)
    @key = key
  end

  def storage_key
    "frivol-test-#{key}" # override the storage key because we don't respond_to? id
  end

  def big_complex_calc
    retrieve :complex => :do_big_complex_calc # do_big_complex_calc is the method to get the default from
  end

  def last_calc_done
    last = retrieve :last => nil # default is nil
    return "never" if last.nil?
    return "#{Time.now - last} seconds ago"
  end

  def do_big_complex_calc
    # Wee! Do some really hard work here...
    # ...still working...
    store :complex => result, :last => Time.now # ...and let's keep the result for at least 10 minutes, as well as the last time we did it
  end
end

Note on Patches/Pull Requests

  • Fork the project.

  • Make your feature addition or bug fix.

  • Add tests for it. This is important so I don’t break it in a future version unintentionally.

  • Commit, do not mess with rakefile, version, or history. (if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)

  • Send me a pull request. Bonus points for topic branches.

Copyright © 2010 Marc Heiligers. See LICENSE for details.