Ruby on Rails
HowtoAvoidSessionRestoreError

Problem

In development, I used to frequently get a SessionRestoreError message (which I’ll call SRE) when I added a feature and reloaded the page. Actually, the sequence was:

At this point, no amount of code-fixing and reloading will make the SRE go away.

If I add a feature without introducing a bug, there’s no SRE. And I don’t think the SRE occurred after every bug-fix-reload tapdance.

Cause

SREs occur because the session can’t be restored. That is, Rails receives a request, is about to dispatch to a controller, but first it needs to restore the session in case the controller wants to use it. If for some reason it can’t do that, it throws an SRE.

Obviously, something buggers up the session when the bug is encountered. I have no idea why that would be.

If you get whacked by an SRE, you almost certainly need to manually delete your session object(s). In a typical file-based sessions scheme (typical for a development environment, that is), use a command like this:

  rm $TMP/ruby_sess.*

If you are using db-based sessions (see HowtoChangeSessionStore), you may use the rake task ‘purge_sessions_table’:

  rake purge_sessions_table

That’s so important I’ll repeat it with emphasis:

If you get whacked by a SessionRestoreError, you need to manually delete your session object(s)—typically files.

If you don’t care about old sessions sitting around restarting the browser can also solve it.

Normal Solution

The normal cause of an SRE is that the session object contains an instance of one of your model classes (e.g. Article), which rails doesn’t know about, because it hasn’t come across a require "article" line. Say Rails is invoking the “Admin” controller, and it needs to know about articles. Then, in order to avoid SREs, you should do this:

  class AdminController < ApplicationController
    model :article        # INSERT THIS LINE

    # The rest of your code...
  end

The model :article line tells Rails that this controller needs to know about articles, so it had better learn about them in case the session contains them. See also WhenToUseTheModelMethod.

If That Doesn’t Work…

The problem described above (SREs occuring after a bug-fix-reload tapdance) is different. I made sure the controllers were aware of all the models by putting lots of model statements in my ApplicationController.

Eventually, I got sick of the SREs slowing down my development cycle, so I took drastic measures:

I simplified my session.

That’s right, instead of storing a User object in the session, I just stored the user_id, and recreated the User object with each request.

And since then, my SRE blues have gone away.

But Isn’t That Expensive?

Recreating the User object from a user_id every request may or may not be expensive; I don’t care. The database may be caching it to be super fast. Perhaps later on I can spinkle some Rails caching magic on my code to cache it at that level. Maybe in production I’ll change it back so the User object is kept in the session.

The point is, doing it this way is fast enough for development, and removes that obnoxious drain on the development cycle.

But consider this important note from AlisdairMcDiarmid:

It’s important that you don’t store the User object in the session, for security reasons. If the User is later deleted, or has its attributes changed, you run the risk of the session containing a stale object. This is particularly a problem if your application isn’t the only accessor of the database.

The End

If anyone has experience with squashing SREs, please let me know (gsinclair at gmail dot com). I’d even just like to know if you’re getting them like I was. Perhaps I was the only person in all of Railsdom suffering this annoyance.

—GavinSinclair


Problems with multiple applications on the same server.

I ran across it the other day, using one browser to access to different Rails applications. Very annoying
DavidCorbin?


Agreed. It’s turned into a show pauser for us; we’re using the “new and improved” HowToSetTheBaseURLsOfYourRailsApps technique for running multiple rail’s applications and in testing discovered that once a customer visits one rails-powered part of out site they are locked out of all the others until they delete the cookie or we kill their /tmp/rubuy_ses* file.

There seem to be several ways to work around this, but I haven’t decided which is the “best” yet. The two top contenters are:

The second is arguable more correct (and potentially easier to get working) but potentially fails if one app is “nested” inside the URL space of the other.

However, we got the first idea working easily enough (one line of code) and well enough (only fails if you are grafting two copies of the same application to different paths in the same site and expect them to share session data) that I’m considering it resolved for the moment.

MarkusQ

DHH’s response: We’ve kinda solved this problem by giving every app their own /tmp directory now. So you’ll never store sessions from two apps in the same dir.


Why is there not a solution that involves rescue SessionRestoreError? What I’m getting from reading this is something like this.


rescue SessionRestoreError
  `rm $TMP/ruby_sess.*`
  retry
end

that can’t be good. The current default handling of that error by just showing a “Not Found” error seems completely retarded.


Correspondance from Emil Marceta

To Gavin Sinclair, 17 April 2006

I’ve been wrestling with another aspect of problems described at [this page].

Check out this ticket : http://dev.rubyonrails.org/ticket/4638

This is exactly the problem I bumped into. I have model classes organised in namespaces, that I stored in the session.

The symptom was exactly as described in the ticket; Webrick would simply hang with 100% cpu. Arrgh.

I’m using [Marshal.dump(obj)].pack('m') (i.e. marshal + base64) as a workaround for my session objects now.

That patch works, but the whole session management area probably deserves another look.

Perhaps, in the spirit of the lazy loading that RoR uses, a given session object should be marshalled in only when referenced, instead of when the session gets loaded from the storage. That would ensure that things work in the most of the cases, since the object had to be initially created in the first place and all the necessary classes were required. The current design requires that the session objects have more / less ‘global’ visibility.

I’m thinking that this would be more natural / in line with how other RoR plumbing works; less surprises.


category: Howto, OpenQuestion