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

Merged bug fix, 82895, from trunk:

When using ZEO Client Storages, Errors occured when trying to store
objects too big to fit in the ZEO cache file.
parent 098d18d0
Whats new in ZODB 3.8.1
=======================
Bugs Fixed:
- When using ZEO Client Storages, Errors occured when trying to store
objects too big to fit in the ZEO cache file.
What's new on ZODB 3.8.0 What's new on ZODB 3.8.0
======================== ========================
......
...@@ -269,6 +269,8 @@ class ClientCache(object): ...@@ -269,6 +269,8 @@ class ClientCache(object):
if self.version[oid] != (version, start_tid): if self.version[oid] != (version, start_tid):
raise ValueError("data already exists for version %r" raise ValueError("data already exists for version %r"
% self.version[oid][0]) % self.version[oid][0])
if not self.fc.add(o):
return # too large
self.version[oid] = version, start_tid self.version[oid] = version, start_tid
self._trace(0x50, oid, version, start_tid, dlen=len(data)) self._trace(0x50, oid, version, start_tid, dlen=len(data))
else: else:
...@@ -280,6 +282,8 @@ class ClientCache(object): ...@@ -280,6 +282,8 @@ class ClientCache(object):
"already have current data for oid") "already have current data for oid")
else: else:
return return
if not self.fc.add(o):
return # too large
self.current[oid] = start_tid self.current[oid] = start_tid
self._trace(0x52, oid, version, start_tid, dlen=len(data)) self._trace(0x52, oid, version, start_tid, dlen=len(data))
else: else:
...@@ -287,10 +291,11 @@ class ClientCache(object): ...@@ -287,10 +291,11 @@ class ClientCache(object):
p = start_tid, end_tid p = start_tid, end_tid
if p in L: if p in L:
return # duplicate store return # duplicate store
if not self.fc.add(o):
return # too large
bisect.insort_left(L, p) bisect.insort_left(L, p)
self._trace(0x54, oid, version, start_tid, end_tid, self._trace(0x54, oid, version, start_tid, end_tid,
dlen=len(data)) dlen=len(data))
self.fc.add(o)
## ##
# Remove all knowledge of noncurrent revisions of oid, both in # Remove all knowledge of noncurrent revisions of oid, both in
...@@ -960,7 +965,8 @@ class FileCache(object): ...@@ -960,7 +965,8 @@ class FileCache(object):
## ##
# Add Object object to the cache. This may evict existing objects, to # Add Object object to the cache. This may evict existing objects, to
# make room (and almost certainly will, in steady state once the cache # make room (and almost certainly will, in steady state once the cache
# is first full). The object must not already be in the cache. # is first full). The object must not already be in the cache. If the
# object is too large for the cache, False is returned, otherwise True.
def add(self, object): def add(self, object):
size = OBJECT_HEADER_SIZE + object.size size = OBJECT_HEADER_SIZE + object.size
# A number of cache simulation experiments all concluded that the # A number of cache simulation experiments all concluded that the
...@@ -968,7 +974,7 @@ class FileCache(object): ...@@ -968,7 +974,7 @@ class FileCache(object):
# objects simply weren't cached. For now, we ignore the request # objects simply weren't cached. For now, we ignore the request
# only if the entire cache file is too small to hold the object. # only if the entire cache file is too small to hold the object.
if size > self.maxsize - ZEC3_HEADER_SIZE: if size > self.maxsize - ZEC3_HEADER_SIZE:
return return False
assert object.key not in self.key2entry assert object.key not in self.key2entry
assert len(object.key[0]) == 8 assert len(object.key[0]) == 8
...@@ -979,6 +985,7 @@ class FileCache(object): ...@@ -979,6 +985,7 @@ class FileCache(object):
available = self._makeroom(size) available = self._makeroom(size)
self._writeobj(object, available) self._writeobj(object, available)
return True
## ##
# Evict the object represented by Entry `e` from the cache, freeing # Evict the object represented by Entry `e` from the cache, freeing
......
...@@ -152,5 +152,36 @@ class CacheTests(unittest.TestCase): ...@@ -152,5 +152,36 @@ class CacheTests(unittest.TestCase):
eq(copy.current, self.cache.current) eq(copy.current, self.cache.current)
eq(copy.noncurrent, self.cache.noncurrent) eq(copy.noncurrent, self.cache.noncurrent)
def testCurrentObjectLargerThanCache(self):
if self.cache.path:
os.remove(self.cache.path)
cache = ZEO.cache.ClientCache(size=50)
cache.open()
# We store an object that is a bit larger than the cache can handle.
cache.store(n1, '', n2, None, "x"*64)
# We can see that it was not stored.
self.assertEquals(None, self.cache.load(n1))
# If an object cannot be stored in the cache, it must not be
# recorded as current.
self.assert_(n1 not in self.cache.current)
# Regression test: invalidation must still work.
cache.invalidate(n1, '', n2)
def testOldObjectLargerThanCache(self):
if self.cache.path:
os.remove(self.cache.path)
cache = ZEO.cache.ClientCache(size=50)
cache.open()
# We store an object that is a bit larger than the cache can handle.
cache.store(n1, '', n2, n3, "x"*64)
# We can see that it was not stored.
self.assertEquals(None, self.cache.load(n1))
# If an object cannot be stored in the cache, it must not be
# recorded as non-current.
self.assert_((n2, n3) not in cache.noncurrent[n1])
def test_suite(): def test_suite():
return unittest.makeSuite(CacheTests) return unittest.makeSuite(CacheTests)
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