[Web-SIG] Session interface, v2

Mike Orr mso at oz.net
Sat Aug 20 21:56:31 CEST 2005

Ian Bicking wrote:

>Same location:

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.

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)
* Scenario 2: Same, but the apps are in separate processes.  The 
dispatcher remains.  (forking servers)
* 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.
* Scenario 4: Two apps in different virtual hosts.
* Scenario 5: Two apps in different webservers.
* Others: ??

Which situations are you trying to support, which session-related 
objects would there be, and how would they interrelate?  At what point 
do we say scenarios won't attract enough users to justify our time?

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 

>class SessionError(Exception):
>     pass
>class InvalidSession(SessionError):
>     """
>     Raised when an invalid session ID is used.
>     """
>class SessionNotFound(SessionError, LookupError):
>     """
>     Raised when a session can't be found.
>     """
>class ConflictError(SessionError):
>     """
>     Raised when the ``locking_policy`` is ``optimistic``, and a
>     session being saved is stale.
>     """
>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.

>class ISessionListener:

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

>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?

>     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.

>     def delete_expired_sessions():
>         """Scan for and delete any expired sessions.
>         ???: How are sessions defined to be expired?  Should listeners
>         participate?  Should they be able to cancel an expiration?
>         """
>     def session_ids():
>         """Return a list of session IDs.
>         ???: Should this return other metadata, like last accessed
>         time?
>         """

If so, it shouldn't be called .session_ids().

>class ISession:
>     manager = """Reference to parent ISessionManager object"""
>     def __init__(id, manager, read_only, last_accessed, creation_time, 
>         """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.

> 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?

More information about the Web-SIG mailing list