Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/master' into no-more-load
Browse files Browse the repository at this point in the history
  • Loading branch information
Jim Fulton committed Jun 6, 2016
2 parents c1d79d7 + b7ea4e6 commit 6c0b560
Show file tree
Hide file tree
Showing 9 changed files with 88 additions and 68 deletions.
5 changes: 5 additions & 0 deletions CHANGES.rst
Expand Up @@ -26,6 +26,11 @@
transaction.
https://github.com/zopefoundation/ZODB/pull/52

- DemoStorage: add support for conflict resolution and fix history()
https://github.com/zopefoundation/ZODB/pull/58

- Fixed: FileStorage loadBefore didn't handle deleted/undone data correctly.

4.2.0 (2015-06-02)
==================

Expand Down
15 changes: 0 additions & 15 deletions setup.py
Expand Up @@ -23,23 +23,8 @@
version = "4.3.0.dev0"

import os
import sys
from setuptools import setup, find_packages

if sys.version_info < (2, 6):
print("This version of ZODB requires Python 2.6 or higher")
sys.exit(0)

if (3,) < sys.version_info < (3, 2):
print("This version of ZODB requires Python 3.2 or higher")
sys.exit(0)


# The (non-obvious!) choices for the Trove Development Status line:
# Development Status :: 5 - Production/Stable
# Development Status :: 4 - Beta
# Development Status :: 3 - Alpha

classifiers = """\
Development Status :: 4 - Beta
Intended Audience :: Developers
Expand Down
7 changes: 6 additions & 1 deletion src/ZODB/ConflictResolution.py
Expand Up @@ -313,4 +313,9 @@ class ConflictResolvingStorage(object):
def registerDB(self, wrapper):
self._crs_untransform_record_data = wrapper.untransform_record_data
self._crs_transform_record_data = wrapper.transform_record_data
super(ConflictResolvingStorage, self).registerDB(wrapper)
try:
m = super(ConflictResolvingStorage, self).registerDB
except AttributeError:
pass
else:
m(wrapper)
24 changes: 20 additions & 4 deletions src/ZODB/DemoStorage.py
Expand Up @@ -31,12 +31,13 @@
import ZODB.POSException
import ZODB.utils
import zope.interface
from .ConflictResolution import ConflictResolvingStorage, ResolvedSerial

@zope.interface.implementer(
ZODB.interfaces.IStorage,
ZODB.interfaces.IStorageIteration,
)
class DemoStorage(object):
class DemoStorage(ConflictResolvingStorage):


def __init__(self, name=None, base=None, changes=None,
Expand Down Expand Up @@ -112,7 +113,7 @@ def close(self):
def _copy_methods_from_changes(self, changes):
for meth in (
'_lock_acquire', '_lock_release',
'getSize', 'history', 'isReadOnly', 'registerDB',
'getSize', 'isReadOnly',
'sortKey', 'tpc_transaction', 'tpc_vote',
):
setattr(self, meth, getattr(changes, meth))
Expand All @@ -137,6 +138,20 @@ def getTid(self, oid):
except ZODB.POSException.POSKeyError:
return self.base.getTid(oid)

def history(self, oid, size=1):
try:
r = self.changes.history(oid, size)
except ZODB.POSException.POSKeyError:
r = []
size -= len(r)
if size:
try:
r += self.base.history(oid, size)
except ZODB.POSException.POSKeyError:
if not r:
raise
return r

def iterator(self, start=None, end=None):
for t in self.base.iterator(start, end):
yield t
Expand Down Expand Up @@ -281,8 +296,9 @@ def store(self, oid, serial, data, version, transaction):
old = serial

if old != serial:
raise ZODB.POSException.ConflictError(
oid=oid, serials=(old, serial)) # XXX untested branch
rdata = self.tryToResolveConflict(oid, old, serial, data)
self.changes.store(oid, old, rdata, '', transaction)
return ResolvedSerial

return self.changes.store(oid, serial, data, '', transaction)

Expand Down
8 changes: 5 additions & 3 deletions src/ZODB/FileStorage/FileStorage.py
Expand Up @@ -490,11 +490,13 @@ def loadBefore(self, oid, tid):
if not pos:
return None

if h.back:
if h.plen:
return _file.read(h.plen), h.tid, end_tid
elif h.back:
data, _, _, _ = self._loadBack_impl(oid, h.back, _file=_file)
return data, h.tid, end_tid
else:
return _file.read(h.plen), h.tid, end_tid
raise POSKeyError(oid)

def store(self, oid, oldserial, data, version, transaction):
if self._is_read_only:
Expand Down Expand Up @@ -1645,7 +1647,7 @@ def read_index(file, name, index, tindex, stop=b'\377'*8,
maxoid = index.maxKey()
except ValueError:
# The index is empty.
maxoid == z64
pass # maxoid is already equal to z64

return pos, maxoid, ltid

Expand Down
2 changes: 1 addition & 1 deletion src/ZODB/MappingStorage.py
Expand Up @@ -105,7 +105,7 @@ def history(self, oid, size=1):
tids.reverse()
return [
dict(
time = ZODB.TimeStamp.TimeStamp(tid),
time = ZODB.TimeStamp.TimeStamp(tid).timeTime(),
tid = tid,
serial = tid,
user_name = self._transactions[tid].user,
Expand Down
54 changes: 20 additions & 34 deletions src/ZODB/tests/HistoryStorage.py
Expand Up @@ -17,45 +17,31 @@
all these tests.
"""

