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
class NEOStorageError(POSException.StorageError): pass
class NEOStorageConflictError(NEOStorageError): pass
class NEOStorageNotFoundError(NEOStorageError): pass
class NEOStorageConnectionFailure(NEOStorageError): pass
......@@ -43,3 +43,52 @@ class BaseHandler(EventHandler):
else:
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 @@
import logging
from neo.client.handlers.handler import BaseHandler
from neo import protocol
from neo.client.handlers.handler import BaseHandler, AnswerBaseHandler
from neo.protocol import MASTER_NODE_TYPE, STORAGE_NODE_TYPE, CLIENT_NODE_TYPE, \
INVALID_UUID, RUNNING_STATE, TEMPORARILY_DOWN_STATE
from neo.node import MasterNode, StorageNode
......@@ -26,72 +25,9 @@ from neo.pt import MTPartitionTable as PartitionTable
from neo.util import dump
from neo import decorators
class PrimaryBaseHandler(BaseHandler):
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):
class PrimaryBootstrapHandler(AnswerBaseHandler):
""" 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):
app = self.app
app.trying_master_node = None
......@@ -185,21 +121,35 @@ class PrimaryBootstrapHandler(BaseHandler):
def handleAnswerNodeInformation(self, conn, packet, node_list):
pass
class PrimaryNotificationsHandler(PrimaryBaseHandler):
class PrimaryNotificationsHandler(BaseHandler):
""" Handler that process the notifications from the primary master """
def connectionClosed(self, conn):
logging.critical("connection to primary master node closed")
# Close connection
self._closePrimaryMasterConnection(conn)
BaseHandler.connectionClosed(self, conn)
app = self.app
if app.master_conn is not None:
assert conn is app.master_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):
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)
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)
def handleStopOperation(self, conn, packet):
......@@ -345,23 +295,9 @@ class PrimaryNotificationsHandler(PrimaryBaseHandler):
for queue in queue_set:
queue.put((conn, None))
class PrimaryAnswersHandler(PrimaryBaseHandler):
class PrimaryAnswersHandler(AnswerBaseHandler):
""" 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):
app = self.app
app.setTID(tid)
......
......@@ -17,11 +17,11 @@
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 ZODB.TimeStamp import TimeStamp
class StorageBaseHandler(BaseHandler):
class StorageEventHandler(BaseHandler):
def _dealWithStorageFailure(self, conn, node):
app = self.app
......@@ -45,26 +45,26 @@ class StorageBaseHandler(BaseHandler):
node = self.app.nm.getNodeByServer(conn.getAddress())
logging.info("connection to storage node %s closed", node.getServer())
self._dealWithStorageFailure(conn, node)
super(StorageBaseHandler, self).connectionClosed(conn)
super(StorageEventHandler, self).connectionClosed(conn)
def timeoutExpired(self, conn):
node = self.app.nm.getNodeByServer(conn.getAddress())
self._dealWithStorageFailure(conn, node)
super(StorageBaseHandler, self).timeoutExpired(conn)
super(StorageEventHandler, self).timeoutExpired(conn)
def peerBroken(self, conn):
node = self.app.nm.getNodeByServer(conn.getAddress())
self._dealWithStorageFailure(conn, node)
super(StorageBaseHandler, self).peerBroken(conn)
class StorageBootstrapHandler(StorageBaseHandler):
""" Handler used when connecting to a storage node """
super(StorageEventHandler, self).peerBroken(conn)
def connectionFailed(self, conn):
# Connection to a storage node failed
node = self.app.nm.getNodeByServer(conn.getAddress())
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):
app = self.app
......@@ -76,11 +76,7 @@ class StorageBootstrapHandler(StorageBaseHandler):
node = app.nm.getNodeByServer(conn.getAddress())
# It can be eiter a master node or a storage node
if node_type != STORAGE_NODE_TYPE:
conn.lock()
try:
conn.close()
finally:
conn.release()
conn.close()
return
if conn.getAddress() != (ip_address, port):
# The server address is different! Then why was
......@@ -88,17 +84,13 @@ class StorageBootstrapHandler(StorageBaseHandler):
logging.error('%s:%d is waiting for %s:%d',
conn.getAddress()[0], conn.getAddress()[1], ip_address, port)
app.nm.remove(node)
conn.lock()
try:
conn.close()
finally:
conn.release()
conn.close()
return
conn.setUUID(uuid)
node.setUUID(uuid)
class StorageAnswersHandler(StorageBaseHandler):
class StorageAnswersHandler(AnswerBaseHandler):
""" Handle all messages related to ZODB operations """
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