Commit 056eaa64 authored by Tim Peters's avatar Tim Peters

Port from ZODB 3.2.

DemoStorage:  Added implementations for registerDB() and new_oid().  As
Tres discovered the hard way, wrapping a ZEO client storage as a
DemoStorage base storage yields insane behavior otherwise.

BaseStorage.new_oid():  Rewrite to eliminate recursion, and hence also the
need for the undocumented and irregular `last=` argument.

Other:  removed the `last=` argument to new_oid() every place that felt
compelled to spread that insanity ;-).  Seriously, it served no purpose
at all elsewhere, and looks like people just cut 'n pasted in fear.
parent 1784f286
...@@ -2,6 +2,14 @@ What's new in ZODB3 3.4a4? ...@@ -2,6 +2,14 @@ What's new in ZODB3 3.4a4?
========================== ==========================
Release date: DD-MMM-2005 Release date: DD-MMM-2005
DemoStorage
-----------
Appropriate implementations of the storage API's ``registerDB()`` and
``new_oid()`` methods were added, delegating to the base storage. This was
needed to support wrapping a ZEO client storage as a ``DemoStorage`` base
storage, as some new Zope tests want to do.
Tools Tools
----- -----
...@@ -9,6 +17,16 @@ Tools ...@@ -9,6 +17,16 @@ Tools
actually went in several months go, but wasn't noted here at the time. actually went in several months go, but wasn't noted here at the time.
Thanks to Dmitry Vasiliev for contributing code and tests. Thanks to Dmitry Vasiliev for contributing code and tests.
BaseStorage
-----------
``new_oid()``'s undocumented ``last=`` argument was removed. It was used
only for internal recursion, and injured code sanity elsewhere because not
all storages included it in their ``new_oid()``'s signature. Straightening
this out required adding ``last=`` everywhere, or removing it everywhere.
Since recursion isn't actually needed, and there was no other use for
``last=``, removing it everywhere was the obvious choice.
What's new in ZODB3 3.4a3? What's new in ZODB3 3.4a3?
========================== ==========================
......
...@@ -259,11 +259,8 @@ class StorageServer: ...@@ -259,11 +259,8 @@ class StorageServer:
def modifiedInVersion(self, oid): def modifiedInVersion(self, oid):
return self.rpc.call('modifiedInVersion', oid) return self.rpc.call('modifiedInVersion', oid)
def new_oid(self, last=None): def new_oid(self):
if last is None: return self.rpc.call('new_oid')
return self.rpc.call('new_oid')
else:
return self.rpc.call('new_oid', last)
def store(self, oid, serial, data, version, trans): def store(self, oid, serial, data, version, trans):
return self.rpc.call('store', oid, serial, data, version, trans) return self.rpc.call('store', oid, serial, data, version, trans)
......
...@@ -19,6 +19,7 @@ import cPickle ...@@ -19,6 +19,7 @@ import cPickle
import threading import threading
import time import time
import logging import logging
from struct import pack as _structpack, unpack as _structunpack
from persistent.TimeStamp import TimeStamp from persistent.TimeStamp import TimeStamp
...@@ -96,10 +97,15 @@ class BaseStorage(UndoLogCompatible): ...@@ -96,10 +97,15 @@ class BaseStorage(UndoLogCompatible):
t=time.time() t=time.time()
t=self._ts=apply(TimeStamp,(time.gmtime(t)[:5]+(t%60,))) t=self._ts=apply(TimeStamp,(time.gmtime(t)[:5]+(t%60,)))
self._tid = `t` self._tid = `t`
# ._oid is the highest oid in use (0 is always in use -- it's
# a reserved oid for the root object). Our new_oid() method
# increments it by 1, and returns the result. It's really a
# 64-bit integer stored as an 8-byte big-endian string.
if base is None: if base is None:
self._oid=z64 self._oid = z64
else: else:
self._oid=base._oid self._oid = base._oid
def abortVersion(self, src, transaction): def abortVersion(self, src, transaction):
if transaction is not self._transaction: if transaction is not self._transaction:
...@@ -138,24 +144,22 @@ class BaseStorage(UndoLogCompatible): ...@@ -138,24 +144,22 @@ class BaseStorage(UndoLogCompatible):
def modifiedInVersion(self, oid): def modifiedInVersion(self, oid):
return '' return ''
def new_oid(self, last=None): def new_oid(self):
# 'last' is only for internal use, not part of the public API
if self._is_read_only: if self._is_read_only:
raise POSException.ReadOnlyError() raise POSException.ReadOnlyError()
if last is None: self._lock_acquire()
self._lock_acquire() try:
try: last = self._oid
last=self._oid d = ord(last[-1])
d=ord(last[-1]) if d < 255: # fast path for the usual case
if d < 255: last=last[:-1]+chr(d+1) last = last[:-1] + chr(d+1)
else: last=self.new_oid(last[:-1]) else: # there's a carry out of the last byte
self._oid=last last_as_long, = _structunpack(">Q", last)
return last last = _structpack(">Q", last_as_long + 1)
finally: self._lock_release() self._oid = last
else: return last
d=ord(last[-1]) finally:
if d < 255: return last[:-1]+chr(d+1)+'\0'*(8-len(last)) self._lock_release()
else: return self.new_oid(last[:-1])
# Update the maximum oid in use, under protection of a lock. The # Update the maximum oid in use, under protection of a lock. The
# maximum-in-use attribute is changed only if possible_new_max_oid is # maximum-in-use attribute is changed only if possible_new_max_oid is
......
...@@ -81,16 +81,17 @@ and call it to monitor the storage. ...@@ -81,16 +81,17 @@ and call it to monitor the storage.
""" """
import base64, time import base64, time
from ZODB import POSException, BaseStorage from ZODB import POSException
from ZODB.utils import z64, oid_repr from ZODB.utils import z64, oid_repr
from ZODB.BaseStorage import BaseStorage
from persistent.TimeStamp import TimeStamp from persistent.TimeStamp import TimeStamp
from cPickle import loads from cPickle import loads
from BTrees import OOBTree from BTrees import OOBTree
class DemoStorage(BaseStorage.BaseStorage): class DemoStorage(BaseStorage):
def __init__(self, name='Demo Storage', base=None, quota=None): def __init__(self, name='Demo Storage', base=None, quota=None):
BaseStorage.BaseStorage.__init__(self, name, base) BaseStorage.__init__(self, name, base)
# We use a BTree because the items are sorted! # We use a BTree because the items are sorted!
self._data = OOBTree.OOBTree() self._data = OOBTree.OOBTree()
...@@ -105,6 +106,23 @@ class DemoStorage(BaseStorage.BaseStorage): ...@@ -105,6 +106,23 @@ class DemoStorage(BaseStorage.BaseStorage):
raise POSException.StorageError, ( raise POSException.StorageError, (
"Demo base storage has version data") "Demo base storage has version data")
# While we officially don't support wrapping a non-read-only base
# storage, it has proved useful for test suites to wrap a ClientStorage
# in DemoStorage. The least we can do to help support that case is
# to arrange for invalidations to get delivered to the base storage.
def registerDB(self, db, limit):
if self._base is not None: # delegate
self._base.registerDB(db, limit)
# When DemoStorage needs to create a new oid, and there is a base
# storage, it must use that storage's new_oid() method. Else
# DemoStorage may end up assigning "new" oids that are already in use
# by the base storage, leading to a variety of "impossible" problems.
def new_oid(self):
if self._base is None:
return BaseStorage.new_oid(self)
else:
return self._base.new_oid()
def __len__(self): def __len__(self):
base=self._base base=self._base
......
...@@ -1406,8 +1406,10 @@ class FileStorage(BaseStorage.BaseStorage, ...@@ -1406,8 +1406,10 @@ class FileStorage(BaseStorage.BaseStorage,
index = self._index index = self._index
oid = index.minKey(next) oid = index.minKey(next)
oid_as_long, = unpack(">Q", oid)
next_oid = pack(">Q", oid_as_long + 1)
try: try:
next_oid = index.minKey(self.new_oid(oid)) next_oid = index.minKey(next_oid)
except ValueError: # "empty tree" error except ValueError: # "empty tree" error
next_oid = None next_oid = None
......
...@@ -391,7 +391,7 @@ class IStorage(Interface): ...@@ -391,7 +391,7 @@ class IStorage(Interface):
def history(oid, version, length=1, filter=None): def history(oid, version, length=1, filter=None):
"""TODO""" """TODO"""
def new_oid(last=None): def new_oid():
"""TODO""" """TODO"""
def set_max_oid(possible_new_max_oid): def set_max_oid(possible_new_max_oid):
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment