1
0
Fork 0

Merge branch 'devel' of https://github.com/triAGENS/ArangoDB into devel

Conflicts:
	arangod/Aql/ExecutionNode.h
This commit is contained in:
Jan Steemann 2014-09-26 20:23:52 +02:00
commit 9e47f3ca27
15 changed files with 336 additions and 32 deletions

View File

@ -28,6 +28,7 @@
#include "Aql/ExecutionBlock.h" #include "Aql/ExecutionBlock.h"
#include "Aql/ExecutionEngine.h" #include "Aql/ExecutionEngine.h"
#include "Basics/StringUtils.h" #include "Basics/StringUtils.h"
#include "Basics/StringBuffer.h"
#include "Basics/json-utilities.h" #include "Basics/json-utilities.h"
#include "HashIndex/hash-index.h" #include "HashIndex/hash-index.h"
#include "Utils/Exception.h" #include "Utils/Exception.h"
@ -40,6 +41,7 @@ using namespace triagens::aql;
using Json = triagens::basics::Json; using Json = triagens::basics::Json;
using JsonHelper = triagens::basics::JsonHelper; using JsonHelper = triagens::basics::JsonHelper;
using StringBuffer = triagens::basics::StringBuffer;
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// --SECTION-- struct AggregatorGroup // --SECTION-- struct AggregatorGroup
@ -3932,6 +3934,34 @@ size_t ScatterBlock::skipSomeForShard (size_t atLeast, size_t atMost, std::strin
// --SECTION-- class RemoteBlock // --SECTION-- class RemoteBlock
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
////////////////////////////////////////////////////////////////////////////////
/// @brief timeout
////////////////////////////////////////////////////////////////////////////////
double const RemoteBlock::defaultTimeOut = 3600.0;
////////////////////////////////////////////////////////////////////////////////
/// @brief local helper to throw an exception if a HTTP request went wrong
////////////////////////////////////////////////////////////////////////////////
static void throwExceptionAfterBadSyncRequest (ClusterCommResult* res) {
if (res->status == CL_COMM_TIMEOUT) {
// No reply, we give up:
THROW_ARANGO_EXCEPTION_MESSAGE(TRI_ERROR_CLUSTER_TIMEOUT,
"timeout in cluster AQL operation");
}
if (res->status == CL_COMM_ERROR) {
// This could be a broken connection or an Http error:
if (res->result == nullptr || ! res->result->isComplete()) {
// there is no result
THROW_ARANGO_EXCEPTION_MESSAGE(TRI_ERROR_CLUSTER_CONNECTION_LOST,
"lost connection within cluster");
}
// In this case a proper HTTP error was reported by the DBserver,
THROW_ARANGO_EXCEPTION(TRI_ERROR_CLUSTER_AQL_COMMUNICATION);
}
}
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
/// @brief initialize /// @brief initialize
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
@ -3969,15 +3999,103 @@ int RemoteBlock::shutdown () {
AqlItemBlock* RemoteBlock::getSome (size_t atLeast, AqlItemBlock* RemoteBlock::getSome (size_t atLeast,
size_t atMost) { size_t atMost) {
return nullptr; // For every call we simply forward via HTTP
ClusterComm* cc = ClusterComm::instance();
std::unique_ptr<ClusterCommResult> res;
// Later, we probably want to set these sensibly:
ClientTransactionID const clientTransactionId = "AQL";
CoordTransactionID const coordTransactionId = 1;
Json body(Json::Array, 2);
body("atLeast", Json(static_cast<double>(atLeast)))
("atMost", Json(static_cast<double>(atMost)));
std::string bodyString(body.toString());
std::map<std::string, std::string> headers;
res.reset(cc->syncRequest(clientTransactionId,
coordTransactionId,
_server,
rest::HttpRequest::HTTP_REQUEST_PUT,
std::string("/_db/")
+ _engine->getTransaction()->vocbase()->_name
+ "/_api/aql/getSome/" + _queryId,
bodyString,
headers,
3600.0));
throwExceptionAfterBadSyncRequest(res.get());
// If we get here, then res->result is the response which will be
// a serialized AqlItemBlock:
StringBuffer const& responseBodyBuf(res->result->getBody());
Json responseBodyJson(TRI_UNKNOWN_MEM_ZONE,
TRI_JsonString(TRI_UNKNOWN_MEM_ZONE,
responseBodyBuf.begin()));
if (JsonHelper::getBooleanValue(responseBodyJson.json(), "exhausted", true)) {
return nullptr;
}
else {
auto items = new triagens::aql::AqlItemBlock(responseBodyJson);
return items;
}
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
/// @brief skipSome /// @brief skipSome
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
ClusterCommResult* RemoteBlock::sendRequest (
rest::HttpRequest::HttpRequestType type,
std::string urlPart,
std::string const& body) {
ClusterComm* cc = ClusterComm::instance();
// Later, we probably want to set these sensibly:
ClientTransactionID const clientTransactionId = "AQL";
CoordTransactionID const coordTransactionId = 1;
std::map<std::string, std::string> headers;
return cc->syncRequest(clientTransactionId,
coordTransactionId,
_server,
type,
std::string("/_db/")
+ _engine->getTransaction()->vocbase()->_name
+ urlPart + _queryId,
body,
headers,
defaultTimeOut);
}
size_t RemoteBlock::skipSome (size_t atLeast, size_t atMost) { size_t RemoteBlock::skipSome (size_t atLeast, size_t atMost) {
return 0; // For every call we simply forward via HTTP
Json body(Json::Array, 2);
body("atLeast", Json(static_cast<double>(atLeast)))
("atMost", Json(static_cast<double>(atMost)));
std::string bodyString(body.toString());
std::unique_ptr<ClusterCommResult> res;
res.reset(sendRequest(rest::HttpRequest::HTTP_REQUEST_PUT,
"/_api/aql/skipSome/",
bodyString));
throwExceptionAfterBadSyncRequest(res.get());
// If we get here, then res->result is the response which will be
// a serialized AqlItemBlock:
StringBuffer const& responseBodyBuf(res->result->getBody());
Json responseBodyJson(TRI_UNKNOWN_MEM_ZONE,
TRI_JsonString(TRI_UNKNOWN_MEM_ZONE,
responseBodyBuf.begin()));
if (JsonHelper::getBooleanValue(responseBodyJson.json(), "error", true)) {
THROW_ARANGO_EXCEPTION(TRI_ERROR_CLUSTER_AQL_COMMUNICATION);
}
size_t skipped = JsonHelper::getNumericValue<size_t>(responseBodyJson.json(),
"skipped", 0);
return skipped;
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////

View File

@ -39,6 +39,7 @@
#include "Utils/AqlTransaction.h" #include "Utils/AqlTransaction.h"
#include "Utils/transactions.h" #include "Utils/transactions.h"
#include "Utils/V8TransactionContext.h" #include "Utils/V8TransactionContext.h"
#include "Cluster/ClusterComm.h"
namespace triagens { namespace triagens {
namespace aql { namespace aql {
@ -1638,20 +1639,33 @@ public:
class RemoteBlock : public ExecutionBlock { class RemoteBlock : public ExecutionBlock {
////////////////////////////////////////////////////////////////////////////////
/// @brief constructors/destructors
////////////////////////////////////////////////////////////////////////////////
public: public:
RemoteBlock (ExecutionEngine* engine, RemoteBlock (ExecutionEngine* engine,
RemoteNode const* en, RemoteNode const* en,
std::string const& server, std::string const& server,
std::string const& ownName) std::string const& ownName,
std::string const& queryId)
: ExecutionBlock(engine, en), : ExecutionBlock(engine, en),
_server(server), _server(server),
_ownName(ownName) { _ownName(ownName),
_queryId(queryId) {
TRI_ASSERT(! queryId.empty());
} }
~RemoteBlock () { ~RemoteBlock () {
} }
////////////////////////////////////////////////////////////////////////////////
/// @brief timeout
////////////////////////////////////////////////////////////////////////////////
static double const defaultTimeOut;
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
/// @brief initialize /// @brief initialize
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
@ -1702,19 +1716,35 @@ public:
int64_t remaining () final; int64_t remaining () final;
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
/// @brief our server, can be like "shard:S1000" or like "server:Claus" /// @brief internal method to send a request
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
private: private:
std::string _server; triagens::arango::ClusterCommResult* sendRequest (
rest::HttpRequest::HttpRequestType type,
std::string urlPart,
std::string const& body);
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
/// @brief our server, can be like "shard:S1000" or like "server:Claus" /// @brief our server, can be like "shard:S1000" or like "server:Claus"
////////////////////////////////////////////////////////////////////////////////
std::string _server;
////////////////////////////////////////////////////////////////////////////////
/// @brief our own identity, in case of the coordinator this is empty,
/// in case of the DBservers, this is the shard ID as a string
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
std::string _ownName; std::string _ownName;
////////////////////////////////////////////////////////////////////////////////
/// @brief the ID of the query on the server as a string
////////////////////////////////////////////////////////////////////////////////
std::string _queryId;
}; };
} // namespace triagens::aql } // namespace triagens::aql

View File

@ -451,7 +451,8 @@ struct CoordinatorInstanciator : public WalkerWorker<ExecutionNode> {
// now we'll create a remote node for each shard and add it to the gather node // now we'll create a remote node for each shard and add it to the gather node
auto&& shardIds = static_cast<GatherNode const*>((*en))->collection()->shardIds(); auto&& shardIds = static_cast<GatherNode const*>((*en))->collection()->shardIds();
for (auto shardId : shardIds) { for (auto shardId : shardIds) {
ExecutionBlock* r = new RemoteBlock(engine.get(), remoteNode, "shard:" + shardId, ""); // TODO: pass actual queryId into RemoteBlock
ExecutionBlock* r = new RemoteBlock(engine.get(), remoteNode, "shard:" + shardId, "", "" /*TODO*/);
try { try {
engine.get()->addBlock(r); engine.get()->addBlock(r);

View File

@ -114,6 +114,22 @@ namespace triagens {
return _query; return _query;
} }
////////////////////////////////////////////////////////////////////////////////
/// @brief initializeCursor, could be called multiple times
////////////////////////////////////////////////////////////////////////////////
int initializeCursor (AqlItemBlock* items, size_t pos) {
return _root->initializeCursor(items, pos);
}
////////////////////////////////////////////////////////////////////////////////
/// @brief shutdown, will be called exactly once for the whole query
////////////////////////////////////////////////////////////////////////////////
int shutdown () {
return _root->shutdown();
}
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
/// @brief getSome /// @brief getSome
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
@ -122,6 +138,14 @@ namespace triagens {
return _root->getSome(atLeast, atMost); return _root->getSome(atLeast, atMost);
} }
////////////////////////////////////////////////////////////////////////////////
/// @brief skipSome
////////////////////////////////////////////////////////////////////////////////
size_t skipSome (size_t atLeast, size_t atMost) {
return _root->skipSome(atLeast, atMost);
}
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
/// @brief getOne /// @brief getOne
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////

View File

@ -93,10 +93,10 @@ void ExecutionNode::validateType (int type) {
} }
} }
void ExecutionNode::getSortElements(SortElementVector elements, void ExecutionNode::getSortElements(SortElementVector& elements,
ExecutionPlan* plan, ExecutionPlan* plan,
triagens::basics::Json const& oneNode, triagens::basics::Json const& oneNode,
char const* which) char const* which)
{ {
triagens::basics::Json jsonElements = oneNode.get("elements"); triagens::basics::Json jsonElements = oneNode.get("elements");
if (! jsonElements.isList()){ if (! jsonElements.isList()){

View File

@ -505,17 +505,17 @@ namespace triagens {
static Variable* varFromJson (Ast* ast, static Variable* varFromJson (Ast* ast,
triagens::basics::Json const& base, triagens::basics::Json const& base,
const char *variableName, char const* variableName,
bool optional = false); bool optional = false);
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
/// @brief factory for sort Elements from json. /// @brief factory for sort Elements from json.
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
static void getSortElements(SortElementVector elements, static void getSortElements (SortElementVector& elements,
ExecutionPlan* plan, ExecutionPlan* plan,
triagens::basics::Json const& oneNode, triagens::basics::Json const& oneNode,
char const* which); char const* which);
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
/// @brief toJsonHelper, for a generic node /// @brief toJsonHelper, for a generic node
@ -1697,7 +1697,7 @@ namespace triagens {
/// @brief get Variables Used Here including ASC/DESC /// @brief get Variables Used Here including ASC/DESC
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
SortElementVector getElements () const { SortElementVector const & getElements () const {
return _elements; return _elements;
} }
@ -2866,11 +2866,11 @@ namespace triagens {
/// @brief get Variables Used Here including ASC/DESC /// @brief get Variables Used Here including ASC/DESC
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
SortElementVector getElements () const { SortElementVector const & getElements () const {
return _elements; return _elements;
} }
void setElements (SortElementVector const src) { void setElements (SortElementVector const & src) {
_elements = src; _elements = src;
} }
@ -2890,7 +2890,6 @@ namespace triagens {
return _collection; return _collection;
} }
private: private:
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////

View File

@ -381,7 +381,8 @@ void RestAqlHandler::deleteQuery (std::string const& idString) {
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
/// @brief PUT method for /_api/aql/<operation>/<queryId>, this is using /// @brief PUT method for /_api/aql/<operation>/<queryId>, this is using
/// the part of the cursor API with side effects. /// the part of the cursor API with side effects.
/// <operation>: can be "getSome" or "skip". /// <operation>: can be "getSome" or "skip" or "initializeCursor" or
/// "shutdown".
/// The body must be a Json with the following attributes: /// The body must be a Json with the following attributes:
/// For the "getSome" operation one has to give: /// For the "getSome" operation one has to give:
/// "atLeast": /// "atLeast":
@ -395,6 +396,19 @@ void RestAqlHandler::deleteQuery (std::string const& idString) {
/// AqlItemBlock. /// AqlItemBlock.
/// If "atLeast" is not given it defaults to 1, if "atMost" is not /// If "atLeast" is not given it defaults to 1, if "atMost" is not
/// given it defaults to ExecutionBlock::DefaultBatchSize. /// given it defaults to ExecutionBlock::DefaultBatchSize.
/// For the "skipSome" operation one has to give:
/// "atLeast":
/// "atMost": both must be positive integers, the cursor skips never
/// more than "atMost" items and tries to skip at least
/// "atLeast". Note that it is possible to skip fewer than
/// "atLeast", for example if there are only fewer items
/// left. However, the implementation may skip fewer items
/// than "atLeast" for internal reasons, for example to avoid
/// excessive copying. The result is a JSON object with a
/// single attribute "skipped" containing the number of
/// skipped items.
/// If "atLeast" is not given it defaults to 1, if "atMost" is not
/// given it defaults to ExecutionBlock::DefaultBatchSize.
/// For the "skip" operation one should give: /// For the "skip" operation one should give:
/// "number": must be a positive integer, the cursor skips as many items, /// "number": must be a positive integer, the cursor skips as many items,
/// possibly exhausting the cursor. /// possibly exhausting the cursor.
@ -402,6 +416,13 @@ void RestAqlHandler::deleteQuery (std::string const& idString) {
/// "errorMessage" (if applicable) and "exhausted" (boolean) /// "errorMessage" (if applicable) and "exhausted" (boolean)
/// to indicate whether or not the cursor is exhausted. /// to indicate whether or not the cursor is exhausted.
/// If "number" is not given it defaults to 1. /// If "number" is not given it defaults to 1.
/// For the "initializeCursor" operation, one has to bind the following
/// attributes:
/// "items": This is a serialised AqlItemBlock with usually only one row
/// and the correct number of columns.
/// "pos": The number of the row in "items" to take, usually 0.
/// For the "shutdown" operation no additional arguments are required and
/// an empty JSON object in the body is OK.
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
void RestAqlHandler::useQuery (std::string const& operation, void RestAqlHandler::useQuery (std::string const& operation,
@ -447,9 +468,27 @@ void RestAqlHandler::useQuery (std::string const& operation,
} }
} }
} }
else if (operation == "getSome") {
auto atLeast = JsonHelper::getNumericValue<uint64_t>(queryJson.json(),
"atLeast", 1);
auto atMost = JsonHelper::getNumericValue<uint64_t>(queryJson.json(),
"atMost", ExecutionBlock::DefaultBatchSize);
size_t skipped;
try {
skipped = query->engine()->skipSome(atLeast, atMost);
}
catch (...) {
_queryRegistry->close(vocbase, qId);
generateError(HttpResponse::SERVER_ERROR, TRI_ERROR_HTTP_SERVER_ERROR,
"skipSome lead to an exception");
return;
}
answerBody("skipped", Json(static_cast<double>(skipped)))
("error", Json(false));
}
else if (operation == "skip") { else if (operation == "skip") {
auto number = JsonHelper::getNumericValue<uint64_t>(queryJson.json(), auto number = JsonHelper::getNumericValue<uint64_t>(queryJson.json(),
"number", 1); "number", 1);
try { try {
bool exhausted = query->engine()->skip(number); bool exhausted = query->engine()->skip(number);
answerBody("exhausted", Json(exhausted)) answerBody("exhausted", Json(exhausted))
@ -462,6 +501,38 @@ void RestAqlHandler::useQuery (std::string const& operation,
return; return;
} }
} }
else if (operation == "initializeCursor") {
auto pos = JsonHelper::getNumericValue<size_t>(queryJson.json(),
"pos", 0);
std::unique_ptr<AqlItemBlock> items;
int res;
try {
items.reset(new AqlItemBlock(queryJson.get("items")));
res = query->engine()->initializeCursor(items.get(), pos);
}
catch (...) {
_queryRegistry->close(vocbase, qId);
generateError(HttpResponse::SERVER_ERROR, TRI_ERROR_HTTP_SERVER_ERROR,
"initializeCursor lead to an exception");
return;
}
answerBody("error", res == TRI_ERROR_NO_ERROR ? Json(false) : Json(true))
("code", Json(static_cast<double>(res)));
}
else if (operation == "shutdown") {
int res;
try {
res = query->engine()->shutdown();
}
catch (...) {
_queryRegistry->close(vocbase, qId);
generateError(HttpResponse::SERVER_ERROR, TRI_ERROR_HTTP_SERVER_ERROR,
"shutdown lead to an exception");
return;
}
answerBody("error", res == TRI_ERROR_NO_ERROR ? Json(false) : Json(true))
("code", Json(static_cast<double>(res)));
}
else { else {
_queryRegistry->close(vocbase, qId); _queryRegistry->close(vocbase, qId);
generateError(HttpResponse::NOT_FOUND, TRI_ERROR_HTTP_NOT_FOUND); generateError(HttpResponse::NOT_FOUND, TRI_ERROR_HTTP_NOT_FOUND);

View File

@ -860,7 +860,7 @@ void RestReplicationHandler::handleTrampolineCoordinator () {
} }
if (res->status == CL_COMM_ERROR) { if (res->status == CL_COMM_ERROR) {
// This could be a broken connection or an Http error: // This could be a broken connection or an Http error:
if (res->result == 0 || !res->result->isComplete()) { if (res->result == nullptr || !res->result->isComplete()) {
// there is no result // there is no result
delete res; delete res;
generateError(HttpResponse::BAD, TRI_ERROR_CLUSTER_CONNECTION_LOST, generateError(HttpResponse::BAD, TRI_ERROR_CLUSTER_CONNECTION_LOST,

View File

@ -144,6 +144,7 @@
"ERROR_CLUSTER_ONLY_ON_COORDINATOR" : { "code" : 1471, "message" : "this operation is only valid on a coordinator in a cluster" }, "ERROR_CLUSTER_ONLY_ON_COORDINATOR" : { "code" : 1471, "message" : "this operation is only valid on a coordinator in a cluster" },
"ERROR_CLUSTER_READING_PLAN_AGENCY" : { "code" : 1472, "message" : "error reading Plan in agency" }, "ERROR_CLUSTER_READING_PLAN_AGENCY" : { "code" : 1472, "message" : "error reading Plan in agency" },
"ERROR_CLUSTER_COULD_NOT_TRUNCATE_COLLECTION" : { "code" : 1473, "message" : "could not truncate collection" }, "ERROR_CLUSTER_COULD_NOT_TRUNCATE_COLLECTION" : { "code" : 1473, "message" : "could not truncate collection" },
"ERROR_CLUSTER_AQL_COMMUNICATION" : { "code" : 1474, "message" : "error in cluster internal communication for AQL" },
"ERROR_QUERY_KILLED" : { "code" : 1500, "message" : "query killed" }, "ERROR_QUERY_KILLED" : { "code" : 1500, "message" : "query killed" },
"ERROR_QUERY_PARSE" : { "code" : 1501, "message" : "%s" }, "ERROR_QUERY_PARSE" : { "code" : 1501, "message" : "%s" },
"ERROR_QUERY_EMPTY" : { "code" : 1502, "message" : "query is empty" }, "ERROR_QUERY_EMPTY" : { "code" : 1502, "message" : "query is empty" },

View File

@ -144,6 +144,7 @@
"ERROR_CLUSTER_ONLY_ON_COORDINATOR" : { "code" : 1471, "message" : "this operation is only valid on a coordinator in a cluster" }, "ERROR_CLUSTER_ONLY_ON_COORDINATOR" : { "code" : 1471, "message" : "this operation is only valid on a coordinator in a cluster" },
"ERROR_CLUSTER_READING_PLAN_AGENCY" : { "code" : 1472, "message" : "error reading Plan in agency" }, "ERROR_CLUSTER_READING_PLAN_AGENCY" : { "code" : 1472, "message" : "error reading Plan in agency" },
"ERROR_CLUSTER_COULD_NOT_TRUNCATE_COLLECTION" : { "code" : 1473, "message" : "could not truncate collection" }, "ERROR_CLUSTER_COULD_NOT_TRUNCATE_COLLECTION" : { "code" : 1473, "message" : "could not truncate collection" },
"ERROR_CLUSTER_AQL_COMMUNICATION" : { "code" : 1474, "message" : "error in cluster internal communication for AQL" },
"ERROR_QUERY_KILLED" : { "code" : 1500, "message" : "query killed" }, "ERROR_QUERY_KILLED" : { "code" : 1500, "message" : "query killed" },
"ERROR_QUERY_PARSE" : { "code" : 1501, "message" : "%s" }, "ERROR_QUERY_PARSE" : { "code" : 1501, "message" : "%s" },
"ERROR_QUERY_EMPTY" : { "code" : 1502, "message" : "query is empty" }, "ERROR_QUERY_EMPTY" : { "code" : 1502, "message" : "query is empty" },

View File

@ -86,6 +86,7 @@ var _ = require("underscore");
var testFuncs = {}; var testFuncs = {};
var print = require("internal").print; var print = require("internal").print;
var time = require("internal").time;
var fs = require("fs"); var fs = require("fs");
var download = require("internal").download; var download = require("internal").download;
var wait = require("internal").wait; var wait = require("internal").wait;
@ -398,17 +399,34 @@ function runThere (options, instanceInfo, file) {
function executeAndWait (cmd, args) { function executeAndWait (cmd, args) {
var pid = executeExternal(cmd, args); var pid = executeExternal(cmd, args);
var startTime = time();
var res = statusExternal(pid, true); var res = statusExternal(pid, true);
var deltaTime = time() - startTime;
if (res.status === "TERMINATED") { if (res.status === "TERMINATED") {
print("Finished: " + res.status + " Exitcode: " + res.exit + " Time Elapsed: " + deltaTime);
if (res.exit === 0) { if (res.exit === 0) {
return { status: true, message: "" }; return { status: true, message: "", duration: deltaTime};
} }
else { else {
return { status: false, message: "exit code was " + res.exit}; return { status: false, message: "exit code was " + res.exit, duration: deltaTime};
} }
} }
else if (res.status === "ABORTED") {
print("Finished: " + res.status + " Signal: " + res.signal + " Time Elapsed: " + deltaTime);
return {
status: false,
message: "irregular termination: " + res.status + " Exit-Signal: " + res.signal,
duration: deltaTime
};
}
else { else {
return { status: false, message: "irregular termination: " + res.status}; print("Finished: " + res.status + " Exitcode: " + res.exit + " Time Elapsed: " + deltaTime);
return {
status: false,
message: "irregular termination: " + res.status + " Exit-Code: " + res.exit,
duration: deltaTime
};
} }
} }
@ -671,19 +689,35 @@ function rubyTests (options, ssl) {
"--format", "d", "--require", tmpname, "--format", "d", "--require", tmpname,
fs.join("UnitTests","HttpInterface",n)]; fs.join("UnitTests","HttpInterface",n)];
var pid = executeExternal("rspec", args); var pid = executeExternal("rspec", args);
var startTime = time();
var r = statusExternal(pid, true); var r = statusExternal(pid, true);
var deltaTime = time() - startTime;
if (r.status === "TERMINATED") { if (r.status === "TERMINATED") {
print("Finished: " + r.status + " Signal: " + r.exit + " Time Elapsed: " + deltaTime);
if (r.exit === 0) { if (r.exit === 0) {
result[n] = { status: true, message: "" }; result[n] = { status: true, message: "", duration: deltaTime };
} }
else { else {
result[n] = { status: false, message: "exit code was " + r.exit}; result[n] = { status: false, message: " exit code was " + r.exit, duration: deltaTime};
} }
} }
else { else if (r.status === "ABORTED") {
result[n] = { status: false, message: "irregular termination: " + r.status}; print("Finished: " + r.status + " Signal: " + r.exit + " Time Elapsed: " + deltaTime);
result[n] = {
status: false,
message: "irregular termination: " + r.status + " Exit-Signal: " + r.signal,
duration: deltaTime
};
} }
else {
print("Finished: " + r.status + " Exit Status: " + r.exit + " Time Elapsed: " + deltaTime);
result[n] = {
status: false,
message: "irregular termination: " + r.status + " Exit-code: " + r.exit,
duration: deltaTime
};
}
if (r.status === false && !options.force) { if (r.status === false && !options.force) {
break; break;
} }

View File

@ -180,6 +180,7 @@ ERROR_CLUSTER_UNSUPPORTED,1470,"unsupported operation or parameter","Will be rai
ERROR_CLUSTER_ONLY_ON_COORDINATOR,1471,"this operation is only valid on a coordinator in a cluster","Will be raised if there is an attempt to run a coordinator-only operation on a different type of node." ERROR_CLUSTER_ONLY_ON_COORDINATOR,1471,"this operation is only valid on a coordinator in a cluster","Will be raised if there is an attempt to run a coordinator-only operation on a different type of node."
ERROR_CLUSTER_READING_PLAN_AGENCY,1472,"error reading Plan in agency","Will be raised if a coordinator or DBserver cannot read the Plan in the agency." ERROR_CLUSTER_READING_PLAN_AGENCY,1472,"error reading Plan in agency","Will be raised if a coordinator or DBserver cannot read the Plan in the agency."
ERROR_CLUSTER_COULD_NOT_TRUNCATE_COLLECTION,1473,"could not truncate collection","Will be raised if a coordinator cannot truncate all shards of a cluster collection." ERROR_CLUSTER_COULD_NOT_TRUNCATE_COLLECTION,1473,"could not truncate collection","Will be raised if a coordinator cannot truncate all shards of a cluster collection."
ERROR_CLUSTER_AQL_COMMUNICATION,1474,"error in cluster internal communication for AQL","Will be raised if the internal communication of the cluster for AQL produces an error."
################################################################################ ################################################################################
## ArangoDB query errors ## ArangoDB query errors

View File

@ -140,6 +140,7 @@ void TRI_InitialiseErrorMessages (void) {
REG_ERROR(ERROR_CLUSTER_ONLY_ON_COORDINATOR, "this operation is only valid on a coordinator in a cluster"); REG_ERROR(ERROR_CLUSTER_ONLY_ON_COORDINATOR, "this operation is only valid on a coordinator in a cluster");
REG_ERROR(ERROR_CLUSTER_READING_PLAN_AGENCY, "error reading Plan in agency"); REG_ERROR(ERROR_CLUSTER_READING_PLAN_AGENCY, "error reading Plan in agency");
REG_ERROR(ERROR_CLUSTER_COULD_NOT_TRUNCATE_COLLECTION, "could not truncate collection"); REG_ERROR(ERROR_CLUSTER_COULD_NOT_TRUNCATE_COLLECTION, "could not truncate collection");
REG_ERROR(ERROR_CLUSTER_AQL_COMMUNICATION, "error in cluster internal communication for AQL");
REG_ERROR(ERROR_QUERY_KILLED, "query killed"); REG_ERROR(ERROR_QUERY_KILLED, "query killed");
REG_ERROR(ERROR_QUERY_PARSE, "%s"); REG_ERROR(ERROR_QUERY_PARSE, "%s");
REG_ERROR(ERROR_QUERY_EMPTY, "query is empty"); REG_ERROR(ERROR_QUERY_EMPTY, "query is empty");

View File

@ -341,6 +341,9 @@
/// - 1473: @LIT{could not truncate collection} /// - 1473: @LIT{could not truncate collection}
/// Will be raised if a coordinator cannot truncate all shards of a cluster /// Will be raised if a coordinator cannot truncate all shards of a cluster
/// collection. /// collection.
/// - 1474: @LIT{error in cluster internal communication for AQL}
/// Will be raised if the internal communication of the cluster for AQL
/// produces an error.
/// - 1500: @LIT{query killed} /// - 1500: @LIT{query killed}
/// Will be raised when a running query is killed by an explicit admin /// Will be raised when a running query is killed by an explicit admin
/// command. /// command.
@ -1996,6 +1999,17 @@ void TRI_InitialiseErrorMessages (void);
#define TRI_ERROR_CLUSTER_COULD_NOT_TRUNCATE_COLLECTION (1473) #define TRI_ERROR_CLUSTER_COULD_NOT_TRUNCATE_COLLECTION (1473)
////////////////////////////////////////////////////////////////////////////////
/// @brief 1474: ERROR_CLUSTER_AQL_COMMUNICATION
///
/// error in cluster internal communication for AQL
///
/// Will be raised if the internal communication of the cluster for AQL
/// produces an error.
////////////////////////////////////////////////////////////////////////////////
#define TRI_ERROR_CLUSTER_AQL_COMMUNICATION (1474)
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
/// @brief 1500: ERROR_QUERY_KILLED /// @brief 1500: ERROR_QUERY_KILLED
/// ///

View File

@ -31,7 +31,9 @@ function resultsToXml(results, baseName) {
.text(xmlEscape(String(attrs[a]))).text("\""); .text(xmlEscape(String(attrs[a]))).text("\"");
} }
close && this.text("/"); if (close) {
this.text("/");
}
this.text(">\n"); this.text(">\n");
return this; return this;
@ -64,6 +66,13 @@ function resultsToXml(results, baseName) {
} }
} }
if ((!results[testrun][test].status) &&
results[testrun][test].hasOwnProperty('message'))
{
xml.elem("failure");
xml.text('<![CDATA[' + JSON.stringify(results[testrun][test].message) + ']]>\n');
xml.elem("/failure");
}
xml.elem("/testsuite"); xml.elem("/testsuite");
var fn = baseName + testrun.replace(/\//g, '_') + '_' + test.replace(/\//g, '_') + ".xml"; var fn = baseName + testrun.replace(/\//g, '_') + '_' + test.replace(/\//g, '_') + ".xml";
//print('Writing: '+ fn); //print('Writing: '+ fn);