Commit 4c61df28 authored by Vincent Pelletier's avatar Vincent Pelletier

Big rework on client handlers:

- Split completely expected packet handlers from asynchronous event handlers
- Remove NEOStorageConnectionFailure exception and replace it by a more general "ConnectionClosed" exception, purely internal to client app.
- Modify waitMessage to redirect to a handler based on remote peer type
- Fix multiple code paths taken when master doesn't accept connections (or closes them) while trying to conenct to it.
Also:
- remove unused imports found by pyflakes


git-svn-id: https://svn.erp5.org/repos/neo/branches/prototype3@773 71dcc9de-d417-0410-9af5-da40c76e7ee4
parent a0f2936c
This diff is collapsed.
...@@ -19,4 +19,3 @@ from ZODB import POSException ...@@ -19,4 +19,3 @@ from ZODB import POSException
class NEOStorageError(POSException.StorageError): pass class NEOStorageError(POSException.StorageError): pass
class NEOStorageConflictError(NEOStorageError): pass class NEOStorageConflictError(NEOStorageError): pass
class NEOStorageNotFoundError(NEOStorageError): pass class NEOStorageNotFoundError(NEOStorageError): pass
class NEOStorageConnectionFailure(NEOStorageError): pass
...@@ -43,3 +43,52 @@ class BaseHandler(EventHandler): ...@@ -43,3 +43,52 @@ class BaseHandler(EventHandler):
else: else:
queue.put((conn, packet)) queue.put((conn, packet))
def _notifyQueues(self, conn):
"""
Put fake packets to task queues so that threads waiting for an
answer get notified of the disconnection.
"""
queue_set = set()
conn_id = id(conn)
for key in self.dispatcher.message_table.keys():
if conn_id == key[0]:
queue = self.dispatcher.message_table.pop(key)
queue_set.add(queue)
for queue in queue_set:
queue.put((conn, None))
def connectionClosed(self, conn):
super(BaseHandler, self).connectionClosed(conn)
self._notifyQueues(conn)
def timeoutExpired(self, conn):
super(BaseHandler, self).timeoutExpired(conn)
conn.lock()
try:
conn.close()
finally:
conn.release()
self._notifyQueues(conn)
def connectionFailed(self, conn):
super(BaseHandler, self).connectionFailed(conn)
self._notifyQueues(conn)
def unexpectedInAnswerHandler(*args, **kw):
raise Exception('Unexpected event in an answer handler')
class AnswerBaseHandler(EventHandler):
def __init__(self, app):
self.app = app
super(AnswerBaseHandler, self).__init__()
connectionStarted = unexpectedInAnswerHandler
connectionCompleted = unexpectedInAnswerHandler
connectionFailed = unexpectedInAnswerHandler
connectionAccepted = unexpectedInAnswerHandler
timeoutExpired = unexpectedInAnswerHandler
connectionClosed = unexpectedInAnswerHandler
packetReceived = unexpectedInAnswerHandler
peerBroken = unexpectedInAnswerHandler
...@@ -17,8 +17,7 @@ ...@@ -17,8 +17,7 @@
import logging import logging
from neo.client.handlers.handler import BaseHandler from neo.client.handlers.handler import BaseHandler, AnswerBaseHandler
from neo import protocol
from neo.protocol import MASTER_NODE_TYPE, STORAGE_NODE_TYPE, CLIENT_NODE_TYPE, \ from neo.protocol import MASTER_NODE_TYPE, STORAGE_NODE_TYPE, CLIENT_NODE_TYPE, \
INVALID_UUID, RUNNING_STATE, TEMPORARILY_DOWN_STATE INVALID_UUID, RUNNING_STATE, TEMPORARILY_DOWN_STATE
from neo.node import MasterNode, StorageNode from neo.node import MasterNode, StorageNode
...@@ -26,72 +25,9 @@ from neo.pt import MTPartitionTable as PartitionTable ...@@ -26,72 +25,9 @@ from neo.pt import MTPartitionTable as PartitionTable
from neo.util import dump from neo.util import dump
from neo import decorators from neo import decorators
class PrimaryBaseHandler(BaseHandler): class PrimaryBootstrapHandler(AnswerBaseHandler):
def _closePrimaryMasterConnection(self, conn):
"""
This method is not part of EvenHandler API.
"""
app = self.app
if app.master_conn is not None:
assert conn is app.master_conn
app.master_conn.lock()
try:
app.master_conn.close()
finally:
app.master_conn.release()
app.master_conn = None
app.primary_master_node = None
class PrimaryBootstrapHandler(BaseHandler):
""" Bootstrap handler used when looking for the primary master """ """ Bootstrap handler used when looking for the primary master """
def connectionCompleted(self, conn):
app = self.app
if app.trying_master_node is None:
# Should not happen.
raise RuntimeError('connection completed while not trying to connect')
super(PrimaryBootstrapHandler, self).connectionCompleted(conn)
def connectionFailed(self, conn):
app = self.app
if app.trying_master_node is None:
# Should not happen.
raise RuntimeError('connection failed while not trying to connect')
if app.trying_master_node is app.primary_master_node:
# Tried to connect to a primary master node and failed.
# So this would effectively mean that it is dead.
app.primary_master_node = None
app.trying_master_node = None
super(PrimaryBootstrapHandler, self).connectionFailed(conn)
def timeoutExpired(self, conn):
app = self.app
if app.trying_master_node is app.primary_master_node:
# If a primary master node timeouts, I should not rely on it.
app.primary_master_node = None
app.trying_master_node = None
super(PrimaryBootstrapHandler, self).timeoutExpired(conn)
def connectionClosed(self, conn):
app = self.app
if app.trying_master_node is app.primary_master_node:
# If a primary master node closes, I should not rely on it.
app.primary_master_node = None
app.trying_master_node = None
super(PrimaryBootstrapHandler, self).connectionClosed(conn)
def peerBroken(self, conn):
app = self.app
if app.trying_master_node is app.primary_master_node:
# If a primary master node gets broken, I should not rely
# on it.
app.primary_master_node = None
app.trying_master_node = None
super(PrimaryBootstrapHandler, self).peerBroken(conn)
def handleNotReady(self, conn, packet, message): def handleNotReady(self, conn, packet, message):
app = self.app app = self.app
app.trying_master_node = None app.trying_master_node = None
...@@ -185,21 +121,35 @@ class PrimaryBootstrapHandler(BaseHandler): ...@@ -185,21 +121,35 @@ class PrimaryBootstrapHandler(BaseHandler):
def handleAnswerNodeInformation(self, conn, packet, node_list): def handleAnswerNodeInformation(self, conn, packet, node_list):
pass pass
class PrimaryNotificationsHandler(PrimaryBaseHandler): class PrimaryNotificationsHandler(BaseHandler):
""" Handler that process the notifications from the primary master """ """ Handler that process the notifications from the primary master """
def connectionClosed(self, conn): def connectionClosed(self, conn):
logging.critical("connection to primary master node closed") app = self.app
# Close connection if app.master_conn is not None:
self._closePrimaryMasterConnection(conn) assert conn is app.master_conn
BaseHandler.connectionClosed(self, conn) logging.critical("connection to primary master node closed")
conn.lock()
try:
app.master_conn.close()
finally:
conn.release()
app.master_conn = None
app.primary_master_node = None
super(PrimaryNotificationsHandler, self).connectionClosed(conn)
def timeoutExpired(self, conn): def timeoutExpired(self, conn):
logging.critical("connection timeout to primary master node expired") app = self.app
if app.master_conn is not None:
assert conn is app.master_conn
logging.critical("connection timeout to primary master node expired")
BaseHandler.timeoutExpired(self, conn) BaseHandler.timeoutExpired(self, conn)
def peerBroken(self, conn): def peerBroken(self, conn):
logging.critical("primary master node is broken") app = self.app
if app.master_conn is not None:
assert conn is app.master_conn
logging.critical("primary master node is broken")
BaseHandler.peerBroken(self, conn) BaseHandler.peerBroken(self, conn)
def handleStopOperation(self, conn, packet): def handleStopOperation(self, conn, packet):
...@@ -345,23 +295,9 @@ class PrimaryNotificationsHandler(PrimaryBaseHandler): ...@@ -345,23 +295,9 @@ class PrimaryNotificationsHandler(PrimaryBaseHandler):
for queue in queue_set: for queue in queue_set:
queue.put((conn, None)) queue.put((conn, None))
class PrimaryAnswersHandler(PrimaryBaseHandler): class PrimaryAnswersHandler(AnswerBaseHandler):
""" Handle that process expected packets from the primary master """ """ Handle that process expected packets from the primary master """
def connectionClosed(self, conn):
logging.critical("connection to primary master node closed")
# Close connection
self._closePrimaryMasterConnection(conn)
super(PrimaryAnswersHandler, self).connectionClosed(conn)
def timeoutExpired(self, conn):
logging.critical("connection timeout to primary master node expired")
super(PrimaryAnswersHandler, self).timeoutExpired(conn)
def peerBroken(self, conn):
logging.critical("primary master node is broken")
super(PrimaryAnswersHandler, self).peerBroken(conn)
def handleAnswerNewTID(self, conn, packet, tid): def handleAnswerNewTID(self, conn, packet, tid):
app = self.app app = self.app
app.setTID(tid) app.setTID(tid)
......
...@@ -17,11 +17,11 @@ ...@@ -17,11 +17,11 @@
import logging import logging
from neo.client.handlers.handler import BaseHandler from neo.client.handlers.handler import BaseHandler, AnswerBaseHandler
from neo.protocol import STORAGE_NODE_TYPE from neo.protocol import STORAGE_NODE_TYPE
from ZODB.TimeStamp import TimeStamp from ZODB.TimeStamp import TimeStamp
class StorageBaseHandler(BaseHandler): class StorageEventHandler(BaseHandler):
def _dealWithStorageFailure(self, conn, node): def _dealWithStorageFailure(self, conn, node):
app = self.app app = self.app
...@@ -45,26 +45,26 @@ class StorageBaseHandler(BaseHandler): ...@@ -45,26 +45,26 @@ class StorageBaseHandler(BaseHandler):
node = self.app.nm.getNodeByServer(conn.getAddress()) node = self.app.nm.getNodeByServer(conn.getAddress())
logging.info("connection to storage node %s closed", node.getServer()) logging.info("connection to storage node %s closed", node.getServer())
self._dealWithStorageFailure(conn, node) self._dealWithStorageFailure(conn, node)
super(StorageBaseHandler, self).connectionClosed(conn) super(StorageEventHandler, self).connectionClosed(conn)
def timeoutExpired(self, conn): def timeoutExpired(self, conn):
node = self.app.nm.getNodeByServer(conn.getAddress()) node = self.app.nm.getNodeByServer(conn.getAddress())
self._dealWithStorageFailure(conn, node) self._dealWithStorageFailure(conn, node)
super(StorageBaseHandler, self).timeoutExpired(conn) super(StorageEventHandler, self).timeoutExpired(conn)
def peerBroken(self, conn): def peerBroken(self, conn):
node = self.app.nm.getNodeByServer(conn.getAddress()) node = self.app.nm.getNodeByServer(conn.getAddress())
self._dealWithStorageFailure(conn, node) self._dealWithStorageFailure(conn, node)
super(StorageBaseHandler, self).peerBroken(conn) super(StorageEventHandler, self).peerBroken(conn)
class StorageBootstrapHandler(StorageBaseHandler):
""" Handler used when connecting to a storage node """
def connectionFailed(self, conn): def connectionFailed(self, conn):
# Connection to a storage node failed # Connection to a storage node failed
node = self.app.nm.getNodeByServer(conn.getAddress()) node = self.app.nm.getNodeByServer(conn.getAddress())
self._dealWithStorageFailure(conn, node) self._dealWithStorageFailure(conn, node)
super(StorageBootstrapHandler, self).connectionFailed(conn) super(StorageEventHandler, self).connectionFailed(conn)
class StorageBootstrapHandler(AnswerBaseHandler):
""" Handler used when connecting to a storage node """
def handleNotReady(self, conn, packet, message): def handleNotReady(self, conn, packet, message):
app = self.app app = self.app
...@@ -76,11 +76,7 @@ class StorageBootstrapHandler(StorageBaseHandler): ...@@ -76,11 +76,7 @@ class StorageBootstrapHandler(StorageBaseHandler):
node = app.nm.getNodeByServer(conn.getAddress()) node = app.nm.getNodeByServer(conn.getAddress())
# It can be eiter a master node or a storage node # It can be eiter a master node or a storage node
if node_type != STORAGE_NODE_TYPE: if node_type != STORAGE_NODE_TYPE:
conn.lock() conn.close()
try:
conn.close()
finally:
conn.release()
return return
if conn.getAddress() != (ip_address, port): if conn.getAddress() != (ip_address, port):
# The server address is different! Then why was # The server address is different! Then why was
...@@ -88,17 +84,13 @@ class StorageBootstrapHandler(StorageBaseHandler): ...@@ -88,17 +84,13 @@ class StorageBootstrapHandler(StorageBaseHandler):
logging.error('%s:%d is waiting for %s:%d', logging.error('%s:%d is waiting for %s:%d',
conn.getAddress()[0], conn.getAddress()[1], ip_address, port) conn.getAddress()[0], conn.getAddress()[1], ip_address, port)
app.nm.remove(node) app.nm.remove(node)
conn.lock() conn.close()
try:
conn.close()
finally:
conn.release()
return return
conn.setUUID(uuid) conn.setUUID(uuid)
node.setUUID(uuid) node.setUUID(uuid)
class StorageAnswersHandler(StorageBaseHandler): class StorageAnswersHandler(AnswerBaseHandler):
""" Handle all messages related to ZODB operations """ """ Handle all messages related to ZODB operations """
def handleAnswerObject(self, conn, packet, oid, start_serial, end_serial, def handleAnswerObject(self, conn, packet, oid, start_serial, end_serial,
......
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