I’m using active_record_store for storing my session information. I can
add
custom attributes to the session and then access these for that user
session
within a controller say, however what I would like to do is iterate
through
ALL session data whilst iterating access the attributes in the session
data
storage.
That is in the database “sessions” table there seems to be a “data”
column
for which rails stores all the session attributes. I’m trying to work
out
how to access the attributes in this data?
Is there a way to do this? I haven’t found a session class method that
assists yet.
Background: I’m just wanting to store the UserId in the session which is
then persisted to the database with the session -> can then iterate
through
all active sessions to see who is logged in.
PS. The other aspect of my question is to decide whether I need to add
an
explicit column in my sessions table for user_id, so that the user_id is
held in it’s own table as opposed to serialized within the rails session
data. That is if I can’t work out how to access it from the session
data
then I’ll have to put it into a special column (an overhead I know) so
that
it gives me the means of accessing the user id information (when
producing a
list of active users).
Save yourself some trouble and **DO NOT DO THAT*** . That will be
such a performance killer that it will never be able to hold up when
you get a good number of sessions active. That code will loop over
every single session and unmarshall it to search it for the user_id.
In even a small rails app that hasn’t cleaned out the session
recently that can be thousands and thousands of sessions. Looping
over all these sessions is going to kill your server.
You are much better off altering the actual sessions table and
adding a user_id column that can get set in an after or before filter
somewhere. Seriously, you will save yourself a lot of hassle if you
avoid the loop over all sessions and unmarshal approach Greg.
Cheers-
– Ezra Z.
– Lead Rails Evangelist
– [email protected]
– Engine Y., Serious Rails Hosting
– (866) 518-YARD (9273)
You are much better off altering the actual sessions table and
adding a user_id column that can get set in an after or before filter
somewhere. Seriously, you will save yourself a lot of hassle if you
avoid the loop over all sessions and unmarshal approach Greg.
While I’m sure Ezra’s right it does bring up a couple other issues.
“In even a small rails app that hasn’t cleaned out the session
recently”
That should NEVER be the case. Yes it is by default, but you should be
taking steps to avoid that. Yes, you can have a cron job run but
personally I hate the idea of having such an important part of my
system be dependant upon a completely separate mechanism. Makes it
harder to port between os’s, some isps don’t give you access to your
crontab, etc. My solution is to just hook the login process. Every
time someone logs in have it finish off with a call to the db to
delete old sessions.
The assumption that because there’s a record in the session table
the person is “logged in”. You’ve GOT to be doing some checking to
see WHEN that session record was created or last touched. The problem
is that you can’t wipe out sessions to early or you’ll annoy your
users. So you have to leave them there for a while. If you leave them
for only half an hour (not recommended) you’ll still be listing a
bunch of users as “logged in” who haven’t touched your system for
nearly 30 minutes.
For most sites this isn’t an issue but… Should you be so
fortunate as to write the next basecamp you probably don’t want to be
doing a database write for every single page load. Reads are quick and
easy on the db writes are slower and can require it to reindex things.
So my previous note about checking when it was last updated can become
problematic because it requires constantly writing to that table. Then
again, maybe this last one is just me being overly cautious.
CON - Some minimal extra complexity in the code (i.e. for each
request what has to be done)
ADV - Perhaps faster when do you want to pull back a list of
active users
Right but keep in mind that the session gets read from and then
written to the db on every request anyways. So the session is already
going to be read from the db at the beginning of each request. If you
set the user_id column of the session object in a before filter it is
no extra db overhead because the session was already read from the
db. Then at the end of the request, the session has to be written
back out to the db anyways so setting the user_id field when this
happens does not create extra db queries if done right.
Option 2 - Use marshalled session data (stored in the data column
in session table)
ADV - Can easily add userID to the session when performing normal
authentication/authorisation checks, so no additional overhead
( e.g. find session via AR then update)
CON - Perhaps slower to iterate through list, BUT the only time
one would need to do this would be for someone requesting a list of
active users logged on.
The real problem with this way of doing things is memory and cpu
time. Lets say you keep your sessions cleaned out pretty regularly.
So assume 200 sessions laying around at any one time(rails makes a
lot of sessions entries so this number is conservative). So each of
these 200 sessions now need to be loaded into memory and unmarshaled
to see the user_id inside the session. That means 200 times however
much data is in each of those sessions. This will end up making your
app leak memory or at least taking a lot more then it needs. Plus it
will burn your cpu while it unmarshalls and un base64 encodes all
that session data.
I kinda thought Option 2 may be better as it avoids an extract
database hit per request, in return for a slower “get me active
sessions” call which is only occasionally when requested by a user.
I think that you can add a user_id column to the sessions model and
not incur anymore database queries then the session is already doing
without it. Its just a matter of writing and reading the user_id from
the Session model instead of as a hash in its data part.
Cheers-
-Ezra
table and
That should NEVER be the case. Yes it is by default, but you should be
see WHEN that session record was created or last touched. The problem
So my previous note about checking when it was last updated can become
problematic because it requires constantly writing to that table. Then
again, maybe this last one is just me being overly cautious.
kate = masukomi
– Ezra Z.
– Lead Rails Evangelist
– [email protected]
– Engine Y., Serious Rails Hosting
– (866) 518-YARD (9273)
Hi Ezra/Kate - thanks for the advice. I have been wondering which way
to go
here. My thinking was as follows:
Assumptions
Inactive sessions are cleaned regularly (via some approach)
Option 1 - Add specific UserId column to sessions table
CON - Need to perform an database update to put the userId in place,
each
request (unless there’s a way in one’s before_filter to tell it’s a
request
associated with the creation of the session for the first time I guess)
CON - Some minimal extra complexity in the code (i.e. for each request
what has to be done)
ADV - Perhaps faster when do you want to pull back a list of active
users
Option 2 - Use marshalled session data (stored in the data column in
session
table)
ADV - Can easily add userID to the session when performing normal
authentication/authorisation checks, so no additional overhead (e.g.
find
session via AR then update)
CON - Perhaps slower to iterate through list, BUT the only time one
would
need to do this would be for someone requesting a list of active users
logged on.
I kinda thought Option 2 may be better as it avoids an extract database
hit
per request, in return for a slower “get me active sessions” call which
is
only occasionally when requested by a user.
Thanks Ezra - this is helping me - one last clarification hopefully:
When you say “Then at the end of the request, the session has to be
written
back out to the db anyways so setting the user_id field when this
happens
does not create extra db queries if done right” , what did you mean by
“if
done right”?
Do I really need to work out how to integrate the writing of the
additional
“user_id” column into the rails session infra-structure to do this? Or
can
I do the simple concept of creating a model that happens to hook into
the
“sessions” table, and use this model as a means to update an existing
session record with the “user_id”?
If the latter of the above two is ok (noting it simpler for me) can you
explain how rails ensures this is not an extra database hit? Does
activeRecord bundle all the requests up until the commit and then issue
them
to the database is some optimized form perhaps?
that happens to hook into the “sessions” table, and use this model
as a means to update an existing session record with the “user_id”?
If the latter of the above two is ok (noting it simpler for me) can
you explain how rails ensures this is not an extra database hit?
Does activeRecord bundle all the requests up until the commit and
then issue them to the database is some optimized form perhaps?
Tks again
Ok so when you use ActiveRecord sessions there is already a Session
model hidden away in rails. The way to access it is to use the model
method on the session object. So assume you have a user_id column
added to the normal session table. When you want to store the user_id
you can do this:
session.model.user_id=42
session.model returns a handle on the Session ActiveRecord object.
So you can now use session.model_user.id to retrieve the user_id when
you search for who is online
Since the session is read from the db at the beginning of each
request and then written back out at the end of each request the
user_id will be saved automatically for you at the same time it saves
the data blob into the session table. Hence the no extra db queries
to do it this way.
.
Cheers-
– Ezra Z.
– Lead Rails Evangelist
– [email protected]
– Engine Y., Serious Rails Hosting
– (866) 518-YARD (9273)
oh Ezra, where do you recommend that the user_id is added to the
session?
i.e. in the first before_filter in the application.rb? would this be
sufficient?
Checkout http://habtm.com/articles/2005/12/15/whos-online for a way of
adding data to the session table by creating a new model that uses the
same table, creating new columns in the session table and then saving
data in those columns for later direct access.
I would set the user_id in the same place you would have done it if
you had stored it in the normal sessions hash. So yes a before filter
in application controller works fine. You can always do a
skip_before_filter in other controllers that don’t need to set the
user_id. So when you log in a user and you would normally be storing
the user_id right in the normal session hash, you instead assign it
to session.model.user_id