blob: d6bbaa878099240ae7f84a8f2fe531e980cd3036 [file] [log] [blame]
/*
Copyright (c) 2018-2019 Nokia.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#include <sstream>
#include "private/createlogger.hpp"
#include "private/error.hpp"
using namespace shareddatalayer;
using namespace shareddatalayer::redis;
namespace
{
/* Error codes under this category are not set directly. All SDL implementation specific error codes can be
* mapped to these error codes. These error codes are further on mapped to error codes which are available to
* SDL clients (shareddatalayer::Error category).
* We could directly map implementation specific error codes to client error codes but having this intermediate
* mapping gives some benefits:
* - We can easily provide more error categories for clients (e.g. severity, etc.) than just client error code
* classification if needed.
* - We can implement SDL internal error handling logic based on these internal error codes if needed.
* - If implementation specific error would be directly mapped to client error codes, mapping implementation would
* easily be quite complicated (especially if the amount of implementation specific errors/error categories increases).
*/
class InternalErrorCategory : public std::error_category
{
public:
InternalErrorCategory() = default;
const char* name() const noexcept override;
std::string message(int condition) const override;
};
const char* InternalErrorCategory::name() const noexcept
{
return "SDL-internal-errorcodes";
}
std::string InternalErrorCategory::message(int) const
{
return "Only for SDL internal usage.";
}
const std::error_category& getInternalErrorCategory() noexcept
{
static const InternalErrorCategory theInternalErrorCategory;
return theInternalErrorCategory;
}
/* This error category is used by both AsyncHiredisCommandDispatcher and AsyncHiredisClusterCommandDispatcher,
* thus it is defined here. Error categories related to single class are defined in the files of the corresponding class.
* AsyncHiredisCommandDispatcher and AsyncHiredisClusterCommandDispatcher can use common error category as error
* handling is identical in those two classes. Also, only one of those classes is always used at a time (depending
* on deployment).
*/
class AsyncRedisCommandDispatcherErrorCategory: public std::error_category
{
public:
AsyncRedisCommandDispatcherErrorCategory() = default;
const char* name() const noexcept override;
std::string message(int condition) const override;
std::error_condition default_error_condition(int condition) const noexcept override;
};
const char* AsyncRedisCommandDispatcherErrorCategory::name() const noexcept
{
/* As the correct dispacther is selected during runtime, we do not known here (without additional implementation)
* which dispatcher (redis/rediscluster) is currently in use. At least for now, we do not indicate in error
* category name the exact dispacther type but return the same name for all dispatchers.
* Main reason for this decision was that in error investigation situations we anyway need to have some other efficient
* way to figure out what kind of deployment was used as there can be error situations which do not generate any error
* code. Thus it was not seen worth the errort to implement correct dispatcher name display to error category name.
* Detailed dispacther name display can be added later if a need for that arises.
*/
return "asyncrediscommanddispatcher";
}
std::string AsyncRedisCommandDispatcherErrorCategory::message(int condition) const
{
switch (static_cast<AsyncRedisCommandDispatcherErrorCode>(condition))
{
case AsyncRedisCommandDispatcherErrorCode::SUCCESS:
return std::error_code().message();
case AsyncRedisCommandDispatcherErrorCode::CONNECTION_LOST:
return "redis connection lost";
case AsyncRedisCommandDispatcherErrorCode::PROTOCOL_ERROR:
return "redis protocol error";
case AsyncRedisCommandDispatcherErrorCode::OUT_OF_MEMORY:
return "redis out of memory";
case AsyncRedisCommandDispatcherErrorCode::DATASET_LOADING:
return "redis dataset still being loaded into memory";
case AsyncRedisCommandDispatcherErrorCode::NOT_CONNECTED:
return "not connected to redis, SDL operation not started";
case AsyncRedisCommandDispatcherErrorCode::UNKNOWN_ERROR:
return "redis error";
case AsyncRedisCommandDispatcherErrorCode::IO_ERROR:
return "redis I/O error";
case AsyncRedisCommandDispatcherErrorCode::WRITING_TO_SLAVE:
return "writing to slave";
case AsyncRedisCommandDispatcherErrorCode::END_MARKER:
logErrorOnce("AsyncRedisCommandDispatcherErrorCode::END_MARKER is not meant to be queried (it is only for enum loop control)");
return "unsupported error code for message()";
default:
return "description missing for AsyncRedisCommandDispatcherErrorCategory error: " + std::to_string(condition);
}
}
std::error_condition AsyncRedisCommandDispatcherErrorCategory::default_error_condition(int condition) const noexcept
{
switch (static_cast<AsyncRedisCommandDispatcherErrorCode>(condition))
{
case AsyncRedisCommandDispatcherErrorCode::SUCCESS:
return InternalError::SUCCESS;
case AsyncRedisCommandDispatcherErrorCode::CONNECTION_LOST:
return InternalError::BACKEND_CONNECTION_LOST;
case AsyncRedisCommandDispatcherErrorCode::PROTOCOL_ERROR:
return InternalError::BACKEND_REJECTED_REQUEST;
case AsyncRedisCommandDispatcherErrorCode::OUT_OF_MEMORY:
return InternalError::BACKEND_ERROR;
case AsyncRedisCommandDispatcherErrorCode::DATASET_LOADING:
return InternalError::BACKEND_NOT_READY;
case AsyncRedisCommandDispatcherErrorCode::NOT_CONNECTED:
return InternalError::SDL_NOT_CONNECTED_TO_BACKEND;
case AsyncRedisCommandDispatcherErrorCode::UNKNOWN_ERROR:
return InternalError::BACKEND_ERROR;
case AsyncRedisCommandDispatcherErrorCode::IO_ERROR:
return InternalError::BACKEND_ERROR;
case AsyncRedisCommandDispatcherErrorCode::WRITING_TO_SLAVE:
return InternalError::BACKEND_ERROR;
case AsyncRedisCommandDispatcherErrorCode::END_MARKER:
logErrorOnce("AsyncRedisCommandDispatcherErrorCode::END_MARKER is not meant to be mapped to InternalError (it is only for enum loop control)");
return InternalError::SDL_ERROR_CODE_LOGIC_ERROR;
default:
std::ostringstream msg;
msg << "default error condition missing for AsyncRedisCommandDispatcherErrorCategory error: "
<< condition;
logErrorOnce(msg.str());
return InternalError::SDL_ERROR_CODE_LOGIC_ERROR;
}
}
const std::error_category& getAsyncRedisCommandDispatcherErrorCategory() noexcept
{
static const AsyncRedisCommandDispatcherErrorCategory theAsyncRedisCommandDispatcherErrorCategory;
return theAsyncRedisCommandDispatcherErrorCategory;
}
}
namespace shareddatalayer
{
std::error_condition make_error_condition(InternalError errorCode)
{
return {static_cast<int>(errorCode), getInternalErrorCategory()};
}
namespace redis
{
std::error_code make_error_code(AsyncRedisCommandDispatcherErrorCode errorCode)
{
return std::error_code(static_cast<int>(errorCode), getAsyncRedisCommandDispatcherErrorCategory());
}
AsyncRedisCommandDispatcherErrorCode& operator++ (AsyncRedisCommandDispatcherErrorCode& ecEnum)
{
if (ecEnum == AsyncRedisCommandDispatcherErrorCode::END_MARKER)
throw std::out_of_range("for AsyncRedisCommandDispatcherErrorCode& operator ++");
ecEnum = AsyncRedisCommandDispatcherErrorCode(static_cast<std::underlying_type<AsyncRedisCommandDispatcherErrorCode>::type>(ecEnum) + 1);
return ecEnum;
}
}
}