[Zope-Checkins] CVS: Zope/lib/python/Products/TemporaryFolder - TemporaryStorage.py:1.4

Chris McDonough chrism@zope.com
Tue, 20 Nov 2001 22:13:37 -0500


Update of /cvs-repository/Zope/lib/python/Products/TemporaryFolder
In directory cvs.zope.org:/tmp/cvs-serv17968

Modified Files:
	TemporaryStorage.py 
Log Message:
Made rudimentary conflict resolution work in TemporaryStorage reliably

=== Zope/lib/python/Products/TemporaryFolder/TemporaryStorage.py 1.3 => 1.4 ===
 A storage implementation which uses RAM to persist objects, much like
 MappingStorage.  Unlike MappingStorage, it needs not be packed to get rid of
-non-cyclic garbage.  This is a ripoff of Jim's Packless bsddb3 storage.
+non-cyclic garbage and it does rudimentary conflict resolution.  This is a
+ripoff of Jim's Packless bsddb3 storage.
 
 $Id$
 """
@@ -98,7 +99,14 @@
 from ZODB.referencesf import referencesf
 from ZODB import POSException
 from ZODB.BaseStorage import BaseStorage
-from ZODB.ConflictResolution import ConflictResolvingStorage, ResolvedSerial
+from ZODB.ConflictResolution import ConflictResolvingStorage, ResolvedSerial,\
+     bad_classes, bad_class, _classFactory, PersistentReference, \
+     PersistentReferenceFactory, persistent_id, state
+from cStringIO import StringIO
+from cPickle import Unpickler, Pickler
+
+# number of transactions for which to keep prior object revisions
+CONFLICT_CACHE_SIZE = 100
 
 class ReferenceCountError(POSException.POSError):
     """ An error occured while decrementing a reference to an object in
@@ -110,7 +118,7 @@
     available tempfile space and RAM consumption and restart the server
     process."""
 
-class TemporaryStorage(BaseStorage):# , ConflictResolvingStorage):
+class TemporaryStorage(BaseStorage, ConflictResolvingStorage):
 
     def __init__(self, name='TemporaryStorage'):
         """
@@ -118,6 +126,9 @@
         referenceCount -- mapping of oid to count
         oreferences -- mapping of oid to a sequence of its referenced oids
         opickle -- mapping of oid to pickle
+        _tmp -- used by 'store' to collect changes before finalization
+        _conflict_cache -- cache of recently-written object revisions
+        _transaction_counter -- rotating counter used in conflict resolution
         """
         BaseStorage.__init__(self, name)
 
@@ -126,6 +137,8 @@
         self._oreferences={}
         self._opickle={}
         self._tmp = []
+        self._conflict_cache = {}
+        self._transaction_counter = 0
         self._oid = '\0\0\0\0\0\0\0\0'
 
     def __len__(self):
@@ -136,6 +149,10 @@
 
     def _clear_temp(self):
         self._tmp = []
+        if self._transaction_counter % CONFLICT_CACHE_SIZE == 0:
+            # clear the revision cache before we run out of RAM.
+            self._transaction_counter = 0
+            self._conflict_cache = {}
         
     def close(self):
         """
@@ -151,17 +168,18 @@
         finally:
             self._lock_release()
 
-## This loadSerial doesn't work for resolution of conficts.  :-(  I
-## haven't figured out why that's the case.  As a result, I'm disabling
-## conflict resolution in the storage until I have time to figure out why.
-
-##     def loadSerial(self, oid, serial):
-##         """ only a stub to make conflict resolution work! """
-##         self._lock_acquire()
-##         try:
-##             return self._opickle[oid]
-##         finally:
-##             self._lock_release()
+    def loadSerial(self, oid, serial, marker=[]):
+        """ this is only useful to make conflict resolution work.  It
+        does not actually implement all the semantics that a revisioning
+        storage needs! """
+        self._lock_acquire()
+        try:
+            data = self._conflict_cache.get((oid, serial), marker)
+            if data is marker:
+                raise POSException.ConflictError, (oid, serial)
+            return data
+        finally:
+            self._lock_release()
             
     def store(self, oid, serial, data, version, transaction):
         if transaction is not self._transaction:
@@ -175,20 +193,18 @@
             if self._index.has_key(oid):
                 oserial=self._index[oid]
                 if serial != oserial:
-                    raise POSException.ConflictError, (serial, oserial)
-##                     data=self.tryToResolveConflict(oid, oserial, serial, data)
-##                     if not data:
-##                         raise POSException.ConflictError, (serial,oserial)
+                    data=self.tryToResolveConflict(oid, oserial, serial, data)
+                    if not data:
+                        raise POSException.ConflictError, (serial,oserial)
             else:
                 oserial = serial
             newserial=self._serial
             self._tmp.append((oid, data))
-##             return serial == oserial and newserial or ResolvedSerial
-            return newserial
+            self._conflict_cache[(oid, newserial)] = data
+            return serial == oserial and newserial or ResolvedSerial
         finally:
             self._lock_release()
 
-
     def _finish(self, tid, u, d, e):
         zeros={}
         referenceCount=self._referenceCount
@@ -263,7 +279,8 @@
                 if oid == '\0\0\0\0\0\0\0\0': continue
                 self._takeOutGarbage(oid)
 
-        self._tmp = []
+        self._transaction_counter = self._transaction_counter + 1
+        self._clear_temp()
 
     def _takeOutGarbage(self, oid):
         # take out the garbage.
@@ -317,6 +334,7 @@
             self._lock_release()
 
 
+