[Zodb-checkins] SVN: ZODB/trunk/src/ Renamed IStorageDB to IStorageWrapper and expanded it to provide

Jim Fulton jim at zope.com
Thu May 13 15:06:48 EDT 2010


Log message for revision 112295:
  Renamed IStorageDB to IStorageWrapper and expanded it to provide
  methods for transforming and untransforming data records. This was
  needed to make storage wrappers that transform data work with
  conflict resolution.
  

Changed:
  U   ZODB/trunk/src/CHANGES.txt
  U   ZODB/trunk/src/ZEO/StorageServer.py
  U   ZODB/trunk/src/ZODB/ConflictResolution.py
  U   ZODB/trunk/src/ZODB/DB.py
  U   ZODB/trunk/src/ZODB/interfaces.py
  U   ZODB/trunk/src/ZODB/tests/ConflictResolution.py

-=-
Modified: ZODB/trunk/src/CHANGES.txt
===================================================================
--- ZODB/trunk/src/CHANGES.txt	2010-05-13 19:06:45 UTC (rev 112294)
+++ ZODB/trunk/src/CHANGES.txt	2010-05-13 19:06:47 UTC (rev 112295)
@@ -32,6 +32,11 @@
   - You can now pass None (rather than a storage or file name) to get
     a database with a mapping storage.
 
+- Renamed IStorageDB to IStorageWrapper and expanded it to provide
+  methods for transforming and untransforming data records. This was
+  needed to make storage wrappers that transform data work with
+  conflict resolution.
+
 Bugs Fixed
 ----------
 

Modified: ZODB/trunk/src/ZEO/StorageServer.py
===================================================================
--- ZODB/trunk/src/ZEO/StorageServer.py	2010-05-13 19:06:45 UTC (rev 112294)
+++ ZODB/trunk/src/ZEO/StorageServer.py	2010-05-13 19:06:47 UTC (rev 112295)
@@ -778,6 +778,7 @@
     def invalidateCache(self):
         self.server._invalidateCache(self.storage_id)
 
+    transform_record_data = untransform_record_data = lambda self, data: data
 
 class StorageServer:
 

Modified: ZODB/trunk/src/ZODB/ConflictResolution.py
===================================================================
--- ZODB/trunk/src/ZODB/ConflictResolution.py	2010-05-13 19:06:45 UTC (rev 112294)
+++ ZODB/trunk/src/ZODB/ConflictResolution.py	2010-05-13 19:06:47 UTC (rev 112295)
@@ -53,6 +53,7 @@
 
 def state(self, oid, serial, prfactory, p=''):
     p = p or self.loadSerial(oid, serial)
+    p = self._crs_wrapper.untransform_record_data(p)
     file = StringIO(p)
     unpickler = Unpickler(file)
     unpickler.find_global = find_global
