[Web-SIG] Session interface, v2

Ian Bicking ianb at colorstudy.com
Sat Aug 20 23:46:00 CEST 2005


Mike Orr wrote:
> Ian Bicking wrote:
> 
>> Same location:
>>
>> http://svn.colorstudy.com/home/ianb/scarecrow_session_interface.py
>>  
>>
> 
> 
> Good work.
> 
> 
>> This version separates out SessionManager from SessionStore, and 
>> suggests that managers be per-application (or maybe per-framework).
>>
> There's per-application/per-framework at the class level and instance 
> level; I'm not sure which you're referring to.
> 
> Regarding the former, I was saying we may be able to make a generic 
> SessionManager class usable as-is by several frameworks.  You seemed to 
> doubt this, but I argued we should at least try.

I don't think several frameworks should share a single SessionManager 
*instance*.  But I do think we can make a class that can embody all the 
features that are need for typical sessions, and frameworks use 
instances.  I think people who want something else from a session -- 
application-specific storage, for instance -- may need their own 
SessionManager class.

> The latter only matters in multi-application deployments, where several 
> applications (possibly different frameworks) are sharing a session.  
> Several features below seem to exist only for this environment, and I'm 
> having a hard time evaluating them without knowing the complete use 
> cases you're trying to support.  You've said bits about that but maybe 
> we can flesh it out.
> 
> * Scenario 1: Two apps mounted at /foo and /bar, using a common Paste 
> dispatcher.  Both applications are embedded in the same process.  
> (threaded or asynchronous servers)

This is the case that drives a lot of the issues.  Say, for instance, 
that the two application's are instances of the same basic app (e.g., 
blogs for two different users).  If they share a session they'll 
overwrite each other's values, or become hopelessly confused by 
seemingly inconsistent data.

If each of them has a separate app id and separate session managers, 
then they'll never see the other's data.  But you can only do that with 
some fixed id (generated randomly or by hand, either way probably stored 
in the configuration).  Hmm... potentially you could just generate such 
an id from the configuration file's name (if not given something more 
specific); that's a little sloppy, but generally likely to be unique and 
stable.

> * Scenario 2: Same, but the apps are in separate processes.  The 
> dispatcher remains.  (forking servers)

If the two apps share the same pool of long-lived worker processes, then 
all the same issues remain as with scenario 1.  This isn't really an 
issue of threaded vs. multiprocess, but an issue of processes that run 
multiple independent applications over time.  A common pool of worker 
processes would be similar to PHP, except that PHP tends to throw away 
more information each request... though I believe session clobbering 
would be a problem in PHP if you had two apps on the same domain that 
shared a session variable name.

> * Scenario 3: Two apps mounted at /foo and /bar, using separate handlers 
> in the Apache config.  At no point is there a common Python process 
> between them.

It depends on the configuration, but clobbering could happen here too. 
If both apps use the same session id (e.g., they use the same cookie 
name) and share session store configuration (they are writing to the 
same location), then it will be a problem.  Using session managers with 
separate app ids they can share session store configuration safely.

> * Scenario 4: Two apps in different virtual hosts.

Probably not an issue because the session id won't be shared.  A good 
session id manager might be able to handle this, though, but forwarding 
the user between the two hosts with a special GET variable that triggers 
the setting of a cookie; if that was happening it would be like scenario 3.

> * Scenario 5: Two apps in different webservers.

Much like scenario 4; problems are possible without the session manager, 
but increasingly less likely.

Most conflict issues could also be fixed by not sharing a session id 
between applications (and probably using a configurable session cookie 
name).

> * Others: ??
> 
> Which situations are you trying to support, which session-related 
> objects would there be, and how would they interrelate?  

I want to support all of them.  In part this is because I have a vision 
of much more granular applications, so I want it to be possible to 
deploy small applications with little risk of interaction problems.

> At what point 
> do we say scenarios won't attract enough users to justify our time?

Well, I'm just thinking about the simple session stores, not much along 
the application-specific stores.  So I'm leaving something out there.

> I'm also not sure how these would relate to your "application inversion" 
> paradigm.  I'm used to applications as single long-running units that 
> can hold shared state.  But your Paste implementation seems to suggest 
> instantiating the application for each URL, and maybe the application 
> would last for only one request.  I'm not sure how easy that will be to 
> port some applications to it, or how this impacts the session 
> classees/instances.

I don't think this really relates a whole lot.  Paste doesn't need to 
instantiate for each URL, it could fetch an already-instantiated 
application just as well.  paste.urlmap only dispatches to pre-existing 
applications, for instance, while paste.urlparser instantiates.

