Objects in Session State Revisited

Hi Folks,

I'm still a bit confused about the issue of storing "complex" objects in session state rather than storing simple objects.

It's my understanding that everything is an object in Ruby. If my understanding is correct, then storing a string or an integer in session means storing an object.

It would seem then that the problem of storing objects in session is a matter of how "complex" those objects are, and that problems may arise due if much work is required by the runtime to unmarshal larger amounts of structured data.

With this is mind, how "bad" would it be to store the following in session: Struct.new("User", :name, :role).new("jdoe", "admin")

I would prefer to keep the data pertaining to "user" (in this case) encapsulated in a single struct and to store that struct in session. Is this categorically a bad thing to do?

Thanks, Scott

I think you'd be better off with

session[:user_name] = "jdoe" session[:user_role] = "admin"

and if your users are in the db, you'd be far better off just sticking the id in the session.

Whatever you do, you'll want to create some kind of current_user and current_user= methods to encapsulate the access. That way you can change it around all you want.

Pat

Thanks for the reply, Pat.

I'm still not sure *why* I'm better off though.

-s

They're simpler and smaller. I'm not quite sure how else to explain it.

Pat

Smaller I grok. The simpler part depends on what's involved in cleaning session state for the number of state items supporting a completed task flow that requires multiple requests.

If a given task flow's session state is stored as a single item, it's much easier to avoid what amounts to orphaned global memory variables.

Clearing an instance of a LoanApplication object (not a model object) from session state is easier to code, easier to test, and easier to assure than clearing 10, 20, 50, or what ever number of individual session state slots.

My notion here is that one session state slot per interaction is a cleaner pattern - regardless of the platofrm.. be it Rails, ASP, JSP, etc.

If there are serious technical limitations to storing a state value object in session state in Rails, I'd love to hear them. If we're talking about a culturally-embedded reticence to this pattern, it might be valuable to crack that nut open in community dialog...

Thanks, Scott

What if you are storing a model object in the session that could potentially be modified from more than one process? Say you have your User object in the session and an Admin disables that User. If you are just referencing the User object in the session, the change in status won't be picked up by already existing sessions.

If you just have the user id in the session and load the object on each request cycle this problem is avoided.

-jc

sbellware wrote:

JC,

User is a bit of an edge case since it has the special concurrency considerations that you're talking about.

But for the sake of argument, if I have a threat model for the app that suggests that instantaneous restrictions placed on a user when an admin deactivates the user are not necessary, then I don't need to hit the database with each request to get the user's permissions. The permissions and roles can live in session state along with the username.

In situations like this, I've also stored user roles in an encrypted authentication ticket with an expiration time different than the session timeout. I wouldn't necessarily assume a need to have the freshest possible access control information for a user unless the requirements called for it.

I don't think that this is an entirely realistic solution for every app, but I'm putting it out there to suggest that there are threat models that allow for some latency when a user is deactivated.

Ultimately, my question still remains about using a single state value object for task flow state for a task flow that requires multiple requests to fulfill.

I'd like to feel good about using state value objects rather than using a myriad of individual session slots. I'm still not clear on the technical limitations of doing this in Rails.

Thanks again, Scott

sbellware wrote:

Smaller I grok. The simpler part depends on what's involved in cleaning session state for the number of state items supporting a completed task flow that requires multiple requests.

If a given task flow's session state is stored as a single item, it's much easier to avoid what amounts to orphaned global memory variables.

Clearing an instance of a LoanApplication object (not a model object) from session state is easier to code, easier to test, and easier to assure than clearing 10, 20, 50, or what ever number of individual session state slots.

My notion here is that one session state slot per interaction is a cleaner pattern - regardless of the platofrm.. be it Rails, ASP, JSP, etc.

If there are serious technical limitations to storing a state value object in session state in Rails, I'd love to hear them. If we're talking about a culturally-embedded reticence to this pattern, it might be valuable to crack that nut open in community dialog...

One argument against storing larger things in the session could be that the session is unmarshalled on each request thus requiring a potentially severe overhead for requests not related to the thing you are storing in the session. Actually I haven't read the source so I don't know if the session object is lazily initialized so thight might me a non-issue.

Another concern is in regard to changes to code. If you decide to change the structure of the object old sessions will turn stale when you update the code. That may or may not be an issue depending on the use case.

I do agree that having a ton of session variables is also a bad thing. It's all to easy to forget about some of these. I prefer putting stuff in the database and keeping the amount of information in the session to a bare minimum. I have a hunch the ideas behind REST may be helpful in this regard.

Sure, there are definitely ways to handle the issues around storing objects in the session. I was just pointing out one of the potential issues with it.

There is nothing inherently bad about storing things in the session. The session exists so we can fake out having stateful applications using a stateless protocol. In AWD they say of the session, "You can store anything you want, subject to a few restrictions and caveats." So, it is up to the developer to be aware of the caveats and decide what is acceptable to store in the session given the situation they are coding for.

I also agree that if you are going to store a set of related data in the session it is better to have it contained in an Object of some sort referenced by a single key ( e.g. session[:my_object] = MyObject.new( some_stuff ) ) as opposed to session[:user_name] = 'foo', session[user_role] = 'bar' & etc.

-jc

sbellware wrote:

The main reason for not putting complex objects in the session, complex typically being defined as Model (ActiveRecord) objects, is that the marshalling code is so smart it's dumb. If it encounters a model attribute that doesn't exist anymore, it'll typically blow up, and unless you want all your users sessions invalidated by your rescue code, you don't want this happening. I've seen some good discussions on this "out there", just look around, you'll probably find some.

That being said, let's address your "keeping it encapsulated" desire...

Models don't have to be ActiveRecord based, and they don't have to be stored as objects in the session. Create yourself a CurrentUser model that manipulates a session[:current_user] key in the session and have that be a hash, like session[:current_user][:id] and session[:current_user][:user_name]. Have that CurrentUser model be a singleton or a normal object that is instantiated by a before_filter, either way, both work just as well but the singleton design applies better in my mind to the problem at hand. A little meta-programming gives you the ability to define the attribute that'll be stored in the session with something similar to attr_accessor. There, it's all nice and encapsulated.

Cleanup, easy, session.delete(:current_user) or whatever you're usage requires.

Just remember one very important thing, a Model is what you need it to be, it encapsulates things that would be ugly, unclean, and just plain wrong to put elsewhere into very nice, compact packages ... of your design.

Many thanks for these thoughtful replies!

-s