From 4029026c3b4c6c99460f52deec8d43b4a189c691 Mon Sep 17 00:00:00 2001 From: Matej Ferencevic Date: Thu, 11 Apr 2019 10:02:12 +0200 Subject: [PATCH] Add bolt verbose error Reviewers: msantl Reviewed By: msantl Subscribers: pullbot Differential Revision: https://phabricator.memgraph.io/D1948 --- src/communication/bolt/v1/exceptions.hpp | 78 +++++++++++++++++++ .../bolt/v1/states/executing.hpp | 18 +---- src/memgraph_init.hpp | 1 + src/raft/exceptions.hpp | 10 ++- 4 files changed, 90 insertions(+), 17 deletions(-) create mode 100644 src/communication/bolt/v1/exceptions.hpp diff --git a/src/communication/bolt/v1/exceptions.hpp b/src/communication/bolt/v1/exceptions.hpp new file mode 100644 index 000000000..a6d630d1b --- /dev/null +++ b/src/communication/bolt/v1/exceptions.hpp @@ -0,0 +1,78 @@ +/// @file + +#pragma once + +#include + +#include "utils/exceptions.hpp" + +namespace communication::bolt { + +/** + * Used to indicate something is wrong with the client but the transaction is + * kept open for a potential retry. + * + * The most common use case for throwing this error is if something is wrong + * with the query. Perhaps a simple syntax error that can be fixed and query + * retried. + */ +class ClientError : public utils::BasicException { + public: + using utils::BasicException::BasicException; +}; + +/** + * All exceptions that are sent to the client consist of two parts. The first + * part is `code` it specifies the type of exception that was raised. The second + * part is `message` which is a pretty error message that can be displayed to a + * human. + * + * The first part is more interesting for machine processing. It should have the + * following format: `Memgraph.[Classification].[Category].[Title]` It is + * defined here: https://neo4j.com/docs/status-codes/current/ + * + * The first part of the `code` is always Memgraph, it indicates the database. + * The second part is `classification`. The `classification` part is specified + * by Neo to always be one of "ClientError", "ClientNotification", + * "TransientError" or "DatabaseError". We won't use "ClientNotification". + * + * The `category` and `title` parts can be freely specified. + */ +class VerboseError : public utils::BasicException { + public: + enum class Classification { + // Client and Database errors mean that the client shouldn't retry the + // query. + CLIENT_ERROR, + DATABASE_ERROR, + // Transient error means that the client should retry the query. + TRANSIENT_ERROR, + }; + + template + VerboseError(Classification classification, const std::string &category, + const std::string &title, const std::string &format, + Args &&... args) + : BasicException(format, std::forward(args)...), + code_(fmt::format("Memgraph.{}.{}.{}", + ClassificationToString(classification), category, + title)) {} + + const std::string &code() const noexcept { return code_; } + + private: + std::string ClassificationToString(Classification classification) { + switch (classification) { + case Classification::CLIENT_ERROR: + return "ClientError"; + case Classification::DATABASE_ERROR: + return "DatabaseError"; + case Classification::TRANSIENT_ERROR: + return "TransientError"; + } + } + + std::string code_; +}; + +} // namespace communication::bolt diff --git a/src/communication/bolt/v1/states/executing.hpp b/src/communication/bolt/v1/states/executing.hpp index 41fbf8392..e45394da6 100644 --- a/src/communication/bolt/v1/states/executing.hpp +++ b/src/communication/bolt/v1/states/executing.hpp @@ -7,28 +7,18 @@ #include #include "communication/bolt/v1/codes.hpp" +#include "communication/bolt/v1/exceptions.hpp" #include "communication/bolt/v1/state.hpp" #include "communication/bolt/v1/value.hpp" -#include "utils/exceptions.hpp" namespace communication::bolt { -/** - * Used to indicate something is wrong with the client but the transaction is - * kept open for a potential retry. - * - * The most common use case for throwing this error is if something is wrong - * with the query. Perhaps a simple syntax error that can be fixed and query - * retried. - */ -class ClientError : public utils::BasicException { - public: - using utils::BasicException::BasicException; -}; - // TODO (mferencevic): revise these error messages inline std::pair ExceptionToErrorMessage( const std::exception &e) { + if (auto *verbose = dynamic_cast(&e)) { + return {verbose->code(), verbose->what()}; + } if (dynamic_cast(&e)) { // Clients expect 4 strings separated by dots. First being database name // (for example: Neo, Memgraph...), second being either ClientError, diff --git a/src/memgraph_init.hpp b/src/memgraph_init.hpp index 373092726..02042f5e3 100644 --- a/src/memgraph_init.hpp +++ b/src/memgraph_init.hpp @@ -11,6 +11,7 @@ #include "audit/log.hpp" #include "auth/auth.hpp" +#include "communication/bolt/v1/exceptions.hpp" #include "communication/bolt/v1/session.hpp" #include "communication/init.hpp" #include "communication/session.hpp" diff --git a/src/raft/exceptions.hpp b/src/raft/exceptions.hpp index 1fe2b218b..bf038bd08 100644 --- a/src/raft/exceptions.hpp +++ b/src/raft/exceptions.hpp @@ -2,15 +2,19 @@ #pragma once -#include "utils/exceptions.hpp" +#include "communication/bolt/v1/exceptions.hpp" namespace raft { /// Base exception class used for all exceptions that can occur within the /// Raft protocol. -class RaftException : public utils::BasicException { +class RaftException : public communication::bolt::VerboseError { public: - using utils::BasicException::BasicException; + template + RaftException(const std::string &format, Args &&... args) + : communication::bolt::VerboseError( + communication::bolt::VerboseError::Classification::DATABASE_ERROR, + "Raft", "Error", format, std::forward(args)...) {} }; /// This exception should be thrown when attempting to transition between