[Zodb-checkins] SVN: ZODB/trunk/src/ZEO/ Bug Fixed: Failures in tpc_finish of client-storages weren't handled
Jim Fulton
jim at zope.com
Tue Sep 23 10:28:07 EDT 2008
Log message for revision 91397:
Bug Fixed: Failures in tpc_finish of client-storages weren't handled
correctly, leaving the client storage in an inconsistent state.
Changed:
U ZODB/trunk/src/ZEO/ClientStorage.py
U ZODB/trunk/src/ZEO/tests/ConnectionTests.py
U ZODB/trunk/src/ZEO/tests/testZEO.py
-=-
Modified: ZODB/trunk/src/ZEO/ClientStorage.py
===================================================================
--- ZODB/trunk/src/ZEO/ClientStorage.py 2008-09-23 14:28:03 UTC (rev 91396)
+++ ZODB/trunk/src/ZEO/ClientStorage.py 2008-09-23 14:28:06 UTC (rev 91397)
@@ -1075,20 +1075,28 @@
# tpc_cond condition variable prevents more than one
# thread from calling tpc_finish() at a time.
tid = self._server.tpc_finish(id(txn))
- self._lock.acquire() # for atomic processing of invalidations
+
try:
- self._update_cache(tid)
- if f is not None:
- f(tid)
- finally:
- self._lock.release()
+ self._lock.acquire() # for atomic processing of invalidations
+ try:
+ self._update_cache(tid)
+ if f is not None:
+ f(tid)
+ finally:
+ self._lock.release()
- r = self._check_serials()
- assert r is None or len(r) == 0, "unhandled serialnos: %s" % r
+ r = self._check_serials()
+ assert r is None or len(r) == 0, "unhandled serialnos: %s" % r
+ except:
+ # The server successfully committed. If we get a failure
+ # here, our own state will be in question, so reconnect.
+ self._connection.close()
+ raise
+
+ self.end_transaction()
finally:
self._load_lock.release()
self._iterator_gc()
- self.end_transaction()
def _update_cache(self, tid):
"""Internal helper to handle objects modified by a transaction.
Modified: ZODB/trunk/src/ZEO/tests/ConnectionTests.py
===================================================================
--- ZODB/trunk/src/ZEO/tests/ConnectionTests.py 2008-09-23 14:28:03 UTC (rev 91396)
+++ ZODB/trunk/src/ZEO/tests/ConnectionTests.py 2008-09-23 14:28:06 UTC (rev 91397)
@@ -1075,6 +1075,8 @@
self.assert_(storage.is_connected())
# We expect finish to fail.
self.assertRaises(ClientDisconnected, storage.tpc_finish, t)
+ storage.tpc_abort(t)
+
# Now we think we've committed the second transaction, but we really
# haven't. A third one should produce a POSKeyError on the server,
# which manifests as a ConflictError on the client.
Modified: ZODB/trunk/src/ZEO/tests/testZEO.py
===================================================================
--- ZODB/trunk/src/ZEO/tests/testZEO.py 2008-09-23 14:28:03 UTC (rev 91396)
+++ ZODB/trunk/src/ZEO/tests/testZEO.py 2008-09-23 14:28:06 UTC (rev 91397)
@@ -1006,7 +1006,105 @@
"""
+def tpc_finish_error():
+ r"""Server errors in tpc_finish weren't handled properly.
+ >>> import ZEO.ClientStorage
+
+ >>> class Connection:
+ ... def __init__(self, client):
+ ... self.client = client
+ ... def get_addr(self):
+ ... return 'server'
+ ... def is_async(self):
+ ... return True
+ ... def register_object(self, ob):
+ ... pass
+ ... def close(self):
+ ... print 'connection closed'
+
+ >>> class ConnectionManager:
+ ... def __init__(self, addr, client, tmin, tmax):
+ ... self.client = client
+ ... def connect(self, sync=1):
+ ... self.client.notifyConnected(Connection(self.client))
+
+ >>> class StorageServer:
+ ... should_fail = True
+ ... def __init__(self, conn):
+ ... self.conn = conn
+ ... self.t = None
+ ... def get_info(self):
+ ... return {}
+ ... def endZeoVerify(self):
+ ... self.conn.client.endVerify()
+ ... def lastTransaction(self):
+ ... return '\0'*8
+ ... def tpc_begin(self, t, *args):
+ ... if self.t is not None:
+ ... raise TypeError('already trans')
+ ... self.t = t
+ ... print 'begin', args
+ ... def vote(self, t):
+ ... if self.t != t:
+ ... raise TypeError('bad trans')
+ ... print 'vote'
+ ... def tpc_finish(self, *args):
+ ... if self.should_fail:
+ ... raise TypeError()
+ ... print 'finish'
+ ... def tpc_abort(self, t):
+ ... if self.t != t:
+ ... raise TypeError('bad trans')
+ ... self.t = None
+ ... print 'abort'
+ ... def iterator_gc(*args):
+ ... pass
+
+ >>> class ClientStorage(ZEO.ClientStorage.ClientStorage):
+ ... ConnectionManagerClass = ConnectionManager
+ ... StorageServerStubClass = StorageServer
+
+ >>> class Transaction:
+ ... user = 'test'
+ ... description = ''
+ ... _extension = {}
+
+ >>> cs = ClientStorage(('', ''))
+ >>> t1 = Transaction()
+ >>> cs.tpc_begin(t1)
+ begin ('test', '', {}, None, ' ')
+
+ >>> cs.tpc_vote(t1)
+ vote
+
+ >>> cs.tpc_finish(t1)
+ Traceback (most recent call last):
+ ...
+ TypeError
+
+ >>> cs.tpc_abort(t1)
+ abort
+
+ >>> t2 = Transaction()
+ >>> cs.tpc_begin(t2)
+ begin ('test', '', {}, None, ' ')
+ >>> cs.tpc_vote(t2)
+ vote
+
+ If client storage has an internal error after the storage finish
+ succeeeds, it will close the connection, which will force a
+ restart and reverification.
+
+ >>> StorageServer.should_fail = False
+ >>> cs._update_cache = lambda : None
+ >>> try: cs.tpc_finish(t2)
+ ... except: pass
+ ... else: print "Should have failed"
+ finish
+ connection closed
+ """
+
test_classes = [FileStorageTests, FileStorageRecoveryTests,
MappingStorageTests, DemoStorageTests,
BlobAdaptedFileStorageTests, BlobWritableCacheTests,
More information about the Zodb-checkins
mailing list