Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
Z
ZODB
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Analytics
Analytics
CI / CD
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Kirill Smelkov
ZODB
Commits
d8a161cb
Commit
d8a161cb
authored
Jun 09, 2005
by
Jim Fulton
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Added cross-database-reference support.
parent
fc3e142c
Changes
3
Hide whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
161 additions
and
6 deletions
+161
-6
src/ZODB/cross-database-references.txt
src/ZODB/cross-database-references.txt
+82
-0
src/ZODB/serialize.py
src/ZODB/serialize.py
+38
-6
src/ZODB/tests/testcrossdatabasereferences.py
src/ZODB/tests/testcrossdatabasereferences.py
+41
-0
No files found.
src/ZODB/cross-database-references.txt
0 → 100644
View file @
d8a161cb
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.
src/ZODB/serialize.py
View file @
d8a161cb
...
@@ -188,6 +188,10 @@ class ObjectWriter:
...
@@ -188,6 +188,10 @@ class ObjectWriter:
>>> class DummyJar:
>>> class DummyJar:
... def new_oid(self):
... def new_oid(self):
... return 42
... return 42
... def db(self):
... return self
... databases = {}
>>> jar = DummyJar()
>>> jar = DummyJar()
>>> class O:
>>> class O:
... _p_jar = jar
... _p_jar = jar
...
@@ -312,15 +316,26 @@ class ObjectWriter:
...
@@ -312,15 +316,26 @@ class ObjectWriter:
# NOTE! Persistent classes don't (and can't) subclass persistent.
# NOTE! Persistent classes don't (and can't) subclass persistent.
database_name
=
None
if
oid
is
None
:
if
oid
is
None
:
oid
=
obj
.
_p_oid
=
self
.
_jar
.
new_oid
()
oid
=
obj
.
_p_oid
=
self
.
_jar
.
new_oid
()
obj
.
_p_jar
=
self
.
_jar
obj
.
_p_jar
=
self
.
_jar
self
.
_stack
.
append
(
obj
)
self
.
_stack
.
append
(
obj
)
elif
obj
.
_p_jar
is
not
self
.
_jar
:
elif
obj
.
_p_jar
is
not
self
.
_jar
:
raise
InvalidObjectReference
(
"Attempt to store an object from a foreign "
try
:
"database connection"
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
)
klass
=
type
(
obj
)
if
hasattr
(
klass
,
'__getnewargs__'
):
if
hasattr
(
klass
,
'__getnewargs__'
):
...
@@ -333,11 +348,15 @@ class ObjectWriter:
...
@@ -333,11 +348,15 @@ class ObjectWriter:
# __getnewargs__ of its own, we'll lose the optimization
# __getnewargs__ of its own, we'll lose the optimization
# of caching the class info.
# of caching the class info.
if
database_name
:
return
[
'n'
,
(
database_name
,
oid
)]
return
oid
return
oid
# Note that we never get here for persistent classes.
# Note that we never get here for persistent classes.
# We'll use driect refs for normal classes.
# We'll use driect refs for normal classes.
if
database_name
:
return
[
'm'
,
(
database_name
,
oid
,
klass
)]
return
oid
,
klass
return
oid
,
klass
def
serialize
(
self
,
obj
):
def
serialize
(
self
,
obj
):
...
@@ -475,7 +494,14 @@ class ObjectReader:
...
@@ -475,7 +494,14 @@ class ObjectReader:
self
.
_cache
[
oid
]
=
obj
self
.
_cache
[
oid
]
=
obj
return
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
):
def
load_persistent_weakref
(
self
,
oid
):
obj
=
WeakRef
.
__new__
(
WeakRef
)
obj
=
WeakRef
.
__new__
(
WeakRef
)
...
@@ -491,7 +517,13 @@ class ObjectReader:
...
@@ -491,7 +517,13 @@ class ObjectReader:
return
obj
return
obj
return
self
.
_conn
.
get
(
oid
)
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
):
def
_new_object
(
self
,
klass
,
args
):
if
not
args
and
not
myhasattr
(
klass
,
"__getnewargs__"
):
if
not
args
and
not
myhasattr
(
klass
,
"__getnewargs__"
):
...
...
src/ZODB/tests/testcrossdatabasereferences.py
0 → 100644
View file @
d8a161cb
##############################################################################
#
# 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'
)
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment