mirror of https://gitee.com/bigwinds/arangodb
256 lines
9.1 KiB
C++
256 lines
9.1 KiB
C++
////////////////////////////////////////////////////////////////////////////////
|
|
/// DISCLAIMER
|
|
///
|
|
/// Copyright 2019 ArangoDB GmbH, Cologne, Germany
|
|
///
|
|
/// 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.
|
|
///
|
|
/// Copyright holder is ArangoDB GmbH, Cologne, Germany
|
|
///
|
|
/// @author Simon Grätzer
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
#include "Utils.h"
|
|
|
|
#include "Agency/AgencyFeature.h"
|
|
#include "Agency/Agent.h"
|
|
#include "Basics/Common.h"
|
|
#include "Basics/NumberUtils.h"
|
|
#include "Cluster/ClusterInfo.h"
|
|
#include "Logger/Logger.h"
|
|
#include "Network/Methods.h"
|
|
#include "VocBase/ticks.h"
|
|
|
|
#include <velocypack/velocypack-aliases.h>
|
|
|
|
namespace arangodb {
|
|
namespace network {
|
|
|
|
int resolveDestination(DestinationId const& dest, std::string& endpoint) {
|
|
using namespace arangodb;
|
|
|
|
if (dest.find("tcp://") == 0 || dest.find("ssl://") == 0) {
|
|
endpoint = dest;
|
|
return TRI_ERROR_NO_ERROR; // all good
|
|
}
|
|
|
|
// Now look up the actual endpoint:
|
|
auto* ci = ClusterInfo::instance();
|
|
if (!ci) {
|
|
return TRI_ERROR_SHUTTING_DOWN;
|
|
}
|
|
|
|
// This sets result.shardId, result.serverId and result.endpoint,
|
|
// depending on what dest is. Note that if a shardID is given, the
|
|
// responsible server is looked up, if a serverID is given, the endpoint
|
|
// is looked up, both can fail and immediately lead to a CL_COMM_ERROR
|
|
// state.
|
|
ServerID serverID;
|
|
if (dest.find("shard:") == 0) {
|
|
ShardID shardID = dest.substr(6);
|
|
{
|
|
std::shared_ptr<std::vector<ServerID>> resp = ci->getResponsibleServer(shardID);
|
|
if (!resp->empty()) {
|
|
serverID = (*resp)[0];
|
|
} else {
|
|
LOG_TOPIC("60ee8", ERR, Logger::CLUSTER)
|
|
<< "cannot find responsible server for shard '" << shardID << "'";
|
|
return TRI_ERROR_CLUSTER_BACKEND_UNAVAILABLE;
|
|
}
|
|
}
|
|
LOG_TOPIC("64670", DEBUG, Logger::CLUSTER) << "Responsible server: " << serverID;
|
|
} else if (dest.find("server:") == 0) {
|
|
serverID = dest.substr(7);
|
|
} else {
|
|
std::string errorMessage = "did not understand destination '" + dest + "'";
|
|
LOG_TOPIC("77a84", ERR, Logger::COMMUNICATION)
|
|
<< "did not understand destination '" << dest << "'";
|
|
return TRI_ERROR_CLUSTER_BACKEND_UNAVAILABLE;
|
|
}
|
|
|
|
endpoint = ci->getServerEndpoint(serverID);
|
|
if (endpoint.empty()) {
|
|
if (serverID.find(',') != std::string::npos) {
|
|
TRI_ASSERT(false);
|
|
}
|
|
std::string errorMessage =
|
|
"did not find endpoint of server '" + serverID + "'";
|
|
LOG_TOPIC("f29ef", ERR, Logger::COMMUNICATION)
|
|
<< "did not find endpoint of server '" << serverID << "'";
|
|
return TRI_ERROR_CLUSTER_BACKEND_UNAVAILABLE;
|
|
}
|
|
return TRI_ERROR_NO_ERROR;
|
|
}
|
|
|
|
OperationResult opResultFromBody(arangodb::velocypack::Buffer<uint8_t> const& body,
|
|
int defaultErrorCode) {
|
|
if (body.size() > 0) {
|
|
return opResultFromBody(VPackSlice(body.data()), defaultErrorCode);
|
|
}
|
|
return OperationResult(defaultErrorCode);
|
|
}
|
|
|
|
OperationResult opResultFromBody(std::shared_ptr<VPackBuilder> const& body, int defaultErrorCode) {
|
|
if (body) {
|
|
return opResultFromBody(body->slice(), defaultErrorCode);
|
|
}
|
|
return OperationResult(defaultErrorCode);
|
|
}
|
|
|
|
OperationResult opResultFromBody(VPackSlice body, int defaultErrorCode) {
|
|
// read the error number from the response and use it if present
|
|
if (body.isObject()) {
|
|
VPackSlice num = body.get(StaticStrings::ErrorNum);
|
|
VPackSlice msg = body.get(StaticStrings::ErrorMessage);
|
|
if (num.isNumber()) {
|
|
if (msg.isString()) {
|
|
// found an error number and an error message, so let's use it!
|
|
return OperationResult(Result(num.getNumericValue<int>(), msg.copyString()));
|
|
}
|
|
// we found an error number, so let's use it!
|
|
return OperationResult(num.getNumericValue<int>());
|
|
}
|
|
}
|
|
|
|
return OperationResult(defaultErrorCode);
|
|
}
|
|
|
|
/// @brief extract the error code form the body
|
|
int errorCodeFromBody(arangodb::velocypack::Slice body) {
|
|
if (body.isObject()) {
|
|
VPackSlice num = body.get(StaticStrings::ErrorNum);
|
|
if (num.isNumber()) {
|
|
// we found an error number, so let's use it!
|
|
return num.getNumericValue<int>();
|
|
}
|
|
}
|
|
return TRI_ERROR_ILLEGAL_NUMBER;
|
|
}
|
|
|
|
Result resultFromBody(std::shared_ptr<arangodb::velocypack::Builder> const& body,
|
|
int defaultError) {
|
|
|
|
// read the error number from the response and use it if present
|
|
if (body) {
|
|
return resultFromBody(body->slice(), defaultError);
|
|
}
|
|
|
|
return Result(defaultError);
|
|
}
|
|
|
|
Result resultFromBody(arangodb::velocypack::Slice slice,
|
|
int defaultError) {
|
|
// read the error number from the response and use it if present
|
|
if (slice.isObject()) {
|
|
VPackSlice num = slice.get(StaticStrings::ErrorNum);
|
|
VPackSlice msg = slice.get(StaticStrings::ErrorMessage);
|
|
if (num.isNumber()) {
|
|
if (msg.isString()) {
|
|
// found an error number and an error message, so let's use it!
|
|
return Result(num.getNumericValue<int>(), msg.copyString());
|
|
}
|
|
// we found an error number, so let's use it!
|
|
return Result(num.getNumericValue<int>());
|
|
}
|
|
}
|
|
return Result(defaultError);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief Extract all error baby-style error codes and store them in a map
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
void errorCodesFromHeaders(network::Headers headers,
|
|
std::unordered_map<int, size_t>& errorCounter,
|
|
bool includeNotFound) {
|
|
auto const& codes = headers.find(StaticStrings::ErrorCodes);
|
|
if (codes != headers.end()) {
|
|
auto parsedCodes = VPackParser::fromJson(codes->second);
|
|
VPackSlice codesSlice = parsedCodes->slice();
|
|
if (!codesSlice.isObject()) {
|
|
return;
|
|
}
|
|
|
|
for (auto const& code : VPackObjectIterator(codesSlice)) {
|
|
VPackValueLength codeLength;
|
|
char const* codeString = code.key.getString(codeLength);
|
|
int codeNr = NumberUtils::atoi_zero<int>(codeString, codeString + codeLength);
|
|
if (includeNotFound || codeNr != TRI_ERROR_ARANGO_DOCUMENT_NOT_FOUND) {
|
|
errorCounter[codeNr] += code.value.getNumericValue<size_t>();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
int fuerteToArangoErrorCode(network::Response const& res) {
|
|
// This function creates an error code from a ClusterCommResult,
|
|
// but only if it is a communication error. If the communication
|
|
// was successful and there was an HTTP error code, this function
|
|
// returns TRI_ERROR_NO_ERROR.
|
|
// If TRI_ERROR_NO_ERROR is returned, then the result was CL_COMM_RECEIVED
|
|
// and .answer can safely be inspected.
|
|
|
|
switch (res.error) {
|
|
case fuerte::Error::NoError:
|
|
return TRI_ERROR_NO_ERROR;
|
|
|
|
case fuerte::Error::CouldNotConnect:
|
|
return TRI_ERROR_CLUSTER_BACKEND_UNAVAILABLE;
|
|
|
|
case fuerte::Error::CloseRequested:
|
|
case fuerte::Error::ConnectionClosed:
|
|
return TRI_ERROR_CLUSTER_CONNECTION_LOST;
|
|
|
|
case fuerte::Error::Timeout: // No reply, we give up:
|
|
return TRI_ERROR_CLUSTER_TIMEOUT;
|
|
|
|
case fuerte::Error::QueueCapacityExceeded: // there is no result
|
|
case fuerte::Error::ReadError:
|
|
case fuerte::Error::WriteError:
|
|
case fuerte::Error::Canceled:
|
|
case fuerte::Error::ProtocolError:
|
|
return TRI_ERROR_CLUSTER_CONNECTION_LOST;
|
|
}
|
|
|
|
return TRI_ERROR_INTERNAL;
|
|
}
|
|
|
|
/// @brief Create Cluster Communication result for insert
|
|
OperationResult clusterResultInsert(arangodb::fuerte::StatusCode code,
|
|
std::shared_ptr<VPackBuffer<uint8_t>> body,
|
|
OperationOptions const& options,
|
|
std::unordered_map<int, size_t> const& errorCounter) {
|
|
switch (code) {
|
|
case fuerte::StatusAccepted:
|
|
return OperationResult(Result(), std::move(body), nullptr, options, errorCounter);
|
|
case fuerte::StatusCreated: {
|
|
OperationOptions copy = options;
|
|
copy.waitForSync = true; // wait for sync is abused herea
|
|
// operationResult should get a return code.
|
|
return OperationResult(Result(), std::move(body), nullptr, copy, errorCounter);
|
|
}
|
|
case fuerte::StatusPreconditionFailed:
|
|
return network::opResultFromBody(*body, TRI_ERROR_ARANGO_CONFLICT);
|
|
case fuerte::StatusBadRequest:
|
|
return network::opResultFromBody(*body, TRI_ERROR_INTERNAL);
|
|
case fuerte::StatusNotFound:
|
|
return network::opResultFromBody(*body, TRI_ERROR_ARANGO_DATA_SOURCE_NOT_FOUND);
|
|
case fuerte::StatusConflict:
|
|
return network::opResultFromBody(*body, TRI_ERROR_ARANGO_UNIQUE_CONSTRAINT_VIOLATED);
|
|
default:
|
|
return network::opResultFromBody(*body, TRI_ERROR_INTERNAL);
|
|
}
|
|
}
|
|
} // namespace network
|
|
} // namespace arangodb
|