>> def create_session_id():
>>  
>>
> 
> Could go into a SessionCookie class, along with anything else that can 
> be used by both session-based and sessionless fans.

It could, but session IDs can come from elsewhere.  E.g., you might want 
to use it as an argument in an XMLRPC class.  So I think it's pretty 
independent of any particular browser identification technique.

>> class ISessionListener:
>>  
>>
> 
> 
> Is this just an extra, or what are listeners for?  Is this for 
> per-application behavior with a shared manager?

It's kind of an extra.  I'm not really sure what would be done with it. 
  An example I gave before about storing files and only storing the 
filename in the session would be helped by listeners, as you could add a 
file-deleting listener that was triggered on session delete.  Anytime 
when you put data associated with the session somewhere outside of the 
session store I think this will be useful.

>> class ISessionManager:
>>
>>     id = """The string-identifier for this session manager.
>>
>>     All applications that share this session manager need to use the
>>     same id when creating the session manager.
>>  
>>
> 
> 
> With this rule I was expecting some central repository of session 
> managers, and factory functions a la logger.getLogger(), but there 
> doesn't seem to be any.  What's the purpose of the SessionManager id?

The session manager id is used by the session store, to keep the 
sessions separate.  Actual session data is keyed by (session_manager_id, 
session_id), so that separate applications have separate 
session_manager_ids, and separate browsers have separate session_ids.

>>     locking_policy = """The lock policy.
>>
>>     This is one of these strings:
>>
>>     ``'optimistic'``:
>>       Optimistic locking; concurrent sessions may be opened for writing;
>>       however, if a session is saved that was loaded before the last save
>>       of the session, a ConflictError will be raised.
>>
>>     ``'lossy'``:
>>       First-come-first-serve.  No locking is done; if a session is 
>> written
>>       it overwrites any other session data that was written.
>>
>>     ``'serialized`'':
>>       All sessions opened for writing are serialized; the request is
>>       blocked until session is available to be opened.
>>     """
>>  
>>
> 
> 
> Optimistic locking sounds like a pain.  The application would have to 
> catch the error and then... what?  Say "Sorry, your form input was 
> thrown away."  Redo the operation somehow (isn't that the same as lossy 
> operation?).  Reconcile the two states somehow (how?)?  Not that we 
> shouldn't provide it, just that it will need more howto documentation.

It is a bit of a pain.  In Zope they catch ConflictErrors, roll back 
everything, and restart the request.  I've had this bite me, as it just 
makes the contention worse, but for sessions in particular it might not 
be so bad (as long as *everything* is transactional and can be rolled back).

Anyway, it's there mostly for the frameworks that already know how to 
handle this.

>> class ISession:
>>
>>     manager = """Reference to parent ISessionManager object"""
>>
>>     def __init__(id, manager, read_only, last_accessed, creation_time, 
>> data):
>>         """Create the session object
>>
>>     def touch():
>>         """Update the session's last_accessed time.
>>
>>         Typically just calls ``self.manager.touch(self.id)``
>>         """
>>
>>     def commit():
>>         """Calls ``self.manager.save_session(self)``
>>         """
>>
>>     def rollback():
>>         """Calls ``self.manager.unlock_session(self)``.
>>
>>         Also calls ``session_listener.rollback(self)``.
>>         """
>>  
>>
> 
> 
> These look like they don't belong here.  The application already has a 
> reference to the SessionManager and should call it directly.  It points 
> up a difference in philosophy between the session being a "dumb object" 
> (no reference to the manager) vs being manager-aware.  Is the latter 
> necessary?  Are you thinking of cases where the session would be 
> provided by the middleware, then the application would have dispose of 
> the session at the end of the request?  The middleware could provide a 
> reference to the session manager for this.  Although that would expose 
> irrelevant methods.

Mostly these are there both to make the interface slightly nicer (many 
times you won't have to interact with the session manager), and to 
facilitate per-session session listeners.  I'm not sure per-session 
listeners are a good idea, though.

>> class ISessionStore:
>>      def load_session(session_store_id, session_id, read_only,
>> session_factory):
>>      def session_ids(session_store_id):
>>      def delete_session(session_store_id, session_id):
>>      def touch(session_store_id, session_id):
>>      def write_lock_session(session_store_id, session_id):
> 
> 
> Isn't session_store_id 'self'?  Specifying it seems to imply this is a 
> meta SessionStore, not an individual store.  Why would a deployment have 
> multiple stores?

Oops, this was a leftover from when SessionManager was named 
SessionStore.  These should all be session_manager_id.  Fixed in svn.


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


More information about the Web-SIG mailing list