[Zodb-checkins] SVN: ZODB/branches/jim-storage-api-cleanup/src/Z
checkpointing
Jim Fulton
jim at zope.com
Tue Apr 24 19:35:32 EDT 2007
Log message for revision 74732:
checkpointing
Changed:
U ZODB/branches/jim-storage-api-cleanup/src/ZEO/ClientStorage.py
U ZODB/branches/jim-storage-api-cleanup/src/ZEO/ServerStub.py
U ZODB/branches/jim-storage-api-cleanup/src/ZEO/StorageServer.py
A ZODB/branches/jim-storage-api-cleanup/src/ZEO/interfaces.py
U ZODB/branches/jim-storage-api-cleanup/src/ZEO/tests/Cache.py
U ZODB/branches/jim-storage-api-cleanup/src/ZEO/tests/testConversionSupport.py
U ZODB/branches/jim-storage-api-cleanup/src/ZODB/BaseStorage.py
U ZODB/branches/jim-storage-api-cleanup/src/ZODB/Connection.py
U ZODB/branches/jim-storage-api-cleanup/src/ZODB/DB.py
U ZODB/branches/jim-storage-api-cleanup/src/ZODB/DemoStorage.py
U ZODB/branches/jim-storage-api-cleanup/src/ZODB/FileStorage/FileStorage.py
U ZODB/branches/jim-storage-api-cleanup/src/ZODB/interfaces.py
U ZODB/branches/jim-storage-api-cleanup/src/ZODB/tests/BasicStorage.py
U ZODB/branches/jim-storage-api-cleanup/src/ZODB/tests/IteratorStorage.py
U ZODB/branches/jim-storage-api-cleanup/src/ZODB/tests/ReadOnlyStorage.py
U ZODB/branches/jim-storage-api-cleanup/src/ZODB/tests/TransactionalUndoStorage.py
U ZODB/branches/jim-storage-api-cleanup/src/ZODB/tests/VersionStorage.py
-=-
Modified: ZODB/branches/jim-storage-api-cleanup/src/ZEO/ClientStorage.py
===================================================================
--- ZODB/branches/jim-storage-api-cleanup/src/ZEO/ClientStorage.py 2007-04-24 23:33:02 UTC (rev 74731)
+++ ZODB/branches/jim-storage-api-cleanup/src/ZEO/ClientStorage.py 2007-04-24 23:35:31 UTC (rev 74732)
@@ -266,8 +266,7 @@
self._pickler = None
self._info = {'length': 0, 'size': 0, 'name': 'ZEO Client',
- 'supportsUndo':0, 'supportsVersions': 0,
- 'supportsTransactionalUndo': 0}
+ 'supportsUndo':0, 'supportsVersions': 0}
self._tbuf = self.TransactionBufferClass()
self._db = None
@@ -670,10 +669,6 @@
"""Storage API: return whether we support versions."""
return self._info['supportsVersions']
- def supportsTransactionalUndo(self):
- """Storage API: return whether we support transactional undo."""
- return self._info['supportsTransactionalUndo']
-
def isReadOnly(self):
"""Storage API: return whether we are in read-only mode."""
if self._is_read_only:
@@ -732,15 +727,15 @@
return self._server.history(oid, version, length)
def record_iternext(self, next=None):
- """Storage API: get the mext database record.
+ """Storage API: get the next database record.
This is part of the conversion-support API.
"""
return self._server.record_iternext(next)
- def getSerial(self, oid):
+ def getTid(self, oid):
"""Storage API: return current serial number for oid."""
- return self._server.getSerial(oid)
+ return self._server.getTid(oid)
def loadSerial(self, oid, serial):
"""Storage API: load a historical revision of an object."""
Modified: ZODB/branches/jim-storage-api-cleanup/src/ZEO/ServerStub.py
===================================================================
--- ZODB/branches/jim-storage-api-cleanup/src/ZEO/ServerStub.py 2007-04-24 23:33:02 UTC (rev 74731)
+++ ZODB/branches/jim-storage-api-cleanup/src/ZEO/ServerStub.py 2007-04-24 23:35:31 UTC (rev 74732)
@@ -275,8 +275,8 @@
def loadBlob(self, oid, serial, version, offset):
return self.rpc.call('loadBlob', oid, serial, version, offset)
- def getSerial(self, oid):
- return self.rpc.call('getSerial', oid)
+ def getTid(self, oid):
+ return self.rpc.call('getTid', oid)
def loadSerial(self, oid, serial):
return self.rpc.call('loadSerial', oid, serial)
Modified: ZODB/branches/jim-storage-api-cleanup/src/ZEO/StorageServer.py
===================================================================
--- ZODB/branches/jim-storage-api-cleanup/src/ZEO/StorageServer.py 2007-04-24 23:33:02 UTC (rev 74731)
+++ ZODB/branches/jim-storage-api-cleanup/src/ZEO/StorageServer.py 2007-04-24 23:35:31 UTC (rev 74732)
@@ -141,8 +141,8 @@
def __repr__(self):
tid = self.transaction and repr(self.transaction.id)
if self.storage:
- stid = (self.storage._transaction and
- repr(self.storage._transaction.id))
+ stid = (self.storage.tpc_transaction() and
+ repr(self.storage.tpc_transaction().id))
else:
stid = None
name = self.__class__.__name__
@@ -152,14 +152,32 @@
log(msg, level=level, label=self.log_label, exc_info=exc_info)
def setup_delegation(self):
- """Delegate several methods to the storage"""
- self.versionEmpty = self.storage.versionEmpty
- self.versions = self.storage.versions
- self.getSerial = self.storage.getSerial
+ """Delegate several methods to the storage
+ """
+
+ info = self.get_info()
+ if info['supportsVersions']:
+ self.versionEmpty = self.storage.versionEmpty
+ self.versions = self.storage.versions
+ self.modifiedInVersion = self.storage.modifiedInVersion
+ else:
+ self.versionEmpty = lambda version: True
+ self.versions = lambda max=None: ()
+ self.modifiedInVersion = lambda oid: ''
+ def commitVersion(*a, **k):
+ raise NotImplementedError
+ self.commitVersion = self.abortVersion = commitVersion
+
+ if not info['supportsUndo']:
+ self.undoLog = self.undoInfo = lambda *a,**k: ()
+ def undo(*a, **k):
+ raise NotImplementedError
+ self.undo = undo
+
+ self.getTid = self.storage.getTid
self.history = self.storage.history
self.load = self.storage.load
self.loadSerial = self.storage.loadSerial
- self.modifiedInVersion = self.storage.modifiedInVersion
record_iternext = getattr(self.storage, 'record_iternext', None)
if record_iternext is not None:
self.record_iternext = record_iternext
@@ -240,11 +258,27 @@
self)
def get_info(self):
- return {'length': len(self.storage),
- 'size': self.storage.getSize(),
- 'name': self.storage.getName(),
- 'supportsUndo': self.storage.supportsUndo(),
- 'supportsVersions': self.storage.supportsVersions(),
+ storage = self.storage
+
+ try:
+ supportsVersions = storage.supportsVersions
+ except AttributeError:
+ supportsVersions = False
+ else:
+ supportsVersions = supportsVersions()
+
+ try:
+ supportsUndo = storage.supportsUndo
+ except AttributeError:
+ supportsUndo = False
+ else:
+ supportsUndo = supportsUndo()
+
+ return {'length': len(storage),
+ 'size': storage.getSize(),
+ 'name': storage.getName(),
+ 'supportsUndo': supportsUndo,
+ 'supportsVersions': supportsVersions,
'extensionMethods': self.getExtensionMethods(),
'supports_record_iternext': hasattr(self, 'record_iternext'),
}
@@ -292,7 +326,7 @@
def verify(self, oid, version, tid):
try:
- t = self.storage.getTid(oid)
+ t = self.getTid(oid)
except KeyError:
self.client.invalidateVerify((oid, ""))
else:
@@ -309,7 +343,7 @@
self.verifying = 1
self.stats.verifying_clients += 1
try:
- os = self.storage.getTid(oid)
+ os = self.getTid(oid)
except KeyError:
self.client.invalidateVerify((oid, ''))
# It's not clear what we should do now. The KeyError
@@ -624,7 +658,7 @@
def _wait(self, thunk):
# Wait for the storage lock to be acquired.
self._thunk = thunk
- if self.storage._transaction:
+ if self.storage.tpc_transaction():
d = Delay()
self.storage._waiting.append((d, self))
self.log("Transaction blocked waiting for storage. "
Added: ZODB/branches/jim-storage-api-cleanup/src/ZEO/interfaces.py
===================================================================
--- ZODB/branches/jim-storage-api-cleanup/src/ZEO/interfaces.py 2007-04-24 23:33:02 UTC (rev 74731)
+++ ZODB/branches/jim-storage-api-cleanup/src/ZEO/interfaces.py 2007-04-24 23:35:31 UTC (rev 74732)
@@ -0,0 +1,66 @@
+##############################################################################
+#
+# Copyright (c) 2006 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (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.
+#
+##############################################################################
+
+import zope.interface
+
+class IServeable(zope.interface.Interface):
+ """Interface provided by storages that can be served by ZEO
+ """
+
+ def getTid(oid):
+ """The last transaction to change an object
+
+ Return the transaction id of the last transaction that committed a
+ change to an object with the given object id.
+
+ """
+
+ def tpc_transaction():
+ """The current transaction being committed.
+
+ If a storage is participating in a two-phase commit, then
+ return the transaction (object) being committed. Otherwise
+ return None.
+ """
+
+ def loadEx(oid, version):
+ """Load current object data for a version
+
+ Return the current data, serial (transaction id) and version
+ for an object in a version.
+
+ If an object has been modified in the given version, then the
+ data and serial are for the most current revision of the
+ object and the returned version will match the given version.
+
+ If an object hasn't been modified in a version, or has been
+ modified in a version other than the given one, then the data,
+ and serial for the most recent non-version revision will be
+ returned along with an empty version string.
+
+ If a storage doesn't support versions, it should ignore the
+ version argument.
+ """
+
+ def lastInvalidations(size):
+ """Get recent transaction invalidations
+
+ This method is optional and is used to get invalidations
+ performed by the most recent transactions.
+
+ An iterable of up to size entries must be returned, where each
+ entry is a transaction id and a sequence of object-id/version
+ pairs describing the objects and versions written by the
+ transaction, ordered starting at the most recent.
+ """
Property changes on: ZODB/branches/jim-storage-api-cleanup/src/ZEO/interfaces.py
___________________________________________________________________
Name: svn:keywords
+ Id
Name: svn:eol-style
+ native
Modified: ZODB/branches/jim-storage-api-cleanup/src/ZEO/tests/Cache.py
===================================================================
--- ZODB/branches/jim-storage-api-cleanup/src/ZEO/tests/Cache.py 2007-04-24 23:33:02 UTC (rev 74731)
+++ ZODB/branches/jim-storage-api-cleanup/src/ZEO/tests/Cache.py 2007-04-24 23:35:31 UTC (rev 74732)
@@ -34,11 +34,6 @@
info = self._storage.undoInfo(0, 20)
tid = info[0]['id']
- # We may need to bail at this point if the storage doesn't
- # support transactional undo
- if not self._storage.supportsTransactionalUndo():
- return
-
# Now start an undo transaction
t = Transaction()
t.note('undo1')
Modified: ZODB/branches/jim-storage-api-cleanup/src/ZEO/tests/testConversionSupport.py
===================================================================
--- ZODB/branches/jim-storage-api-cleanup/src/ZEO/tests/testConversionSupport.py 2007-04-24 23:33:02 UTC (rev 74731)
+++ ZODB/branches/jim-storage-api-cleanup/src/ZEO/tests/testConversionSupport.py 2007-04-24 23:35:31 UTC (rev 74732)
@@ -17,7 +17,7 @@
class FakeStorageBase:
def __getattr__(self, name):
- if name in ('versionEmpty', 'versions', 'getSerial',
+ if name in ('versionEmpty', 'versions',
'history', 'load', 'loadSerial', 'modifiedInVersion',
'lastTransaction', 'getSize', 'getName', 'supportsUndo',
'supportsVersions'):
Modified: ZODB/branches/jim-storage-api-cleanup/src/ZODB/BaseStorage.py
===================================================================
--- ZODB/branches/jim-storage-api-cleanup/src/ZODB/BaseStorage.py 2007-04-24 23:33:02 UTC (rev 74731)
+++ ZODB/branches/jim-storage-api-cleanup/src/ZODB/BaseStorage.py 2007-04-24 23:35:31 UTC (rev 74732)
@@ -112,16 +112,6 @@
else:
self._oid = oid
- def abortVersion(self, src, transaction):
- if transaction is not self._transaction:
- raise POSException.StorageTransactionError(self, transaction)
- return self._tid, []
-
- def commitVersion(self, src, dest, transaction):
- if transaction is not self._transaction:
- raise POSException.StorageTransactionError(self, transaction)
- return self._tid, []
-
def close(self):
pass
@@ -144,11 +134,8 @@
return len(self)*300 # WAG!
def history(self, oid, version, length=1, filter=None):
- pass
+ return ()
- def modifiedInVersion(self, oid):
- return ''
-
def new_oid(self):
if self._is_read_only:
raise POSException.ReadOnlyError()
@@ -183,12 +170,6 @@
def isReadOnly(self):
return self._is_read_only
- def supportsUndo(self):
- return 0
-
- def supportsVersions(self):
- return 0
-
def tpc_abort(self, transaction):
self._lock_acquire()
try:
@@ -205,7 +186,7 @@
def _abort(self):
"""Subclasses should redefine this to supply abort actions"""
- pass
+ raise NotImplementedError
def tpc_begin(self, transaction, tid=None, status=' '):
if self._is_read_only:
@@ -243,10 +224,13 @@
finally:
self._lock_release()
+ def tpc_transaction(self):
+ return self._transaction
+
def _begin(self, tid, u, d, e):
"""Subclasses should redefine this to supply transaction start actions.
"""
- pass
+ raise NotImplementedError
def tpc_vote(self, transaction):
self._lock_acquire()
@@ -260,7 +244,7 @@
def _vote(self):
"""Subclasses should redefine this to supply transaction vote actions.
"""
- pass
+ raise NotImplementedError
def tpc_finish(self, transaction, f=None):
# It's important that the storage calls the function we pass
@@ -292,25 +276,7 @@
"""
pass
- def undo(self, transaction_id, txn):
- if self._is_read_only:
- raise POSException.ReadOnlyError()
- raise POSException.UndoError('non-undoable transaction')
-
- def undoLog(self, first, last, filter=None):
- return ()
-
- def versionEmpty(self, version):
- return 1
-
- def versions(self, max=None):
- return ()
-
- def pack(self, t, referencesf):
- if self._is_read_only:
- raise POSException.ReadOnlyError()
-
- def getSerial(self, oid):
+ def getTid(self, oid):
self._lock_acquire()
try:
v = self.modifiedInVersion(oid)
Modified: ZODB/branches/jim-storage-api-cleanup/src/ZODB/Connection.py
===================================================================
--- ZODB/branches/jim-storage-api-cleanup/src/ZODB/Connection.py 2007-04-24 23:33:02 UTC (rev 74731)
+++ ZODB/branches/jim-storage-api-cleanup/src/ZODB/Connection.py 2007-04-24 23:35:31 UTC (rev 74732)
@@ -231,6 +231,9 @@
if obj is not None:
return obj
+ # This appears to be an MVCC violation because we are loading
+ # the must recent data when perhaps we shouldnt. The key is
+ # that we are only creating a ghost!
p, serial = self._storage.load(oid, self._version)
obj = self._reader.getGhost(p)
Modified: ZODB/branches/jim-storage-api-cleanup/src/ZODB/DB.py
===================================================================
--- ZODB/branches/jim-storage-api-cleanup/src/ZODB/DB.py 2007-04-24 23:33:02 UTC (rev 74731)
+++ ZODB/branches/jim-storage-api-cleanup/src/ZODB/DB.py 2007-04-24 23:35:31 UTC (rev 74732)
@@ -15,6 +15,8 @@
$Id$"""
+import warnings
+
import cPickle, cStringIO, sys
import threading
from time import time, ctime
@@ -31,6 +33,7 @@
import transaction
+
logger = logging.getLogger('ZODB.DB')
class _ConnectionPool(object):
@@ -77,23 +80,28 @@
# a list (we push only "on the right", but may pop from both ends).
self.available = []
- # Change our belief about the expected maximum # of live connections.
- # If the pool_size is smaller than the current value, this may discard
- # the oldest available connections.
def set_pool_size(self, pool_size):
+ """Change our belief about the expected maximum # of live connections.
+
+ If the pool_size is smaller than the current value, this may discard
+ the oldest available connections.
+ """
self.pool_size = pool_size
self._reduce_size()
- # Register a new available connection. We must not know about c already.
- # c will be pushed onto the available stack even if we're over the
- # pool size limit.
def push(self, c):
+ """Register a new available connection.
+
+ We must not know about c already. c will be pushed onto the available
+ stack even if we're over the pool size limit.
+ """
assert c not in self.all
assert c not in self.available
self._reduce_size(strictly_less=True)
self.all.add(c)
self.available.append(c)
- n, limit = len(self.all), self.pool_size
+ n = len(self.all)
+ limit = self.pool_size
if n > limit:
reporter = logger.warn
if n > 2 * limit:
@@ -101,20 +109,25 @@
reporter("DB.open() has %s open connections with a pool_size "
"of %s", n, limit)
- # Reregister an available connection formerly obtained via pop(). This
- # pushes it on the stack of available connections, and may discard
- # older available connections.
def repush(self, c):
+ """Reregister an available connection formerly obtained via pop().
+
+ This pushes it on the stack of available connections, and may discard
+ older available connections.
+ """
assert c in self.all
assert c not in self.available
self._reduce_size(strictly_less=True)
self.available.append(c)
- # Throw away the oldest available connections until we're under our
- # target size (strictly_less=False) or no more than that (strictly_less=
- # True, the default).
def _reduce_size(self, strictly_less=False):
- target = self.pool_size - bool(strictly_less)
+ """Throw away the oldest available connections until we're under our
+ target size (strictly_less=False, the default) or no more than that
+ (strictly_less=True).
+ """
+ target = self.pool_size
+ if strictly_less:
+ target -= 1
while len(self.available) > target:
c = self.available.pop(0)
self.all.remove(c)
@@ -132,11 +145,13 @@
# now, and `c` would be left in a user-visible crazy state.
c._resetCache()
- # Pop an available connection and return it, or return None if none are
- # available. In the latter case, the caller should create a new
- # connection, register it via push(), and call pop() again. The
- # caller is responsible for serializing this sequence.
def pop(self):
+ """Pop an available connection and return it.
+
+ Return None if none are available - in this case, the caller should
+ create a new connection, register it via push(), and call pop() again.
+ The caller is responsible for serializing this sequence.
+ """
result = None
if self.available:
result = self.available.pop()
@@ -145,8 +160,8 @@
assert result in self.all
return result
- # For every live connection c, invoke f(c).
def map(self, f):
+ """For every live connection c, invoke f(c)."""
self.all.map(f)
class DB(object):
@@ -227,8 +242,6 @@
self._version_pool_size = version_pool_size
self._version_cache_size = version_cache_size
- self._miv_cache = {}
-
# Setup storage
self._storage=storage
self.references = ZODB.serialize.referencesf
@@ -238,7 +251,13 @@
storage.registerDB(self, None) # Backward compat
if not hasattr(storage, 'tpc_vote'):
+ warnings.warn(
+ "Storage doesn't have a tpc_vote and this violates "
+ "the stirage API. Violently monkeypatching in a do-nothing "
+ "tpc_vote.",
+ DeprecationWarning, 2)
storage.tpc_vote = lambda *args: None
+
try:
storage.load(z64, '')
except KeyError:
@@ -268,14 +287,46 @@
database_name)
databases[database_name] = self
- # Pass through methods:
- for m in ['history', 'supportsUndo', 'supportsVersions', 'undoLog',
- 'versionEmpty', 'versions']:
- setattr(self, m, getattr(storage, m))
+ self._setupUndoMethods()
+ self._setupVersionMethods()
+ self.history = storage.history
- if hasattr(storage, 'undoInfo'):
- self.undoInfo = storage.undoInfo
+ def _setupUndoMethods(self):
+ storage = self._storage
+ try:
+ self.supportsUndo = storage.supportsUndo
+ except AttributeError:
+ self.supportsUndo = lambda : False
+ if self.supportsUndo():
+ self.undoLog = storage.undoLog
+ if hasattr(storage, 'undoInfo'):
+ self.undoInfo = storage.undoInfo
+ else:
+ self.undoLog = self.undoInfo = lambda *a,**k: ()
+ def undo(*a, **k):
+ raise NotImplementedError
+ self.undo = undo
+
+ def _setupVersionMethods(self):
+ storage = self._storage
+ try:
+ self.supportsVersions = storage.supportsVersions
+ except AttributeError:
+ self.supportsVersions = lambda : False
+
+ if self.supportsVersions():
+ self.versionEmpty = storage.versionEmpty
+ self.versions = storage.versions
+ self.modifiedInVersion = storage.modifiedInVersion
+ else:
+ self.versionEmpty = lambda version: True
+ self.versions = lambda max=None: ()
+ self.modifiedInVersion = lambda oid: ''
+ def commitVersion(*a, **k):
+ raise NotImplementedError
+ self.commitVersion = self.abortVersion = commitVersion
+
# This is called by Connection.close().
def _returnToPool(self, connection):
"""Return a connection to the pool.
@@ -471,12 +522,6 @@
"""
if connection is not None:
version = connection._version
- # Update modified in version cache
- for oid in oids:
- h = hash(oid) % 131
- o = self._miv_cache.get(h, None)
- if o is not None and o[0]==oid:
- del self._miv_cache[h]
# Notify connections.
def inval(c):
@@ -487,20 +532,9 @@
def invalidateCache(self):
"""Invalidate each of the connection caches
- """
- self._miv_cache.clear()
+ """
self._connectionMap(lambda c: c.invalidateCache())
- def modifiedInVersion(self, oid):
- h = hash(oid) % 131
- cache = self._miv_cache
- o = cache.get(h, None)
- if o and o[0] == oid:
- return o[1]
- v = self._storage.modifiedInVersion(oid)
- cache[h] = oid, v
- return v
-
def objectCount(self):
return len(self._storage)
@@ -687,17 +721,18 @@
txn = transaction.get()
txn.register(TransactionalUndo(self, id))
- def versionEmpty(self, version):
- return self._storage.versionEmpty(version)
-
-
resource_counter_lock = threading.Lock()
resource_counter = 0
class ResourceManager(object):
"""Transaction participation for a version or undo resource."""
+ # XXX This implementation is broken. Subclasses invalidate oids
+ # in their commit calls. Invalidations should not be sent until
+ # tpc_finish is called. In fact, invalidations should be sent to
+ # the db *while* tpc_finish is being called on the storage.
+
def __init__(self, db):
self._db = db
# Delegate the actual 2PC methods to the storage
@@ -729,10 +764,10 @@
# argument to the methods below is self.
def abort(self, obj, txn):
- pass
+ raise NotImplementedError
def commit(self, obj, txn):
- pass
+ raise NotImplementedError
class CommitVersion(ResourceManager):
@@ -742,6 +777,7 @@
self._dest = dest
def commit(self, ob, t):
+ # XXX see XXX in ResourceManager
dest = self._dest
tid, oids = self._db._storage.commitVersion(self._version,
self._dest,
@@ -760,6 +796,7 @@
self._version = version
def commit(self, ob, t):
+ # XXX see XXX in ResourceManager
tid, oids = self._db._storage.abortVersion(self._version, t)
self._db.invalidate(tid,
dict.fromkeys(oids, 1),
@@ -772,5 +809,6 @@
self._tid = tid
def commit(self, ob, t):
+ # XXX see XXX in ResourceManager
tid, oids = self._db._storage.undo(self._tid, t)
self._db.invalidate(tid, dict.fromkeys(oids, 1))
Modified: ZODB/branches/jim-storage-api-cleanup/src/ZODB/DemoStorage.py
===================================================================
--- ZODB/branches/jim-storage-api-cleanup/src/ZODB/DemoStorage.py 2007-04-24 23:33:02 UTC (rev 74731)
+++ ZODB/branches/jim-storage-api-cleanup/src/ZODB/DemoStorage.py 2007-04-24 23:35:31 UTC (rev 74732)
@@ -213,13 +213,14 @@
self._lock_release()
def loadEx(self, oid, version):
+ raise TypeError("untested")
self._lock_acquire()
try:
try:
oid, pre, vdata, p, tid = self._index[oid]
except KeyError:
if self._base:
- return self._base.load(oid, '')
+ return self._base.loadEx(oid, '')
raise KeyError(oid)
ver = ""
Modified: ZODB/branches/jim-storage-api-cleanup/src/ZODB/FileStorage/FileStorage.py
===================================================================
--- ZODB/branches/jim-storage-api-cleanup/src/ZODB/FileStorage/FileStorage.py 2007-04-24 23:33:02 UTC (rev 74731)
+++ ZODB/branches/jim-storage-api-cleanup/src/ZODB/FileStorage/FileStorage.py 2007-04-24 23:35:31 UTC (rev 74732)
@@ -940,9 +940,6 @@
self._file.truncate(self._pos)
self._nextpos=0
- def supportsTransactionalUndo(self):
- return 1
-
def _undoDataInfo(self, oid, pos, tpos):
"""Return the tid, data pointer, data, and version for the oid
record at pos"""
@@ -1440,7 +1437,10 @@
except ValueError: # "empty tree" error
next_oid = None
- data, tid = self.load(oid, "") # ignore versions
+ # ignore versions
+ # XXX if the object was created in a version, this will fail.
+ data, tid = self.load(oid, "")
+
return oid, tid, data, next_oid
Modified: ZODB/branches/jim-storage-api-cleanup/src/ZODB/interfaces.py
===================================================================
--- ZODB/branches/jim-storage-api-cleanup/src/ZODB/interfaces.py 2007-04-24 23:33:02 UTC (rev 74731)
+++ ZODB/branches/jim-storage-api-cleanup/src/ZODB/interfaces.py 2007-04-24 23:35:31 UTC (rev 74732)
@@ -18,6 +18,7 @@
from zope.interface import Interface, Attribute
+
class IConnection(Interface):
"""Connection to ZODB for loading and storing objects.
@@ -289,7 +290,6 @@
This invalidates *all* objects in the cache. If the connection
is open, subsequent reads will fail until a new transaction
begins or until the connection os reopned.
-
"""
class IStorageDB(Interface):
@@ -345,37 +345,6 @@
TODO: This interface is incomplete.
"""
-## __init__ methods don't belong in interfaces:
-##
-## def __init__(storage,
-## pool_size=7,
-## cache_size=400,
-## version_pool_size=3,
-## version_cache_size=100,
-## database_name='unnamed',
-## databases=None,
-## ):
-## """Create an object database.
-
-## storage: the storage used by the database, e.g. FileStorage
-## pool_size: expected maximum number of open connections
-## cache_size: target size of Connection object cache, in number of
-## objects
-## version_pool_size: expected maximum number of connections (per
-## version)
-## version_cache_size: target size of Connection object cache for
-## version connections, in number of objects
-## database_name: when using a multi-database, the name of this DB
-## within the database group. It's a (detected) error if databases
-## is specified too and database_name is already a key in it.
-## This becomes the value of the DB's database_name attribute.
-## databases: when using a multi-database, a mapping to use as the
-## binding of this DB's .databases attribute. It's intended
-## that the second and following DB's added to a multi-database
-## pass the .databases attribute set on the first DB added to the
-## collection.
-## """
-
databases = Attribute("""\
A mapping from database name to DB (database) object.
@@ -386,119 +355,455 @@
entry.
""")
- def invalidateCache():
- """Invalidate all objects in the database object caches
+ def open(version='',
+ mvcc=True,
+ transaction_manager=None,
+ synch=True
+ ):
+ """Return an IConnection object for use by application code.
- invalidateCache will be called on each of the database's connections.
+ version: the "version" that all changes will be made
+ in, defaults to no version.
+ mvcc: boolean indicating whether MVCC is enabled
+ transaction_manager: transaction manager to use. None means
+ use the default transaction manager.
+ synch: boolean indicating whether Connection should
+ register for afterCompletion() calls.
+
+ Note that the connection pool is managed as a stack, to
+ increase the likelihood that the connection's stack will
+ include useful objects.
"""
+ # TODO: Should this method be moved into some subinterface?
+ def pack(t=None, days=0):
+ """Pack the storage, deleting unused object revisions.
+
+ A pack is always performed relative to a particular time, by
+ default the current time. All object revisions that are not
+ reachable as of the pack time are deleted from the storage.
+
+ The cost of this operation varies by storage, but it is
+ usually an expensive operation.
+
+ There are two optional arguments that can be used to set the
+ pack time: t, pack time in seconds since the epcoh, and days,
+ the number of days to subtract from t or from the current
+ time if t is not specified.
+ """
+
+ # TODO: Should this method be moved into some subinterface?
+ def undo(id, txn=None):
+ """Undo a transaction identified by id.
+
+ A transaction can be undone if all of the objects involved in
+ the transaction were not modified subsequently, if any
+ modifications can be resolved by conflict resolution, or if
+ subsequent changes resulted in the same object state.
+
+ The value of id should be generated by calling undoLog()
+ or undoInfo(). The value of id is not the same as a
+ transaction id used by other methods; it is unique to undo().
+
+ id: a storage-specific transaction identifier
+ txn: transaction context to use for undo().
+ By default, uses the current transaction.
+ """
+
+ def close():
+ """Close the database and its underlying storage.
+
+ It is important to close the database, because the storage may
+ flush in-memory data structures to disk when it is closed.
+ Leaving the storage open with the process exits can cause the
+ next open to be slow.
+
+ What effect does closing the database have on existing
+ connections? Technically, they remain open, but their storage
+ is closed, so they stop behaving usefully. Perhaps close()
+ should also close all the Connections.
+ """
+
class IStorage(Interface):
"""A storage is responsible for storing and retrieving data of objects.
"""
-## What follows is the union of methods found across various storage
-## implementations. Exactly what "the storage API" is and means has
-## become fuzzy over time. Methods should be uncommented here, or
-## even deleted, as the storage API regains a crisp definition.
+ def close():
+ """Close the storage.
+ """
-## def load(oid, version):
-## """TODO"""
-##
-## def close():
-## """TODO"""
-##
-## def cleanup():
-## """TODO"""
-##
-## def lastSerial():
-## """TODO"""
-##
-## def lastTransaction():
-## """TODO"""
-##
-## def lastTid(oid):
-## """Return last serialno committed for object oid."""
-##
-## def loadSerial(oid, serial):
-## """TODO"""
-##
-## def loadBefore(oid, tid):
-## """TODO"""
-##
-## def iterator(start=None, stop=None):
-## """TODO"""
-##
-## def sortKey():
-## """TODO"""
-##
-## def getName():
-## """TODO"""
-##
-## def getSize():
-## """TODO"""
-##
-## def history(oid, version, length=1, filter=None):
-## """TODO"""
-##
-## def new_oid():
-## """TODO"""
-##
-## def set_max_oid(possible_new_max_oid):
-## """TODO"""
-##
-## def registerDB(db):
-## """TODO"""
-##
-## def isReadOnly():
-## """TODO"""
-##
-## def supportsUndo():
-## """TODO"""
-##
-## def supportsVersions():
-## """TODO"""
-##
-## def tpc_abort(transaction):
-## """TODO"""
-##
-## def tpc_begin(transaction):
-## """TODO"""
-##
-## def tpc_vote(transaction):
-## """TODO"""
-##
-## def tpc_finish(transaction, f=None):
-## """TODO"""
-##
-## def getSerial(oid):
-## """TODO"""
-##
-## def loadSerial(oid, serial):
-## """TODO"""
-##
-## def loadBefore(oid, tid):
-## """TODO"""
-##
-## def getExtensionMethods():
-## """TODO"""
-##
-## def copyTransactionsFrom():
-## """TODO"""
-##
-## def store(oid, oldserial, data, version, transaction):
-## """
-##
-## may return the new serial or not
-## """
+ def getName():
+ """The name of the storage
+ The format and interpretation of this name is storage
+ dependent. It could be a file name, a database name, etc.
+
+ This is used soley for informational purposes.
+ """
+
+ def getSize():
+ """An approximate size of the database, in bytes.
+
+ This is used soley for informational purposes.
+ """
+
+ def history(oid, version, size=1):
+ """Return a sequence of history information dictionaries.
+
+ Up to size objects (including no objects) may be returned.
+
+ The information provides a log of the changes made to the
+ object. Data are reported in reverse chronological order.
+
+ Each dictionary has the following keys:
+
+ time
+ UTC seconds since the epoch (as in time.time) that the
+ object revision was committed.
+ tid
+ The transaction identifier of the transaction that
+ committed the version.
+ version
+ The version that the revision is in. If the storage
+ doesn't support versions, then this must be an empty
+ string.
+ user_name
+ The user identifier, if any (or an empty string) of the
+ user on whos behalf the revision was committed.
+ description
+ The transaction description for the transaction that
+ committed the revision.
+ size
+ The size of the revision data record.
+
+ If the transaction had extension items, then these items are
+ also included if they don't conflict with the keys above.
+ """
+
+ def isReadOnly():
+ """Test whether a storage allows committing new transactions
+
+ For a given storage instance, this method always returns the
+ same value. Read-only-ness is a static property of a storage.
+ """
+
+ def lastTransaction():
+ """Return the id of the last committed transaction
+ """
+
+ def __len__():
+ """The approximate number of objects in the storage
+
+ This is used soley for informational purposes.
+ """
+
+ def load(oid, version):
+ """Load data for an object id and version
+
+ A data record and serial are returned. The serial is a
+ transaction identifier of the transaction that wrote the data
+ record.
+
+ A POSKeyError is raised if there is no record for the object
+ id and version.
+
+ Storages that don't support versions must ignore the version
+ argument.
+ """
+
+ def loadBefore(oid, tid):
+ """Load the object data written before a transaction id
+
+ If there isn't data before the object before the given
+ transaction, then None is returned, otherwise three values are
+ returned:
+
+ - The data record
+
+ - The transaction id of the data record
+
+ - The transaction id of the following revision, if any, or None.
+ """
+
+ def loadSerial(oid, serial):
+ """Load the object record for the give transaction id
+
+ A data record is returned.
+ """
+
+ def new_oid():
+ """Allocate a new object id.
+
+ The object id returned is reserved at least as long as the
+ storage is opened.
+
+ The return value is a string.
+ """
+
+ def pack(pack_time, referencesf):
+ """Pack the storage
+
+ It is up to the storage to interpret this call, however, the
+ general idea is that the storage free space by:
+
+ - discarding object revisions that were old and not current as of the
+ given pack time.
+
+ - garbage collecting objects that aren't reachable from the
+ root object via revisions remaining after discarding
+ revisions that were not current as of the pack time.
+
+ The pack time is given as a UTC time in seconds since the
+ empoch.
+
+ The second argument is a function that should be used to
+ extract object references from database records. This is
+ needed to determine which objects are referenced from object
+ revisions.
+ """
+
+ def registerDB(db):
+ """Register an IStorageDB.
+
+ Note that, for historical reasons, an implementation may
+ require a second argument, however, if required, the None will
+ be passed as the second argument.
+ """
+
+ def sortKey():
+ """Sort key used to order distributed transactions
+
+ When a transaction involved multiple storages, 2-phase commit
+ operations are applied in sort-key order. This must be unique
+ among storages used in a transaction. Obviously, the storage
+ can't assure this, but it should construct the sort key so it
+ has a reasonable chance of being unique.
+ """
+
+ def store(oid, serial, data, version, transaction):
+ """Store data for the object id, oid.
+
+ Arguments:
+
+ oid
+ The object identifier. This is either a string
+ consisting of 8 nulls or a string previously returned by
+ new_oid.
+
+ serial
+ The serial of the data that was read when the object was
+ loaded from the database. If the object was created in
+ the current transaction this will be a string consisting
+ of 8 nulls.
+
+ data
+ The data record. This is opaque to the storage.
+
+ version
+ The version to store the data is. If the storage doesn't
+ support versions, this should be an empty string and the
+ storage is allowed to ignore it.
+
+ transaction
+ A transaction object. This should match the current
+ transaction for the storage, set by tpc_begin.
+
+ The new serial for the object is returned, but not necessarily
+ immediately. It may be returned directly, or un a subsequent
+ store or tpc_vote call.
+
+ The return value may be:
+
+ - None
+
+ - A new serial (string) for the object, or
+
+ - An iterable of object-id and serial pairs giving new serials
+ for objects.
+ """
+
+ def tpc_abort(transaction):
+ """Abort the transaction.
+
+ Any changes made by the transaction are discarded.
+
+ This call is ignored is the storage is not participating in
+ two-phase commit or if the given transaction is not the same
+ as the transaction the storage is commiting.
+ """
+
+ def tpc_begin(transaction):
+ """Begin the two-phase commit process.
+
+ If storage is already participating in a two-phase commit
+ using the same transaction, the call is ignored.
+
+ If the storage is already participating in a two-phase commit
+ using a different transaction, the call blocks until the
+ current transaction ends (commits or aborts).
+ """
+
+ def tpc_finish(transaction, func = lambda: None):
+ """Finish the transaction, making any transaction changes permanent.
+
+ Changes must be made permanent at this point.
+
+ This call is ignored if the storage isn't participating in
+ two-phase commit or if it is commiting a different
+ transaction. Failure of this method is extremely serious.
+ """
+
+ def tpc_vote(transaction):
+ """Provide a storage with an opportunity to veto a transaction
+
+ This call is ignored if the storage isn't participating in
+ two-phase commit or if it is commiting a different
+ transaction. Failure of this method is extremely serious.
+
+ If a transaction can be committed by a storage, then the
+ method should return. If a transaction cannot be committed,
+ then an exception should be raised. If this method returns
+ without an error, then there must not be an error if
+ tpc_finish or tpc_abort is called subsequently.
+
+ The return value can be either None or a sequence of object-id
+ and serial pairs giving new serials for objects who's ids were
+ passed to previous store calls in the same transaction.
+ After the tpc_vote call, bew serials must have been returned,
+ either from tpc_vote or store for objects passed to store.
+ """
+
+class IStorageRestoreable(IStorage):
+
+ def tpc_begin(transaction, tid=None):
+ """Begin the two-phase commit process.
+
+ If storage is already participating in a two-phase commit
+ using the same transaction, the call is ignored.
+
+ If the storage is already participating in a two-phase commit
+ using a different transaction, the call blocks until the
+ current transaction ends (commits or aborts).
+
+ If a transaction id is given, then the transaction will use
+ the given id rather than generating a new id. This is used
+ when copying already committed transactions from another
+ storage.
+ """
+
+ # Note that the current implementation also accepts a status.
+ # This is an artifact of:
+ # - Earlier use of an undo status to undo revisions in place,
+ # and,
+ # - Incorrect pack garbage-collection algorithms (possibly
+ # including the existing FileStorage implementation), that
+ # failed to take into account records after the pack time.
+
+
+ def restore(oid, serial, data, version, prev_txn, transaction):
+ """Write data already committed in a separate database
+
+ The restore method is used when copying data from one database
+ to a replica of the database. It differs from store in that
+ the data have already been committed, so there is no check for
+ conflicts and no new transaction is is used for the data.
+
+ Arguments:
+
+ oid
+ The object id for the record
+
+ serial
+ The transaction identifier that originally committed this object.
+
+ data
+ The record data. This will be None if the transaction
+ undid the creation of the object.
+
+ version
+ The version identifier for the record
+
+ prev_txn
+ The identifier of a previous transaction that held the
+ object data. The target storage can sometimes use this
+ as a hint to save space.
+
+ transaction
+ The current transaction.
+
+ Nothing is returned.
+ """
+
+class IStorageRecordInformation(Interface):
+ """Provide information about a single storage record
+ """
+
+ oid = Attribute("The object id")
+ version = Attribute("The version")
+ data = Attribute("The data record")
+
+class IStorageTransactionInformation(Interface):
+ """Provide information about a storage transaction
+ """
+
+ tid = Attribute("Transaction id")
+ status = Attribute("Transaction Status") # XXX what are valid values?
+ user = Attribute("Transaction user")
+ description = Attribute("Transaction Description")
+ extension = Attribute("Transaction extension data")
+
+ def __iter__():
+ """Return an iterable of IStorageTransactionInformation
+ """
+
+class IStorageIteration(Interface):
+ """API for iterating over the contents of a storage
+
+ Note that this is a future API. Some storages now provide an
+ approximation of this.
+
+ """
+
+ def iterator(start=None, stop=None):
+ """Return an IStorageTransactionInformation iterator.
+
+ An IStorageTransactionInformation iterator is returned for
+ iterating over the transactions in the storage.
+
+ If the start argument is not None, then iteration will start
+ with the first transaction whos identifier is greater than or
+ equal to start.
+
+ If the stop argument is not None, then iteration will end with
+ the last transaction whos identifier is less than or equal to
+ start.
+
+ """
+
class IStorageUndoable(IStorage):
"""A storage supporting transactional undo.
"""
- def undo(transaction_id, txn):
- """TODO"""
+ def supportsUndo():
+ """Return True, indicating that the storage supports undo.
+ """
- def undoLog(first, last, filter=(lambda desc: True)):
+ def undo(transaction_id, transaction):
+ """Undo the transaction corresponding to the given transaction id.
+
+ The transaction id is a value returned from undoInfo or
+ undoLog, which may not be a stored transaction identifier as
+ used elsewhere in the storage APIs.
+
+ This method must only be called in the first phase of
+ two-phase commit (after tpc_begin but before tpc_vote). It
+ returns a serial (transaction id) and a sequence of object ids
+ for objects affected by the transaction.
+
+ """
+ # Used by DB (Actually, by TransactionalUndo)
+
+ def undoLog(first, last, filter=None):
"""Return a sequence of descriptions for undoable transactions.
Application code should call undoLog() on a DB instance instead of on
@@ -551,8 +856,9 @@
could be gotten by passing the positive first-last for
`last` instead.
"""
+ # DB pass through
- def undoInfo(first, last, specification=None):
+ def undoInfo(first=0, last=-20, specification=None):
"""Return a sequence of descriptions for undoable transactions.
This is like `undoLog()`, except for the `specification` argument.
@@ -567,30 +873,84 @@
ZEO client to its ZEO server (while a ZEO client ignores any `filter`
argument passed to `undoLog()`).
"""
+ # DB pass-through
+
+class IStoragePackable(Interface):
+
def pack(t, referencesf):
- """TODO"""
+ """Pack the storage
+ Pack and/or garbage-collect the storage. If the storage does
+ not support undo, then t is ignored. All records for objects
+ that are not reachable from the system root object as of time
+ t, or as of the current time, if undo is not supported, are
+ removed from the storage.
+
+ A storage implementation may treat this method as ano-op. A
+ storage implementation may also delay packing and return
+ immediately. Storage documentation should define the behavior
+ of this method.
+ """
+ # Called by DB
+
+class IStorageCurrentRecordIteration(IStorage):
+
+ def record_iternext(next=None):
+ """Iterate over the records in a storage
+
+ Use like this:
+
+ >>> next = None
+ >>> while 1:
+ ... oid, tid, data, next = storage.record_iternext(next)
+ ... # do things with oid, tid, and data
+ ... if next is None:
+ ... break
+
+ """
+
class IStorageVersioning(IStorage):
"""A storage supporting versions.
+
+ It is likely that version support will disappear from future
+ versions of ZODB. There are known bugs in version handling.
"""
-## What follows is the union of methods found across various version storage
-## implementations. Exactly what "the storage API" is and means has
-## become fuzzy over time. Methods should be uncommented here, or
-## even deleted, as the storage API regains a crisp definition.
+ def supportsVersions():
+ """Return True, idicating that the storage suports versions.
+ """
-## def abortVersion(src, transaction):
-## """TODO"""
-##
-## def commitVersion(src, dest, transaction):
-## """TODO"""
-##
-## def modifiedInVersion(oid):
-## """TODO"""
-##
-## def versionEmpty(version):
-## """TODO"""
-##
-## def versions(max=None):
-## """TODO"""
+ def abortVersion(version, transaction):
+ """Clear any changes made by the given version.
+ """
+ # used by DB
+
+ def commitVersion(source, destination, transaction):
+ """Save version changes
+
+ Store changes made in the source version into the destination
+ version. A VersionCommitError is raised if the source and
+ destination are equal or if the source is an empty string. The
+ destination may be an empty string, in which case the data are
+ saved to non-version storage.
+ """
+ # used by DB
+
+ def versionEmpty(version):
+ """true if the version for the given version string is empty.
+ """
+ # DB pass through
+
+ def modifiedInVersion(oid):
+ """the version that the object was modified in,
+
+ or an empty string if the object was not modified in a version
+ """
+ # DB pass through, sor of. In the past (including present :),
+ # the DB tried to cache this. We'll probably stop bothering.
+
+ def versions(max = None):
+ """A sequence of version strings for active versions
+ """
+ # DB pass through
Modified: ZODB/branches/jim-storage-api-cleanup/src/ZODB/tests/BasicStorage.py
===================================================================
--- ZODB/branches/jim-storage-api-cleanup/src/ZODB/tests/BasicStorage.py 2007-04-24 23:33:02 UTC (rev 74731)
+++ ZODB/branches/jim-storage-api-cleanup/src/ZODB/tests/BasicStorage.py 2007-04-24 23:35:31 UTC (rev 74732)
@@ -161,19 +161,19 @@
revid4 = self._dostore(oid2, revid=revid2, data=p52)
noteq(revid3, revid4)
- def checkGetSerial(self):
- if not hasattr(self._storage, 'getSerial'):
+ def checkGetTid(self):
+ if not hasattr(self._storage, 'getTid'):
return
eq = self.assertEqual
p41, p42 = map(MinPO, (41, 42))
oid = self._storage.new_oid()
- self.assertRaises(KeyError, self._storage.getSerial, oid)
+ self.assertRaises(KeyError, self._storage.getTid, oid)
# Now store a revision
revid1 = self._dostore(oid, data=p41)
- eq(revid1, self._storage.getSerial(oid))
+ eq(revid1, self._storage.getTid(oid))
# And another one
revid2 = self._dostore(oid, revid=revid1, data=p42)
- eq(revid2, self._storage.getSerial(oid))
+ eq(revid2, self._storage.getTid(oid))
def checkTwoArgBegin(self):
# Unsure: how standard is three-argument tpc_begin()?
Modified: ZODB/branches/jim-storage-api-cleanup/src/ZODB/tests/IteratorStorage.py
===================================================================
--- ZODB/branches/jim-storage-api-cleanup/src/ZODB/tests/IteratorStorage.py 2007-04-24 23:33:02 UTC (rev 74731)
+++ ZODB/branches/jim-storage-api-cleanup/src/ZODB/tests/IteratorStorage.py 2007-04-24 23:35:31 UTC (rev 74732)
@@ -87,11 +87,6 @@
pass
def checkUndoZombieNonVersion(self):
- if not hasattr(self._storage, 'supportsTransactionalUndo'):
- return
- if not self._storage.supportsTransactionalUndo():
- return
-
oid = self._storage.new_oid()
revid = self._dostore(oid, data=MinPO(94))
# Get the undo information
Modified: ZODB/branches/jim-storage-api-cleanup/src/ZODB/tests/ReadOnlyStorage.py
===================================================================
--- ZODB/branches/jim-storage-api-cleanup/src/ZODB/tests/ReadOnlyStorage.py 2007-04-24 23:33:02 UTC (rev 74731)
+++ ZODB/branches/jim-storage-api-cleanup/src/ZODB/tests/ReadOnlyStorage.py 2007-04-24 23:35:31 UTC (rev 74732)
@@ -59,6 +59,5 @@
self.assertRaises(ReadOnlyError, self._storage.store,
'\000' * 8, None, '', '', t)
- if self._storage.supportsTransactionalUndo():
- self.assertRaises(ReadOnlyError, self._storage.undo,
- '\000' * 8, t)
+ self.assertRaises(ReadOnlyError, self._storage.undo,
+ '\000' * 8, t)
Modified: ZODB/branches/jim-storage-api-cleanup/src/ZODB/tests/TransactionalUndoStorage.py
===================================================================
--- ZODB/branches/jim-storage-api-cleanup/src/ZODB/tests/TransactionalUndoStorage.py 2007-04-24 23:33:02 UTC (rev 74731)
+++ ZODB/branches/jim-storage-api-cleanup/src/ZODB/tests/TransactionalUndoStorage.py 2007-04-24 23:35:31 UTC (rev 74732)
@@ -143,7 +143,7 @@
eq(zodb_unpickle(data), MinPO(23))
self._iterate()
- def checkCreationUndoneGetSerial(self):
+ def checkCreationUndoneGetTid(self):
# create an object
oid = self._storage.new_oid()
self._dostore(oid, data=MinPO(23))
@@ -156,9 +156,9 @@
self._storage.undo(tid, t)
self._storage.tpc_vote(t)
self._storage.tpc_finish(t)
- # Check that calling getSerial on an uncreated object raises a KeyError
+ # Check that calling getTid on an uncreated object raises a KeyError
# The current version of FileStorage fails this test
- self.assertRaises(KeyError, self._storage.getSerial, oid)
+ self.assertRaises(KeyError, self._storage.getTid, oid)
def checkUndoCreationBranch1(self):
eq = self.assertEqual
Modified: ZODB/branches/jim-storage-api-cleanup/src/ZODB/tests/VersionStorage.py
===================================================================
--- ZODB/branches/jim-storage-api-cleanup/src/ZODB/tests/VersionStorage.py 2007-04-24 23:33:02 UTC (rev 74731)
+++ ZODB/branches/jim-storage-api-cleanup/src/ZODB/tests/VersionStorage.py 2007-04-24 23:35:31 UTC (rev 74732)
@@ -80,8 +80,8 @@
eq(zodb_unpickle(data), MinPO(12))
data, vrevid = self._storage.load(oid, version)
eq(zodb_unpickle(data), MinPO(15))
- if hasattr(self._storage, 'getSerial'):
- s = self._storage.getSerial(oid)
+ if hasattr(self._storage, 'getTid'):
+ s = self._storage.getTid(oid)
eq(s, max(revid, vrevid))
data, tid, ver = self._storage.loadEx(oid, version)
eq(zodb_unpickle(data), MinPO(15))
@@ -186,7 +186,7 @@
eq = self.assertEqual
oid, version = self._setup_version()
- # Not sure I can write a test for getSerial() in the
+ # Not sure I can write a test for getTid() in the
# presence of aborted versions, because FileStorage and
# Berkeley storage give a different answer. I think Berkeley
# is right and FS is wrong.
@@ -219,11 +219,9 @@
self._storage.tpc_begin(t)
# And try to abort the empty version
- if (hasattr(self._storage, 'supportsTransactionalUndo') and
- self._storage.supportsTransactionalUndo()):
- self.assertRaises(POSException.VersionError,
- self._storage.abortVersion,
- '', t)
+ self.assertRaises(POSException.VersionError,
+ self._storage.abortVersion,
+ '', t)
# But now we really try to abort the version
tid, oids = self._storage.abortVersion(version, t)
@@ -235,9 +233,6 @@
eq(zodb_unpickle(data), MinPO(51))
def checkCommitVersionErrors(self):
- if not (hasattr(self._storage, 'supportsTransactionalUndo') and
- self._storage.supportsTransactionalUndo()):
- return
eq = self.assertEqual
oid1, version1 = self._setup_version('one')
data, revid1 = self._storage.load(oid1, version1)
More information about the Zodb-checkins
mailing list