[Zodb-checkins] SVN: ZODB/trunk/src/ cross-database weakrefs weren't handled correctly.

Jim Fulton jim at zope.com
Mon Apr 26 16:51:59 EDT 2010


Log message for revision 111454:
  cross-database weakrefs weren't handled correctly.
  
  https://bugs.launchpad.net/zodb/+bug/435547
  

Changed:
  U   ZODB/trunk/src/CHANGES.txt
  U   ZODB/trunk/src/ZODB/ConflictResolution.py
  U   ZODB/trunk/src/ZODB/ConflictResolution.txt
  U   ZODB/trunk/src/ZODB/serialize.py
  U   ZODB/trunk/src/persistent/wref.py

-=-
Modified: ZODB/trunk/src/CHANGES.txt
===================================================================
--- ZODB/trunk/src/CHANGES.txt	2010-04-26 20:34:58 UTC (rev 111453)
+++ ZODB/trunk/src/CHANGES.txt	2010-04-26 20:51:59 UTC (rev 111454)
@@ -34,15 +34,19 @@
 
   https://bugs.launchpad.net/zodb/+bug/544305
 
+- When using using a ClientStorage in a Storage server, there was a
+  threading bug that caused clients to get disconnected.
+
 - Fixed a bug that caused savepoint rollback to not properly
   set object state when objects implemented _p_invalidate methods
   that reloaded ther state (unghostifiable objects).
 
   https://bugs.launchpad.net/zodb/+bug/428039
 
-- When using using a ClientStorage in a Storage server, there was a
-  threading bug that caused clients to get disconnected.
+- cross-database wekrefs weren't handled correctly.
 
+  https://bugs.launchpad.net/zodb/+bug/435547
+
 3.10.0a1 (2010-02-08)
 =====================
 

Modified: ZODB/trunk/src/ZODB/ConflictResolution.py
===================================================================
--- ZODB/trunk/src/ZODB/ConflictResolution.py	2010-04-26 20:34:58 UTC (rev 111453)
+++ ZODB/trunk/src/ZODB/ConflictResolution.py	2010-04-26 20:51:59 UTC (rev 111454)
@@ -116,13 +116,17 @@
             # 'm' = multi_persistent: (database_name, oid, klass)
             # 'n' = multi_oid: (database_name, oid)
             # 'w' = persistent weakref: (oid)
+            #    or persistent weakref: (oid, database_name)
             # else it is a weakref: reference_type
             if reference_type == 'm':
                 self.database_name, self.oid, self.klass = data[1]
             elif reference_type == 'n':
                 self.database_name, self.oid = data[1]
             elif reference_type == 'w':
-                self.oid, = data[1]
+                try:
+                    self.oid, = data[1]
+                except ValueError:
+                    self.oid, self.database_name = data[1]
                 self.weak = True
             else:
                 assert len(data) == 1, 'unknown reference format'

Modified: ZODB/trunk/src/ZODB/ConflictResolution.txt
===================================================================
--- ZODB/trunk/src/ZODB/ConflictResolution.txt	2010-04-26 20:34:58 UTC (rev 111453)
+++ ZODB/trunk/src/ZODB/ConflictResolution.txt	2010-04-26 20:51:59 UTC (rev 111454)
@@ -474,6 +474,16 @@
     >>> ref3.weak
     True
 
+    >>> ref3a = PersistentReference(['w', ('my_oid', 'other_db')])
+    >>> ref3a.oid
+    'my_oid'
+    >>> print ref3a.klass
+    None
+    >>> ref3a.database_name
+    'other_db'
+    >>> ref3a.weak
+    True
+
     >>> ref4 = PersistentReference(['m', ('other_db', 'my_oid', 'my_class')])
     >>> ref4.oid
     'my_oid'
@@ -508,7 +518,7 @@
 
     >>> ref1 == ref1 and ref2 == ref2 and ref4 == ref4 and ref5 == ref5
     True
-    >>> ref3 == ref3 and ref6 == ref6 # weak references
+    >>> ref3 == ref3 and ref3a == ref3a and ref6 == ref6 # weak references
     True
 
     Non-weak references with the same oid and database_name are equal.

Modified: ZODB/trunk/src/ZODB/serialize.py
===================================================================
--- ZODB/trunk/src/ZODB/serialize.py	2010-04-26 20:34:58 UTC (rev 111453)
+++ ZODB/trunk/src/ZODB/serialize.py	2010-04-26 20:51:59 UTC (rev 111454)
@@ -100,7 +100,8 @@
     The following reference types are defined:
 
     'w'
-        Persistent weak reference.  The arguments consist of an oid.
+        Persistent weak reference.  The arguments consist of an oid
+        and optionally a database name.
 
     The following are planned for the future:
 
@@ -298,8 +299,8 @@
 
                 oid = obj.oid
                 if oid is None:
-                    obj = obj() # get the referenced object
-                    oid = obj._p_oid
+                    target = obj() # get the referenced object
+                    oid = target._p_oid
                     if oid is None:
                         # Here we are causing the object to be saved in
                         # the database. One could argue that we shouldn't
@@ -308,10 +309,16 @@
                         # assume that the object will be added eventually.
 
                         oid = self._jar.new_oid()
-                        obj._p_jar = self._jar
-                        obj._p_oid = oid
-                        self._stack.append(obj)
-                return ['w', (oid, )]
+                        target._p_jar = self._jar
+                        target._p_oid = oid
+                        self._stack.append(target)
+                    obj.oid = oid
+                    obj.dm = target._p_jar
+                    obj.database_name = obj.dm.db().database_name
+                if obj.dm is self._jar:
+                    return ['w', (oid, )]
+                else:
+                    return ['w', (oid, obj.database_name)]
 
 
         # Since we have an oid, we have either a persistent instance
@@ -523,10 +530,20 @@
     loaders['m'] = load_multi_persistent
 
 
-    def load_persistent_weakref(self, oid):
+    def load_persistent_weakref(self, oid, database_name=None):
         obj = WeakRef.__new__(WeakRef)
         obj.oid = oid
-        obj.dm = self._conn
+        if database_name is None:
+            obj.dm = self._conn
+        else:
+            obj.database_name = database_name
+            try:
+                obj.dm = self._conn.get_connection(database_name)
+            except KeyError:
+                # XXX Not sure what to do here.  It seems wrong to
+                # fail since this is a weak reference.  For now we'll
+                # just pretend that the target object has gone.
+                pass
         return obj
 
     loaders['w'] = load_persistent_weakref
@@ -632,7 +649,7 @@
     return oids
 
 oid_klass_loaders = {
-    'w': lambda oid: None,
+    'w': lambda oid, database_name=None: None,
     }
 
 def get_refs(a_pickle):

Modified: ZODB/trunk/src/persistent/wref.py
===================================================================
--- ZODB/trunk/src/persistent/wref.py	2010-04-26 20:34:58 UTC (rev 111453)
+++ ZODB/trunk/src/persistent/wref.py	2010-04-26 20:51:59 UTC (rev 111454)
@@ -99,7 +99,62 @@
     Always explicitly close databases: :)
 
     >>> db.close()
+    >>> del ob, ref, db, conn1, conn2, conn3
 
+    When multiple databases are in use, a weakref in one database may
+    point to an object in a different database.  Let's create two new
+    databases to demonstrate this.
+
+    >>> dbA = ZODB.tests.util.DB(
+    ...     database_name = 'dbA',
+    ...     )
+    >>> dbB = ZODB.tests.util.DB(
+    ...     database_name = 'dbB',
+    ...     databases = dbA.databases,
+    ...     )
+    >>> connA1 = dbA.open()
+    >>> connB1 = connA1.get_connection('dbB')
+
+    Now create and add a new object and a weak reference, and add them
+    to different databases.
+
+    >>> ob = ZODB.tests.MinPO.MinPO()
+    >>> ref = WeakRef(ob)
+    >>> connA1.root()['ob'] = ob
+    >>> connA1.add(ob)
+    >>> connB1.root()['ref'] = ref
+    >>> transaction.commit()
+
+    After a succesful commit, the reference should know the oid,
+    database name and connection of the object.
+
+    >>> ref.oid == ob._p_oid
+    True
+    >>> ref.database_name == 'dbA'
+    True
+    >>> ref.dm is ob._p_jar is connA1
+    True
+
+    If we open new connections, we should be able to use the reference.
+
+    >>> connA2 = dbA.open()
+    >>> connB2 = connA2.get_connection('dbB')
+    >>> ref2 = connB2.root()['ref']
+    >>> ob2 = connA2.root()['ob']
+    >>> ref2() is ob2
+    True
+    >>> ref2.oid == ob2._p_oid
+    True
+    >>> ref2.database_name == 'dbA'
+    True
+    >>> ref2.dm is ob2._p_jar is connA2
+    True
+
+    Always explicitly close databases: :)
+
+    >>> dbA.close()
+    >>> dbB.close()
+
     """
 
     # We set _p_oid to a marker so that the serialization system can
@@ -110,6 +165,8 @@
         self._v_ob = ob
         self.oid = ob._p_oid
         self.dm = ob._p_jar
+        if self.dm is not None:
+            self.database_name = self.dm.db().database_name
 
     def __call__(self):
         try:
@@ -117,7 +174,7 @@
         except AttributeError:
             try:
                 self._v_ob = self.dm[self.oid]
-            except KeyError:
+            except (KeyError, AttributeError):
                 return None
             return self._v_ob
 



More information about the Zodb-checkins mailing list