Commit 8bc95951 authored by Jim Fulton's avatar Jim Fulton

Added a lastInvalidations that a ZEO server can use to populate its

invalidation queue.
parent 2d59dd41
...@@ -1390,6 +1390,25 @@ class FileStorage(BaseStorage.BaseStorage, ...@@ -1390,6 +1390,25 @@ class FileStorage(BaseStorage.BaseStorage,
"""Return transaction id for last committed transaction""" """Return transaction id for last committed transaction"""
return self._ltid return self._ltid
def lastInvalidations(self, count):
file = self._file
seek = file.seek
read = file.read
self._lock_acquire()
try:
pos = self._pos
while count > 0 and pos > 4:
count -= 1
seek(pos-8)
pos = pos - 8 - u64(read(8))
seek(0)
return [(trans.tid, [(r.oid, r.version) for r in trans])
for trans in FileIterator(self._file, pos=pos)]
finally:
self._lock_release()
def lastTid(self, oid): def lastTid(self, oid):
"""Return last serialno committed for object oid. """Return last serialno committed for object oid.
...@@ -1822,7 +1841,7 @@ class FileIterator(Iterator, FileStorageFormatter): ...@@ -1822,7 +1841,7 @@ class FileIterator(Iterator, FileStorageFormatter):
_ltid = z64 _ltid = z64
_file = None _file = None
def __init__(self, file, start=None, stop=None): def __init__(self, file, start=None, stop=None, pos=4L):
if isinstance(file, str): if isinstance(file, str):
file = open(file, 'rb') file = open(file, 'rb')
self._file = file self._file = file
...@@ -1830,7 +1849,7 @@ class FileIterator(Iterator, FileStorageFormatter): ...@@ -1830,7 +1849,7 @@ class FileIterator(Iterator, FileStorageFormatter):
raise FileStorageFormatError(file.name) raise FileStorageFormatError(file.name)
file.seek(0,2) file.seek(0,2)
self._file_size = file.tell() self._file_size = file.tell()
self._pos = 4L self._pos = pos
assert start is None or isinstance(start, str) assert start is None or isinstance(start, str)
assert stop is None or isinstance(stop, str) assert stop is None or isinstance(stop, str)
if start: if start:
......
...@@ -14,7 +14,9 @@ ...@@ -14,7 +14,9 @@
import os, unittest import os, unittest
import transaction import transaction
import ZODB.FileStorage import ZODB.FileStorage
import ZODB.tests.util
from ZODB import POSException from ZODB import POSException
from ZODB import DB
from ZODB.tests import StorageTestBase, BasicStorage, \ from ZODB.tests import StorageTestBase, BasicStorage, \
TransactionalUndoStorage, VersionStorage, \ TransactionalUndoStorage, VersionStorage, \
...@@ -192,7 +194,6 @@ class FileStorageTests( ...@@ -192,7 +194,6 @@ class FileStorageTests(
# Now the cached 'oid' value is ignored: verify that this is so. # Now the cached 'oid' value is ignored: verify that this is so.
import cPickle as pickle import cPickle as pickle
from ZODB.utils import z64 from ZODB.utils import z64
from ZODB.DB import DB
# Create some data. # Create some data.
db = DB(self._storage) db = DB(self._storage)
...@@ -281,7 +282,6 @@ class FileStorageTests( ...@@ -281,7 +282,6 @@ class FileStorageTests(
# global. # global.
import time import time
from ZODB.DB import DB
from ZODB.utils import U64, p64 from ZODB.utils import U64, p64
from ZODB.FileStorage.format import CorruptedError from ZODB.FileStorage.format import CorruptedError
...@@ -324,7 +324,6 @@ class FileStorageTests( ...@@ -324,7 +324,6 @@ class FileStorageTests(
self.fail("expected CorruptedError") self.fail("expected CorruptedError")
def check_record_iternext(self): def check_record_iternext(self):
from ZODB.DB import DB
db = DB(self._storage) db = DB(self._storage)
conn = db.open() conn = db.open()
...@@ -351,7 +350,6 @@ class FileStorageTests( ...@@ -351,7 +350,6 @@ class FileStorageTests(
else: else:
self.assertNotEqual(next_oid, None) self.assertNotEqual(next_oid, None)
class FileStorageRecoveryTest( class FileStorageRecoveryTest(
StorageTestBase.StorageTestBase, StorageTestBase.StorageTestBase,
RecoveryStorage.RecoveryStorage, RecoveryStorage.RecoveryStorage,
...@@ -409,8 +407,6 @@ def timestamp(minutes): ...@@ -409,8 +407,6 @@ def timestamp(minutes):
def testTimeTravelOnOpen(): def testTimeTravelOnOpen():
""" """
>>> from ZODB.FileStorage import FileStorage >>> from ZODB.FileStorage import FileStorage
>>> from ZODB.DB import DB
>>> import transaction
>>> from zope.testing.loggingsupport import InstalledHandler >>> from zope.testing.loggingsupport import InstalledHandler
Arrange to capture log messages -- they're an important part of Arrange to capture log messages -- they're an important part of
...@@ -484,6 +480,55 @@ def testTimeTravelOnOpen(): ...@@ -484,6 +480,55 @@ def testTimeTravelOnOpen():
>>> handler.uninstall() >>> handler.uninstall()
""" """
def lastInvalidations():
"""
The last invalidations method is used by a storage server to pupulate
it's data structure of recent invalidations. The lastInvalidations
method is passed a count and must return up to count number of the
most recent transactions.
We'll create a FileStorage and populate it with some data, keeping
track of the transactions along the way:
>>> fs = ZODB.FileStorage.FileStorage('t.fs', create=True)
>>> db = DB(fs)
>>> conn = db.open()
>>> from persistent.dict import PersistentDict
>>> last = []
>>> for i in range(100):
... conn.root()[i] = PersistentDict()
... transaction.commit()
... last.append(fs.lastTransaction())
Now, we can call lastInvalidations on it:
>>> invalidations = fs.lastInvalidations(10)
>>> [t for (t, oids) in invalidations] == last[-10:]
True
>>> from ZODB.utils import u64
>>> [[u64(oid) for (oid, version) in oids]
... for (i, oids) in invalidations]
... # doctest: +NORMALIZE_WHITESPACE
[[0L, 91L], [0L, 92L], [0L, 93L], [0L, 94L], [0L, 95L],
[0L, 96L], [0L, 97L], [0L, 98L], [0L, 99L], [0L, 100L]]
If we ask for more transactions than there are, we'll get as many as
there are:
>>> len(fs.lastInvalidations(1000))
101
Of course, calling lastInvalidations on an empty storage refturns no data:
>>> fs.close()
>>> fs = ZODB.FileStorage.FileStorage('t.fs', create=True)
>>> list(fs.lastInvalidations(10))
[]
"""
def test_suite(): def test_suite():
from zope.testing import doctest from zope.testing import doctest
...@@ -491,7 +536,8 @@ def test_suite(): ...@@ -491,7 +536,8 @@ def test_suite():
for klass in [FileStorageTests, Corruption.FileStorageCorruptTests, for klass in [FileStorageTests, Corruption.FileStorageCorruptTests,
FileStorageRecoveryTest, SlowFileStorageTest]: FileStorageRecoveryTest, SlowFileStorageTest]:
suite.addTest(unittest.makeSuite(klass, "check")) suite.addTest(unittest.makeSuite(klass, "check"))
suite.addTest(doctest.DocTestSuite()) suite.addTest(doctest.DocTestSuite(setUp=ZODB.tests.util.setUp,
tearDown=ZODB.tests.util.tearDown))
return suite return suite
if __name__=='__main__': if __name__=='__main__':
......
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