diff --git a/CHANGELOG b/CHANGELOG index e30752dfd6..c61b73916a 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,12 +1,17 @@ v2.1.0 (XXXX-XX-XX) ------------------- +* don't requeue identical context methods in V8 threads in case a method is + already registered + * removed arangod command line option `--database.remove-on-compacted` v2.0.1 (XXXX-XX-XX) ------------------- +* fixed display of missing error messages and codes in arangosh + * when creating a collection via the web interface, the collection type was always "document", regardless of the user's choice diff --git a/UnitTests/Makefile.unittests b/UnitTests/Makefile.unittests index c49a046cc5..b630ca6dce 100755 --- a/UnitTests/Makefile.unittests +++ b/UnitTests/Makefile.unittests @@ -363,6 +363,7 @@ SHELL_COMMON = \ @top_srcdir@/js/common/tests/shell-document.js \ @top_srcdir@/js/common/tests/shell-download.js \ @top_srcdir@/js/common/tests/shell-edge.js \ + @top_srcdir@/js/common/tests/shell-errors.js \ @top_srcdir@/js/common/tests/shell-fs.js \ @top_srcdir@/js/common/tests/shell-graph-traversal.js \ @top_srcdir@/js/common/tests/shell-graph-algorithms.js \ diff --git a/arangod/V8Server/ApplicationV8.cpp b/arangod/V8Server/ApplicationV8.cpp index cef629a599..2d94bb0964 100644 --- a/arangod/V8Server/ApplicationV8.cpp +++ b/arangod/V8Server/ApplicationV8.cpp @@ -60,6 +60,32 @@ using namespace triagens::arango; using namespace triagens::rest; using namespace std; +// ----------------------------------------------------------------------------- +// --SECTION-- class GlobalContextMethods +// ----------------------------------------------------------------------------- + +//////////////////////////////////////////////////////////////////////////////// +/// @brief initialise code for pre-defined actions +//////////////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////////////// +/// @brief reload the routing cache +//////////////////////////////////////////////////////////////////////////////// + +std::string const GlobalContextMethods::CodeReloadRouting = "require(\"org/arangodb/actions\").reloadRouting()"; + +//////////////////////////////////////////////////////////////////////////////// +/// @brief flush the modules cache +//////////////////////////////////////////////////////////////////////////////// + +std::string const GlobalContextMethods::CodeFlushModuleCache = "require(\"internal\").flushModuleCache()"; + +//////////////////////////////////////////////////////////////////////////////// +/// @brief reload AQL functions +//////////////////////////////////////////////////////////////////////////////// + +std::string const GlobalContextMethods::CodeReloadAql = "try { require(\"org/arangodb/ahuacatl\").reload(); } catch (err) { }"; + //////////////////////////////////////////////////////////////////////////////// /// @brief we'll store deprecated config option values in here //////////////////////////////////////////////////////////////////////////////// @@ -128,10 +154,25 @@ namespace { /// @brief adds a global method //////////////////////////////////////////////////////////////////////////////// -void ApplicationV8::V8Context::addGlobalContextMethod (string const& method) { - MUTEX_LOCKER(_globalMethodsLock); +bool ApplicationV8::V8Context::addGlobalContextMethod (string const& method) { + GlobalContextMethods::MethodType type = GlobalContextMethods::getType(method); - _globalMethods.push_back(method); + if (type == GlobalContextMethods::TYPE_UNKNOWN) { + return false; + } + + MUTEX_LOCKER(_globalMethodsLock); + + for (size_t i = 0; i < _globalMethods.size(); ++i) { + if (_globalMethods[i] == type) { + // action is already registered. no need to register it again + return true; + } + } + + // insert action into vector + _globalMethods.push_back(type); + return true; } //////////////////////////////////////////////////////////////////////////////// @@ -141,7 +182,7 @@ void ApplicationV8::V8Context::addGlobalContextMethod (string const& method) { void ApplicationV8::V8Context::handleGlobalContextMethods () { v8::HandleScope scope; - vector copy; + vector copy; { // we need to copy the vector of functions so we do not need to hold the @@ -153,13 +194,14 @@ void ApplicationV8::V8Context::handleGlobalContextMethods () { _globalMethods.clear(); } - for (vector::const_iterator i = copy.begin(); i != copy.end(); ++i) { - string const& func = *i; + for (vector::const_iterator i = copy.begin(); i != copy.end(); ++i) { + GlobalContextMethods::MethodType const type = *i; + string const func = GlobalContextMethods::getCode(type); LOG_DEBUG("executing global context methods '%s' for context %d", func.c_str(), (int) _id); TRI_ExecuteJavaScriptString(_context, - v8::String::New(func.c_str()), + v8::String::New(func.c_str(), func.size()), v8::String::New("global context method"), false); } @@ -366,10 +408,16 @@ void ApplicationV8::exitContext (V8Context* context) { /// @brief adds a global context functions to be executed asap //////////////////////////////////////////////////////////////////////////////// -void ApplicationV8::addGlobalContextMethod (string const& method) { +bool ApplicationV8::addGlobalContextMethod (string const& method) { + bool result = true; + for (size_t i = 0; i < _nrInstances; ++i) { - _contexts[i]->addGlobalContextMethod(method); + if (! _contexts[i]->addGlobalContextMethod(method)) { + result = false; + } } + + return result; } //////////////////////////////////////////////////////////////////////////////// diff --git a/arangod/V8Server/ApplicationV8.h b/arangod/V8Server/ApplicationV8.h index 36db91b951..2d2e4aa745 100644 --- a/arangod/V8Server/ApplicationV8.h +++ b/arangod/V8Server/ApplicationV8.h @@ -55,12 +55,76 @@ namespace triagens { class ApplicationScheduler; } + namespace arango { + +// ----------------------------------------------------------------------------- +// --SECTION-- class GlobalContextMethods +// ----------------------------------------------------------------------------- + + class GlobalContextMethods { + + public: + +//////////////////////////////////////////////////////////////////////////////// +/// @brief method type +//////////////////////////////////////////////////////////////////////////////// + + enum MethodType { + TYPE_UNKNOWN = 0, + TYPE_RELOAD_ROUTING, + TYPE_FLUSH_MODULE_CACHE, + TYPE_RELOAD_AQL + }; + +//////////////////////////////////////////////////////////////////////////////// +/// @brief get a method type number from a type string +//////////////////////////////////////////////////////////////////////////////// + + static MethodType getType (std::string const& type) { + if (type == "reloadRouting") { + return TYPE_RELOAD_ROUTING; + } + if (type == "flushModuleCache") { + return TYPE_FLUSH_MODULE_CACHE; + } + if (type == "reloadAql") { + return TYPE_RELOAD_AQL; + } + + return TYPE_UNKNOWN; + } + +//////////////////////////////////////////////////////////////////////////////// +/// @brief get code for a method +//////////////////////////////////////////////////////////////////////////////// + + static std::string const getCode (MethodType type) { + switch (type) { + case TYPE_RELOAD_ROUTING: + return CodeReloadRouting; + case TYPE_FLUSH_MODULE_CACHE: + return CodeFlushModuleCache; + case TYPE_RELOAD_AQL: + return CodeReloadAql; + case TYPE_UNKNOWN: + default: + return ""; + } + } + +//////////////////////////////////////////////////////////////////////////////// +/// @brief static strings with the code for each method +//////////////////////////////////////////////////////////////////////////////// + + static std::string const CodeReloadRouting; + static std::string const CodeFlushModuleCache; + static std::string const CodeReloadAql; + }; + // ----------------------------------------------------------------------------- // --SECTION-- class ApplicationV8 // ----------------------------------------------------------------------------- - namespace arango { - //////////////////////////////////////////////////////////////////////////////// /// @brief application simple user and session management feature //////////////////////////////////////////////////////////////////////////////// @@ -112,7 +176,7 @@ namespace triagens { /// Caller must hold the _contextCondition. //////////////////////////////////////////////////////////////////////////////// - void addGlobalContextMethod (string const&); + bool addGlobalContextMethod (string const&); //////////////////////////////////////////////////////////////////////////////// /// @brief executes all global methods @@ -132,7 +196,7 @@ namespace triagens { /// @brief open global methods //////////////////////////////////////////////////////////////////////////////// - std::vector _globalMethods; + std::vector _globalMethods; //////////////////////////////////////////////////////////////////////////////// /// @brief number of requests since last GC of the context @@ -230,7 +294,7 @@ namespace triagens { /// @brief adds a global context functions to be executed asap //////////////////////////////////////////////////////////////////////////////// - void addGlobalContextMethod (string const&); + bool addGlobalContextMethod (string const&); //////////////////////////////////////////////////////////////////////////////// /// @brief runs the garbage collection diff --git a/arangod/V8Server/v8-actions.cpp b/arangod/V8Server/v8-actions.cpp index 06034ccc1c..54273efcb3 100644 --- a/arangod/V8Server/v8-actions.cpp +++ b/arangod/V8Server/v8-actions.cpp @@ -784,7 +784,7 @@ static v8::Handle JS_ExecuteGlobalContextFunction (v8::Arguments cons v8::HandleScope scope; if (argv.Length() != 1) { - TRI_V8_EXCEPTION_USAGE(scope, "executeGlobalContextFunction()"); + TRI_V8_EXCEPTION_USAGE(scope, "executeGlobalContextFunction()"); } // extract the action name @@ -794,10 +794,12 @@ static v8::Handle JS_ExecuteGlobalContextFunction (v8::Arguments cons TRI_V8_TYPE_ERROR(scope, " must be a UTF-8 function definition"); } - string def = *utf8def; + string const def = *utf8def; // and pass it to the V8 contexts - GlobalV8Dealer->addGlobalContextMethod(def); + if (! GlobalV8Dealer->addGlobalContextMethod(def)) { + TRI_V8_EXCEPTION_MESSAGE(scope, TRI_ERROR_INTERNAL, "invalid action definition"); + } return scope.Close(v8::Undefined()); } diff --git a/arangod/V8Server/v8-query.cpp b/arangod/V8Server/v8-query.cpp index e919e973b6..c13f25a77a 100644 --- a/arangod/V8Server/v8-query.cpp +++ b/arangod/V8Server/v8-query.cpp @@ -2013,7 +2013,7 @@ static v8::Handle JS_ByExampleQuery (v8::Arguments const& argv) { size_t total = filtered._length; size_t count = 0; bool error = false; - + if (0 < total) { size_t s; size_t e; diff --git a/arangod/V8Server/v8-vocbase.cpp b/arangod/V8Server/v8-vocbase.cpp index 97ff824a72..16fa1a568a 100644 --- a/arangod/V8Server/v8-vocbase.cpp +++ b/arangod/V8Server/v8-vocbase.cpp @@ -6076,6 +6076,7 @@ static v8::Handle JS_DatafileScanVocbaseCol (v8::Arguments const& arg result->Set(v8::String::New("endPosition"), v8::Number::New(scan._endPosition)); result->Set(v8::String::New("numberMarkers"), v8::Number::New(scan._numberMarkers)); result->Set(v8::String::New("status"), v8::Number::New(scan._status)); + result->Set(v8::String::New("isSealed"), v8::Boolean::New(scan._isSealed)); v8::Handle entries = v8::Array::New(); result->Set(v8::String::New("entries"), entries); @@ -10460,7 +10461,7 @@ void TRI_V8ReloadRouting (v8::Handle context) { v8::HandleScope scope; TRI_ExecuteJavaScriptString(context, - v8::String::New("require('internal').executeGlobalContextFunction('require(\\'org/arangodb/actions\\').reloadRouting()')"), + v8::String::New("require('internal').executeGlobalContextFunction('reloadRouting')"), v8::String::New("reload routing"), false); } diff --git a/arangod/VocBase/datafile.c b/arangod/VocBase/datafile.c index 98d55db53e..7f4ffabd42 100644 --- a/arangod/VocBase/datafile.c +++ b/arangod/VocBase/datafile.c @@ -31,6 +31,7 @@ #include "datafile.h" +#include "BasicsC/conversions.h" #include "BasicsC/hashes.h" #include "BasicsC/logging.h" #include "BasicsC/memory-map.h" @@ -421,6 +422,7 @@ static TRI_df_scan_t ScanDatafile (TRI_datafile_t const* datafile) { scan._maximalSize = datafile->_maximalSize; scan._numberMarkers = 0; scan._status = 1; + scan._isSealed = false; // assume false if (datafile->_currentSize == 0) { end = datafile->_data + datafile->_maximalSize; @@ -494,6 +496,7 @@ static TRI_df_scan_t ScanDatafile (TRI_datafile_t const* datafile) { if (marker->_type == TRI_DF_MARKER_FOOTER) { scan._endPosition = currentSize; + scan._isSealed = true; return scan; } @@ -1712,6 +1715,7 @@ TRI_df_scan_t TRI_ScanDatafile (char const* path) { TRI_InitVector(&scan._entries, TRI_CORE_MEM_ZONE, sizeof(TRI_df_scan_entry_t)); scan._status = 5; + scan._isSealed = false; } return scan; diff --git a/arangod/VocBase/datafile.h b/arangod/VocBase/datafile.h index 97ddd661c6..142330b1de 100644 --- a/arangod/VocBase/datafile.h +++ b/arangod/VocBase/datafile.h @@ -214,6 +214,7 @@ typedef struct TRI_df_scan_s { TRI_vector_t _entries; uint32_t _status; + bool _isSealed; } TRI_df_scan_t; diff --git a/arangod/VocBase/server.c b/arangod/VocBase/server.c index 806b602b54..ba4273c729 100644 --- a/arangod/VocBase/server.c +++ b/arangod/VocBase/server.c @@ -803,7 +803,7 @@ static int OpenDatabases (TRI_server_t* server, databaseName, &defaults, isUpgrade, - server->_wasShutdownCleanly); + ! server->_wasShutdownCleanly); TRI_FreeString(TRI_CORE_MEM_ZONE, databaseName); @@ -1863,6 +1863,7 @@ int TRI_StartServer (TRI_server_t* server, return TRI_ERROR_INTERNAL; } + server->_wasShutdownCleanly = (res == TRI_ERROR_NO_ERROR); diff --git a/arangod/VocBase/vocbase.c b/arangod/VocBase/vocbase.c index 6336850c01..0eddf818d4 100644 --- a/arangod/VocBase/vocbase.c +++ b/arangod/VocBase/vocbase.c @@ -1104,7 +1104,7 @@ static int LoadCollectionVocBase (TRI_vocbase_t* vocbase, // release the lock on the collection temporarily // this will allow other threads to check the collection's // status while it is loading (loading may take a long time because of - // disk activity) + // disk activity, index creation etc.) TRI_WRITE_UNLOCK_STATUS_VOCBASE_COL(collection); document = TRI_OpenDocumentCollection(vocbase, collection->_path); diff --git a/etc/arangodb/arango-dfdb.conf.in b/etc/arangodb/arango-dfdb.conf.in index 8c3397eb43..71ac8eeb49 100644 --- a/etc/arangodb/arango-dfdb.conf.in +++ b/etc/arangodb/arango-dfdb.conf.in @@ -5,6 +5,10 @@ directory= @LOCALSTATEDIR@/lib/arangodb # maximal-journal-size=33554432 # remove-on-drop=true +[server] +# set number of threads to 1 so we don't have concurrency +threads = 1 + [javascript] startup-directory = @PKGDATADIR@/js app-path = @LOCALSTATEDIR@/lib/arangodb-apps diff --git a/etc/relative/arango-dfdb.conf b/etc/relative/arango-dfdb.conf index 40be62e7c4..c4472606f0 100644 --- a/etc/relative/arango-dfdb.conf +++ b/etc/relative/arango-dfdb.conf @@ -7,6 +7,8 @@ no-upgrade = true [server] disable-authentication = true +# set number of threads to 1 so we don't have concurrency +threads = 1 [javascript] startup-directory = ./js diff --git a/js/actions/api-system.js b/js/actions/api-system.js index 9e276c304c..70c4c87b01 100644 --- a/js/actions/api-system.js +++ b/js/actions/api-system.js @@ -219,7 +219,7 @@ actions.defineHttp({ prefix : false, callback : function (req, res) { - internal.executeGlobalContextFunction("require(\"org/arangodb/actions\").reloadRouting()"); + internal.executeGlobalContextFunction("reloadRouting"); console.warn("about to flush the routing cache"); actions.resultOk(req, res, actions.HTTP_OK); } @@ -263,7 +263,7 @@ actions.defineHttp({ prefix : false, callback : function (req, res) { - internal.executeGlobalContextFunction("require(\"internal\").flushModuleCache()"); + internal.executeGlobalContextFunction("flushModuleCache"); console.warn("about to flush the modules cache"); actions.resultOk(req, res, actions.HTTP_OK); } diff --git a/js/apps/system/aardvark/frontend/js/bootstrap/module-internal.js b/js/apps/system/aardvark/frontend/js/bootstrap/module-internal.js index d0fe897c64..b9cc5ce840 100644 --- a/js/apps/system/aardvark/frontend/js/bootstrap/module-internal.js +++ b/js/apps/system/aardvark/frontend/js/bootstrap/module-internal.js @@ -82,20 +82,19 @@ this.message = this.toString(); }; - exports.ArangoError.prototype = new Error(); //Error.prototype; - - exports.ArangoError.prototype._PRINT = function (context) { - context.output += this.toString(); - }; + exports.ArangoError.prototype = new Error(); + } + + exports.ArangoError.prototype._PRINT = function (context) { + context.output += this.toString(); + }; - exports.ArangoError.prototype.toString = function() { - var errorNum = this.errorNum; - var errorMessage = this.errorMessage || this.message; + exports.ArangoError.prototype.toString = function() { + var errorNum = this.errorNum; + var errorMessage = this.errorMessage || this.message; - return "[ArangoError " + errorNum + ": " + errorMessage + "]"; - }; - - } + return "[ArangoError " + errorNum + ": " + errorMessage + "]"; + }; //////////////////////////////////////////////////////////////////////////////// /// @brief SleepAndRequeue diff --git a/js/common/bootstrap/module-internal.js b/js/common/bootstrap/module-internal.js index d0fe897c64..b9cc5ce840 100644 --- a/js/common/bootstrap/module-internal.js +++ b/js/common/bootstrap/module-internal.js @@ -82,20 +82,19 @@ this.message = this.toString(); }; - exports.ArangoError.prototype = new Error(); //Error.prototype; - - exports.ArangoError.prototype._PRINT = function (context) { - context.output += this.toString(); - }; + exports.ArangoError.prototype = new Error(); + } + + exports.ArangoError.prototype._PRINT = function (context) { + context.output += this.toString(); + }; - exports.ArangoError.prototype.toString = function() { - var errorNum = this.errorNum; - var errorMessage = this.errorMessage || this.message; + exports.ArangoError.prototype.toString = function() { + var errorNum = this.errorNum; + var errorMessage = this.errorMessage || this.message; - return "[ArangoError " + errorNum + ": " + errorMessage + "]"; - }; - - } + return "[ArangoError " + errorNum + ": " + errorMessage + "]"; + }; //////////////////////////////////////////////////////////////////////////////// /// @brief SleepAndRequeue diff --git a/js/common/tests/shell-errors.js b/js/common/tests/shell-errors.js new file mode 100644 index 0000000000..dba5a31620 --- /dev/null +++ b/js/common/tests/shell-errors.js @@ -0,0 +1,196 @@ +//////////////////////////////////////////////////////////////////////////////// +/// @brief test the error codes +/// +/// @file +/// +/// DISCLAIMER +/// +/// Copyright 2010-2012 triagens GmbH, Cologne, Germany +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// +/// Copyright holder is triAGENS GmbH, Cologne, Germany +/// +/// @author Jan Steemann +/// @author Copyright 2012, triAGENS GmbH, Cologne, Germany +//////////////////////////////////////////////////////////////////////////////// + +var jsunity = require("jsunity"); +var arangodb = require("org/arangodb"); +var ArangoError = require("org/arangodb").ArangoError; +var ERRORS = arangodb.errors; + +// ----------------------------------------------------------------------------- +// --SECTION-- errors +// ----------------------------------------------------------------------------- + +//////////////////////////////////////////////////////////////////////////////// +/// @brief test suite +//////////////////////////////////////////////////////////////////////////////// + +function ErrorsSuite () { + + var throwError = function (code, message) { + var err = new ArangoError(); + err.errorNum = code; + err.errorMessage = message; + throw err; + }; + + return { + + setUp : function () { + }, + + tearDown : function () { + }, + +//////////////////////////////////////////////////////////////////////////////// +/// @brief test built-in error +//////////////////////////////////////////////////////////////////////////////// + + testBuiltIn : function () { + try { + throw "foo"; + fail(); + } + catch (err) { + assertFalse(err instanceof ArangoError); + assertEqual("string", typeof err); + assertEqual("foo", err); + } + }, + +//////////////////////////////////////////////////////////////////////////////// +/// @brief test built-in error +//////////////////////////////////////////////////////////////////////////////// + + testTypeErrorBuiltIn : function () { + try { + throw new TypeError("invalid type!"); + fail(); + } + catch (err) { + assertFalse(err instanceof ArangoError); + assertEqual("invalid type!", err.message); + } + }, + +//////////////////////////////////////////////////////////////////////////////// +/// @brief test struct +//////////////////////////////////////////////////////////////////////////////// + + testArangoErrorStruct : function () { + try { + throwError(1, "the foxx"); + fail(); + } + catch (err) { + assertTrue(err.hasOwnProperty("errorNum")); + assertTrue(err.hasOwnProperty("errorMessage")); + assertTrue(err.hasOwnProperty("error")); + + assertEqual("number", typeof err.errorNum); + assertEqual("string", typeof err.errorMessage); + assertEqual("boolean", typeof err.error); + + assertEqual(1, err.errorNum); + assertEqual("the foxx", err.errorMessage); + assertTrue(err.error); + + assertTrue(err instanceof ArangoError); + } + }, + +//////////////////////////////////////////////////////////////////////////////// +/// @brief test a custom code +//////////////////////////////////////////////////////////////////////////////// + + testArangoErrorCustom : function () { + try { + throwError(12345, "myerrormessage"); + fail(); + } + catch (err) { + assertEqual(12345, err.errorNum); + assertEqual("myerrormessage", err.errorMessage); + } + }, + +//////////////////////////////////////////////////////////////////////////////// +/// @brief test a custom message +//////////////////////////////////////////////////////////////////////////////// + + testArangoErrorMessage : function () { + try { + throwError(ERRORS.ERROR_ARANGO_DOCUMENT_TYPE_INVALID.code, ERRORS.ERROR_ARANGO_DOCUMENT_TYPE_INVALID.message); + fail(); + } + catch (err) { + assertEqual(ERRORS.ERROR_ARANGO_DOCUMENT_TYPE_INVALID.code, err.errorNum); + assertEqual(ERRORS.ERROR_ARANGO_DOCUMENT_TYPE_INVALID.message, err.errorMessage); + } + }, + +//////////////////////////////////////////////////////////////////////////////// +/// @brief test to string +//////////////////////////////////////////////////////////////////////////////// + + testArangoToString1 : function () { + var e = ERRORS.ERROR_ARANGO_DOCUMENT_TYPE_INVALID; + + try { + throwError(e.code, e.message); + fail(); + } + catch (err) { + assertEqual("[ArangoError " + e.code + ": " + e.message + "]", err.toString()); + } + }, + +//////////////////////////////////////////////////////////////////////////////// +/// @brief test to string +//////////////////////////////////////////////////////////////////////////////// + + testArangoToString2 : function () { + var e = ERRORS.ERROR_ARANGO_DOCUMENT_NOT_FOUND; + + try { + throwError(e.code, e.message + ": did not find document"); + fail(); + } + catch (err) { + assertEqual("[ArangoError " + e.code + ": " + e.message + ": did not find document]", err.toString()); + } + } + + }; +} + +// ----------------------------------------------------------------------------- +// --SECTION-- main +// ----------------------------------------------------------------------------- + +//////////////////////////////////////////////////////////////////////////////// +/// @brief executes the test suite +//////////////////////////////////////////////////////////////////////////////// + +jsunity.run(ErrorsSuite); + +return jsunity.done(); + +// Local Variables: +// mode: outline-minor +// outline-regexp: "^\\(/// @brief\\|/// @addtogroup\\|// --SECTION--\\|/// @page\\|/// @}\\)" +// End: + diff --git a/js/server/arango-dfdb.js b/js/server/arango-dfdb.js index 357aff0716..888f16744e 100644 --- a/js/server/arango-dfdb.js +++ b/js/server/arango-dfdb.js @@ -61,34 +61,52 @@ function WipeDatafile (collection, type, datafile, lastGoodPos) { } //////////////////////////////////////////////////////////////////////////////// -/// @brief checks a journal +/// @brief queries user to wipe a datafile //////////////////////////////////////////////////////////////////////////////// -function DeepCheckJournal (collection, type, datafile, scan, lastGoodPos) { +function QueryWipeDatafile (collection, type, datafile, scan, lastGoodPos) { var entries = scan.entries; if (entries.length == 0) { - printf("WARNING: The journal is empty. Even the header is missing. Going\n"); - printf(" to remove the datafile.\n"); + if (type === "journal" || type === "compactor") { + printf("WARNING: The journal is empty. Even the header is missing. Going\n"); + printf(" to remove the file.\n"); + } + else { + printf("WARNING: The datafile is empty. Even the header is missing. Going\n"); + printf(" to remove the datafile. This should never happen. Datafiles\n"); + printf(" are append-only. Make sure your hard disk does not contain\n"); + printf(" any hardware errors.\n"); + } printf("\n"); RemoveDatafile(collection, type, datafile); - return; } - if (entries.length === lastGoodPos + 3 && entries[lastGoodPos + 2].status === 2) { - printf("WARNING: The journal was not closed properly, the last entry is corrupted.\n"); - printf(" This might happen ArangoDB was killed and the last entry was not\n"); - printf(" fully written to disk. Going to remove the last entry.\n"); - printf("\n"); + var ask = true; + if (type === "journal") { + if (entries.length === lastGoodPos + 3 && entries[lastGoodPos + 2].status === 2) { + printf("WARNING: The journal was not closed properly, the last entry is corrupted.\n"); + printf(" This might happen ArangoDB was killed and the last entry was not\n"); + printf(" fully written to disk. Going to remove the last entry.\n"); + ask = false; + } + else { + printf("WARNING: The journal was not closed properly, the last entries are corrupted.\n"); + printf(" This might happen ArangoDB was killed and the last entries were not\n"); + printf(" fully written to disk.\n"); + } } else { - printf("WARNING: The journal was not closed properly, the last entries is corrupted.\n"); - printf(" This might happen ArangoDB was killed and the last entries were not\n"); - printf(" fully written to disk.\n"); - printf("\n"); + printf("WARNING: The datafile contains corrupt entries. This should never happen.\n"); + printf(" Datafiles are append-only. Make sure your hard disk does not contain\n"); + printf(" any hardware errors.\n"); + } + printf("\n"); + + if (ask) { printf("Wipe the last entries (Y/N)? "); var line = console.getline(); @@ -104,57 +122,34 @@ function DeepCheckJournal (collection, type, datafile, scan, lastGoodPos) { } //////////////////////////////////////////////////////////////////////////////// -/// @brief checks a datafile +/// @brief prints details about entries //////////////////////////////////////////////////////////////////////////////// -function DeepCheckDatafile (collection, type, datafile, scan, lastGoodPos) { - var entries = scan.entries; +function PrintEntries (entries, amount) { + var start, end; - if (entries.length == 0) { - printf("WARNING: The datafile is empty. Even the header is missing. Going\n"); - printf(" to remove the datafile. This should never happen. Datafiles\n"); - printf(" are append-only. Make sure your hard disk does not contain\n"); - printf(" any hardware errors.\n"); - printf("\n"); + if (amount > 0) { + start = 0; + end = amount; + if (end > entries.length) { + end = entries.length; + } + } + else { + start = entries.length + amount - 1; + if (start < 0) { + return; + } + if (start < Math.abs(amount)) { + return; + } - RemoveDatafile(collection, type, datafile); - - return; + end = entries.length; } - printf("WARNING: The datafile contains corrupt entries. This should never happen.\n"); - printf(" Datafiles are append-only. Make sure your hard disk does not contain\n"); - printf(" any hardware errors.\n"); - printf("\n"); - - printf("Wipe the last entries (Y/N)? "); - var line = console.getline(); - - if (line !== "yes" && line !== "YES" && line !== "y" && line !== "Y") { - printf("ABORTING\n"); - return; - } - - var entry = entries[lastGoodPos]; - - WipeDatafile(collection, type, datafile, entry.position + entry.size); -} - -//////////////////////////////////////////////////////////////////////////////// -/// @brief checks a datafile deeply -//////////////////////////////////////////////////////////////////////////////// - -function DeepCheckDatafile (collection, type, datafile, scan) { - var entries = scan.entries; - - printf("Entries\n"); - - var lastGood = 0; - var lastGoodPos = 0; - var stillGood = true; - - for (var i = 0; i < entries.length; ++i) { + for (var i = start; i < end; ++i) { var entry = entries[i]; + var s = "unknown"; switch (entry.status) { @@ -165,7 +160,31 @@ function DeepCheckDatafile (collection, type, datafile, scan) { case 5: s = "FAILED (crc mismatch)"; break; } - printf(" %d: status %s type %d size %d\n", i, s, entry.type, entry.size); + printf(" %d: status %s type %d size %d, tick %s\n", i, s, entry.type, entry.size, entry.tick); + } +} + +//////////////////////////////////////////////////////////////////////////////// +/// @brief checks a datafile deeply +//////////////////////////////////////////////////////////////////////////////// + +function DeepCheckDatafile (collection, type, datafile, scan, details) { + var entries = scan.entries; + + printf("Entries\n"); + + var lastGood = 0; + var lastGoodPos = 0; + var stillGood = true; + + if (details) { + // print details + PrintEntries(entries, 10); + PrintEntries(entries, -10); + } + + for (var i = 0; i < entries.length; ++i) { + var entry = entries[i]; if (entry.status === 1 || entry.status === 2) { if (stillGood) { @@ -182,12 +201,7 @@ function DeepCheckDatafile (collection, type, datafile, scan) { printf(" Last good position: %d\n", lastGood.position + lastGood.size); printf("\n"); - if (type === "journal" || type === "compactor") { - DeepCheckJournal(collection, type, datafile, scan, lastGoodPos); - } - else { - DeepCheckDatafile(collection, type, datafile, scan, lastGoodPos); - } + QueryWipeDatafile(collection, type, datafile, scan, lastGoodPos); } printf("\n"); @@ -197,7 +211,7 @@ function DeepCheckDatafile (collection, type, datafile, scan) { /// @brief checks a datafile //////////////////////////////////////////////////////////////////////////////// -function CheckDatafile (collection, type, datafile, issues) { +function CheckDatafile (collection, type, datafile, issues, details) { printf("Datafile\n"); printf(" path: %s\n", datafile); printf(" type: %s\n", type); @@ -208,6 +222,8 @@ function CheckDatafile (collection, type, datafile, issues) { printf(" maximal size: %d\n", scan.maximalSize); printf(" total used: %d\n", scan.endPosition); printf(" # of entries: %d\n", scan.numberMarkers); + printf(" status: %d\n", scan.status); + printf(" isSealed: %s\n", scan.isSealed ? "yes" : "no"); // set default value to unknown var statusMessage = "UNKNOWN (" + scan.status + ")"; @@ -217,9 +233,12 @@ function CheckDatafile (collection, type, datafile, issues) { case 1: statusMessage = "OK"; color = internal.COLORS.COLOR_GREEN; + if (! scan.isSealed && type === "datafile") { + color = internal.COLORS.COLOR_YELLOW; + } break; - case 1: + case 2: statusMessage = "NOT OK (reached empty marker)"; color = internal.COLORS.COLOR_RED; break; @@ -296,7 +315,7 @@ function CheckDatafile (collection, type, datafile, issues) { return; } - if (scan.entries.length == 2 && scan.entries[1].type !== 2000) { + if (scan.entries.length === 2 && scan.entries[1].type !== 2000) { // asserting a TRI_COL_MARKER_HEADER as second marker statusMessage = "datafile contains no collection header marker at pos #1!"; color = internal.COLORS.COLOR_YELLOW; @@ -316,19 +335,40 @@ function CheckDatafile (collection, type, datafile, issues) { RemoveDatafile(collection, type, datafile); return; } + + if (type !== "journal" && scan.entries.length === 3 && scan.entries[2].type === 0) { + // got the two initial header markers but nothing else... + statusMessage = "datafile is empty but not sealed"; + color = internal.COLORS.COLOR_YELLOW; - if (scan.status === 1) { + issues.push({ + collection: collection.name(), + path: datafile, + type: type, + status: scan.status, + message: statusMessage, + color: color + }); + + printf(color); + printf("WARNING: %s\n", statusMessage); + printf(internal.COLORS.COLOR_RESET); + RemoveDatafile(collection, type, datafile); return; } - DeepCheckDatafile(collection, type, datafile, scan); + if (scan.status === 1 && scan.isSealed) { + return; + } + + DeepCheckDatafile(collection, type, datafile, scan, details); } //////////////////////////////////////////////////////////////////////////////// /// @brief checks a collection //////////////////////////////////////////////////////////////////////////////// -function CheckCollection (collection, issues) { +function CheckCollection (collection, issues, details) { printf("Database\n"); printf(" name: %s\n", internal.db._name()); printf(" path: %s\n", internal.db._path()); @@ -348,15 +388,15 @@ function CheckCollection (collection, issues) { printf("\n"); for (var i = 0; i < datafiles.journals.length; ++i) { - CheckDatafile(collection, "journal", datafiles.journals[i], issues); + CheckDatafile(collection, "journal", datafiles.journals[i], issues, details); } for (var i = 0; i < datafiles.datafiles.length; ++i) { - CheckDatafile(collection, "datafiles", datafiles.datafiles[i], issues); + CheckDatafile(collection, "datafile", datafiles.datafiles[i], issues, details); } for (var i = 0; i < datafiles.compactors.length; ++i) { - CheckDatafile(collection, "compactor", datafiles.compactors[i], issues); + CheckDatafile(collection, "compactor", datafiles.compactors[i], issues, details); } } @@ -378,7 +418,7 @@ function main (argv) { return 0; }; - + printf("%s\n", " ___ _ __ _ _ ___ ___ ___ "); printf("%s\n", " / \\__ _| |_ __ _ / _(_) | ___ / \\/ __\\ / _ \\"); printf("%s\n", " / /\\ / _` | __/ _` | |_| | |/ _ \\ / /\\ /__\\// / /_\\/"); @@ -398,9 +438,11 @@ function main (argv) { printf(" %d: %s\n", i, databases[i]); } + var line; + printf("Database to check: "); while (true) { - var line = console.getline(); + line = console.getline(); if (line == "") { printf("Exiting. Please wait.\n"); @@ -445,7 +487,7 @@ function main (argv) { var a = []; while (true) { - var line = console.getline(); + line = console.getline(); if (line == "") { printf("Exiting. Please wait.\n"); @@ -471,6 +513,23 @@ function main (argv) { } } } + + printf("\n"); + printf("Prints details (yes/no)? "); + + var details = false; + while (true) { + line = console.getline(); + if (line === "") { + printf("Exiting. Please wait.\n"); + return; + } + + if (line === "yes" || line === "YES" || line === "y" || line === "Y") { + details = true; + } + break; + } var issues = [ ]; @@ -496,7 +555,7 @@ function main (argv) { printf("\n"); - CheckCollection(collection, issues); + CheckCollection(collection, issues, details); } diff --git a/js/server/bootstrap/module-internal.js b/js/server/bootstrap/module-internal.js index 3e9ab5f0e0..f1ae61abea 100644 --- a/js/server/bootstrap/module-internal.js +++ b/js/server/bootstrap/module-internal.js @@ -189,7 +189,7 @@ } else { internal.reloadAqlFunctions = function () { - internal.executeGlobalContextFunction("try { require(\"org/arangodb/ahuacatl\").reload(); } catch (err) { }"); + internal.executeGlobalContextFunction("reloadAql"); require("org/arangodb/ahuacatl").reload(); }; } diff --git a/js/server/modules/org/arangodb/foxx/manager.js b/js/server/modules/org/arangodb/foxx/manager.js index cb0f47f038..2506174888 100644 --- a/js/server/modules/org/arangodb/foxx/manager.js +++ b/js/server/modules/org/arangodb/foxx/manager.js @@ -957,7 +957,7 @@ exports.mount = function (appId, mount, options) { } if (typeof options.reload === "undefined" || options.reload === true) { - executeGlobalContextFunction("require(\"org/arangodb/actions\").reloadRouting()"); + executeGlobalContextFunction("reloadRouting"); } return { appId: app._id, mountId: doc._key, mount: mount }; @@ -1048,7 +1048,7 @@ exports.unmount = function (mount) { getStorage().remove(doc); - executeGlobalContextFunction("require(\"org/arangodb/actions\").reloadRouting()"); + executeGlobalContextFunction("reloadRouting"); return { appId: doc.app, mount: doc.mount, options: doc.options }; }; @@ -1104,7 +1104,7 @@ exports.purge = function (key) { // remove the app getStorage().remove(doc); - executeGlobalContextFunction("require(\"org/arangodb/actions\").reloadRouting()"); + executeGlobalContextFunction("reloadRouting"); // we can be sure this is a database-specific app and no system app var path = fs.join(module.appPath(), doc.path); @@ -1215,45 +1215,55 @@ exports.devTeardown = function (filename) { exports.appRoutes = function () { 'use strict'; - + var aal = getStorage(); - var find = aal.byExample({ type: "mount", active: true }); - var routes = []; + return arangodb.db._executeTransaction({ + collections: { + read: [ aal.name() ] + }, + params: { + aal : aal + }, + action: function (params) { + var find = params.aal.byExample({ type: "mount", active: true }); - while (find.hasNext()) { - var doc = find.next(); + var routes = []; - var appId = doc.app; - var mount = doc.mount; - var options = doc.options || { }; + while (find.hasNext()) { + var doc = find.next(); + var appId = doc.app; + var mount = doc.mount; + var options = doc.options || { }; - try { - var app = module.createApp(appId, options || {}); + try { + var app = module.createApp(appId, options || {}); - if (app === null) { - throw new Error("Cannot find application '" + appId + "'"); + if (app === null) { + throw new Error("Cannot find application '" + appId + "'"); + } + + var r = routingAalApp(app, mount, options); + + if (r === null) { + throw new Error("Cannot compute the routing table for foxx application '" + + app._id + "', check the log file for errors!"); + } + + routes.push(r); + + if (!developmentMode) { + console.log("Mounted foxx app '%s' on '%s'", appId, mount); + } + } + catch (err) { + console.error("Cannot mount foxx app '%s': %s", appId, String(err.stack || err)); + } } - var r = routingAalApp(app, mount, options); - - if (r === null) { - throw new Error("Cannot compute the routing table for foxx application '" - + app._id + "', check the log file for errors!"); - } - - routes.push(r); - - if (!developmentMode) { - console.log("Mounted foxx app '%s' on '%s'", appId, mount); - } + return routes; } - catch (err) { - console.error("Cannot mount foxx app '%s': %s", appId, String(err.stack || err)); - } - } - - return routes; + }); }; //////////////////////////////////////////////////////////////////////////////// diff --git a/lib/BasicsC/logging.c b/lib/BasicsC/logging.c index 7000253ca3..c73d5909b7 100644 --- a/lib/BasicsC/logging.c +++ b/lib/BasicsC/logging.c @@ -511,7 +511,7 @@ static int GenerateMessage (char* buffer, m += n; // ............................................................................. - // check if we must disply the line number + // check if we must display the line number // ............................................................................. sln = ShowLineNumber; diff --git a/lib/Rest/Handler.h b/lib/Rest/Handler.h index 193dc27ea9..f21ff45643 100644 --- a/lib/Rest/Handler.h +++ b/lib/Rest/Handler.h @@ -98,10 +98,9 @@ namespace triagens { } case Handler::HANDLER_FAILED: + default: return Job::status_t(Job::JOB_FAILED); } - - return Job::status_t(Job::JOB_FAILED); } status_e status; diff --git a/lib/V8/v8-utils.cpp b/lib/V8/v8-utils.cpp index f755bb433a..23007b9a92 100644 --- a/lib/V8/v8-utils.cpp +++ b/lib/V8/v8-utils.cpp @@ -2275,7 +2275,7 @@ static v8::Handle JS_Wait (v8::Arguments const& argv) { if (gc) { // wait with gc v8::V8::LowMemoryNotification(); - while(! v8::V8::IdleNotification()) { + while (! v8::V8::IdleNotification()) { } size_t i = 0;