Commit 13c1b2d3 authored by Jeremy Hylton's avatar Jeremy Hylton

Rationalize disconnected exceptions so there is one root exception.

ZEO.Exceptions.ClientDisconnected will always be raised when a client
is disconnected.  There's also a subclass of this exception in
ZEO.zrpc.error so that it's possible to distinguish whether the error
occurred in the RPC layer or at the storage layer.
parent c17b365c
...@@ -16,15 +16,8 @@ ...@@ -16,15 +16,8 @@
Public contents of this module: Public contents of this module:
ClientStorage -- the main class, implementing the Storage API ClientStorage -- the main class, implementing the Storage API
ClientStorageError -- exception raised by ClientStorage
UnrecognizedResult -- exception raised by ClientStorage
ClientDisconnected -- exception raised by ClientStorage
""" """
# XXX TO DO
# set self._storage = stub later, in endVerify
# if wait is given, wait until verify is complete
import cPickle import cPickle
import os import os
import socket import socket
...@@ -35,7 +28,8 @@ import types ...@@ -35,7 +28,8 @@ import types
from ZEO import ClientCache, ServerStub from ZEO import ClientCache, ServerStub
from ZEO.TransactionBuffer import TransactionBuffer from ZEO.TransactionBuffer import TransactionBuffer
from ZEO.Exceptions import Disconnected from ZEO.Exceptions \
import ClientStorageError, UnrecognizedResult, ClientDisconnected
from ZEO.zrpc.client import ConnectionManager from ZEO.zrpc.client import ConnectionManager
from ZODB import POSException from ZODB import POSException
...@@ -50,15 +44,6 @@ try: ...@@ -50,15 +44,6 @@ try:
except ImportError: except ImportError:
ResolvedSerial = 'rs' ResolvedSerial = 'rs'
class ClientStorageError(POSException.StorageError):
"""An error occured in the ZEO Client Storage."""
class UnrecognizedResult(ClientStorageError):
"""A server call returned an unrecognized result."""
class ClientDisconnected(ClientStorageError, Disconnected):
"""The database storage is disconnected from the storage."""
def tid2time(tid): def tid2time(tid):
return str(TimeStamp(tid)) return str(TimeStamp(tid))
...@@ -303,7 +288,7 @@ class ClientStorage: ...@@ -303,7 +288,7 @@ class ClientStorage:
self._ready.wait(30) self._ready.wait(30)
if self._ready.isSet(): if self._ready.isSet():
break break
log2(INFO, "Waiting to connect to server") log2(INFO, "Wait for cache verification to finish")
else: else:
# If there is no mainloop running, this code needs # If there is no mainloop running, this code needs
# to call poll() to cause asyncore to handle events. # to call poll() to cause asyncore to handle events.
...@@ -317,7 +302,7 @@ class ClientStorage: ...@@ -317,7 +302,7 @@ class ClientStorage:
cn.pending(30) cn.pending(30)
if self._ready.isSet(): if self._ready.isSet():
break break
log2(INFO, "Waiting to connect to server") log2(INFO, "Wait for cache verification to finish")
def close(self): def close(self):
"""Storage API: finalize the storage, releasing external resources.""" """Storage API: finalize the storage, releasing external resources."""
......
...@@ -13,6 +13,14 @@ ...@@ -13,6 +13,14 @@
############################################################################## ##############################################################################
"""Exceptions for ZEO.""" """Exceptions for ZEO."""
class Disconnected(Exception): from ZODB.POSException import StorageError
"""Exception raised when a ZEO client is disconnected from the
ZEO server.""" class ClientStorageError(StorageError):
"""An error occured in the ZEO Client Storage."""
class UnrecognizedResult(ClientStorageError):
"""A server call returned an unrecognized result."""
class ClientDisconnected(ClientStorageError):
"""The database storage is disconnected from the storage."""
...@@ -21,7 +21,7 @@ from ZODB.TimeStamp import TimeStamp ...@@ -21,7 +21,7 @@ from ZODB.TimeStamp import TimeStamp
from ZODB.tests.StorageTestBase import zodb_pickle, MinPO from ZODB.tests.StorageTestBase import zodb_pickle, MinPO
import ZEO.ClientStorage import ZEO.ClientStorage
from ZEO.Exceptions import Disconnected from ZEO.Exceptions import ClientDisconnected
from ZEO.tests.TestThread import TestThread from ZEO.tests.TestThread import TestThread
ZERO = '\0'*8 ZERO = '\0'*8
...@@ -56,7 +56,7 @@ class WorkerThread(TestThread): ...@@ -56,7 +56,7 @@ class WorkerThread(TestThread):
self.storage.tpc_finish(self.trans) self.storage.tpc_finish(self.trans)
else: else:
self.storage.tpc_abort(self.trans) self.storage.tpc_abort(self.trans)
except Disconnected: except ClientDisconnected:
pass pass
def myvote(self): def myvote(self):
......
...@@ -27,7 +27,7 @@ import time ...@@ -27,7 +27,7 @@ import time
import zLOG import zLOG
from ZEO.ClientStorage import ClientStorage from ZEO.ClientStorage import ClientStorage
from ZEO.Exceptions import Disconnected from ZEO.Exceptions import ClientDisconnected
from ZEO.zrpc.marshal import Marshaller from ZEO.zrpc.marshal import Marshaller
from ZEO.tests import forker from ZEO.tests import forker
...@@ -200,7 +200,7 @@ class ConnectionTests(CommonSetupTearDown): ...@@ -200,7 +200,7 @@ class ConnectionTests(CommonSetupTearDown):
try: try:
self._dostore() self._dostore()
break break
except Disconnected: except ClientDisconnected:
self._storage.sync() self._storage.sync()
time.sleep(0.5) time.sleep(0.5)
...@@ -264,7 +264,7 @@ class ConnectionTests(CommonSetupTearDown): ...@@ -264,7 +264,7 @@ class ConnectionTests(CommonSetupTearDown):
# Poll until the client disconnects # Poll until the client disconnects
self.pollDown() self.pollDown()
# Stores should fail now # Stores should fail now
self.assertRaises(Disconnected, self._dostore) self.assertRaises(ClientDisconnected, self._dostore)
# Restart the server # Restart the server
self.startServer(create=0) self.startServer(create=0)
...@@ -274,12 +274,13 @@ class ConnectionTests(CommonSetupTearDown): ...@@ -274,12 +274,13 @@ class ConnectionTests(CommonSetupTearDown):
self._dostore() self._dostore()
def checkDisconnectionError(self): def checkDisconnectionError(self):
# Make sure we get a Disconnected when we try to read an # Make sure we get a ClientDisconnected when we try to read an
# object when we're not connected to a storage server and the # object when we're not connected to a storage server and the
# object is not in the cache. # object is not in the cache.
self.shutdownServer() self.shutdownServer()
self._storage = self.openClientStorage('test', 1000, wait=0) self._storage = self.openClientStorage('test', 1000, wait=0)
self.assertRaises(Disconnected, self._storage.load, 'fredwash', '') self.assertRaises(ClientDisconnected,
self._storage.load, 'fredwash', '')
def checkBasicPersistence(self): def checkBasicPersistence(self):
# Verify cached data persists across client storage instances. # Verify cached data persists across client storage instances.
...@@ -345,10 +346,8 @@ class ConnectionTests(CommonSetupTearDown): ...@@ -345,10 +346,8 @@ class ConnectionTests(CommonSetupTearDown):
try: try:
self._dostore(oid, data=obj) self._dostore(oid, data=obj)
break break
except Disconnected: except ClientDisconnected:
# Maybe the exception mess is better now # Maybe the exception mess is better now
## except (Disconnected, select.error,
## threading.ThreadError, socket.error):
zLOG.LOG("checkReconnection", zLOG.INFO, zLOG.LOG("checkReconnection", zLOG.INFO,
"Error after server restart; retrying.", "Error after server restart; retrying.",
error=sys.exc_info()) error=sys.exc_info())
...@@ -389,7 +388,7 @@ class ConnectionTests(CommonSetupTearDown): ...@@ -389,7 +388,7 @@ class ConnectionTests(CommonSetupTearDown):
try: try:
self._dostore() self._dostore()
except Disconnected: except ClientDisconnected:
pass pass
else: else:
self._storage.close() self._storage.close()
...@@ -504,7 +503,7 @@ class ReconnectionTests(CommonSetupTearDown): ...@@ -504,7 +503,7 @@ class ReconnectionTests(CommonSetupTearDown):
# Poll until the client disconnects # Poll until the client disconnects
self.pollDown() self.pollDown()
# Stores should fail now # Stores should fail now
self.assertRaises(Disconnected, self._dostore) self.assertRaises(ClientDisconnected, self._dostore)
# Restart the server # Restart the server
self.startServer(create=0, read_only=1) self.startServer(create=0, read_only=1)
...@@ -533,7 +532,7 @@ class ReconnectionTests(CommonSetupTearDown): ...@@ -533,7 +532,7 @@ class ReconnectionTests(CommonSetupTearDown):
# Poll until the client disconnects # Poll until the client disconnects
self.pollDown() self.pollDown()
# Stores should fail now # Stores should fail now
self.assertRaises(Disconnected, self._dostore) self.assertRaises(ClientDisconnected, self._dostore)
# Restart the server, this time read-write # Restart the server, this time read-write
self.startServer(create=0) self.startServer(create=0)
...@@ -566,7 +565,7 @@ class ReconnectionTests(CommonSetupTearDown): ...@@ -566,7 +565,7 @@ class ReconnectionTests(CommonSetupTearDown):
try: try:
self._dostore() self._dostore()
break break
except (Disconnected, ReadOnlyError): except (ClientDisconnected, ReadOnlyError):
# If the client isn't connected at all, sync() returns # If the client isn't connected at all, sync() returns
# quickly and the test fails because it doesn't wait # quickly and the test fails because it doesn't wait
# long enough for the client. # long enough for the client.
...@@ -691,7 +690,7 @@ class TimeoutTests(CommonSetupTearDown): ...@@ -691,7 +690,7 @@ class TimeoutTests(CommonSetupTearDown):
storage.tpc_begin(txn) storage.tpc_begin(txn)
storage.tpc_vote(txn) storage.tpc_vote(txn)
time.sleep(2) time.sleep(2)
self.assertRaises(Disconnected, storage.tpc_finish, txn) self.assertRaises(ClientDisconnected, storage.tpc_finish, txn)
def checkTimeoutOnAbort(self): def checkTimeoutOnAbort(self):
storage = self.openClientStorage() storage = self.openClientStorage()
......
...@@ -19,7 +19,7 @@ from ZODB.Transaction import Transaction ...@@ -19,7 +19,7 @@ from ZODB.Transaction import Transaction
from ZODB.tests.StorageTestBase import zodb_pickle, MinPO from ZODB.tests.StorageTestBase import zodb_pickle, MinPO
import ZEO.ClientStorage import ZEO.ClientStorage
from ZEO.Exceptions import Disconnected from ZEO.Exceptions import ClientDisconnected
ZERO = '\0'*8 ZERO = '\0'*8
...@@ -86,7 +86,7 @@ class AbortsAfterBeginFailsThread(BasicThread): ...@@ -86,7 +86,7 @@ class AbortsAfterBeginFailsThread(BasicThread):
self.gotValueError = 1 self.gotValueError = 1
try: try:
self.storage.tpc_abort(self.trans) self.storage.tpc_abort(self.trans)
except Disconnected: except ClientDisconnected:
self.gotDisconnected = 1 self.gotDisconnected = 1
......
...@@ -39,7 +39,6 @@ from ZODB.tests import StorageTestBase, BasicStorage, VersionStorage, \ ...@@ -39,7 +39,6 @@ from ZODB.tests import StorageTestBase, BasicStorage, VersionStorage, \
# ZEO imports # ZEO imports
from ZEO.ClientStorage import ClientStorage from ZEO.ClientStorage import ClientStorage
from ZEO.Exceptions import Disconnected
# ZEO test support # ZEO test support
from ZEO.tests import forker, Cache from ZEO.tests import forker, Cache
......
...@@ -12,10 +12,16 @@ ...@@ -12,10 +12,16 @@
# #
############################################################################## ##############################################################################
from ZODB import POSException from ZODB import POSException
from ZEO.Exceptions import Disconnected from ZEO.Exceptions import ClientDisconnected
class ZRPCError(POSException.StorageError): class ZRPCError(POSException.StorageError):
pass pass
class DisconnectedError(ZRPCError, Disconnected): class DisconnectedError(ZRPCError, ClientDisconnected):
"""The database storage is disconnected from the storage server.""" """The database storage is disconnected from the storage server.
The error occurred because a problem in the low-level RPC connection,
or because the connection was closed.
"""
# This subclass is raised when zrpc catches the error.
...@@ -15,11 +15,11 @@ ...@@ -15,11 +15,11 @@
import asyncore, struct import asyncore, struct
import threading import threading
from ZEO.Exceptions import Disconnected
import zLOG
from types import StringType from types import StringType
from ZEO.zrpc.log import log, short_repr from ZEO.zrpc.log import log, short_repr
from ZEO.zrpc.error import DisconnectedError
import zLOG
import socket, errno import socket, errno
...@@ -209,10 +209,8 @@ class SizedMessageAsyncConnection(asyncore.dispatcher): ...@@ -209,10 +209,8 @@ class SizedMessageAsyncConnection(asyncore.dispatcher):
level=zLOG.TRACE) level=zLOG.TRACE)
if self.__closed: if self.__closed:
raise Disconnected, ( raise DisconnectedError(
"This action is temporarily unavailable." "This action is temporarily unavailable.<p>")
"<p>"
)
self.__output_lock.acquire() self.__output_lock.acquire()
try: try:
# do two separate appends to avoid copying the message string # do two separate appends to avoid copying the message string
......
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