durusmail: quixote-users: Interface for session persistence
Interface for session persistence
2002-05-18
2002-05-18
2002-05-19
2002-05-20
2002-05-20
2002-05-20
2002-05-20
2002-05-20
2002-05-21
2002-05-24
2002-05-24
2002-05-24
2002-05-18
2002-05-20
2002-05-20
Interface for session persistence
Greg Ward
2002-05-18
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



reply