From e2cfa056c089068655c659bf0f0369f90cb8c56a Mon Sep 17 00:00:00 2001 From: Jan Steemann Date: Sat, 1 Nov 2014 16:36:58 +0100 Subject: [PATCH 1/8] pass query warnings around in cluster --- arangod/Aql/ExecutionBlock.cpp | 21 ++- arangod/Aql/Query.cpp | 56 +++---- arangod/Aql/Query.h | 12 +- arangod/Aql/RestAqlHandler.cpp | 10 ++ js/server/tests/aql-modify-cluster.js | 193 ++++++++++++----------- js/server/tests/aql-modify-noncluster.js | 1 - 6 files changed, 163 insertions(+), 130 deletions(-) diff --git a/arangod/Aql/ExecutionBlock.cpp b/arangod/Aql/ExecutionBlock.cpp index 794c8d62e5..7c79a8ff9d 100644 --- a/arangod/Aql/ExecutionBlock.cpp +++ b/arangod/Aql/ExecutionBlock.cpp @@ -4706,13 +4706,30 @@ int RemoteBlock::shutdown (int errorCode) { return TRI_ERROR_NO_ERROR; } - // 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())); + // read "warnings" attribute if present and add it our query + if (responseBodyJson.isArray()) { + auto warnings = responseBodyJson.get("warnings"); + if (warnings.isList()) { + auto query = _engine->getQuery(); + for (size_t i = 0; i < warnings.size(); ++i) { + auto warning = warnings.at(i); + if (warning.isArray()) { + auto code = warning.get("code"); + auto message = warning.get("message"); + if (code.isNumber() && message.isString()) { + query->registerWarning(static_cast(code.json()->_value._number), + message.json()->_value._string.data); + } + } + } + } + } + return JsonHelper::getNumericValue (responseBodyJson.json(), "code", TRI_ERROR_INTERNAL); LEAVE_BLOCK diff --git a/arangod/Aql/Query.cpp b/arangod/Aql/Query.cpp index 94eabdd290..d79b0f133e 100644 --- a/arangod/Aql/Query.cpp +++ b/arangod/Aql/Query.cpp @@ -917,6 +917,34 @@ bool Query::getBooleanOption (char const* option, bool defaultValue) const { return valueJson->_value._boolean; } +//////////////////////////////////////////////////////////////////////////////// +/// @brief convert the list of warnings to JSON +//////////////////////////////////////////////////////////////////////////////// + +TRI_json_t* Query::warningsToJson () const { + if (_warnings.empty()) { + return nullptr; + } + + size_t const n = _warnings.size(); + TRI_json_t* json = TRI_CreateList2Json(TRI_UNKNOWN_MEM_ZONE, n); + + if (json != nullptr) { + for (size_t i = 0; i < n; ++i) { + TRI_json_t* error = TRI_CreateArray2Json(TRI_UNKNOWN_MEM_ZONE, 2); + + if (error != nullptr) { + TRI_Insert3ArrayJson(TRI_UNKNOWN_MEM_ZONE, error, "code", TRI_CreateNumberJson(TRI_UNKNOWN_MEM_ZONE, static_cast(_warnings[i].first))); + TRI_Insert3ArrayJson(TRI_UNKNOWN_MEM_ZONE, error, "message", TRI_CreateString2CopyJson(TRI_UNKNOWN_MEM_ZONE, _warnings[i].second.c_str(), _warnings[i].second.size())); + + TRI_PushBack3ListJson(TRI_UNKNOWN_MEM_ZONE, json, error); + } + } + } + + return json; +} + // ----------------------------------------------------------------------------- // --SECTION-- private methods // ----------------------------------------------------------------------------- @@ -1092,34 +1120,6 @@ void Query::setPlan (ExecutionPlan *plan) { _plan = plan; } -//////////////////////////////////////////////////////////////////////////////// -/// @brief convert the list of warnings to JSON -//////////////////////////////////////////////////////////////////////////////// - -TRI_json_t* Query::warningsToJson () const { - if (_warnings.empty()) { - return nullptr; - } - - size_t const n = _warnings.size(); - TRI_json_t* json = TRI_CreateList2Json(TRI_UNKNOWN_MEM_ZONE, n); - - if (json != nullptr) { - for (size_t i = 0; i < n; ++i) { - TRI_json_t* error = TRI_CreateArray2Json(TRI_UNKNOWN_MEM_ZONE, 2); - - if (error != nullptr) { - TRI_Insert3ArrayJson(TRI_UNKNOWN_MEM_ZONE, error, "code", TRI_CreateNumberJson(TRI_UNKNOWN_MEM_ZONE, static_cast(_warnings[i].first))); - TRI_Insert3ArrayJson(TRI_UNKNOWN_MEM_ZONE, error, "message", TRI_CreateString2CopyJson(TRI_UNKNOWN_MEM_ZONE, _warnings[i].second.c_str(), _warnings[i].second.size())); - - TRI_PushBack3ListJson(TRI_UNKNOWN_MEM_ZONE, json, error); - } - } - } - - return json; -} - //////////////////////////////////////////////////////////////////////////////// /// @brief create a TransactionContext //////////////////////////////////////////////////////////////////////////////// diff --git a/arangod/Aql/Query.h b/arangod/Aql/Query.h index 5bc4a17310..e75b4d7553 100644 --- a/arangod/Aql/Query.h +++ b/arangod/Aql/Query.h @@ -405,6 +405,12 @@ namespace triagens { bool getBooleanOption (char const*, bool) const; +//////////////////////////////////////////////////////////////////////////////// +/// @brief convert the list of warnings to JSON +//////////////////////////////////////////////////////////////////////////////// + + TRI_json_t* warningsToJson () const; + // ----------------------------------------------------------------------------- // --SECTION-- private methods // ----------------------------------------------------------------------------- @@ -452,12 +458,6 @@ namespace triagens { void cleanupPlanAndEngine (int); -//////////////////////////////////////////////////////////////////////////////// -/// @brief convert the list of warnings to JSON -//////////////////////////////////////////////////////////////////////////////// - - TRI_json_t* warningsToJson () const; - //////////////////////////////////////////////////////////////////////////////// /// @brief create a TransactionContext //////////////////////////////////////////////////////////////////////////////// diff --git a/arangod/Aql/RestAqlHandler.cpp b/arangod/Aql/RestAqlHandler.cpp index 91e8c09e2e..f863cb34ef 100644 --- a/arangod/Aql/RestAqlHandler.cpp +++ b/arangod/Aql/RestAqlHandler.cpp @@ -841,7 +841,17 @@ void RestAqlHandler::handleUseQuery (std::string const& operation, try { res = query->engine()->shutdown(errorCode); // pass errorCode to shutdown + + // return statistics answerBody("stats", query->getStats()); + + // return warnings if present + auto warnings = query->warningsToJson(); + if (warnings != nullptr) { + answerBody("warnings", Json(TRI_UNKNOWN_MEM_ZONE, warnings)); + } + + // delete the query from the registry _queryRegistry->destroy(_vocbase, _qId, errorCode); } catch (...) { diff --git a/js/server/tests/aql-modify-cluster.js b/js/server/tests/aql-modify-cluster.js index 5e96cbe232..1e6addd66c 100644 --- a/js/server/tests/aql-modify-cluster.js +++ b/js/server/tests/aql-modify-cluster.js @@ -36,6 +36,14 @@ var cluster = require("org/arangodb/cluster"); var getModifyQueryResults = helper.getModifyQueryResults; var assertQueryError = helper.assertQueryError; +var sanitizeStats = function (stats) { + // remove these members from the stats because they don't matter + // for the comparisons + delete stats.scannedFull; + delete stats.scannedIndex; + return stats; +}; + //////////////////////////////////////////////////////////////////////////////// /// @brief test suite //////////////////////////////////////////////////////////////////////////////// @@ -203,11 +211,11 @@ function ahuacatlRemoveSuite () { //////////////////////////////////////////////////////////////////////////////// testRemoveNothing : function () { - var expected = { executed: 0, ignored: 0 }; + var expected = { writesExecuted: 0, writesIgnored: 0 }; var actual = getModifyQueryResults("FOR d IN " + cn1 + " FILTER d.value1 < 0 REMOVE d IN " + cn1); assertEqual(100, c1.count()); - assertEqual(expected, actual.operations); + assertEqual(expected, sanitizeStats(actual)); }, //////////////////////////////////////////////////////////////////////////////// @@ -215,11 +223,11 @@ function ahuacatlRemoveSuite () { //////////////////////////////////////////////////////////////////////////////// testRemoveNothingBind : function () { - var expected = { executed: 0, ignored: 0 }; + var expected = { writesExecuted: 0, writesIgnored: 0 }; var actual = getModifyQueryResults("FOR d IN @@cn FILTER d.value1 < 0 REMOVE d IN @@cn", { "@cn": cn1 }); assertEqual(100, c1.count()); - assertEqual(expected, actual.operations); + assertEqual(expected, sanitizeStats(actual)); }, //////////////////////////////////////////////////////////////////////////////// @@ -268,11 +276,11 @@ function ahuacatlRemoveSuite () { //////////////////////////////////////////////////////////////////////////////// testRemoveIgnore1 : function () { - var expected = { executed: 0, ignored: 100 }; + var expected = { writesExecuted: 0, writesIgnored: 100 }; var actual = getModifyQueryResults("FOR d IN @@cn REMOVE 'foo' IN @@cn OPTIONS { ignoreErrors: true }", { "@cn": cn1 }); assertEqual(100, c1.count()); - assertEqual(expected, actual.operations); + assertEqual(expected, sanitizeStats(actual)); }, //////////////////////////////////////////////////////////////////////////////// @@ -280,11 +288,11 @@ function ahuacatlRemoveSuite () { //////////////////////////////////////////////////////////////////////////////// testRemoveIgnore2 : function () { - var expected = { executed: 100, ignored: 1 }; + var expected = { writesExecuted: 100, writesIgnored: 1 }; var actual = getModifyQueryResults("FOR i IN 0..100 REMOVE CONCAT('test', TO_STRING(i)) IN @@cn OPTIONS { ignoreErrors: true }", { "@cn": cn1 }); assertEqual(0, c1.count()); - assertEqual(expected, actual.operations); + assertEqual(expected, sanitizeStats(actual)); }, //////////////////////////////////////////////////////////////////////////////// @@ -292,11 +300,11 @@ function ahuacatlRemoveSuite () { //////////////////////////////////////////////////////////////////////////////// testRemoveAll1 : function () { - var expected = { executed: 100, ignored: 0 }; + var expected = { writesExecuted: 100, writesIgnored: 0 }; var actual = getModifyQueryResults("FOR d IN @@cn REMOVE d IN @@cn", { "@cn": cn1 }); assertEqual(0, c1.count()); - assertEqual(expected, actual.operations); + assertEqual(expected, sanitizeStats(actual)); }, //////////////////////////////////////////////////////////////////////////////// @@ -304,11 +312,11 @@ function ahuacatlRemoveSuite () { //////////////////////////////////////////////////////////////////////////////// testRemoveAll2 : function () { - var expected = { executed: 100, ignored: 0 }; + var expected = { writesExecuted: 100, writesIgnored: 0 }; var actual = getModifyQueryResults("FOR d IN @@cn REMOVE d._key IN @@cn", { "@cn": cn1 }); assertEqual(0, c1.count()); - assertEqual(expected, actual.operations); + assertEqual(expected, sanitizeStats(actual)); }, //////////////////////////////////////////////////////////////////////////////// @@ -316,11 +324,11 @@ function ahuacatlRemoveSuite () { //////////////////////////////////////////////////////////////////////////////// testRemoveAll3 : function () { - var expected = { executed: 100, ignored: 0 }; + var expected = { writesExecuted: 100, writesIgnored: 0 }; var actual = getModifyQueryResults("FOR d IN @@cn REMOVE { _key: d._key } IN @@cn", { "@cn": cn1 }); assertEqual(0, c1.count()); - assertEqual(expected, actual.operations); + assertEqual(expected, sanitizeStats(actual)); }, //////////////////////////////////////////////////////////////////////////////// @@ -328,11 +336,11 @@ function ahuacatlRemoveSuite () { //////////////////////////////////////////////////////////////////////////////// testRemoveAll4 : function () { - var expected = { executed: 100, ignored: 0 }; + var expected = { writesExecuted: 100, writesIgnored: 0 }; var actual = getModifyQueryResults("FOR i IN 0..99 REMOVE { _key: CONCAT('test', TO_STRING(i)) } IN @@cn", { "@cn": cn1 }); assertEqual(0, c1.count()); - assertEqual(expected, actual.operations); + assertEqual(expected, sanitizeStats(actual)); }, //////////////////////////////////////////////////////////////////////////////// @@ -340,24 +348,23 @@ function ahuacatlRemoveSuite () { //////////////////////////////////////////////////////////////////////////////// testRemoveAll5 : function () { - var expected = { executed: 100, ignored: 0 }; + var expected = { writesExecuted: 100, writesIgnored: 0 }; var actual = getModifyQueryResults("FOR d IN @@cn REMOVE d INTO @@cn", { "@cn": cn1 }); assertEqual(0, c1.count()); - assertEqual(expected, actual.operations); + assertEqual(expected, sanitizeStats(actual)); }, - //////////////////////////////////////////////////////////////////////////////// /// @brief test remove //////////////////////////////////////////////////////////////////////////////// testRemoveHalf : function () { - var expected = { executed: 50, ignored: 0 }; + var expected = { writesExecuted: 50, writesIgnored: 0 }; var actual = getModifyQueryResults("FOR i IN 0..99 FILTER i % 2 == 0 REMOVE { _key: CONCAT('test', TO_STRING(i)) } IN @@cn", { "@cn": cn1 }); assertEqual(50, c1.count()); - assertEqual(expected, actual.operations); + assertEqual(expected, sanitizeStats(actual)); }, //////////////////////////////////////////////////////////////////////////////// @@ -373,11 +380,11 @@ function ahuacatlRemoveSuite () { //////////////////////////////////////////////////////////////////////////////// testSingle : function () { - var expected = { executed: 1, ignored: 0 }; + var expected = { writesExecuted: 1, writesIgnored: 0 }; var actual = getModifyQueryResults("REMOVE 'test0' IN @@cn", { "@cn": cn1 }); assertEqual(99, c1.count()); - assertEqual(expected, actual.operations); + assertEqual(expected, sanitizeStats(actual)); }, //////////////////////////////////////////////////////////////////////////////// @@ -396,12 +403,12 @@ function ahuacatlRemoveSuite () { //////////////////////////////////////////////////////////////////////////////// testTwoCollectionsJoin1 : function () { - var expected = { executed: 50, ignored: 0 }; + var expected = { writesExecuted: 50, writesIgnored: 0 }; var actual = getModifyQueryResults("FOR d IN @@cn1 FILTER d.value1 < 50 REMOVE { _key: d._key } IN @@cn2", { "@cn1": cn1, "@cn2": cn2 }); assertEqual(100, c1.count()); assertEqual(0, c2.count()); - assertEqual(expected, actual.operations); + assertEqual(expected, sanitizeStats(actual)); }, //////////////////////////////////////////////////////////////////////////////// @@ -409,12 +416,12 @@ function ahuacatlRemoveSuite () { //////////////////////////////////////////////////////////////////////////////// testTwoCollectionsJoin2 : function () { - var expected = { executed: 48, ignored: 0 }; + var expected = { writesExecuted: 48, writesIgnored: 0 }; var actual = getModifyQueryResults("FOR d IN @@cn1 FILTER d.value1 >= 2 && d.value1 < 50 REMOVE { _key: d._key } IN @@cn2", { "@cn1": cn1, "@cn2": cn2 }); assertEqual(100, c1.count()); assertEqual(2, c2.count()); - assertEqual(expected, actual.operations); + assertEqual(expected, sanitizeStats(actual)); }, //////////////////////////////////////////////////////////////////////////////// @@ -422,12 +429,12 @@ function ahuacatlRemoveSuite () { //////////////////////////////////////////////////////////////////////////////// testTwoCollectionsIgnoreErrors1 : function () { - var expected = { executed: 50, ignored: 50 }; + var expected = { writesExecuted: 50, writesIgnored: 50 }; var actual = getModifyQueryResults("FOR d IN @@cn1 REMOVE { _key: d._key } IN @@cn2 OPTIONS { ignoreErrors: true }", { "@cn1": cn1, "@cn2": cn2 }); assertEqual(100, c1.count()); assertEqual(0, c2.count()); - assertEqual(expected, actual.operations); + assertEqual(expected, sanitizeStats(actual)); }, //////////////////////////////////////////////////////////////////////////////// @@ -435,12 +442,12 @@ function ahuacatlRemoveSuite () { //////////////////////////////////////////////////////////////////////////////// testTwoCollectionsIgnoreErrors2 : function () { - var expected = { executed: 0, ignored: 100 }; + var expected = { writesExecuted: 0, writesIgnored: 100 }; var actual = getModifyQueryResults("FOR d IN @@cn1 REMOVE { _key: CONCAT('foo', d._key) } IN @@cn2 OPTIONS { ignoreErrors: true }", { "@cn1": cn1, "@cn2": cn2 }); assertEqual(100, c1.count()); assertEqual(50, c2.count()); - assertEqual(expected, actual.operations); + assertEqual(expected, sanitizeStats(actual)); }, //////////////////////////////////////////////////////////////////////////////// @@ -448,11 +455,11 @@ function ahuacatlRemoveSuite () { //////////////////////////////////////////////////////////////////////////////// testRemoveWaitForSync : function () { - var expected = { executed: 100, ignored: 0 }; + var expected = { writesExecuted: 100, writesIgnored: 0 }; var actual = getModifyQueryResults("FOR d IN @@cn REMOVE d IN @@cn OPTIONS { waitForSync: true }", { "@cn": cn1 }); assertEqual(0, c1.count()); - assertEqual(expected, actual.operations); + assertEqual(expected, sanitizeStats(actual)); }, //////////////////////////////////////////////////////////////////////////////// @@ -466,12 +473,12 @@ function ahuacatlRemoveSuite () { for (var i = 0; i < 100; ++i) { edge.save("UnitTestsAhuacatlRemove1/foo" + i, "UnitTestsAhuacatlRemove2/bar", { what: i, _key: "test" + i }); } - var expected = { executed: 10, ignored: 0 }; + var expected = { writesExecuted: 10, writesIgnored: 0 }; var actual = getModifyQueryResults("FOR i IN 0..9 REMOVE CONCAT('test', TO_STRING(i)) IN @@cn", { "@cn": edge.name() }); assertEqual(100, c1.count()); assertEqual(90, edge.count()); - assertEqual(expected, actual.operations); + assertEqual(expected, sanitizeStats(actual)); db._drop("UnitTestsAhuacatlEdge"); } @@ -526,11 +533,11 @@ function ahuacatlInsertSuite () { //////////////////////////////////////////////////////////////////////////////// testInsertNothing : function () { - var expected = { executed: 0, ignored: 0 }; + var expected = { writesExecuted: 0, writesIgnored: 0 }; var actual = getModifyQueryResults("FOR d IN " + cn1 + " FILTER d.value1 < 0 INSERT { foxx: true } IN " + cn1); assertEqual(100, c1.count()); - assertEqual(expected, actual.operations); + assertEqual(expected, sanitizeStats(actual)); }, //////////////////////////////////////////////////////////////////////////////// @@ -538,11 +545,11 @@ function ahuacatlInsertSuite () { //////////////////////////////////////////////////////////////////////////////// testInsertNothingBind : function () { - var expected = { executed: 0, ignored: 0 }; + var expected = { writesExecuted: 0, writesIgnored: 0 }; var actual = getModifyQueryResults("FOR d IN @@cn FILTER d.value1 < 0 INSERT { foxx: true } IN @@cn", { "@cn": cn1 }); assertEqual(100, c1.count()); - assertEqual(expected, actual.operations); + assertEqual(expected, sanitizeStats(actual)); }, //////////////////////////////////////////////////////////////////////////////// @@ -606,11 +613,11 @@ function ahuacatlInsertSuite () { //////////////////////////////////////////////////////////////////////////////// testInsertIgnore1 : function () { - var expected = { executed: 0, ignored: 100 }; + var expected = { writesExecuted: 0, writesIgnored: 100 }; var actual = getModifyQueryResults("FOR d IN @@cn INSERT d IN @@cn OPTIONS { ignoreErrors: true }", { "@cn": cn1 }); assertEqual(100, c1.count()); - assertEqual(expected, actual.operations); + assertEqual(expected, sanitizeStats(actual)); }, //////////////////////////////////////////////////////////////////////////////// @@ -618,11 +625,11 @@ function ahuacatlInsertSuite () { //////////////////////////////////////////////////////////////////////////////// testInsertIgnore2 : function () { - var expected = { executed: 1, ignored: 50 }; + var expected = { writesExecuted: 1, writesIgnored: 50 }; var actual = getModifyQueryResults("FOR i IN 50..100 INSERT { _key: CONCAT('test', TO_STRING(i)) } IN @@cn OPTIONS { ignoreErrors: true }", { "@cn": cn1 }); assertEqual(101, c1.count()); - assertEqual(expected, actual.operations); + assertEqual(expected, sanitizeStats(actual)); }, //////////////////////////////////////////////////////////////////////////////// @@ -630,11 +637,11 @@ function ahuacatlInsertSuite () { //////////////////////////////////////////////////////////////////////////////// testInsertIgnore3 : function () { - var expected = { executed: 51, ignored: 50 }; + var expected = { writesExecuted: 51, writesIgnored: 50 }; var actual = getModifyQueryResults("FOR i IN 0..100 INSERT { _key: CONCAT('test', TO_STRING(i)) } IN @@cn OPTIONS { ignoreErrors: true }", { "@cn": cn2 }); assertEqual(101, c2.count()); - assertEqual(expected, actual.operations); + assertEqual(expected, sanitizeStats(actual)); }, //////////////////////////////////////////////////////////////////////////////// @@ -642,11 +649,11 @@ function ahuacatlInsertSuite () { //////////////////////////////////////////////////////////////////////////////// testInsertIgnore4 : function () { - var expected = { executed: 0, ignored: 100 }; + var expected = { writesExecuted: 0, writesIgnored: 100 }; var actual = getModifyQueryResults("FOR i IN 0..99 INSERT { _key: CONCAT('test', TO_STRING(i)) } IN @@cn OPTIONS { ignoreErrors: true }", { "@cn": cn1 }); assertEqual(100, c1.count()); - assertEqual(expected, actual.operations); + assertEqual(expected, sanitizeStats(actual)); }, //////////////////////////////////////////////////////////////////////////////// @@ -654,11 +661,11 @@ function ahuacatlInsertSuite () { //////////////////////////////////////////////////////////////////////////////// testInsertIgnore5 : function () { - var expected = { executed: 50, ignored: 50 }; + var expected = { writesExecuted: 50, writesIgnored: 50 }; var actual = getModifyQueryResults("FOR i IN 0..99 INSERT { _key: CONCAT('test', TO_STRING(i)) } IN @@cn OPTIONS { ignoreErrors: true }", { "@cn": cn2 }); assertEqual(100, c2.count()); - assertEqual(expected, actual.operations); + assertEqual(expected, sanitizeStats(actual)); }, //////////////////////////////////////////////////////////////////////////////// @@ -666,11 +673,11 @@ function ahuacatlInsertSuite () { //////////////////////////////////////////////////////////////////////////////// testInsertEmpty : function () { - var expected = { executed: 100, ignored: 0 }; + var expected = { writesExecuted: 100, writesIgnored: 0 }; var actual = getModifyQueryResults("FOR d IN @@cn INSERT { } IN @@cn", { "@cn": cn1 }); assertEqual(200, c1.count()); - assertEqual(expected, actual.operations); + assertEqual(expected, sanitizeStats(actual)); }, //////////////////////////////////////////////////////////////////////////////// @@ -678,12 +685,12 @@ function ahuacatlInsertSuite () { //////////////////////////////////////////////////////////////////////////////// testInsertCopy : function () { - var expected = { executed: 50, ignored: 0 }; + var expected = { writesExecuted: 50, writesIgnored: 0 }; var actual = getModifyQueryResults("FOR i IN 50..99 INSERT { _key: CONCAT('test', TO_STRING(i)) } IN @@cn", { "@cn": cn2 }); assertEqual(100, c1.count()); assertEqual(100, c2.count()); - assertEqual(expected, actual.operations); + assertEqual(expected, sanitizeStats(actual)); }, //////////////////////////////////////////////////////////////////////////////// @@ -691,12 +698,12 @@ function ahuacatlInsertSuite () { //////////////////////////////////////////////////////////////////////////////// testSingle : function () { - var expected = { executed: 1, ignored: 0 }; + var expected = { writesExecuted: 1, writesIgnored: 0 }; var actual = getModifyQueryResults("INSERT { value: 'foobar', _key: 'test' } IN @@cn", { "@cn": cn1 }); assertEqual(101, c1.count()); assertEqual("foobar", c1.document("test").value); - assertEqual(expected, actual.operations); + assertEqual(expected, sanitizeStats(actual)); }, //////////////////////////////////////////////////////////////////////////////// @@ -704,11 +711,11 @@ function ahuacatlInsertSuite () { //////////////////////////////////////////////////////////////////////////////// testInsertWaitForSync : function () { - var expected = { executed: 50, ignored: 0 }; + var expected = { writesExecuted: 50, writesIgnored: 0 }; var actual = getModifyQueryResults("FOR i IN 1..50 INSERT { value: i } INTO @@cn OPTIONS { waitForSync: true }", { "@cn": cn2 }); assertEqual(100, c1.count()); - assertEqual(expected, actual.operations); + assertEqual(expected, sanitizeStats(actual)); }, //////////////////////////////////////////////////////////////////////////////// @@ -761,10 +768,10 @@ function ahuacatlInsertSuite () { db._drop("UnitTestsAhuacatlEdge"); var edge = db._createEdgeCollection("UnitTestsAhuacatlEdge"); - var expected = { executed: 50, ignored: 0 }; + var expected = { writesExecuted: 50, writesIgnored: 0 }; var actual = getModifyQueryResults("FOR i IN 1..50 INSERT { _key: CONCAT('test', TO_STRING(i)), _from: CONCAT('UnitTestsAhuacatlInsert1/', TO_STRING(i)), _to: CONCAT('UnitTestsAhuacatlInsert2/', TO_STRING(i)), value: [ i ], sub: { foo: 'bar' } } INTO @@cn", { "@cn": edge.name() }); - assertEqual(expected, actual.operations); + assertEqual(expected, sanitizeStats(actual)); assertEqual(50, edge.count()); for (var i = 1; i <= 50; ++i) { @@ -828,10 +835,10 @@ function ahuacatlUpdateSuite () { //////////////////////////////////////////////////////////////////////////////// testUpdateNothing : function () { - var expected = { executed: 0, ignored: 0 }; + var expected = { writesExecuted: 0, writesIgnored: 0 }; var actual = getModifyQueryResults("FOR d IN " + cn1 + " FILTER d.value1 < 0 UPDATE { foxx: true } IN " + cn1); - assertEqual(expected, actual.operations); + assertEqual(expected, sanitizeStats(actual)); }, //////////////////////////////////////////////////////////////////////////////// @@ -839,10 +846,10 @@ function ahuacatlUpdateSuite () { //////////////////////////////////////////////////////////////////////////////// testUpdateNothingBind : function () { - var expected = { executed: 0, ignored: 0 }; + var expected = { writesExecuted: 0, writesIgnored: 0 }; var actual = getModifyQueryResults("FOR d IN @@cn FILTER d.value1 < 0 UPDATE { foxx: true } IN @@cn", { "@cn": cn1 }); - assertEqual(expected, actual.operations); + assertEqual(expected, sanitizeStats(actual)); }, //////////////////////////////////////////////////////////////////////////////// @@ -893,10 +900,10 @@ function ahuacatlUpdateSuite () { testUpdateIgnore1 : function () { c1.ensureUniqueConstraint("value3"); - var expected = { executed: 1, ignored: 99 }; + var expected = { writesExecuted: 1, writesIgnored: 99 }; var actual = getModifyQueryResults("FOR d IN @@cn UPDATE d WITH { value3: 1 } IN @@cn OPTIONS { ignoreErrors: true }", { "@cn": cn1 }); - assertEqual(expected, actual.operations); + assertEqual(expected, sanitizeStats(actual)); }, //////////////////////////////////////////////////////////////////////////////// @@ -905,10 +912,10 @@ function ahuacatlUpdateSuite () { testUpdateIgnore2 : function () { c1.ensureUniqueConstraint("value1"); - var expected = { executed: 0, ignored: 51 }; + var expected = { writesExecuted: 0, writesIgnored: 51 }; var actual = getModifyQueryResults("FOR i IN 50..100 UPDATE { _key: CONCAT('test', TO_STRING(i)), value1: 1 } IN @@cn OPTIONS { ignoreErrors: true }", { "@cn": cn1 }); - assertEqual(expected, actual.operations); + assertEqual(expected, sanitizeStats(actual)); }, //////////////////////////////////////////////////////////////////////////////// @@ -916,10 +923,10 @@ function ahuacatlUpdateSuite () { //////////////////////////////////////////////////////////////////////////////// testUpdateEmpty1 : function () { - var expected = { executed: 100, ignored: 0 }; + var expected = { writesExecuted: 100, writesIgnored: 0 }; var actual = getModifyQueryResults("FOR d IN @@cn UPDATE { _key: d._key } IN @@cn", { "@cn": cn1 }); - assertEqual(expected, actual.operations); + assertEqual(expected, sanitizeStats(actual)); for (var i = 0; i < 100; ++i) { var doc = c1.document("test" + i); assertEqual(i, doc.value1); @@ -932,10 +939,10 @@ function ahuacatlUpdateSuite () { //////////////////////////////////////////////////////////////////////////////// testUpdateEmpty2 : function () { - var expected = { executed: 100, ignored: 0 }; + var expected = { writesExecuted: 100, writesIgnored: 0 }; var actual = getModifyQueryResults("FOR d IN @@cn UPDATE d IN @@cn", { "@cn": cn1 }); - assertEqual(expected, actual.operations); + assertEqual(expected, sanitizeStats(actual)); for (var i = 0; i < 100; ++i) { var doc = c1.document("test" + i); assertEqual(i, doc.value1); @@ -956,11 +963,11 @@ function ahuacatlUpdateSuite () { //////////////////////////////////////////////////////////////////////////////// testSingle : function () { - var expected = { executed: 1, ignored: 0 }; + var expected = { writesExecuted: 1, writesIgnored: 0 }; var actual = getModifyQueryResults("UPDATE { value: 'foobar', _key: 'test17' } IN @@cn", { "@cn": cn1 }); assertEqual("foobar", c1.document("test17").value); - assertEqual(expected, actual.operations); + assertEqual(expected, sanitizeStats(actual)); }, //////////////////////////////////////////////////////////////////////////////// @@ -968,9 +975,9 @@ function ahuacatlUpdateSuite () { //////////////////////////////////////////////////////////////////////////////// testUpdateOldValue : function () { - var expected = { executed: 100, ignored: 0 }; + var expected = { writesExecuted: 100, writesIgnored: 0 }; var actual = getModifyQueryResults("FOR d IN @@cn UPDATE { _key: d._key, value1: d.value2, value2: d.value1, value3: d.value1 + 5 } IN @@cn", { "@cn": cn1 }); - assertEqual(expected, actual.operations); + assertEqual(expected, sanitizeStats(actual)); for (var i = 0; i < 100; ++i) { var doc = c1.document("test" + i); @@ -985,9 +992,9 @@ function ahuacatlUpdateSuite () { //////////////////////////////////////////////////////////////////////////////// testUpdateWaitForSync : function () { - var expected = { executed: 50, ignored: 0 }; + var expected = { writesExecuted: 50, writesIgnored: 0 }; var actual = getModifyQueryResults("FOR i IN 1..50 UPDATE { _key: CONCAT('test', TO_STRING(i)) } INTO @@cn OPTIONS { waitForSync: true }", { "@cn": cn1 }); - assertEqual(expected, actual.operations); + assertEqual(expected, sanitizeStats(actual)); for (var i = 0; i < 100; ++i) { var doc = c1.document("test" + i); @@ -1001,9 +1008,9 @@ function ahuacatlUpdateSuite () { //////////////////////////////////////////////////////////////////////////////// testUpdateKeepNullDefault : function () { - var expected = { executed: 100, ignored: 0 }; + var expected = { writesExecuted: 100, writesIgnored: 0 }; var actual = getModifyQueryResults("FOR d IN @@cn UPDATE d._key WITH { value1: null, value3: 'foobar', value9: null } INTO @@cn", { "@cn": cn1 }); - assertEqual(expected, actual.operations); + assertEqual(expected, sanitizeStats(actual)); for (var i = 0; i < 100; ++i) { var doc = c1.document("test" + i); @@ -1019,9 +1026,9 @@ function ahuacatlUpdateSuite () { //////////////////////////////////////////////////////////////////////////////// testUpdateKeepNullTrue : function () { - var expected = { executed: 100, ignored: 0 }; + var expected = { writesExecuted: 100, writesIgnored: 0 }; var actual = getModifyQueryResults("FOR d IN @@cn UPDATE d._key WITH { value1: null, value3: 'foobar', value9: null } INTO @@cn OPTIONS { keepNull: true }", { "@cn": cn1 }); - assertEqual(expected, actual.operations); + assertEqual(expected, sanitizeStats(actual)); for (var i = 0; i < 100; ++i) { var doc = c1.document("test" + i); @@ -1037,9 +1044,9 @@ function ahuacatlUpdateSuite () { //////////////////////////////////////////////////////////////////////////////// testUpdateKeepNullFalse : function () { - var expected = { executed: 100, ignored: 0 }; + var expected = { writesExecuted: 100, writesIgnored: 0 }; var actual = getModifyQueryResults("FOR d IN @@cn UPDATE d._key WITH { value1: null, value3: 'foobar', value9: null } INTO @@cn OPTIONS { keepNull: false }", { "@cn": cn1 }); - assertEqual(expected, actual.operations); + assertEqual(expected, sanitizeStats(actual)); for (var i = 0; i < 100; ++i) { var doc = c1.document("test" + i); @@ -1055,9 +1062,9 @@ function ahuacatlUpdateSuite () { //////////////////////////////////////////////////////////////////////////////// testUpdateFilter : function () { - var expected = { executed: 50, ignored: 0 }; + var expected = { writesExecuted: 50, writesIgnored: 0 }; var actual = getModifyQueryResults("FOR d IN @@cn FILTER d.value1 % 2 == 0 UPDATE d._key WITH { value2: 100 } INTO @@cn", { "@cn": cn1 }); - assertEqual(expected, actual.operations); + assertEqual(expected, sanitizeStats(actual)); for (var i = 0; i < 100; ++i) { var doc = c1.document("test" + i); @@ -1076,10 +1083,10 @@ function ahuacatlUpdateSuite () { testUpdateUpdate : function () { var i; - var expected = { executed: 100, ignored: 0 }; + var expected = { writesExecuted: 100, writesIgnored: 0 }; for (i = 0; i < 5; ++i) { var actual = getModifyQueryResults("FOR d IN @@cn UPDATE d._key WITH { counter: HAS(d, 'counter') ? d.counter + 1 : 1 } INTO @@cn", { "@cn": cn1 }); - assertEqual(expected, actual.operations); + assertEqual(expected, sanitizeStats(actual)); } for (i = 0; i < 100; ++i) { @@ -1094,10 +1101,10 @@ function ahuacatlUpdateSuite () { testReplace1 : function () { var i; - var expected = { executed: 100, ignored: 0 }; + var expected = { writesExecuted: 100, writesIgnored: 0 }; for (i = 0; i < 5; ++i) { var actual = getModifyQueryResults("FOR d IN @@cn REPLACE d._key WITH { value4: 12 } INTO @@cn", { "@cn": cn1 }); - assertEqual(expected, actual.operations); + assertEqual(expected, sanitizeStats(actual)); } for (i = 0; i < 100; ++i) { @@ -1115,10 +1122,10 @@ function ahuacatlUpdateSuite () { testReplace2 : function () { var i; - var expected = { executed: 100, ignored: 0 }; + var expected = { writesExecuted: 100, writesIgnored: 0 }; for (i = 0; i < 5; ++i) { var actual = getModifyQueryResults("FOR d IN @@cn REPLACE { _key: d._key, value4: 13 } INTO @@cn", { "@cn": cn1 }); - assertEqual(expected, actual.operations); + assertEqual(expected, sanitizeStats(actual)); } for (i = 0; i < 100; ++i) { @@ -1136,10 +1143,10 @@ function ahuacatlUpdateSuite () { testReplaceReplace : function () { var i; - var expected = { executed: 100, ignored: 0 }; + var expected = { writesExecuted: 100, writesIgnored: 0 }; for (i = 0; i < 5; ++i) { var actual = getModifyQueryResults("FOR d IN @@cn REPLACE d._key WITH { value1: d.value1 + 1 } INTO @@cn", { "@cn": cn1 }); - assertEqual(expected, actual.operations); + assertEqual(expected, sanitizeStats(actual)); } for (i = 0; i < 100; ++i) { diff --git a/js/server/tests/aql-modify-noncluster.js b/js/server/tests/aql-modify-noncluster.js index e2e6a05103..cdb0d1a8bc 100644 --- a/js/server/tests/aql-modify-noncluster.js +++ b/js/server/tests/aql-modify-noncluster.js @@ -355,7 +355,6 @@ function ahuacatlRemoveSuite () { assertEqual(expected, sanitizeStats(actual)); }, - //////////////////////////////////////////////////////////////////////////////// /// @brief test remove //////////////////////////////////////////////////////////////////////////////// From d1bf47a14a8f2bf5aeb2e392c395f8a5f5573923 Mon Sep 17 00:00:00 2001 From: Jan Steemann Date: Sat, 1 Nov 2014 18:03:57 +0100 Subject: [PATCH 2/8] fixed issue when rolling back remove operations that could not remove from indexes --- arangod/HashIndex/hash-index.cpp | 16 + arangod/VocBase/document-collection.cpp | 18 +- arangod/Wal/DocumentOperation.h | 4 +- .../tests/shell-transactions-noncluster.js | 451 +++++++++++++++++- 4 files changed, 483 insertions(+), 6 deletions(-) diff --git a/arangod/HashIndex/hash-index.cpp b/arangod/HashIndex/hash-index.cpp index eaa986a8a0..5302516954 100644 --- a/arangod/HashIndex/hash-index.cpp +++ b/arangod/HashIndex/hash-index.cpp @@ -242,6 +242,10 @@ static int HashIndexHelperAllocate (TRI_hash_index_t const* hashIndex, static int HashIndex_insert (TRI_hash_index_t* hashIndex, TRI_hash_index_element_t* element, bool isRollback) { + TRI_IF_FAILURE("InsertHashIndex") { + return TRI_ERROR_DEBUG; + } + TRI_index_search_value_t key; int res = FillIndexSearchValueByHashIndexElement(hashIndex, &key, element); @@ -269,6 +273,10 @@ static int HashIndex_insert (TRI_hash_index_t* hashIndex, static int HashIndex_remove (TRI_hash_index_t* hashIndex, TRI_hash_index_element_t* element) { + TRI_IF_FAILURE("RemoveHashIndex") { + return TRI_ERROR_DEBUG; + } + int res = TRI_RemoveElementHashArray(&hashIndex->_hashArray, element); // this might happen when rolling back @@ -331,6 +339,10 @@ static TRI_index_result_t HashIndex_find (TRI_hash_index_t* hashIndex, static int MultiHashIndex_insert (TRI_hash_index_t* hashIndex, TRI_hash_index_element_multi_t* element, bool isRollback) { + TRI_IF_FAILURE("InsertHashIndex") { + return TRI_ERROR_DEBUG; + } + TRI_index_search_value_t key; int res = FillIndexSearchValueByHashIndexElement(hashIndex, &key, element); @@ -358,6 +370,10 @@ static int MultiHashIndex_insert (TRI_hash_index_t* hashIndex, int MultiHashIndex_remove (TRI_hash_index_t* hashIndex, TRI_hash_index_element_multi_t* element) { + TRI_IF_FAILURE("RemoveHashIndex") { + return TRI_ERROR_DEBUG; + } + TRI_index_search_value_t key; int res = FillIndexSearchValueByHashIndexElement(hashIndex, &key, element); diff --git a/arangod/VocBase/document-collection.cpp b/arangod/VocBase/document-collection.cpp index fc12779ce9..c8cc8fa0d3 100644 --- a/arangod/VocBase/document-collection.cpp +++ b/arangod/VocBase/document-collection.cpp @@ -259,6 +259,10 @@ static inline void SetRevision (TRI_document_collection_t* document, static int InsertPrimaryIndex (TRI_document_collection_t* document, TRI_doc_mptr_t const* header, bool isRollback) { + TRI_IF_FAILURE("InsertPrimaryIndex") { + return TRI_ERROR_DEBUG; + } + TRI_doc_mptr_t* found; TRI_ASSERT(document != nullptr); @@ -294,6 +298,10 @@ static int InsertPrimaryIndex (TRI_document_collection_t* document, static int InsertSecondaryIndexes (TRI_document_collection_t* document, TRI_doc_mptr_t const* header, bool isRollback) { + TRI_IF_FAILURE("InsertSecondaryIndexes") { + return TRI_ERROR_DEBUG; + } + if (! document->useSecondaryIndexes()) { return TRI_ERROR_NO_ERROR; } @@ -329,9 +337,9 @@ static int InsertSecondaryIndexes (TRI_document_collection_t* document, static int DeletePrimaryIndex (TRI_document_collection_t* document, TRI_doc_mptr_t const* header, bool isRollback) { - // ............................................................................. - // remove from main index - // ............................................................................. + TRI_IF_FAILURE("DeletePrimaryIndex") { + return TRI_ERROR_DEBUG; + } TRI_doc_mptr_t* found = static_cast(TRI_RemoveKeyPrimaryIndex(&document->_primaryIndex, TRI_EXTRACT_MARKER_KEY(header))); // ONLY IN INDEX, PROTECTED by RUNTIME @@ -353,6 +361,10 @@ static int DeleteSecondaryIndexes (TRI_document_collection_t* document, return TRI_ERROR_NO_ERROR; } + TRI_IF_FAILURE("DeleteSecondaryIndexes") { + return TRI_ERROR_DEBUG; + } + int result = TRI_ERROR_NO_ERROR; size_t const n = document->_allIndexes._length; diff --git a/arangod/Wal/DocumentOperation.h b/arangod/Wal/DocumentOperation.h index 40bb25441a..956f66c23e 100644 --- a/arangod/Wal/DocumentOperation.h +++ b/arangod/Wal/DocumentOperation.h @@ -118,7 +118,9 @@ namespace triagens { header->copy(oldHeader); } else if (type == TRI_VOC_DOCUMENT_OPERATION_REMOVE) { - document->_headersPtr->relink(header, &oldHeader); // PROTECTED by trx in trxCollection + if (status != StatusType::CREATED) { + document->_headersPtr->relink(header, &oldHeader); // PROTECTED by trx in trxCollection + } } status = StatusType::SWAPPED; diff --git a/js/server/tests/shell-transactions-noncluster.js b/js/server/tests/shell-transactions-noncluster.js index d0e40d1592..3926aba1d5 100644 --- a/js/server/tests/shell-transactions-noncluster.js +++ b/js/server/tests/shell-transactions-noncluster.js @@ -4227,6 +4227,452 @@ function transactionServerFailuresSuite () { internal.wait(0); }, +//////////////////////////////////////////////////////////////////////////////// +/// @brief test: rollback in case of a server-side fail +//////////////////////////////////////////////////////////////////////////////// + + testInsertUniqueHashIndexServerFailures : function () { + var failures = [ "InsertPrimaryIndex", "InsertSecondaryIndexes", "InsertHashIndex" ]; + + failures.forEach (function (f) { + internal.debugClearFailAt(); + db._drop(cn); + c = db._create(cn); + + c.ensureUniqueConstraint("value"); + + internal.debugSetFailAt(f); + + try { + c.save({ value: 1 }); + fail(); + } + catch (err) { + assertEqual(internal.errors.ERROR_DEBUG.code, err.errorNum); + } + + assertEqual(0, c.count()); + }); + }, + +//////////////////////////////////////////////////////////////////////////////// +/// @brief test: rollback in case of a server-side fail +//////////////////////////////////////////////////////////////////////////////// + + testInsertUniqueHashIndexServerFailuresTrx : function () { + var failures = [ "InsertPrimaryIndex", "InsertSecondaryIndexes", "InsertHashIndex" ]; + + failures.forEach (function (f) { + internal.debugClearFailAt(); + db._drop(cn); + c = db._create(cn); + + c.ensureUniqueConstraint("value"); + + db._executeTransaction({ + collections: { + write: cn + }, + action: function () { + c.save({ value: 1 }); + + internal.debugSetFailAt(f); + try { + c.save({ value: 2 }); + fail(); + } + catch (err) { + assertEqual(internal.errors.ERROR_DEBUG.code, err.errorNum); + } + + assertEqual(1, c.count()); + internal.debugClearFailAt(); + } + }); + }); + }, + +//////////////////////////////////////////////////////////////////////////////// +/// @brief test: rollback in case of a server-side fail +//////////////////////////////////////////////////////////////////////////////// + + testInsertUniqueHashIndexServerFailuresRollback : function () { + var failures = [ "InsertPrimaryIndex", "InsertSecondaryIndexes", "InsertHashIndex" ]; + + failures.forEach (function (f) { + internal.debugClearFailAt(); + db._drop(cn); + c = db._create(cn); + + c.ensureUniqueConstraint("value"); + + try { + db._executeTransaction({ + collections: { + write: cn + }, + action: function () { + c.save({ value: 1 }); + + internal.debugSetFailAt(f); + c.save({ value: 2 }); + } + }); + fail(); + } + catch (err) { + assertEqual(internal.errors.ERROR_DEBUG.code, err.errorNum); + } + + assertEqual(0, c.count()); + internal.debugClearFailAt(); + }); + }, + +//////////////////////////////////////////////////////////////////////////////// +/// @brief test: rollback in case of a server-side fail +//////////////////////////////////////////////////////////////////////////////// + + testInsertNonUniqueHashIndexServerFailures : function () { + var failures = [ "InsertPrimaryIndex", "InsertSecondaryIndexes", "InsertHashIndex" ]; + + failures.forEach (function (f) { + internal.debugClearFailAt(); + db._drop(cn); + c = db._create(cn); + + c.ensureHashIndex("value"); + + internal.debugSetFailAt(f); + + try { + c.save({ value: 1 }); + fail(); + } + catch (err) { + assertEqual(internal.errors.ERROR_DEBUG.code, err.errorNum); + } + + assertEqual(0, c.count()); + }); + }, + +//////////////////////////////////////////////////////////////////////////////// +/// @brief test: rollback in case of a server-side fail +//////////////////////////////////////////////////////////////////////////////// + + testInsertNonUniqueHashIndexServerFailuresTrx : function () { + var failures = [ "InsertPrimaryIndex", "InsertSecondaryIndexes", "InsertHashIndex" ]; + + failures.forEach (function (f) { + internal.debugClearFailAt(); + db._drop(cn); + c = db._create(cn); + + c.ensureHashIndex("value"); + + db._executeTransaction({ + collections: { + write: cn + }, + action: function () { + c.save({ value: 1 }); + + internal.debugSetFailAt(f); + try { + c.save({ value: 2 }); + fail(); + } + catch (err) { + assertEqual(internal.errors.ERROR_DEBUG.code, err.errorNum); + } + + assertEqual(1, c.count()); + internal.debugClearFailAt(); + } + }); + }); + }, + +//////////////////////////////////////////////////////////////////////////////// +/// @brief test: rollback in case of a server-side fail +//////////////////////////////////////////////////////////////////////////////// + + testInsertNonUniqueHashIndexServerFailuresRollback : function () { + var failures = [ "InsertPrimaryIndex", "InsertSecondaryIndexes", "InsertHashIndex" ]; + + failures.forEach (function (f) { + internal.debugClearFailAt(); + db._drop(cn); + c = db._create(cn); + + c.ensureHashIndex("value"); + + try { + db._executeTransaction({ + collections: { + write: cn + }, + action: function () { + c.save({ value: 1 }); + + internal.debugSetFailAt(f); + c.save({ value: 2 }); + } + }); + fail(); + } + catch (err) { + assertEqual(internal.errors.ERROR_DEBUG.code, err.errorNum); + } + + assertEqual(0, c.count()); + internal.debugClearFailAt(); + }); + }, + +//////////////////////////////////////////////////////////////////////////////// +/// @brief test: rollback in case of a server-side fail +//////////////////////////////////////////////////////////////////////////////// + + testRemoveUniqueHashIndexServerFailures : function () { + var failures = [ "DeletePrimaryIndex", "DeleteSecondaryIndexes", "RemoveHashIndex" ]; + + failures.forEach (function (f) { + internal.debugClearFailAt(); + db._drop(cn); + c = db._create(cn); + + for (var i = 0; i < 1000; ++i) { + c.save({ value: i }); + } + c.ensureUniqueConstraint("value"); + + internal.debugSetFailAt(f); + + try { + c.truncate(); + fail(); + } + catch (err) { + assertEqual(internal.errors.ERROR_DEBUG.code, err.errorNum); + } + + assertEqual(1000, c.count()); + }); + }, + +//////////////////////////////////////////////////////////////////////////////// +/// @brief test: rollback in case of a server-side fail +//////////////////////////////////////////////////////////////////////////////// + + testRemoveNonUniqueHashIndexServerFailures : function () { + var failures = [ "DeletePrimaryIndex", "DeleteSecondaryIndexes", "RemoveHashIndex" ]; + + failures.forEach (function (f) { + internal.debugClearFailAt(); + db._drop(cn); + c = db._create(cn); + + for (var i = 0; i < 1000; ++i) { + c.save({ value: i % 10 }); + } + c.ensureHashIndex("value"); + + internal.debugSetFailAt(f); + + try { + c.truncate(); + fail(); + } + catch (err) { + assertEqual(internal.errors.ERROR_DEBUG.code, err.errorNum); + } + + assertEqual(1000, c.count()); + }); + }, + +//////////////////////////////////////////////////////////////////////////////// +/// @brief test: rollback in case of a server-side fail +//////////////////////////////////////////////////////////////////////////////// + + testRemoveNonUniqueHashIndexServerFailuresTrx : function () { + var failures = [ "DeletePrimaryIndex", "DeleteSecondaryIndexes", "RemoveHashIndex" ]; + + failures.forEach (function (f) { + internal.debugClearFailAt(); + db._drop(cn); + c = db._create(cn); + + c.ensureHashIndex("value"); + for (var i = 0; i < 1000; ++i) { + c.save({ _key: "test" + i, value: i % 10 }); + } + + db._executeTransaction({ + collections: { + write: cn + }, + action: function () { + for (var j = 0; j < 10; ++j) { + c.remove("test" + j); + } + + internal.debugSetFailAt(f); + + try { + c.remove("test10"); + fail(); + } + catch (err) { + assertEqual(internal.errors.ERROR_DEBUG.code, err.errorNum); + } + } + }); + + assertEqual(990, c.count()); + internal.debugClearFailAt(); + }); + }, + +//////////////////////////////////////////////////////////////////////////////// +/// @brief test: rollback in case of a server-side fail +//////////////////////////////////////////////////////////////////////////////// + + testRemoveNonUniqueHashIndexServerFailuresRollback : function () { + var failures = [ "DeletePrimaryIndex", "DeleteSecondaryIndexes", "RemoveHashIndex" ]; + + failures.forEach (function (f) { + internal.debugClearFailAt(); + db._drop(cn); + c = db._create(cn); + + for (var i = 0; i < 1000; ++i) { + c.save({ _key: "test" + i, value: i % 10 }); + } + + c.ensureHashIndex("value"); + + try { + db._executeTransaction({ + collections: { + write: cn + }, + action: function () { + for (var j = 0; j < 10; ++j) { + c.remove("test" + j); + } + + internal.debugSetFailAt(f); + c.remove("test10"); + } + }); + fail(); + } + catch (err) { + assertEqual(internal.errors.ERROR_DEBUG.code, err.errorNum); + } + + assertEqual(1000, c.count()); + internal.debugClearFailAt(); + }); + }, + +//////////////////////////////////////////////////////////////////////////////// +/// @brief test: rollback in case of a server-side fail +//////////////////////////////////////////////////////////////////////////////// + + testRemoveUniqueSkiplistServerFailures : function () { + var failures = [ "DeletePrimaryIndex", "DeleteSecondaryIndexes" ]; + + failures.forEach (function (f) { + internal.debugClearFailAt(); + db._drop(cn); + c = db._create(cn); + + for (var i = 0; i < 1000; ++i) { + c.save({ value: i }); + } + c.ensureUniqueSkiplist("value"); + + internal.debugSetFailAt(f); + + try { + c.truncate(); + fail(); + } + catch (err) { + assertEqual(internal.errors.ERROR_DEBUG.code, err.errorNum); + } + + assertEqual(1000, c.count()); + }); + }, + +//////////////////////////////////////////////////////////////////////////////// +/// @brief test: rollback in case of a server-side fail +//////////////////////////////////////////////////////////////////////////////// + + testRemoveNonUniqueSkiplistServerFailures : function () { + var failures = [ "DeletePrimaryIndex", "DeleteSecondaryIndexes" ]; + + failures.forEach (function (f) { + internal.debugClearFailAt(); + db._drop(cn); + c = db._create(cn); + + for (var i = 0; i < 1000; ++i) { + c.save({ value: i % 10 }); + } + c.ensureSkiplist("value"); + + internal.debugSetFailAt(f); + + try { + c.truncate(); + fail(); + } + catch (err) { + assertEqual(internal.errors.ERROR_DEBUG.code, err.errorNum); + } + + assertEqual(1000, c.count()); + }); + }, + +//////////////////////////////////////////////////////////////////////////////// +/// @brief test: rollback in case of a server-side fail +//////////////////////////////////////////////////////////////////////////////// + + testRemoveMultipleIndexesServerFailures : function () { + var failures = [ "DeletePrimaryIndex", "DeleteSecondaryIndexes", "RemoveHashIndex" ]; + + failures.forEach (function (f) { + internal.debugClearFailAt(); + db._drop(cn); + c = db._create(cn); + + for (var i = 0; i < 1000; ++i) { + c.save({ value: i % 10 }); + } + c.ensureSkiplist("value"); + c.ensureHashIndex("value"); + + internal.debugSetFailAt(f); + + try { + c.truncate(); + fail(); + } + catch (err) { + assertEqual(internal.errors.ERROR_DEBUG.code, err.errorNum); + } + + assertEqual(1000, c.count()); + }); + }, + //////////////////////////////////////////////////////////////////////////////// /// @brief test: rollback in case of a server-side fail //////////////////////////////////////////////////////////////////////////////// @@ -5025,7 +5471,7 @@ function transactionServerFailuresSuite () { if (internal.debugCanUseFailAt()) { jsunity.run(transactionServerFailuresSuite); } - +/* jsunity.run(transactionInvocationSuite); jsunity.run(transactionCollectionsSuite); jsunity.run(transactionOperationsSuite); @@ -5035,7 +5481,7 @@ jsunity.run(transactionRollbackSuite); jsunity.run(transactionCountSuite); jsunity.run(transactionCrossCollectionSuite); jsunity.run(transactionConstraintsSuite); - +*/ return jsunity.done(); // ----------------------------------------------------------------------------- @@ -5046,3 +5492,4 @@ return jsunity.done(); // mode: outline-minor // outline-regexp: "\\(/// @brief\\|/// @addtogroup\\|// --SECTION--\\|/// @page\\|/// @\\}\\)" // End: + From e669dc7c296c2b6a628a7e3882ca2be92b7afdcf Mon Sep 17 00:00:00 2001 From: Jan Steemann Date: Sat, 1 Nov 2014 18:06:15 +0100 Subject: [PATCH 3/8] re-activated tests --- js/server/tests/shell-transactions-noncluster.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/js/server/tests/shell-transactions-noncluster.js b/js/server/tests/shell-transactions-noncluster.js index 3926aba1d5..1359b2ffbd 100644 --- a/js/server/tests/shell-transactions-noncluster.js +++ b/js/server/tests/shell-transactions-noncluster.js @@ -5471,7 +5471,7 @@ function transactionServerFailuresSuite () { if (internal.debugCanUseFailAt()) { jsunity.run(transactionServerFailuresSuite); } -/* + jsunity.run(transactionInvocationSuite); jsunity.run(transactionCollectionsSuite); jsunity.run(transactionOperationsSuite); @@ -5481,7 +5481,7 @@ jsunity.run(transactionRollbackSuite); jsunity.run(transactionCountSuite); jsunity.run(transactionCrossCollectionSuite); jsunity.run(transactionConstraintsSuite); -*/ + return jsunity.done(); // ----------------------------------------------------------------------------- From 9862c854647d9c88671ebe1971adebb77bcad5f4 Mon Sep 17 00:00:00 2001 From: Jan Steemann Date: Sat, 1 Nov 2014 18:21:04 +0100 Subject: [PATCH 4/8] fixed typos --- js/server/modules/org/arangodb/testing.js | 34 +++++++++++------------ 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/js/server/modules/org/arangodb/testing.js b/js/server/modules/org/arangodb/testing.js index bd52bff66a..71c444c510 100644 --- a/js/server/modules/org/arangodb/testing.js +++ b/js/server/modules/org/arangodb/testing.js @@ -36,7 +36,9 @@ /// to perform and the second is an options object. For `which` the following /// values are allowed: /// Empty will give you a complete list. -var functionDoku = { +//////////////////////////////////////////////////////////////////////////////// + +var functionsDocumentation = { 'all' : " do all tests (marked with [x])", "shell_server_perf" : "bulk tests intended to get an overview of executiontime needed.", "single_client" : "run one test suite isolated via the arangosh; options required\n" + @@ -45,7 +47,7 @@ var functionDoku = { " Run without to get more detail" }; -var optiondoku = [ +var optionsDocumentation = [ '', ' The following properties of `options` are defined:', '', @@ -65,7 +67,7 @@ var optiondoku = [ ' - `cleanup`: if set to true (the default), the cluster data files', ' and logs are removed after termination of the test.', ' - `jasmineReportFormat`: this option is passed on to the `format`', - ' option of the Jasmin options object, only for Jasmin tests.', + ' option of the Jasmine options object, only for Jasmine tests.', '', ' - `valgrind`: if set to true the arangods are run with the valgrind', ' memory checker', @@ -76,7 +78,6 @@ var optiondoku = [ ' - `portOffset`: move our base port by n ports up', '' ]; -//////////////////////////////////////////////////////////////////////////////// var _ = require("underscore"); var cleanupDirectories = []; @@ -146,14 +147,14 @@ function printUsage () { print(' where "which" is one of:\n'); var i; var checkAll; - var oneFunctionDoku; + var oneFunctionDocumentation; for (i in testFuncs) { if (testFuncs.hasOwnProperty(i)) { - if (functionDoku.hasOwnProperty(i)) { - oneFunctionDoku = ' - ' + functionDoku[i]; + if (functionsDocumentation.hasOwnProperty(i)) { + oneFunctionDocumentation = ' - ' + functionsDocumentation[i]; } else { - oneFunctionDoku = ''; + oneFunctionDocumentation = ''; } if (allTests.indexOf(i) !== -1) { checkAll = '[x]'; @@ -161,18 +162,17 @@ function printUsage () { else { checkAll = ' '; } - print(' ' + checkAll + ' '+i+' ' + oneFunctionDoku); + print(' ' + checkAll + ' '+i+' ' + oneFunctionDocumentation); } } - for (i in optiondoku) { - if (optiondoku.hasOwnProperty(i)) { - print(optiondoku[i]); + for (i in optionsDocumentation) { + if (optionsDocumentation.hasOwnProperty(i)) { + print(optionsDocumentation[i]); } } } -function filterTestcaseByOptions (testname, options, whichFilter) -{ +function filterTestcaseByOptions (testname, options, whichFilter) { if ((testname.indexOf("-cluster") !== -1) && (options.cluster === false)) { whichFilter.filter = 'noncluster'; return false; @@ -560,13 +560,13 @@ function runThere (options, instanceInfo, file) { var t; if (file.indexOf("-spec") === -1) { t = 'var runTest = require("jsunity").runTest; '+ - 'return runTest('+JSON.stringify(file)+');'; + 'return runTest(' + JSON.stringify(file) + ');'; } else { var jasmineReportFormat = options.jasmineReportFormat || 'progress'; t = 'var executeTestSuite = require("jasmine").executeTestSuite; '+ - 'return executeTestSuite(['+JSON.stringify(file)+'],{"format": '+ - JSON.stringify(jasmineReportFormat)+'});'; + 'return executeTestSuite([' + JSON.stringify(file) + '],{"format": '+ + JSON.stringify(jasmineReportFormat) + '});'; } var o = makeAuthorisationHeaders(options); o.method = "POST"; From 1ae150d90a9b4ec71bc927eb3a39813720f9d244 Mon Sep 17 00:00:00 2001 From: Jan Steemann Date: Sat, 1 Nov 2014 21:02:00 +0100 Subject: [PATCH 5/8] control number of v8 contexts independently from number of server threads added command-line option `--javascript.v8-contexts` to control the number of V8 contexts created in arangod. Previously, the number of V8 contexts was equal to the number of server threads (as specified by option `--server.threads`). However, it may be sensible to create different amounts of threads and V8 contexts. If the option is not specified, the number of V8 contexts created will be equal to the number of server threads. Thus no change in configuration is required to keep the old behavior. --- CHANGELOG | 10 ++++++++++ arangod/RestServer/ArangoServer.cpp | 24 ++++++++++++++++++------ arangod/RestServer/ArangoServer.h | 23 +++++++++++++++++++---- 3 files changed, 47 insertions(+), 10 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 2821033608..29062a0961 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,6 +1,16 @@ v2.3.0 (XXXX-XX-XX) ------------------- +* added command-line option `--javascript.v8-contexts` to control the number of + V8 contexts created in arangod. + + Previously, the number of V8 contexts was equal to the number of server threads + (as specified by option `--server.threads`). However, it may be sensible to + create different amounts of threads and V8 contexts. If the option is not + specified, the number of V8 contexts created will be equal to the number of + server threads. Thus no change in configuration is required to keep the old + behavior. + * removed bitarray indexes * removed internal "_admin/modules/flush" in order to fix requireApp diff --git a/arangod/RestServer/ArangoServer.cpp b/arangod/RestServer/ArangoServer.cpp index 8830ff2fc9..ed7282a19f 100644 --- a/arangod/RestServer/ArangoServer.cpp +++ b/arangod/RestServer/ArangoServer.cpp @@ -281,6 +281,7 @@ ArangoServer::ArangoServer (int argc, char** argv) _disableAuthenticationUnixSockets(false), _dispatcherThreads(8), _dispatcherQueueSize(8192), + _v8Contexts(8), _databasePath(), _defaultMaximalSize(TRI_JOURNAL_DEFAULT_MAXIMAL_SIZE), _defaultWaitForSync(false), @@ -560,6 +561,7 @@ void ArangoServer::buildApplicationServer () { additional["THREAD Options:help-admin"] ("server.threads", &_dispatcherThreads, "number of threads for basic operations") + ("javascript.v8-contexts", &_v8Contexts, "number of V8 contexts that are created for executing JavaScript actions") ; additional["Server Options:help-extended"] @@ -795,25 +797,35 @@ int ArangoServer::startupServer () { TRI_ASSERT(vocbase != nullptr); + // initialise V8 - size_t concurrency = _dispatcherThreads; + if (! _applicationServer->programOptions().has("javascript.v8-contexts")) { + // the option was added recently so it's not always set + // the behavior in older ArangoDB was to create one V8 context per dispatcher thread + _v8Contexts = _dispatcherThreads; + } + + if (_v8Contexts < 1) { + _v8Contexts = 1; + } if (mode == OperationMode::MODE_CONSOLE) { // one V8 instance is taken by the console if (startServer) { - ++concurrency; + ++_v8Contexts; } } else if (mode == OperationMode::MODE_UNITTESTS || mode == OperationMode::MODE_SCRIPT) { - if (concurrency == 1) { - // at least two to allow the test-runner and the scheduler to use a V8 - concurrency = 2; + if (_v8Contexts == 1) { + // at least two to allow both the test-runner and the scheduler to use a V8 instance + _v8Contexts = 2; } } _applicationV8->setVocbase(vocbase); - _applicationV8->setConcurrency(concurrency); + _applicationV8->setConcurrency(_v8Contexts); _applicationV8->defineDouble("DISPATCHER_THREADS", _dispatcherThreads); + _applicationV8->defineDouble("V8_CONTEXTS", _v8Contexts); // ............................................................................. // prepare everything diff --git a/arangod/RestServer/ArangoServer.h b/arangod/RestServer/ArangoServer.h index 3bff12ac35..bd8d3dcc8a 100644 --- a/arangod/RestServer/ArangoServer.h +++ b/arangod/RestServer/ArangoServer.h @@ -317,12 +317,12 @@ namespace triagens { bool _disableAuthenticationUnixSockets; //////////////////////////////////////////////////////////////////////////////// -/// @brief number of dispatcher threads for non-database worker +/// @brief number of dispatcher threads /// @startDocuBlock serverThreads /// `--server.threads number` /// -/// Specifies the *number* of threads that are spawned to handle action -/// requests using Rest, JavaScript, or Ruby. +/// Specifies the *number* of threads that are spawned to handle HTTP REST +/// requests. /// @endDocuBlock //////////////////////////////////////////////////////////////////////////////// @@ -330,7 +330,7 @@ namespace triagens { //////////////////////////////////////////////////////////////////////////////// /// @brief maximum size of the dispatcher queue for asynchronous requests -/// @startDocuBlock serverAuthenticationDisable +/// @startDocuBlock schedulerMaximalQueueSize /// `--scheduler.maximal-queue-size size` /// /// Specifies the maximum *size* of the dispatcher queue for asynchronous @@ -343,6 +343,21 @@ namespace triagens { int _dispatcherQueueSize; +//////////////////////////////////////////////////////////////////////////////// +/// @brief number of V8 contexts for executing JavaScript actions +/// @startDocuBlock v8Contexts +/// `--server.v8-contexts number` +/// +/// Specifies the *number* of V8 contexts that are created for executing +/// JavaScript code. More contexts allow execute more JavaScript actions in +/// parallel, provided that there are also enough threads available. Please +/// note that each V8 context will use a substantial amount of memory and +/// requires periodic CPU processing time for garbage collection. +/// @endDocuBlock +//////////////////////////////////////////////////////////////////////////////// + + int _v8Contexts; + //////////////////////////////////////////////////////////////////////////////// /// @brief path to the database /// @startDocuBlock DatabaseDirectory From 432430632e4851564c0a1e36eedac67539066999 Mon Sep 17 00:00:00 2001 From: Jan Steemann Date: Sat, 1 Nov 2014 21:03:30 +0100 Subject: [PATCH 6/8] documentation fixes --- .../Books/Users/Arangosh/Configuration.mdpp | 4 ++-- .../Books/Users/ConfigureArango/Arangod.mdpp | 12 ++++++++-- .../Users/ConfigureArango/Communication.mdpp | 8 +++---- .../ConfigureArango/EmergencyConsole.mdpp | 16 ++++++------- .../Users/ConfigureArango/RandomNumbers.mdpp | 4 ---- Documentation/Books/Users/SUMMARY.md | 3 +-- README.md | 24 ++++++++++++------- 7 files changed, 39 insertions(+), 32 deletions(-) delete mode 100644 Documentation/Books/Users/ConfigureArango/RandomNumbers.mdpp diff --git a/Documentation/Books/Users/Arangosh/Configuration.mdpp b/Documentation/Books/Users/Arangosh/Configuration.mdpp index 976f10ba10..4050395acf 100644 --- a/Documentation/Books/Users/Arangosh/Configuration.mdpp +++ b/Documentation/Books/Users/Arangosh/Configuration.mdpp @@ -8,7 +8,7 @@ You can use this to define your own extra variables and functions that you need For example, you could put the following into the *.arangosh.rc* file in your home directory: -``` +```js // var keyword omitted intentionally, // otherwise "timed" would not survive the scope of this script timed = function (cb) { @@ -23,7 +23,7 @@ This will make a function named *timed* available in _arangosh_ in the global sc You can now start _arangosh_ and invoke the function like this: -``` +```js timed(function () { for (var i = 0; i < 1000; ++i) { db.test.save({ value: i }); diff --git a/Documentation/Books/Users/ConfigureArango/Arangod.mdpp b/Documentation/Books/Users/ConfigureArango/Arangod.mdpp index 072076c76c..d33ca8409a 100644 --- a/Documentation/Books/Users/ConfigureArango/Arangod.mdpp +++ b/Documentation/Books/Users/ConfigureArango/Arangod.mdpp @@ -36,6 +36,10 @@ @startDocuBlock serverAllowMethod +!SUBSECTION Server threads +@startDocuBlock serverThreads + + !SUBSECTION Keyfile @startDocuBlock serverKeyfile @@ -64,12 +68,12 @@ @startDocuBlock serverBacklog -!SUBSECTION Disable statics +!SUBSECTION Disable statistics `--disable-statistics value` If this option is *value* is *true*, then ArangoDB's statistics gathering -is turned off. Statistics gathering causes constant CPU activity so using this +is turned off. Statistics gathering causes regular CPU activity so using this option to turn it off might relieve heavy-loaded instances. Note: this option is only available when ArangoDB has not been compiled with the option *--disable-figures*. @@ -90,6 +94,10 @@ the option *--disable-figures*. @startDocuBlock databaseForceSyncProperties +!SUBSECTION V8 Contexts +@startDocuBlock v8Contexts + + !SUBSECTION Frequency @startDocuBlock jsGcFrequency diff --git a/Documentation/Books/Users/ConfigureArango/Communication.mdpp b/Documentation/Books/Users/ConfigureArango/Communication.mdpp index 1bf5775e01..0e72ff7986 100644 --- a/Documentation/Books/Users/ConfigureArango/Communication.mdpp +++ b/Documentation/Books/Users/ConfigureArango/Communication.mdpp @@ -1,17 +1,17 @@ !CHAPTER Command-Line Options for Communication !SUBSECTION Scheduler threads - @startDocuBlock schedulerThreads + !SUBSECTION Scheduler maximal queue size - -@startDocuBlock serverAuthenticationDisable +@startDocuBlock schedulerMaximalQueueSize + !SUBSECTION Scheduler backend - @startDocuBlock schedulerBackend + !SUBSECTION Io backends `--show-io-backends` diff --git a/Documentation/Books/Users/ConfigureArango/EmergencyConsole.mdpp b/Documentation/Books/Users/ConfigureArango/EmergencyConsole.mdpp index ec661d2f1d..ee35ef7020 100644 --- a/Documentation/Books/Users/ConfigureArango/EmergencyConsole.mdpp +++ b/Documentation/Books/Users/ConfigureArango/EmergencyConsole.mdpp @@ -2,7 +2,7 @@ !SUBSECTION In Case Of Disaster -The following command starts a emergency console. +The following command starts an emergency console. **Note**: Never start the emergency console for a database which also has a server attached to it. In general the ArangoDB shell is what you want. @@ -14,18 +14,16 @@ ArangoDB shell [V8 version 3.9.4, DB version 1.x.y] arango> 1 + 2; 3 -arango> db.geo.count(); +arango> var db = require("org/arangodb").db; db.geo.count(); 703 ``` -The emergency console disables the HTTP interface of the server and -opens a JavaScript console on standard output instead. This allows you -to debug and examine collections and documents without interference -from the outside. In most respects the emergency console behaves like -the normal ArangoDB shell - but with exclusive access and no -client/server communication. +The emergency console provides a JavaScript console directly running in the +arangod server process. This allows to debug and examine collections and +documents as with the normal ArangoDB shell, but without client/server +communication. -However, it is very likely that you never need the emergency console +However, it is very likely that you will never need the emergency console unless you are an ArangoDB developer. diff --git a/Documentation/Books/Users/ConfigureArango/RandomNumbers.mdpp b/Documentation/Books/Users/ConfigureArango/RandomNumbers.mdpp deleted file mode 100644 index cbf474e783..0000000000 --- a/Documentation/Books/Users/ConfigureArango/RandomNumbers.mdpp +++ /dev/null @@ -1,4 +0,0 @@ -!SUBSECTION Command-Line Options for Random Numbers - - -@startDocuBlock randomGenerator \ No newline at end of file diff --git a/Documentation/Books/Users/SUMMARY.md b/Documentation/Books/Users/SUMMARY.md index 423a011f73..06821f0c0a 100644 --- a/Documentation/Books/Users/SUMMARY.md +++ b/Documentation/Books/Users/SUMMARY.md @@ -138,14 +138,13 @@ * [Authentication](Sharding/Authentication.md) * [Firewall setup](Sharding/FirewallSetup.md) -* [Configure ArangoDB](ConfigureArango/README.md) +* [Server Configuration](ConfigureArango/README.md) * [Arangod options](ConfigureArango/Arangod.md) * [Write-ahead log options](ConfigureArango/Wal.md) * [Endpoints options](ConfigureArango/Endpoint.md) * [Cluster options](ConfigureArango/Cluster.md) * [Logging options](ConfigureArango/Logging.md) * [Communication options](ConfigureArango/Communication.md) - * [Random numbers](ConfigureArango/RandomNumbers.md) * [Authentication](ConfigureArango/Authentication.md) * [Emergency Console](ConfigureArango/EmergencyConsole.md) diff --git a/README.md b/README.md index 8ab2d295de..8d8908d111 100644 --- a/README.md +++ b/README.md @@ -40,22 +40,28 @@ For Mac OSX users: execute For Windows and Linux users: use the installer script or distribution package from our [download page](http://www.arangodb.com/download). -If the package manager has not already started the ArangoDB server, use +If the package manager has not already started the ArangoDB server, use the +following command to start it. unix> /path/to/sbin/arangod 2012-03-30T12:54:19Z [11794] INFO ArangoDB (version 2.x.y) is ready for business 2012-03-30T12:54:19Z [11794] INFO Have Fun! -`/path/to/sbin` is OS dependent. It will normally by either `/usr/sbin` or `/user/local/sbin`. Point your browser to +`/path/to/sbin` is OS dependent. It will normally be either `/usr/sbin` or `/user/local/sbin`. + +To access ArangoDB in your browser, open the following URL http://localhost:8529/ -and select `Tools / JS Shell`. You can now use the Arango shell from within your browser. Alternative, it is available as command-line tool _arangosh_. +and select `Tools / JS Shell`. You can now use the Arango shell from within your browser. + +Alternatively, a scriptable shell is available as a command-line tool _arangosh_. arangosh> db._create("hello"); arangosh> db.hello.save({ world: "earth" }); -Congratulations! You have created your first collection called `hello` and your first document. To verify your achievements, type: +Congratulations! You have created your first collection named `hello` and your first document. +To verify your achievements, type: arangosh> db.hello.toArray(); @@ -64,15 +70,15 @@ More Information ---------------- Please check the -[Installation Manual](http://www.arangodb.com/manuals/current/InstallManual.html) +[Installation Manual](https://www.arangodb.com/Installing/README.html) for installation and compilation instructions. The -[User Manual](http://www.arangodb.com/manuals/current/UserManual.html) +[User Manual](https://www.arangodb.com/FirstSteps/README.html) has an introductory chapter showing the basic operations of ArangoDB. Or you can use the -[online tutorial](http://www.arangodb.com/try) +[online tutorial](https://www.arangodb.com/tryitout) to play with ArangoDB without installing it locally. @@ -86,7 +92,7 @@ you report them: You can use the Google group for improvements, feature requests, comments -[http://www.arangodb.com/community](http://www.arangodb.com/community) +[http://www.arangodb.com/community](https://www.arangodb.com/community) Citing ArangoDB @@ -96,7 +102,7 @@ Please kindly cite ArangoDB in your publications if it helps your research: ```bibtex @misc{ArangoDB2014, Author = {ArangoDB}, - Title = { {ArangoDB 2.2}: An Open Source multi-purpose database supporting flexible data models for documents, graphs, and key-values.}, + Title = { {ArangoDB 2.3}: An Open source, multi-purpose database supporting flexible data models for documents, graphs, and key-values.}, Year = {2014}, Howpublished = {\url{http://arangodb.com/} } From 749a0aa4088876f246f290219f989bb0b3f101f9 Mon Sep 17 00:00:00 2001 From: Jan Steemann Date: Sat, 1 Nov 2014 21:52:17 +0100 Subject: [PATCH 7/8] fixed cloning of AST nodes --- arangod/Aql/Ast.cpp | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/arangod/Aql/Ast.cpp b/arangod/Aql/Ast.cpp index ad8f33214f..e28bccae3e 100644 --- a/arangod/Aql/Ast.cpp +++ b/arangod/Aql/Ast.cpp @@ -1018,20 +1018,25 @@ AstNode* Ast::clone (AstNode const* node) { } else if (type == NODE_TYPE_VALUE) { switch (node->value.type) { + case VALUE_TYPE_NULL: + copy->value.type = VALUE_TYPE_NULL; + break; case VALUE_TYPE_BOOL: + copy->value.type = VALUE_TYPE_BOOL; copy->setBoolValue(node->getBoolValue()); break; case VALUE_TYPE_INT: + copy->value.type = VALUE_TYPE_INT; copy->setIntValue(node->getIntValue()); break; case VALUE_TYPE_DOUBLE: + copy->value.type = VALUE_TYPE_DOUBLE; copy->setDoubleValue(node->getDoubleValue()); break; case VALUE_TYPE_STRING: + copy->value.type = VALUE_TYPE_STRING; copy->setStringValue(node->getStringValue()); break; - default: { - } } } From 1457c2631b5df0e401197562ef61bc3ce93f7aec Mon Sep 17 00:00:00 2001 From: Frank Celler Date: Sat, 1 Nov 2014 22:39:21 +0100 Subject: [PATCH 8/8] minor cosmetics --- config/generateErrorfile.py | 2 +- js/common/tests/shell-database.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/config/generateErrorfile.py b/config/generateErrorfile.py index ce837e1be5..f2c6816fda 100755 --- a/config/generateErrorfile.py +++ b/config/generateErrorfile.py @@ -41,7 +41,7 @@ def genJsFile(errors): i = i + 1 if i < len(errors): - out = out + ", \n" + out = out + ",\n" else: out = out + "\n" diff --git a/js/common/tests/shell-database.js b/js/common/tests/shell-database.js index f114af90a6..eb087a2112 100644 --- a/js/common/tests/shell-database.js +++ b/js/common/tests/shell-database.js @@ -66,7 +66,7 @@ function DatabaseSuite () { //////////////////////////////////////////////////////////////////////////////// testVersion : function () { - assertMatch(/(^2\.2)|(-devel$)/, internal.db._version()); + assertMatch(/(^2\.3)|(-devel$)/, internal.db._version()); }, ////////////////////////////////////////////////////////////////////////////////