[Zodb-checkins] SVN: ZODB/trunk/src/ZODB/ Added
cross-database-reference support.
Jim Fulton
jim at zope.com
Thu Jun 9 15:15:20 EDT 2005
Log message for revision 30724:
Added cross-database-reference support.
Changed:
A ZODB/trunk/src/ZODB/cross-database-references.txt
U ZODB/trunk/src/ZODB/serialize.py
A ZODB/trunk/src/ZODB/tests/testcrossdatabasereferences.py
-=-
Added: ZODB/trunk/src/ZODB/cross-database-references.txt
===================================================================
--- ZODB/trunk/src/ZODB/cross-database-references.txt 2005-06-09 19:15:18 UTC (rev 30723)
+++ ZODB/trunk/src/ZODB/cross-database-references.txt 2005-06-09 19:15:20 UTC (rev 30724)
@@ -0,0 +1,82 @@
+Cross-Database References
+=========================
+
+Persistent references to objects in different databases within a
+multi-database are allowed.
+
+Lets set up a multi-database with 2 databases:
+
+ >>> import ZODB.tests.util, transaction, persistent
+ >>> databases = {}
+ >>> db1 = ZODB.tests.util.DB(databases=databases, database_name='1')
+ >>> db2 = ZODB.tests.util.DB(databases=databases, database_name='2')
+
+And create a persistent object in the first database:
+
+ >>> tm = transaction.TransactionManager()
+ >>> conn1 = db1.open(transaction_manager=tm)
+ >>> p1 = MyClass()
+ >>> conn1.root()['p'] = p1
+ >>> tm.commit()
+
+Now, we'll create a second persistent object in the second database.
+We'll have a reference to the first object:
+
+ >>> tm = transaction.TransactionManager()
+ >>> conn2 = db2.open(transaction_manager=tm)
+ >>> p2 = MyClass()
+ >>> conn2.root()['p'] = p2
+ >>> p2.p1 = p1
+ >>> tm.commit()
+
+Now, let's open a separate connection to database 2. We use it to
+read p2, use p2 to get to p1, and verify that it is in database 1:
+
+ >>> conn = db2.open()
+ >>> p2x = conn.root()['p']
+ >>> p1x = p2x.p1
+
+ >>> p2x is p2, p2x._p_oid == p2._p_oid, p2x._p_jar.db() is db2
+ (False, True, True)
+
+ >>> p1x is p1, p1x._p_oid == p1._p_oid, p1x._p_jar.db() is db1
+ (False, True, True)
+
+It isn't valid to create references outside a multi database:
+
+ >>> db3 = ZODB.tests.util.DB()
+ >>> conn3 = db3.open(transaction_manager=tm)
+ >>> p3 = MyClass()
+ >>> conn3.root()['p'] = p3
+ >>> tm.commit()
+
+ >>> p2.p3 = p3
+ >>> tm.commit() # doctest: +NORMALIZE_WHITESPACE
+ Traceback (most recent call last):
+ ...
+ InvalidObjectReference:
+ Attempt to store an object from a foreign database connection
+
+NOTE
+----
+
+This implementation is incomplete. It allows creatting and using
+cross-database references, however, there are a number of facilities
+missing:
+
+cross-database garbage collection
+ Garbage collection is done on a database by database basis.
+ If an object on a database only has references to it from other
+ databases, then the object will be garbage collected when it's
+ database is packed. The cross-database references to it will be
+ broken.
+
+cross-database undo
+ Undo is only applied to a single database. Fixing this for
+ mutliple databases is going to be extremely difficult. Undo
+ currently poses consistency problems, so it is not (or should not
+ be) widely used.
+
+Cross-database aware (tolerant) export/import
+ The export/import facility needs to be aware, at least, of cross-database
+ references.
Property changes on: ZODB/trunk/src/ZODB/cross-database-references.txt
___________________________________________________________________
Name: svn:eol-style
+ native
Modified: ZODB/trunk/src/ZODB/serialize.py
===================================================================
--- ZODB/trunk/src/ZODB/serialize.py 2005-06-09 19:15:18 UTC (rev 30723)
+++ ZODB/trunk/src/ZODB/serialize.py 2005-06-09 19:15:20 UTC (rev 30724)
@@ -188,6 +188,10 @@
>>> class DummyJar:
... def new_oid(self):
... return 42
+ ... def db(self):
+ ... return self
+ ... databases = {}
+
>>> jar = DummyJar()
>>> class O:
... _p_jar = jar
@@ -312,16 +316,27 @@
# NOTE! Persistent classes don't (and can't) subclass persistent.
+ database_name = None
+
if oid is None:
oid = obj._p_oid = self._jar.new_oid()
obj._p_jar = self._jar
self._stack.append(obj)
+
elif obj._p_jar is not self._jar:
- raise InvalidObjectReference(
- "Attempt to store an object from a foreign "
- "database connection"
- )
+ try:
+ otherdb = obj._p_jar.db()
+ database_name = otherdb.database_name
+ except AttributeError:
+ otherdb = self
+
+ if self._jar.db().databases.get(database_name) is not otherdb:
+ raise InvalidObjectReference(
+ "Attempt to store an object from a foreign "
+ "database connection"
+ )
+
klass = type(obj)
if hasattr(klass, '__getnewargs__'):
# We don't want to save newargs in object refs.
@@ -333,11 +348,15 @@
# __getnewargs__ of its own, we'll lose the optimization
# of caching the class info.
+ if database_name:
+ return ['n', (database_name, oid)]
return oid
# Note that we never get here for persistent classes.
# We'll use driect refs for normal classes.
+ if database_name:
+ return ['m', (database_name, oid, klass)]
return oid, klass
def serialize(self, obj):
@@ -475,8 +494,15 @@
self._cache[oid] = obj
return obj
- loaders['p'] = load_persistent
+ def load_multi_persistent(self, database_name, oid, klass):
+ conn = self._conn.get_connection(database_name)
+ # TODO, make connection _cache attr public
+ reader = ObjectReader(conn, conn._cache, self._factory)
+ return reader.load_persistent(oid, klass)
+ loaders['m'] = load_multi_persistent
+
+
def load_persistent_weakref(self, oid):
obj = WeakRef.__new__(WeakRef)
obj.oid = oid
@@ -491,8 +517,14 @@
return obj
return self._conn.get(oid)
- loaders['o'] = load_oid
+ def load_multi_oid(self, database_name, oid):
+ conn = self._conn.get_connection(database_name)
+ # TODO, make connection _cache attr public
+ reader = ObjectReader(conn, conn._cache, self._factory)
+ return reader.load_oid(oid)
+ loaders['n'] = load_multi_oid
+
def _new_object(self, klass, args):
if not args and not myhasattr(klass, "__getnewargs__"):
obj = klass.__new__(klass)
Added: ZODB/trunk/src/ZODB/tests/testcrossdatabasereferences.py
===================================================================
--- ZODB/trunk/src/ZODB/tests/testcrossdatabasereferences.py 2005-06-09 19:15:18 UTC (rev 30723)
+++ ZODB/trunk/src/ZODB/tests/testcrossdatabasereferences.py 2005-06-09 19:15:20 UTC (rev 30724)
@@ -0,0 +1,41 @@
+##############################################################################
+#
+# Copyright (c) 2004 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""
+$Id$
+"""
+import unittest
+from zope.testing import doctest
+import persistent
+
+class MyClass(persistent.Persistent):
+ pass
+
+class MyClass_w_getnewargs(persistent.Persistent):
+
+ def __getnewargs__(self):
+ return ()
+
+def test_suite():
+ return unittest.TestSuite((
+ doctest.DocFileSuite('../cross-database-references.txt',
+ globs=dict(MyClass=MyClass),
+ ),
+ doctest.DocFileSuite('../cross-database-references.txt',
+ globs=dict(MyClass=MyClass_w_getnewargs),
+ ),
+ ))
+
+if __name__ == '__main__':
+ unittest.main(defaultTest='test_suite')
+
Property changes on: ZODB/trunk/src/ZODB/tests/testcrossdatabasereferences.py
___________________________________________________________________
Name: svn:keywords
+ Id
Name: svn:eol-style
+ native
More information about the Zodb-checkins
mailing list