diff --git a/cassandra/__init__.py b/cassandra/__init__.py index 296601cb..fe57b2f9 100644 --- a/cassandra/__init__.py +++ b/cassandra/__init__.py @@ -221,6 +221,103 @@ class WriteTimeout(Timeout): self.write_type = write_type +class CoordinationFailure(Exception): + """ + Replicas sent a failure to the coordinator. + """ + + consistency = None + """ The requested :class:`ConsistencyLevel` """ + + required_responses = None + """ The number of required replica responses """ + + received_responses = None + """ + The number of replicas that responded before the coordinator timed out + the operation + """ + + failures = None + """ + The number of replicas that sent a failure message + """ + + def __init__(self, summary_message, consistency=None, required_responses=None, received_responses=None, failures=None): + self.consistency = consistency + self.required_responses = required_responses + self.received_responses = received_responses + self.failures = failures + Exception.__init__(self, summary_message + ' info=' + + repr({'consistency': consistency_value_to_name(consistency), + 'required_responses': required_responses, + 'received_responses': received_responses, + 'failures': failures})) + + +class ReadFailure(CoordinationFailure): + """ + A subclass of :exc:`CoordinationFailure` for read operations. + + This indicates that the replicas sent a failure message to the coordinator. + """ + + data_retrieved = None + """ + A boolean indicating whether the requested data was retrieved + by the coordinator from any replicas before it timed out the + operation + """ + + def __init__(self, message, data_retrieved=None, **kwargs): + CoordinationFailure.__init__(self, message, **kwargs) + self.data_retrieved = data_retrieved + + +class WriteFailure(CoordinationFailure): + """ + A subclass of :exc:`CoordinationFailure` for write operations. + + This indicates that the replicas sent a failure message to the coordinator. + """ + + write_type = None + """ + The type of write operation, enum on :class:`~cassandra.policies.WriteType` + """ + + def __init__(self, message, write_type=None, **kwargs): + CoordinationFailure.__init__(self, message, **kwargs) + self.write_type = write_type + + +class FunctionFailure(Exception): + """ + User Defined Function failed during execution + """ + + keyspace = None + """ + Keyspace of the function + """ + + function = None + """ + Name of the function + """ + + arg_types = None + """ + List of argument type names of the function + """ + + def __init__(self, summary_message, keyspace, function, arg_types): + self.keyspace = keyspace + self.function = function + self.arg_types = arg_types + Exception.__init__(self, summary_message) + + class AlreadyExists(Exception): """ An attempt was made to create a keyspace or table that already exists. diff --git a/cassandra/protocol.py b/cassandra/protocol.py index d81a6507..bdf7ae2b 100644 --- a/cassandra/protocol.py +++ b/cassandra/protocol.py @@ -22,6 +22,7 @@ from six.moves import range import io from cassandra import (Unavailable, WriteTimeout, ReadTimeout, + WriteFailure, ReadFailure, FunctionFailure, AlreadyExists, InvalidRequest, Unauthorized, UnsupportedOperation) from cassandra.marshal import (int32_pack, int32_unpack, uint16_pack, uint16_unpack, @@ -263,6 +264,58 @@ class ReadTimeoutErrorMessage(RequestExecutionException): return ReadTimeout(self.summary_msg(), **self.info) +class ReadFailureMessage(RequestExecutionException): + summary = "Replica(s) failed to execute read" + error_code = 0x1300 + + @staticmethod + def recv_error_info(f): + return { + 'consistency': read_consistency_level(f), + 'received_responses': read_int(f), + 'required_responses': read_int(f), + 'failures': read_int(f), + 'data_retrieved': bool(read_byte(f)), + } + + def to_exception(self): + return ReadFailure(self.summary_msg(), **self.info) + + +class FunctionFailureMessage(RequestExecutionException): + summary = "User Defined Function failure" + error_code = 0x1400 + + @staticmethod + def recv_error_info(f): + return { + 'keyspace': read_string(f), + 'function': read_string(f), + 'arg_types': [read_string(f) for _ in range(read_short(f))], + } + + def to_exception(self): + return FunctionFailure(self.summary_msg(), **self.info) + + +class WriteFailureMessage(RequestExecutionException): + summary = "Replica(s) failed to execute write" + error_code = 0x1500 + + @staticmethod + def recv_error_info(f): + return { + 'consistency': read_consistency_level(f), + 'received_responses': read_int(f), + 'required_responses': read_int(f), + 'failures': read_int(f), + 'write_type': WriteType.name_to_value[read_string(f)], + } + + def to_exception(self): + return WriteFailure(self.summary_msg(), **self.info) + + class SyntaxException(RequestValidationException): summary = 'Syntax error in CQL query' error_code = 0x2000 diff --git a/docs/api/cassandra.rst b/docs/api/cassandra.rst index 90d23d10..fd0de0be 100644 --- a/docs/api/cassandra.rst +++ b/docs/api/cassandra.rst @@ -26,6 +26,15 @@ .. autoexception:: WriteTimeout() :members: +.. autoexception:: ReadFailure() + :members: + +.. autoexception:: WriteFailure() + :members: + +.. autoexception:: FunctionFailure() + :members: + .. autoexception:: AlreadyExists() :members: