OK, I've made all the session management changes that I've been
threatening to make and checked them into CVS. Next version of Quixote
will probably be 0.5 unless someone finds a small, easy-to-fix, and
embarassing bug, in which case we'll roll back the code a bit.
I've gotten to the point in the session mgmt docs where I need to talk
about persistence, and I have realized that the persistence hooks are
biased towards an all-or-nothing approach -- ie. when one session
changes, you have to save all of them. This stems from our use of ZODB,
because we don't really care if one Session object or 47 of them have
changed -- we just call
get_transaction().commit()
and let ZODB worry about the details. Since this happens at the end of
every request, it's guaranteed that only one session will have changed,
though. (Modulo multi-threading, which I'm trying to avoid thinking
about.)
Depending on your application and your persistence mechanism, this could
really suck. Eg. you might save sessions by pickling them one at a time
to a hash file, or to files in a directory (which would suck with
traditional filesystems, but should work pretty well with something like
ReiserFS). In those cases, there really ought to be a way to save a
single session.
So there are a couple of decisions to make. First decision: who should
be responsible for session persistence, the session class or the session
manager class? I strongly favour the session manager, because only it
knows about all sessions and thus it should manage the hash file, or
directory, or database connection, or whatever. Anyone disagree?
That settled, there's the question of the specific interface. Because
we don't want the interface between the publisher and the session
manager to be any more complicated than it needs to be, there should be
exactly two methods for the publisher to call: one to save the state of
the current session, and one to abort any such saving (necessary when
using a transactional object database that automagically mirrors changes
in objects-in-memory to objects-on-disk, like ZODB). The current
interface defined by the standard SessionManager class is dead simple:
def abort(self):
"""
Placeholder for subclasses that implement persistence: abort all
changes to all sessions. Called by SessionPublisher when a
request fails, ie. when it catches an exception other than
PublishError.
"""
def commit(self):
"""
Placeholder for subclasses that implement persistence: save all
changes to all sessions. Called by SessionPublisher when a
request completes successfully, or is interrupted by a
PublishError exception.
"""
I'd like to make two changes here:
* fundamentally, this interface should talk about "the current
session" rather than all sessions. Both methods should take
either a session object or a request object from which the session
object can be fetched. Then it's possible to implement save-one-
session-at-a-time persistence efficiently.
* trivially, I'd like to change the names, which betray a bias towards
transactional databases. There's no such requirement here, so
I don't think the names should reflect one.
Here's the interface I'm leaning towards at the moment:
def forget_changes (self, session):
"""forget_changes(session : Session)
Placeholder for subclasses that implement persistence: forget
about saving changes to the current session. Called by
SessionPublisher when a request fails, ie. when it catches an
exception other than PublishError. Probably only useful when
your persistence system automatically persists in-memory changes
and is transactional (ie. you can use this method to abort the
current transaction).
"""
def save_changes (self, session):
"""save_changes(session : Session)
Placeholder for subclasses that implement persistence: save
changes to the current session. Called by SessionPublisher when
a request completes successfully, or is interrupted by a
PublishError exception.
"""
How's that grab everyone? Feels pretty right to me. I think I'll code
it up and see how hard it is to implement a persistent SessionManager.
Greg