Close and Go BackBack to Viget

Wrapping Rails Session Hash: Follow Up

Matt Swasey
Matt Swasey, Web Developer, October 08, 2009 3

The other week I wrote about wrapping your Rails session variables in current_object methods. There is, however, a performance related bug in line 5 of the code I posted:

class ApplicationController < ActionController::Base  
  helper_method :current_account  
    
  def current_account  
    @current_account ||= Account.find_by_id(session[:current_account_id])  
  end  
  
  def current_account=(account)  
    session[:current_account_id] = account.try(:id)  
  end  
end 

If session[:current_account_id] is nil, current_account will make a useless database query each time it is called. This would happen if you are calling current_account on a public page while a user has yet to log in.

We can fix this in the following way:

...
def current_account
  if session[:current_account_id]
    @current_account ||= Account.find_by_id(session[:current_account_id])
  end
end
...

The above code will only evaluate @current_account if session[:current_account_id] has been set, otherwise it will return nil without hitting the database. Once session[:current_account_id] is set, it will hit the database a single time, memoize the result in @current_account, and return @current_account for the duration of the Rails request cycle. This should help take some load off the database.

If anyone has an even simpler solution, please let me know in the comments!

Jacek Becela said on 10/08 at 01:34 PM

@current_account ||= session[:current_account_id] && Account.find_by_id(session[:current_account_id])

but it’s less readable and acts differently - it defines @current_account regardless of session[:current_account_id] which is not very aesthetic either.

BTW it’s really cool that you can type:

nil && whatever.else.here_as_long[:as_it_is].synactically{|valid| ruby}

and “run” it without ruby complaining.

Jacek Becela said on 10/08 at 01:45 PM

I’m afraid that this revised method (and my “shorter” version) both have another performance-related issue: if the session[:current_user_id] is present but there’s no user by that id in the db, then each use of current_user will result in unnecessary db rountrip. This looks like proper use case for “defined?()” operator ;)

balls johnson said on 10/08 at 08:55 PM

@Jacek or do this (a bit paranoid i think), otherwise, use erlang, its scalez bettar…

def current_account
@current_account ||= begin
unless session[:current_account_id].to_i < 1
Account.find_by_id(session[:current_account_id])
end
end
end

Commenting is not available in this weblog entry.

Next entry: Developer Day Boulder Wrapup

Previous entry: Database Taxonomy

We're the Developers

at Viget Labs. We write about web development trends, tips, best practices, industry events, and our projects — all with an emphasis on Ruby on Rails.

Contact Us

Have any questions, comments, ideas, or secrets to share? Let us know.


How many hours in a day?

Sorry, you need to have Javascript enabled to use this form. (Don't blame us, blame the spammers!) If you'd like to contact us, please visit our Contact page.