ARGH. Seriously: ARGH.
I have spent almost the entire day trying to migrate our Rails apps to use the ActiveRecordStore without errors and I finally just achieved success. This was mostly a case of incorrect documentation retrieved by Googling. The ultimate solution really came with me actually opening up the Rails gems and stepping through the actual ActionController and CGI::Session code. So, here’s my solution.
Firstly, why move away from the cookie store? Well, many reasons: the cookies only hold so much session data before they barf. When you look at the cookies created on your computer you see that they are these very long encoded strings. That results in only a very small amount of storage on some browsers. Secondly, there are temp session files created on your hard disk and those will eventually consume all of your inodes. Plus I think the database is just plain faster when you have a large number of sessions.
At first I started by tweaking the environment.rb files by uncommenting the magic line:
config.action_controller.session_store = :active_record_store
And then the instructions are to run the Rake script generator command to create a database migration:
rake db:sessions:create
Cool. And the final thing was to modify the application.rb CRSF forgery line:
protect_from_forgery :secret => 'xxx_YOUR_SECRET_HERE_xxx'
And that should have done it, right? Well the problem that really consumed today was the problem that I *cannot* use the default session storage table, sessions. No no no, since there is more than one application using this same database I needed to create tables like app1_sessions and app2_sessions.
This is where I went crazy.
The problem is that I’m running Rails 2.2.2 and when you Google you start pulling up Rails 2.3 documentation. Configuration adjustments I was trying kept turning up ineffective and referencing classes that did simply not exist. So what’s a person to do? Well: use the source!
So I peeked into my Mac’s /Library/Ruby/Gems/1.8/gems/rails-2.2.2 directory and poked around that gem and also the actionpack-2.2.2 gem. That’s where I finally figured out that the proper adjustment to the configuration was to modify the three accessible attributes:
CGI::Session::ActiveRecordStore::Session.table_name = 'app1_sessions'
CGI::Session::ActiveRecordStore::Session.primary_key = 'id'
CGI::Session::ActiveRecordStore::Session.data_column_name = 'data'
And if I had done things like this everything would have been fine. But the other compounding problem was that if you do a bit of Googling you end up with this one example that says to set the primary_key to something other than “id” like “session_id”. The problem with this is later when the CGI::Session::ActiveRecordStore goes to try to persist the data it starts echoing out log messages like:
WARNING: Can’t mass-assign these protected attributes: session_id
I was like: “WHAT?! Where is this coming from?!” It took a while to track it down to Line 292 of active_record_store.rb. Yes, this line was the cause of the above message:
@session = @@session_class.new(:session_id => session_id, :data => {})
So the “brilliant” thing was that part above where I had assigned primary_key to “session_id” originally. That’s plain wrong. It should just be “id”. That makes things simple. In fact, if you read the docs inside active_record_store.rb you’ll see this wonderfully friendly comment:
# Note that setting the primary key to the +session_id+
# frees you from having a separate +id+ column if you
# don't want it. However, you must set
# <tt>session.model.id = session.session_id</tt> by hand!
# A before filter on ApplicationController is a good place.
I read that and finally I realized my mistake. After a restart things now work perfectly. I just wish this wasn’t so convoluted.