Commit 95543cf0 authored by Tim Peters's avatar Tim Peters

More cleanup, and a new problem. Notable:

Object.serialize_header():  Stopped stuffing the version string into this
notion of "a header".  Couldn't see any reason for it, and it was
surprising.

ClientCache.invalidate():  Added some asserts to clarify intended
preconditions.  Alas, my guess that it was intended that the passed-in
tid always be greater than the current tid turned out to trigger
errors.  This gets complicated, and hasn't been resolved yet.  The
problem is that the passed-in tid can be (in fact, always is) None
during cache verification.  The code here *really* doesn't seem to
be expecting that, and the on-disk cache file is clearly left in a
wrong state (if we repopulated the cache from the disk file, the
object would not look invalidated any more, it would look current).
Dumped in a giant stack trace so I don't forget this; sent email to
Jeremy asking if he recalls what the real intent was.
parent 81fccefb
...@@ -287,7 +287,6 @@ class ClientCache: ...@@ -287,7 +287,6 @@ class ClientCache:
# @param oid object id # @param oid object id
# @param version name of version to invalidate. # @param version name of version to invalidate.
# @param tid the id of the transaction that wrote a new revision of oid # @param tid the id of the transaction that wrote a new revision of oid
def invalidate(self, oid, version, tid): def invalidate(self, oid, version, tid):
if tid > self.fc.tid: if tid > self.fc.tid:
self.fc.settid(tid) self.fc.settid(tid)
...@@ -307,22 +306,72 @@ class ClientCache: ...@@ -307,22 +306,72 @@ class ClientCache:
self._trace(0x10, oid, version, tid) self._trace(0x10, oid, version, tid)
return return
cur_tid = self.current.pop(oid) cur_tid = self.current.pop(oid)
# XXX. During checkDisconnectedAbort, tid shows up as None here
# sometimes.
#
#if not cur_tid < tid:
# print "OUCH1", cur_tid, tid
# import traceback
# traceback.print_stack()
#assert cur_tid < tid
#
# The stack trace. oid is 1, version '', tid None:
#
# File "C:\Python23\lib\threading.py", line 436, in __bootstrap
# self.run()
# File "C:\Code\ZODB3.3\build\lib.win32-2.3\ZEO\zrpc\client.py", line 293, in run
# success = self.try_connecting(attempt_timeout)
# File "C:\Code\ZODB3.3\build\lib.win32-2.3\ZEO\zrpc\client.py", line 324, in try_connecting
# r = self._connect_wrappers(wrappers, deadline)
# File "C:\Code\ZODB3.3\build\lib.win32-2.3\ZEO\zrpc\client.py", line 385, in _connect_wrappers
# wrap.connect_procedure()
# File "C:\Code\ZODB3.3\build\lib.win32-2.3\ZEO\zrpc\client.py", line 471, in connect_procedure
# self.test_connection()
# File "C:\Code\ZODB3.3\build\lib.win32-2.3\ZEO\zrpc\client.py", line 495, in test_connection
# self.notify_client()
# File "C:\Code\ZODB3.3\build\lib.win32-2.3\ZEO\zrpc\client.py", line 507, in notify_client
# self.client.notifyConnected(self.conn)
# File "C:\Code\ZODB3.3\build\lib.win32-2.3\ZEO\ClientStorage.py", line 516, in notifyConnected
# self._wait_sync()
# File "C:\Code\ZODB3.3\build\lib.win32-2.3\ZEO\ClientStorage.py", line 370, in _wait_sync
# self._connection.pending(30)
# File "C:\Code\ZODB3.3\build\lib.win32-2.3\ZEO\zrpc\connection.py", line 546, in pending
# self.handle_read_event()
# File "C:\Python23\lib\asyncore.py", line 390, in handle_read_event
# self.handle_read()
# File "C:\Code\ZODB3.3\build\lib.win32-2.3\ZEO\zrpc\smac.py", line 219, in handle_read
# self.message_input(msg)
# File "C:\Code\ZODB3.3\build\lib.win32-2.3\ZEO\zrpc\connection.py", line 244, in message_input
# self.handle_request(msgid, flags, name, args)
# File "C:\Code\ZODB3.3\build\lib.win32-2.3\ZEO\zrpc\connection.py", line 269, in handle_request
# ret = meth(*args)
# File "C:\Code\ZODB3.3\build\lib.win32-2.3\ZEO\tests\ConnectionTests.py", line 63, in endVerify
# ClientStorage.endVerify(self)
# File "C:\Code\ZODB3.3\build\lib.win32-2.3\ZEO\ClientStorage.py", line 1093, in endVerify
# self._process_invalidations(InvalidationLogIterator(f))
# File "C:\Code\ZODB3.3\build\lib.win32-2.3\ZEO\ClientStorage.py", line 1074, in _process_invalidations
# self._cache.invalidate(oid, version, tid)
# File "C:\Code\ZODB3.3\build\lib.win32-2.3\ZEO\cache.py", line 313, in invalidate
# traceback.print_stack()
# XXX Want to fetch object without marking it as accessed # XXX Want to fetch object without marking it as accessed
o = self.fc.access((oid, cur_tid)) o = self.fc.access((oid, cur_tid))
assert o is not None
assert o.end_tid is None # i.e., o was current
if o is None: if o is None:
# XXX is this possible? # XXX is this possible? (doubt it; added an assert just above)
return None return None
o.end_tid = tid o.end_tid = tid
self.fc.update(o) self.fc.update(o) # record the new end_tid on disk
self._trace(0x1C, oid, version, tid) self._trace(0x1C, oid, version, tid)
L = self.noncurrent.setdefault(oid, []) L = self.noncurrent.setdefault(oid, [])
bisect.insort_left(L, (cur_tid, tid)) bisect.insort_left(L, (cur_tid, tid))
## ##
# Return the number of object revisions in the cache. # Return the number of object revisions in the cache.
#
# XXX just return len(self.cache)? # XXX just return len(self.cache)?
def __len__(self): def __len__(self):
n = len(self.current) + len(self.version) n = len(self.current) + len(self.version)
if self.noncurrent: if self.noncurrent:
...@@ -488,25 +537,33 @@ class Object(object): ...@@ -488,25 +537,33 @@ class Object(object):
if data is not None: if data is not None:
self.size = self.TOTAL_FIXED_SIZE + len(data) + len(version) self.size = self.TOTAL_FIXED_SIZE + len(data) + len(version)
##
# Return the fixed-sized serialization header as a string: pack end_tid,
# and the lengths of the .version and .data members.
def get_header(self): def get_header(self):
# Return just the fixed-size serialization header.
return struct.pack(self.fmt, return struct.pack(self.fmt,
self.end_tid or z64, self.end_tid or z64,
len(self.version), len(self.version),
len(self.data)) len(self.data))
##
# Write the serialized representation of self to file f, at its current
# position.
def serialize(self, f): def serialize(self, f):
# Write standard form of Object to file f, at its current offset.
f.writelines([self.get_header(), f.writelines([self.get_header(),
self.version, self.version,
self.data, self.data,
self.key[0]]) self.key[0]])
##
# Write the fixed-size header for self, to file f at its current position.
# The only real use for this is when the current revision of an object
# in cache is invalidated. Then the end_tid field gets set to the tid
# of the transaction that caused the invalidation.
def serialize_header(self, f): def serialize_header(self, f):
# Write the fixed-sized serialization header, + the version. f.write(self.get_header())
# Why is the version part of this?
f.writelines([self.get_header(), self.version])
##
# fromFile is a class constructor, unserializing an Object from the # fromFile is a class constructor, unserializing an Object from the
# current position in file f. Exclusive access to f for the duration # current position in file f. Exclusive access to f for the duration
# is assumed. The key is a (oid, start_tid) pair, and the oid must # is assumed. The key is a (oid, start_tid) pair, and the oid must
...@@ -955,7 +1012,9 @@ class FileCache(object): ...@@ -955,7 +1012,9 @@ class FileCache(object):
# Update on-disk representation of Object obj. # Update on-disk representation of Object obj.
# #
# This method should be called when the object header is modified. # This method should be called when the object header is modified.
# obj must be in the cache. # obj must be in the cache. The only real use for this is during
# invalidation, to set the end_tid field on a revision that was current
# (and so had an end_tid of None, but no longer does).
def update(self, obj): def update(self, obj):
e = self.key2entry[obj.key] e = self.key2entry[obj.key]
self.f.seek(e.offset + OBJECT_HEADER_SIZE) self.f.seek(e.offset + OBJECT_HEADER_SIZE)
......
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