@@ -80,13 +81,13 @@
 
     def __cmp__(other):
         '''if other is equivalent reference, return 0; else raise ValueError.
-        
+
         Equivalent in this case means that oid and database_name are the same.
 
         If either is a weak reference, we only support `is` equivalence, and
         otherwise raise a ValueError even if the datbase_names and oids are
         the same, rather than guess at the correct semantics.
-        
+
         It is impossible to sort reliably, since the actual persistent
         class may have its own comparison, and we have no idea what it is.
         We assert that it is reasonably safe to assume that an object is
@@ -135,7 +136,7 @@
 
     def __cmp__(self, other):
         if self is other or (
-            isinstance(other, PersistentReference) and 
+            isinstance(other, PersistentReference) and
             self.oid == other.oid and
             self.database_name == other.database_name and
             not self.weak and
@@ -179,6 +180,7 @@
     # class_tuple, old, committed, newstate = ('',''), 0, 0, 0
     try:
         prfactory = PersistentReferenceFactory()
+        newpickle = self._crs_wrapper.untransform_record_data(newpickle)
         file = StringIO(newpickle)
         unpickler = Unpickler(file)
         unpickler.find_global = find_global
@@ -215,7 +217,7 @@
         pickler.inst_persistent_id = persistent_id
         pickler.dump(meta)
         pickler.dump(resolved)
-        return file.getvalue(1)
+        return self._crs_wrapper.transform_record_data(file.getvalue(1))
     except (ConflictError, BadClassName):
         return None
     except:
@@ -227,7 +229,11 @@
         logger.error("Unexpected error", exc_info=True)
         return None
 
-class ConflictResolvingStorage:
+class ConflictResolvingStorage(object):
     "Mix-in class that provides conflict resolution handling for storages"
 
     tryToResolveConflict = tryToResolveConflict
+
+    def registerDB(self, wrapper):
+        self._crs_wrapper = wrapper
+        super(ConflictResolvingStorage, self).registerDB(wrapper)

Modified: ZODB/trunk/src/ZODB/DB.py
===================================================================
--- ZODB/trunk/src/ZODB/DB.py	2010-05-13 19:06:45 UTC (rev 112294)
+++ ZODB/trunk/src/ZODB/DB.py	2010-05-13 19:06:47 UTC (rev 112295)
@@ -705,6 +705,8 @@
         """
         self._connectionMap(lambda c: c.invalidateCache())
 
+    transform_record_data = untransform_record_data = lambda self, data: data
+
     def objectCount(self):
         return len(self.storage)
 

Modified: ZODB/trunk/src/ZODB/interfaces.py
===================================================================
--- ZODB/trunk/src/ZODB/interfaces.py	2010-05-13 19:06:45 UTC (rev 112294)
+++ ZODB/trunk/src/ZODB/interfaces.py	2010-05-13 19:06:47 UTC (rev 112295)
@@ -287,24 +287,33 @@
         """
 
 
-class IStorageDB(Interface):
-    """Database interface exposed to storages
+class IStorageWrapper(Interface):
+    """Storage wrapper interface
 
-    This interface provides 2 facilities:
+    This interface provides 3 facilities:
 
     - Out-of-band invalidation support
 
-      A storage can notify it's database of object invalidations that
+      A storage can notify it's wrapper of object invalidations that
       don't occur due to direct operations on the storage.  Currently
       this is only used by ZEO client storages to pass invalidation
       messages sent from a server.
 
-    - Record-reference extraction.
+    - Record-reference extraction
 
       The references method can be used to extract referenced object
       IDs from a database record.  This can be used by storages to
-      provide more advanced garbage collection.
+      provide more advanced garbage collection.  A wrapper storage
+      that transforms data will provide a references method that
+      untransforms data passed to it and then pass the data to the
+      layer above it.
 
+    - Record transformation
+
+      A storage wrapper may transform data, for example for
+      compression or encryption.  Methods are provided to transform or
+      untransform data.
+
     This interface may be implemented by storage adapters or other
     intermediaries.  For example, a storage adapter that provides
     encryption and/or compresssion will apply record transformations
@@ -337,7 +346,17 @@
         be created and returned.
         """
 
+    def transform_record_data(data):
+        """Return transformed data
+        """
 
+    def untransform_record_data(data):
+        """Return untransformed data
+        """
+
+IStorageDB = IStorageWrapper # for backward compatibility
+
+
 class IDatabase(IStorageDB):
     """ZODB DB.
     """
@@ -595,12 +614,18 @@
         revisions.
         """
 
-    def registerDB(db):
-        """Register an IStorageDB.
+    def registerDB(wrapper):
+        """Register a storage wrapper IStorageWrapper.
 
+        The passed object is a wrapper object that provides an upcall
+        interface to support composition.
+
         Note that, for historical reasons, an implementation may
         require a second argument, however, if required, the None will
         be passed as the second argument.
+
+        Also, for historical reasons, this is called registerDB rather
+        than register_wrapper.
         """
 
     def sortKey():

Modified: ZODB/trunk/src/ZODB/tests/ConflictResolution.py
===================================================================
--- ZODB/trunk/src/ZODB/tests/ConflictResolution.py	2010-05-13 19:06:45 UTC (rev 112294)
+++ ZODB/trunk/src/ZODB/tests/ConflictResolution.py	2010-05-13 19:06:47 UTC (rev 112295)
@@ -53,9 +53,15 @@
     def _p_resolveConflict(self, oldState, savedState):
         raise RuntimeError("Can't get here; not enough args")
 
+class StorageWrapper:
+
+    transform_record_data = untransform_record_data = lambda self, data: data
+
 class ConflictResolvingStorage:
 
     def checkResolve(self):
+        self._storage.registerDB(StorageWrapper())
+
         obj = PCounter()
         obj.inc()
 
@@ -76,6 +82,8 @@
         self.assertEqual(inst._value, 5)
 
     def checkUnresolvable(self):
+        self._storage.registerDB(StorageWrapper())
+
         obj = PCounter2()
         obj.inc()
 
@@ -97,11 +105,15 @@
             self.fail("Expected ConflictError")
 
     def checkZClassesArentResolved(self):
+        self._storage.registerDB(StorageWrapper())
+
         from ZODB.ConflictResolution import find_global, BadClassName
         dummy_class_tuple = ('*foobar', ())
         self.assertRaises(BadClassName, find_global, '*foobar', ())
 
     def checkBuggyResolve1(self):
+        self._storage.registerDB(StorageWrapper())
+
         obj = PCounter3()
         obj.inc()
 
@@ -120,6 +132,8 @@
                           oid, revid=revid1, data=zodb_pickle(obj))
 
     def checkBuggyResolve2(self):
+        self._storage.registerDB(StorageWrapper())
+
         obj = PCounter4()
         obj.inc()
 
@@ -144,6 +158,8 @@
         # TransactionalUndoStorage test suite.  Except here, conflict
         # resolution should allow us to undo the transaction anyway.
 
+        self._storage.registerDB(StorageWrapper())
+
         obj = PCounter()
         obj.inc()
         oid = self._storage.new_oid()
@@ -166,6 +182,8 @@
         # TransactionalUndoStorage test suite.  Except here, conflict
         # resolution should allow us to undo the transaction anyway.
 
+        self._storage.registerDB(StorageWrapper())
+
         obj = PCounter2()
         obj.inc()
         oid = self._storage.new_oid()



More information about the Zodb-checkins mailing list