[Web-SIG] Session interface

Ian Bicking ianb at colorstudy.com
Wed Aug 17 07:31:12 CEST 2005


Mike Orr wrote:
> Regarding Ian's session interface:
> http://svn.colorstudy.com/home/ianb/scarecrow_session_interface.py
> 
> Ian Bicking wrote:
> 
>> Thinking on it more, probably a good place to start would be agreeing 
>> on specific terminology for the objects involved, since I've seen 
>> several different sets of terminology, many of which use the same 
>> words for different ideas:
>>
>> Session:
>>   An instance of this represents one user/browser's session.
>> SessionStore:
>>   An instance of this represents the persistence mechanism.  This
>>   is a functional component, not embodying any policy.
>> SessionManager:
>>   This is a container for sessions, and uses a SessionStore.  This
>>   contains all the policy for loading, saving, locking, expiring
>>   sessions.
>>  
>>
> 
> 
> At minimum, the SessionManager links the SessionStore, Session, and 
> application together.  It can be generic, along with 
> loading/saving/locking.  (Although we might allow the application to 
> choose a locking policy.)  

That could be a little difficult, since multiple applications may be 
sharing a session.  But at the same time, applications that don't expect 
ConflictError are going to be pissed if you configure your system for 
optimistic locking.

Of course, given a session ID and a session store, each application 
could have its own manager.  Possibly.  Hmm... interesting.  In that 
case each SessionManager needs an id, which is a bit annoying -- it has 
to be stable and shared, because the same SessionManager has to be 
identifiable over multiple processes.  But I hate inventing IDs all over 
the place.  I feel like I'm pulling string keys out of my ass, and if 
I'm going to pull things out of my ass I at least don't want to then put 
them into my code.  I sense UUIDs coming on :(

That said, this isn't the only place I need strings that are unique to 
an application instance.

> But expiring is very application-specific, 
> and it may not be the "application" doing it but a separate cron job.  
> Perhaps most applications will be happy with an "expire all sessions 
> unmodified for N minutes", but some will want to inspect the metadata 
> and others the content.  So maybe all the SessionManager can do is:
> 
>    .delete_session(id)   => pass message directly to SessionStore
>    .iter_sessions()  =>  tuples of (id, metadata)
>    .iter_sessions_with_content() => tuples of (id, metadata, content)

I think metadata is probably good; or lazily-loaded sessions or 
something.  The metadata is important I think, because updating metadata 
shouldn't be effected by locking and whatnot.  I think Mike mentioned a 
problem with locking and updating the timestamp contained in the session 
-- we should avoid that.

> ... where metadata includes the access time and whatever else we 
> decide.  Of course, iterating the content may be disk/memory intensive.

Sure.  We could have a callback to do filtering too, maybe with a 
default filter by expiration time.  Or event callbacks.

> If .delete_expired_sessions() is included, the application would have to 
> subclass SessionManager rather than just using it.  That's not 
> necessarily bad but a potential limitation.  Or the application could 
> kludge up a policy from your methods:
> 
>    cutoff = time.time() - (60 * 60 * 4)
>    for sid in sm.session_ids():
>        if sm.last_accessed(sid) < cutoff:
>            sm.delete_session(sid)
> 
> I suppose kludgy is in the eye of the beholder.  This would not be kludgy:
> 
>    cutoff = time.time() - (60 * 60 * 4)
>    for sid, metadata in sm.iter_sessions():
>        if metadata.atime < cutoff:
>            sm.delete_session(sid)
> 
> Curses on anybody who says, "What's the difference?"
> 
> PS. Kudos for using .names_with_underscores rather than .studlyCaps.
> 
> Your other methods look all right at first glance.  We'll know when we 
> port existing frameworks to it whether it's adequate.  (Or should that 
> be "when we port it to existing frameworks"?  Or "when we make existing 
> frameworks use it as middleware"?)  We'll also have to keep an eye on a 
> usage pattern to recommend for future frameworks, and on whether this 
> API has anything to do with the "sessionless" persistance patterns that 
> have also been proposed.

Acquiring or creating a session ID is outside of the scope of this 
interface, but I think that's much of what would be useful to 
sessionless users.  Or, rather, people who want application-specific 
sessions.

> Interesting ideas you've had about read/write vs read-only sessions.  
> I'd say let's support read-only sessions, and maybe that will encourage 
> applications to use them.
> 
> Session ID cookies seem like a generic thing this class should handle, 
> especially for applications that don't otherwise use cookies.  XML-RPC 
> encapsulates the XML (an necessary evil); why shouldn't we encapsulate 
> the cookie (another necessary evil)?

XML-RPC contains the XML, but it doesn't deal with the transport really. 
  And, just using XML-RPC as an example, what if you want to stuff the 
session ID inside the XML-RPC request instead of in a cookie header?

But anyway, the reason I don't want to handle this is because this would 
be much easier if building upon a Standard That Does Not Yet Exist, and 
I'd rather avoid overlapping with that standard.

>> Does that sound good?  Note that the attached interface conflates 
>> SessionStore and SessionManager.  Some interfaces make an explicit 
>> ApplicationSession, which is contained by Session and keyed off some 
>> application ID; my interface implies that separation, but does not 
>> enforce it, and does not offer any extra functionality at that level 
>> (e.g., per-ApplicationSession locks or transactions).
>>  
>>
> 
> 
> I'm not sure what you mean by ApplicationSession.  Perl's session object 
> is a dictionary, and you can store anything in it.  Our top-level object 
> has to be flexible due to grandfathering, unless we want to force 
> applications to translate to/from our session object to their native 
> session format.  Yet you define certain attributes/methods the Session 
> must have, which legacy Sessions don't.  I guess allow the application 
> to provide a subclass or compatible class, and let it worry about how to 
> upgrade its native session object.

I was thinking of pythonweb's "Store": 
http://pythonweb.org/projects/webmodules/doc/0.5.3/html_multipage/lib/node153.html

I vaguely suggest in the interface that each application should put all 
of its data in a single key (based on the application name).  Now I 
think that should be based on a unique name (not the application name, 
because the application may exist multiple times in the process), and 
maybe with an entirely different manager.

> Regarding sessionless persistence, that reminds me of a disagreement I 
> had with Titus in designing session2.  Quixote provides Session.user 
> default None, but doesn't define what other values it can have.  I put a 
> full-fledged User object with username/group/permission info.  Titus 
> puts a string name and stores everything else in his application 
> database.  So his *SessionStore classes put the name in a VARCHAR column 
> and didn't save the rest of the session data.  I argued that "most 
> people will have a User object, and they'll expect the entire Session to 
> be pickled because that's what PHP/Perl do."  He relented, so the 
> current *SessionStores can be used either way.

In the interface I suggest anything pickleable can go in a key.  This 
requirement has been the source of some controversy in Webware, since 
people wanted to put open file objects and such in the session; mostly 
people coming from Java where apparently that's the norm.  Anyway, it's 
still possible with this interface to have a store that never pickles 
anything; I can just hope no one writes code they expect anyone else to 
use that demands in-memory session storage.  Those are lame even when 
you are using threads.

I think the example shows one reason the session shouldn't be considered 
a public API.  I think it's fine to put the username or the user object 
in the session -- we can debate the pluses and minuses, but it works -- 
but I think you should definitely wrap that implementation detail in 
something else.  E.g., request.user should return 
request.session['user'] or something.

> Perhaps applications should store all session data directly, keyed by 
> session ID (and perhaps "username"), rather than using pickled 
> Sessions.  That would be a good idea for a parallel project.  I'm not 
> sure how relevant that would be to this API except to share "cookie 
> code".  This API + implementations are required in any case, both 
> because "most users" will not consider Python if it doesn't have "robust 
> session handling", and a common library would allow frameworks to use it 
> rather than reinventing the wheel incompatibly.  This is true regardless 
> of the merits of sessions.

I guess if applications each have their own SessionManager, they could 
have their own Session classes, and if they wanted to the Session 
objects could use application-specific storage and even an 
application-specific API (not just a dictionary interface).  I don't 
know what the point of that would be, though, since it's all 
application-specific and not generic, so you might as well just use the 
session ID and ignore the rest of the API.

-- 
Ian Bicking  /  ianb at colorstudy.com  / http://blog.ianbicking.org


More information about the Web-SIG mailing list