Commit 15472c62 authored by Julien Muchembled's avatar Julien Muchembled

storage: fix crash when a client loses connection to the master just before voting

This is similar to commit 7aecdada
and for completeness, we also protect unlock the same way.
parent 298cb6c4
...@@ -647,6 +647,10 @@ class Application(ThreadedApplication): ...@@ -647,6 +647,10 @@ class Application(ThreadedApplication):
txn_context['voted'] = None txn_context['voted'] = None
# We must not go further if connection to master was lost since # We must not go further if connection to master was lost since
# tpc_begin, to lower the probability of failing during tpc_finish. # tpc_begin, to lower the probability of failing during tpc_finish.
# IDEA: We can improve in 2 opposite directions:
# - In the case of big transactions, it would be useful to
# also detect failures earlier.
# - If possible, recover from master failure.
if 'error' in txn_context: if 'error' in txn_context:
raise NEOStorageError(txn_context['error']) raise NEOStorageError(txn_context['error'])
return result return result
......
...@@ -162,7 +162,10 @@ class TransactionManager(object): ...@@ -162,7 +162,10 @@ class TransactionManager(object):
Store transaction information received from client node Store transaction information received from client node
""" """
logging.debug('Vote TXN %s', dump(ttid)) logging.debug('Vote TXN %s', dump(ttid))
transaction = self._transaction_dict[ttid] try:
transaction = self._transaction_dict[ttid]
except KeyError:
raise ProtocolError("unknown ttid %s" % dump(ttid))
object_list = transaction.getObjectList() object_list = transaction.getObjectList()
if txn_info: if txn_info:
user, desc, ext, oid_list = txn_info user, desc, ext, oid_list = txn_info
...@@ -195,7 +198,10 @@ class TransactionManager(object): ...@@ -195,7 +198,10 @@ class TransactionManager(object):
""" """
Unlock transaction Unlock transaction
""" """
tid = self._transaction_dict[ttid].getTID() try:
tid = self._transaction_dict[ttid].getTID()
except KeyError:
raise ProtocolError("unknown ttid %s" % dump(ttid))
logging.debug('Unlock TXN %s (ttid=%s)', dump(tid), dump(ttid)) logging.debug('Unlock TXN %s (ttid=%s)', dump(tid), dump(ttid))
dm = self._app.dm dm = self._app.dm
dm.unlockTransaction(tid, ttid) dm.unlockTransaction(tid, ttid)
......
...@@ -1135,6 +1135,25 @@ class Test(NEOThreadedTest): ...@@ -1135,6 +1135,25 @@ class Test(NEOThreadedTest):
finally: finally:
cluster.stop() cluster.stop()
def testMasterFailureBeforeVote(self):
def waitStoreResponses(orig, *args):
result = orig(*args)
m2c, = cluster.master.getConnectionList(orig.__self__)
m2c.close()
self.tic()
return result
cluster = NEOCluster(storage_count=2, partitions=2)
try:
cluster.start()
t, c = cluster.getTransaction()
c.root()['x'] = PCounter() # 1 store() to each storage
with Patch(cluster.client, waitStoreResponses=waitStoreResponses):
self.assertRaises(POSException.StorageError, t.commit)
self.assertEqual(cluster.neoctl.getClusterState(),
ClusterStates.RUNNING)
finally:
cluster.stop()
def testEmptyTransaction(self): def testEmptyTransaction(self):
cluster = NEOCluster() cluster = NEOCluster()
try: try:
......
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