from time import time
from ZODB.tests.MinPO import MinPO

class HistoryStorage:
def checkSimpleHistory(self):
eq = self.assertEqual
self._checkHistory((11, 12, 13))

def _checkHistory(self, data):
start = time()
# Store a couple of revisions of the object
oid = self._storage.new_oid()
self.assertRaises(KeyError,self._storage.history,oid)
revid1 = self._dostore(oid, data=MinPO(11))
revid2 = self._dostore(oid, revid=revid1, data=MinPO(12))
revid3 = self._dostore(oid, revid=revid2, data=MinPO(13))
revids = [None]
for data in data:
revids.append(self._dostore(oid, revids[-1], MinPO(data)))
revids.reverse()
del revids[-1]
# Now get various snapshots of the object's history
h = self._storage.history(oid, size=1)
eq(len(h), 1)
d = h[0]
eq(d['tid'], revid3)
# Try to get 2 historical revisions
h = self._storage.history(oid, size=2)
eq(len(h), 2)
d = h[0]
eq(d['tid'], revid3)
d = h[1]
eq(d['tid'], revid2)
# Try to get all 3 historical revisions
h = self._storage.history(oid, size=3)
eq(len(h), 3)
d = h[0]
eq(d['tid'], revid3)
d = h[1]
eq(d['tid'], revid2)
d = h[2]
eq(d['tid'], revid1)
# There should be no more than 3 revisions
h = self._storage.history(oid, size=4)
eq(len(h), 3)
d = h[0]
eq(d['tid'], revid3)
d = h[1]
eq(d['tid'], revid2)
d = h[2]
eq(d['tid'], revid1)

for i in range(1, 1 + len(revids)):
h = self._storage.history(oid, size=i)
self.assertEqual([d['tid'] for d in h], revids[:i])
# Check results are sorted by timestamp, in descending order.
a = time()
for d in h:
b = a
a = d['time']
self.assertLess(a, b)
self.assertLess(start, a)
11 changes: 7 additions & 4 deletions src/ZODB/tests/TransactionalUndoStorage.py
Expand Up @@ -179,6 +179,10 @@ def checkUndoCreationBranch1(self):
info = self._storage.undoInfo()
self._undo(info[2]['id'], [oid])
self.assertRaises(KeyError, self._storage.load, oid, '')

# Loading current data via loadBefore should raise a POSKeyError too:
self.assertRaises(KeyError, self._storage.loadBefore, oid,
b'\x7f\xff\xff\xff\xff\xff\xff\xff')
self._iterate()

def checkUndoCreationBranch2(self):
Expand Down Expand Up @@ -286,10 +290,9 @@ def checkTwoObjectUndoAtOnce(self):
t = Transaction()
oids = self._begin_undos_vote(t, tid, tid1)
self._storage.tpc_finish(t)
# We get the finalization stuff called an extra time:
eq(len(oids), 4)
unless(oid1 in oids)
unless(oid2 in oids)
# We may get the finalization stuff called an extra time,
# depending on the implementation.
self.assertEqual(set(oids), set((oid1, oid2)))
data, revid1 = self._storage.load(oid1, '')
eq(zodb_unpickle(data), MinPO(30))
data, revid2 = self._storage.load(oid2, '')
Expand Down
30 changes: 24 additions & 6 deletions src/ZODB/tests/testDemoStorage.py
Expand Up @@ -14,6 +14,7 @@
from ZODB.DB import DB
from ZODB.tests import (
BasicStorage,
ConflictResolution,
HistoryStorage,
IteratorStorage,
MTStorage,
Expand Down Expand Up @@ -42,7 +43,7 @@
class DemoStorageTests(
StorageTestBase.StorageTestBase,
BasicStorage.BasicStorage,

ConflictResolution.ConflictResolvingStorage,
HistoryStorage.HistoryStorage,
IteratorStorage.ExtendedIteratorStorage,
IteratorStorage.IteratorStorage,
Expand Down Expand Up @@ -86,6 +87,23 @@ def checkLoadBeforeUndo(self):
pass # we don't support undo yet
checkUndoZombie = checkLoadBeforeUndo

def checkBaseHistory(self):
def base_only():
yield 11
yield 12
yield 13
self._storage = self._storage.push()
self._checkHistory(base_only())
self._storage = self._storage.pop()
def base_and_changes():
yield 11
yield 12
self._storage = self._storage.push()
yield 13
yield 14
self._checkHistory(base_and_changes())
self._storage = self._storage.pop()


class DemoStorageHexTests(DemoStorageTests):

Expand Down Expand Up @@ -144,11 +162,11 @@ def testSomeDelegation():
>>> class S:
... def __init__(self, name):
... self.name = name
... def registerDB(self, db):
... six.print_(self.name, db)
... def getSize(self):
... six.print_(self.name, 'size')
... def close(self):
... six.print_(self.name, 'closed')
... sortKey = getSize = __len__ = history = getTid = None
... sortKey = __len__ = getTid = None
... tpc_finish = tpc_vote = tpc_transaction = None
... _lock_acquire = _lock_release = lambda self: None
... getName = lambda self: 'S'
Expand All @@ -165,8 +183,8 @@ def testSomeDelegation():
>>> from ZODB.DemoStorage import DemoStorage
>>> storage = DemoStorage(base=S(1), changes=S(2))
>>> storage.registerDB(1)
2 1
>>> storage.getSize()
2 size
>>> storage.close()
1 closed
Expand Down

0 comments on commit 6c0b560

Please sign in to comment.