16
Aug 09
0

Implementing Who's Online with Rails and Memcached

I have wanted to add a “Who’s Online” feature to my forum site for quite some time. Users are more likely to stay and post if they know someone else is there to read what they write. In the first implementation of the site, back in the Rails 1.2 days, sessions were most easily stored in the database with the ActiveRecord SessionStore. This made fetching who is currently using the site easy as all of the necessary information was stored in the database.

Once Rails 2.0 came out, the default session store was using browser cookies. Now it was not so trivial to determine who is on the site. One option was to add a new column to the User model and update it every time the user accessed a page. While this would work, it would mean an extra SQL read and update for every page accessed. The server would have no trouble handling the extra load, but I decided to wait for a better option.

Having not seen a better option in the latest releases of Rails, I implemented my own solution and got my first experience with memcached along the way.

First things first, get memcached installed and running in verbose mode for development:

sudo port install memcached
memcached -vv

Next, set the Rails development environment (in development.rb) to use caching and memcached as the cache store:

config.action_controller.perform_caching = true
config.cache_store = :mem_cache_store

The real work is done by a method in application_controller.rb

before_filter :set_online_users

def set_online_users
  return unless logged_in?

  current_user.last_seen = Time.now
  users = Rails.cache.fetch("online_users") { [current_user] }
  @online_users = users.collect do |user|
    user if user.last_seen > 5.minutes.ago
  end.compact
  unless @online_users.include?(current_user)
    @online_users << current_user
  end
  Rails.cache.write("online_users", @online_users)
end

The final solution is a fast (not verified by benchmarking) solution with zero extra database access. Some readers might notice this is not a thread-safe solution. All lines between the fetch and the write call should be wrapped with a mutex of some sort, but I will leave that for another time.

If you the looking for extra reading, especially about memcached, Railslab: Epsiode 8 is an excellent resource for getting started with memcached and Rails.

Tagged:  

Leave a comment

(required)

(not published) (required)