[Zope-CVS] CVS: Products/Ape/apelib/zodb3 - connection.py:1.3
Shane Hathaway
shane@zope.com
Sat, 29 Mar 2003 14:45:27 -0500
Update of /cvs-repository/Products/Ape/apelib/zodb3
In directory cvs.zope.org:/tmp/cvs-serv13976/apelib/zodb3
Modified Files:
connection.py
Log Message:
Stopped using the _p_serial attribute of persistent objects. ZODB
assumes that _p_serial is a transaction time stamp and uses it to
compute _p_mtime, but that assumption is not valid for Ape. Until
now, Ape has monkey-patched PersistentExtra to make it so the Zope
application doesn't depend so much on _p_mtime. But that patch wasn't
enough, since other parts of Zope depend on _p_mtime.
So now Ape stores the object hashes in a dictionary external to the
object and periodically prunes hashes no longer in use. Once again
Ape got around the need to patch the C code, but only by a narrow
margin. Hopefully, ZODB 4 already provides a better answer for this
dilemma.
=== Products/Ape/apelib/zodb3/connection.py 1.2 => 1.3 ===
--- Products/Ape/apelib/zodb3/connection.py:1.2 Sat Mar 15 20:47:41 2003
+++ Products/Ape/apelib/zodb3/connection.py Sat Mar 29 14:44:56 2003
@@ -28,6 +28,7 @@
import ConflictError, ReadConflictError, InvalidObjectReference, \
StorageError
from ZODB.Connection import Connection
+from ZODB.ConflictResolution import ResolvedSerial
from zLOG import LOG, ERROR
from consts import HASH0, DEBUG
@@ -98,7 +99,6 @@
object._p_oid=oid
object._p_jar=self
object._p_changed=None
- object._p_serial=serial
self._cache[oid] = object
if oid=='\0\0\0\0\0\0\0\0':
@@ -198,8 +198,8 @@
del stack[-1]
oid=object._p_oid
assert oid != 'unmanaged', repr(object)
- serial=getattr(object, '_p_serial', '\0\0\0\0\0\0\0\0')
- if serial == '\0\0\0\0\0\0\0\0':
+ serial = self.getSerial(object)
+ if serial == HASH0:
# new object
self._creating.append(oid)
else:
@@ -249,8 +249,7 @@
ext_refs = event.getExternalRefs()
if ext_refs:
for (ext_keychain, ext_ref) in ext_refs:
- if (not ext_ref._p_serial
- or ext_ref._p_serial == HASH0):
+ if self.getSerial(ext_ref) == HASH0:
ext_oid = oid_encoder.encode(ext_keychain)
if ext_ref._p_jar:
if ext_ref._p_jar != self:
@@ -363,7 +362,7 @@
if unmanaged:
self.handleUnmanaged(object, unmanaged)
- object._p_serial=serial
+ self.setSerial(object, serial)
if invalid:
if object._p_independent():
@@ -447,10 +446,10 @@
for oid, ob in self._cache.items():
if ob._p_changed is not None:
p, serial = self._storage.load(oid, self._version)
- if serial != ob._p_serial:
+ if serial != self.getSerial(ob):
keychain = self._db._oid_encoder.decode(oid)
raise StorageError(
- "Inconsistent hash for keychain %s" % repr(keychain))
+ "Inconsistent serial for keychain %s" % repr(keychain))
def exportFile(self, oid, file=None):
@@ -458,6 +457,94 @@
def importFile(self, file, clue='', customImporters=None):
raise NotImplementedError, 'ZEXP Import not implemented'
+
+
+ # A note on serials: Serials need to be stored independently of
+ # objects because the current Persistent base class uses _p_serial
+ # to derive _p_mtime. Applications like Zope use _p_mtime, but
+ # the _p_serial for Ape isn't always a date, so Ape can't use
+ # _p_serial to store serials. Instead, ApeConnection puts them in
+ # a _serials dictionary.
+
+ _serials = None
+ SERIAL_CLEANUP_THRESHOLD = 1000
+
+ def getSerial(self, ob):
+ oid = ob._p_oid
+ if oid is None or self._cache.get(oid, None) is not ob:
+ return HASH0
+ serials = self._serials
+ if serials is None:
+ return HASH0
+ return serials.get(oid, HASH0)
+
+ def setSerial(self, ob, s):
+ oid = ob._p_oid
+ assert oid is not None
+ if s is None:
+ s = HASH0
+ serials = self._serials
+ if serials is None:
+ serials = {}
+ self._serials = serials
+ if not serials.has_key(oid):
+ # When the number of recorded serials exceeds the number of
+ # cache entries by SERIAL_CLEANUP_THRESHOLD, prune the serials
+ # dictionary.
+ if (len(serials) >= len(self._cache) +
+ self.SERIAL_CLEANUP_THRESHOLD):
+ # clean up
+ cache_get = self._cache.get
+ for oid in serials.keys():
+ ob = cache_get(oid, None)
+ if ob is None or ob._p_changed is None:
+ del serials[oid]
+ serials[oid] = s
+
+ def _handle_serial(self, store_return, oid=None, change=1):
+ """Handle the returns from store() and tpc_vote() calls."""
+
+ # These calls can return different types depending on whether
+ # ZEO is used. ZEO uses asynchronous returns that may be
+ # returned in batches by the ClientStorage. ZEO1 can also
+ # return an exception object and expect that the Connection
+ # will raise the exception.
+
+ # When commit_sub() exceutes a store, there is no need to
+ # update the _p_changed flag, because the subtransaction
+ # tpc_vote() calls already did this. The change=1 argument
+ # exists to allow commit_sub() to avoid setting the flag
+ # again.
+ if not store_return:
+ return
+ if isinstance(store_return, StringType):
+ assert oid is not None
+ serial = store_return
+ obj = self._cache.get(oid, None)
+ if obj is None:
+ return
+ if serial == ResolvedSerial:
+ obj._p_changed = None
+ else:
+ if change:
+ obj._p_changed = 0
+ #obj._p_serial = serial
+ self.setSerial(obj, serial)
+ else:
+ for oid, serial in store_return:
+ if not isinstance(serial, StringType):
+ raise serial
+ obj = self._cache.get(oid, None)
+ if obj is None:
+ continue
+ if serial == ResolvedSerial:
+ obj._p_changed = None
+ else:
+ if change:
+ obj._p_changed = 0
+ #obj._p_serial = serial
+ self.setSerial(obj, serial)
+
class UnmanagedJar: