[Checkins] SVN: lovely.session/trunk/ Made the DataManager savepoint aware.

Adam Groszer agroszer at gmail.com
Thu Sep 25 17:35:16 EDT 2008


Log message for revision 91490:
  Made the DataManager savepoint aware.
  

Changed:
  U   lovely.session/trunk/CHANGES.txt
  U   lovely.session/trunk/src/lovely/session/README.txt
  U   lovely.session/trunk/src/lovely/session/memcached.py

-=-
Modified: lovely.session/trunk/CHANGES.txt
===================================================================
--- lovely.session/trunk/CHANGES.txt	2008-09-25 21:16:36 UTC (rev 91489)
+++ lovely.session/trunk/CHANGES.txt	2008-09-25 21:35:16 UTC (rev 91490)
@@ -2,12 +2,18 @@
 CHANGES
 =======
 
-0.1.5 (unreleased)
+0.2.1 (unreleased)
 ------------------
 
 - ...
 
 
+0.2.0 (2008-09-25)
+------------------
+
+- Made the DataManager savepoint aware.
+
+
 0.1.4 (2008-07-31)
 ------------------
 
@@ -34,4 +40,3 @@
 ------------------
 
 - Fixed dependency on `lovely.memcached`.
-

Modified: lovely.session/trunk/src/lovely/session/README.txt
===================================================================
--- lovely.session/trunk/src/lovely/session/README.txt	2008-09-25 21:16:36 UTC (rev 91489)
+++ lovely.session/trunk/src/lovely/session/README.txt	2008-09-25 21:35:16 UTC (rev 91490)
@@ -15,6 +15,9 @@
 
 Start a memcache instance with : memcached <optional options>
 
+
+Once memcached is running, we can start testing:
+
   >>> from zope import component
   >>> from lovely.memcached.interfaces import IMemcachedClient
   >>> from lovely.memcached.utility import MemcachedClient
@@ -51,13 +54,19 @@
   >>> data
   {'info': 'stored in memcache'}
 
+
+
+Transaction support
+~~~~~~~~~~~~~~~~~~~
+
 Because the MemCacheSession is transaction aware we need to commit the
 transaction to store data in the memcache.
 
   >>> import transaction
+
   >>> transaction.commit()
 
-If we now read session data is is read back from the memcache.
+If we now read session data it is read back from the memcache.
 
   >>> session = sessionData['mySessionId']
   >>> session['myData']
@@ -66,3 +75,77 @@
   >>> sessionData.items()
   [('mySessionId', <lovely.session.memcached.DataManager object at ...>)]
 
+
+MemCacheSession is now also savepoint aware, let's check how that works:
+
+We first set some data:
+
+  >>> session = sessionData['mySessionId']
+  >>> data = session['myData']
+  >>> data['info'] = 'we want to keep this'
+
+Set a savepoint:
+
+  >>> savepoint = transaction.savepoint()
+
+Change the data:
+
+  >>> data['info'] = 'this should be dumped'
+
+Rollback to the previous value:
+
+  >>> savepoint.rollback()
+
+And here it is, the before value:
+
+  >>> data['info']
+  'we want to keep this'
+
+Newly added data must also go away:
+
+We add a new data:
+
+  >>> data['newinfo'] = 'go away'
+
+And a new container:
+
+  >>> newdata = session['myNewData']
+  >>> newdata['foo'] = 'bar'
+
+Roll it back to the previous savepoint:
+
+  >>> savepoint.rollback()
+
+The data is gone:
+
+  >>> data['newinfo']
+  Traceback (most recent call last):
+  ...
+  KeyError: 'newinfo'
+
+The container is empty, because it gets always created on retrieval:
+
+  >>> session['myNewData']
+  {}
+
+Let's see what happens on commit:
+
+  >>> transaction.commit()
+
+If we now read session data it is read back from the memcache.
+
+  >>> session = sessionData['mySessionId']
+  >>> session['myData']
+  {'info': 'we want to keep this'}
+
+The data is not present:
+
+  >>> data['newinfo']
+  Traceback (most recent call last):
+  ...
+  KeyError: 'newinfo'
+
+The container is empty, because it gets always created on retrieval:
+
+  >>> session['myNewData']
+  {}

Modified: lovely.session/trunk/src/lovely/session/memcached.py
===================================================================
--- lovely.session/trunk/src/lovely/session/memcached.py	2008-09-25 21:16:36 UTC (rev 91489)
+++ lovely.session/trunk/src/lovely/session/memcached.py	2008-09-25 21:35:16 UTC (rev 91490)
@@ -23,7 +23,8 @@
 
 import persistent
 import transaction
-from transaction.interfaces import IDataManager
+from transaction.interfaces import ISavepointDataManager
+from transaction.interfaces import IDataManagerSavepoint
 
 from zope import interface
 from zope import component
@@ -152,7 +153,7 @@
 
 class DataManager(object):
     """A data manager for the transaction management of the session data"""
-    interface.implements(IDataManager)
+    interface.implements(ISavepointDataManager)
 
     def __init__(self, sessionData, key):
         self.sessionData = sessionData
@@ -193,3 +194,43 @@
     def sortKey(self):
         return str(id(self))
 
+    def savepoint(self):
+        # When we create the savepoint, we save the existing database state.
+        return Savepoint(self, cPickle.dumps(self._data))
+
+    def _rollback_savepoint(self, data):
+        # When we rollback the savepoint, we restore the saved data.
+        # Caution:  without the pickle, further changes to the database
+        # could reflect in savepoint.data, and then `savepoint` would no
+        # longer contain the originally saved data, and so `savepoint`
+        # couldn't restore the original state if a rollback to this
+        # savepoint was done again.  IOW, pickle is necessary.
+
+        # there is also a small dance around with the container and
+        # and data not to break referenced variables
+        # the MemCachedSessionDataContainer / MemCacheSessionData / MemCachePkgData
+        # seems to be stable so this seems to be reasonable
+
+        state = cPickle.loads(data)
+        self._data.lastAccessTime = state.lastAccessTime
+        for k,v in state.items():
+            self._data[k].update(v)
+            todel = [x for x in self._data[k].keys() if x not in v.keys()]
+            for x in todel:
+                del self._data[k][x]
+
+        todel = [x for x in self._data.keys() if x not in state.keys()]
+        for x in todel:
+            del self._data[x]
+
+
+class Savepoint(object):
+
+    interface.implements(IDataManagerSavepoint)
+
+    def __init__(self, data_manager, data):
+        self.data_manager = data_manager
+        self.data = data
+
+    def rollback(self):
+        self.data_manager._rollback_savepoint(self.data)



More information about the Checkins mailing list