#13 Dangers of Model in Session
Apr 02, 2007 | 5 minutes | Controllers
Be careful when storing a model in a session. It will behave differently than you expect and can easily get out of sync with the database. Instead of storing the model directly in the session, store the id to the model and use that to fetch it from the database.
OK, don't store an object in session because it can get out of sync with DB but what if you make sure to keep it in sync? Wouldn't that be more efficient than hitting the DB so often?
If you hit the DB every time the user wants to look at anything that's going to slow things down.
OK, let's see. If the user updates the object then you're going to have to save it regardless. But if you're just flipping from one page to another, seems pretty important for performance to hang onto the object.
I'd say that's particularly true of a users User obj, which in many apps changes very infrequently and is read often. Every page in the app might need the logged in username, or could need their role if your app uses roles.
It might be more efficient, I haven't done any benchmarking. It probably depends on where the session is stored. The default in edge rails is to store it in the users cookie which is limited on space - another reason not to store large objects there.
Either way, sessions are not designed to be caches. If you need a performance boost, look into a memcache solution (such as cache_fu).
I agree that holding on to _already persisted_ objects in the session is a bad idea.
A seemingly good reason to hold onto an object in session is when you have an object that needs to be composed over many actions, and should not be saved to the database until all of those actions are complete.
Imagine an application where a wizard-type interface is in effect, and you are using the wizard to build up the state of an object. Further assume that the user can decide to abandon this wizard process at any time.
One way to handle this is to hold the new, unsaved object in session and that way, it never gets saved to the database unless that's what the user wants to do.
This way, we don't have to save objects in some intermediate (not yet valid) state, and then delete them if the user abandons them at some point. The abandoned objects will simply never ever be saved.
The downside of this approach is that some objects may be too big to hold in the session before they are saved. In this case, a caching solution may be used to hold these larger types of objects.
You won't see much of a performance problem re-loading each time. Especially in rather simple model's like the one in the video.
Many people store session data in the database nowadays, a good practice, so even if you do store it in the session, you're not saving yourself a round trip to the DB.
I recommend the approach Ryan so eloquently explained.
Right on Wes! That is the only time I ever stash a model in the session. And really at that point I don't consider it a model since a model imho is something that represents data in the DB, which is not the case in this usage. Plus doing it this way prevents hacked methods like creating a bit column in the db table to signify the row (model object) being complete.
you could also store the model in memcached using cache_fu. (no DB hit)
Class User
acts_as_cached
after_save expire_cache
end
User.get_cache(id)
User.reset_cache(id)
the only problem is shared hosts won't have memcached availability.
here I have some examples
http://www.frederico-araujo.com/2007/12/13/make-your-rails-app-faster-with-memcached-part-1
I know Ryan is gonna come up with a memcached/cache_fu screencast sometime...
All screencasts here are awesome.
What a great job!
Thanks a lot Ryan!!!
You say to only store simple objects in the session like arrays, hashes, integers, etc. But what about complex objects that don't need to keep in sync with the db and are temporary, like a shopping cart class? I've been storing the entire shopping cart in the session for a while now and I think the only downside is that the Marshaled object can get pretty big, requiring a long text db column, that and if you make any changes to the shopping cart class you of course have to kill any open sessions. Now that rails 2.0 is no longer storing sessions in the db and file system, is there a size constraint on how much data can be kept in a cookie? Any advice?
thanks for this railscast Ryan!
This does a good job at explaining not to do this, too bad I missed it (or it wasnt around) when I got my first auth plugin that stored the user in the db.
Anyway, for those of us that do have the user in the session (and cant easily remove it), what are the rules for how active record interacts with the session.
For example if I set a attribute on my session object and save it, it will be the same in both the session and the active record right?
If I store a user object in the session, will it store related objects in the session also, how far will it traverse the object tree?
thanks
Joel
It's probably not a great idea to store large objects in a[:session] but it seems wasteful to before_filter a session[:user_id] every time if the session[:user] is never altered and used to verify presence such as user level. Thanks for the Rcast!
This episode has been updated for Rails 5 as a blog post. Dangers of Model in Rails 5 Session
Routes for the users resource in routes.rb. also defined as
{Rails.application.routes.draw do
resources :users, only: [:index, :show, :update]
end}
You can also store the model in memcached using cache_fu. (no DB hit)
Class User
acts_as_cached
after_save expire_cache
end
User.get_cache(id)
User.reset_cache(id)
The users controller stores the user object in the session.
class UsersController < ApplicationController
def index
session[:user] = User.first
end
def show
@user = session[:user]
end
def update
@user = session[:user]
@user.first_name = 'Junk'
end
end
somse