diff --git a/arangod/Ahuacatl/ahuacatl-codegen.c b/arangod/Ahuacatl/ahuacatl-codegen.c index 737ea0f..f390315 100644 --- a/arangod/Ahuacatl/ahuacatl-codegen.c +++ b/arangod/Ahuacatl/ahuacatl-codegen.c @@ -2253,9 +2253,12 @@ char* TRI_GenerateCodeAql (TRI_aql_context_t* const context) { char* code; assert(context); + + TRI_AQL_LOG("generating code"); generator = CreateGenerator(context); if (generator == NULL) { + TRI_AQL_LOG("could not create code generator"); return NULL; } @@ -2272,6 +2275,7 @@ char* TRI_GenerateCodeAql (TRI_aql_context_t* const context) { OutputString(&generator->_buffer, "})()"); if (generator->_error) { + TRI_AQL_LOG("generating code failed"); FreeGenerator(generator); return NULL; @@ -2283,9 +2287,11 @@ char* TRI_GenerateCodeAql (TRI_aql_context_t* const context) { FreeGenerator(generator); if (code) { - TRI_AQL_DUMP("generated code: %s\n", code); + TRI_AQL_LOG("generated code: %s\n", code); } + TRI_AQL_LOG("generating code successful"); + return code; } diff --git a/arangod/Ahuacatl/ahuacatl-collections.c b/arangod/Ahuacatl/ahuacatl-collections.c index 3113434..4d63eb8 100644 --- a/arangod/Ahuacatl/ahuacatl-collections.c +++ b/arangod/Ahuacatl/ahuacatl-collections.c @@ -399,15 +399,19 @@ bool TRI_ReadLockCollectionsAql (TRI_aql_context_t* const context) { TRI_AQL_LOG("read-locking collection '%s'", collection->_name); + LOG_TRACE("before beginRead"); lockResult = documentCollection->beginRead(documentCollection); + LOG_TRACE("after beginRead"); if (lockResult != TRI_ERROR_NO_ERROR) { // couldn't acquire the read lock + LOG_WARNING("read-locking collection '%s' failed", collection->_name); result = false; TRI_SetErrorContextAql(context, TRI_ERROR_QUERY_COLLECTION_LOCK_FAILED, collection->_name); break; } else { collection->_readLocked = true; + TRI_AQL_LOG("read-locked collection '%s'", collection->_name); } } @@ -447,7 +451,9 @@ void TRI_ReadUnlockCollectionsAql (TRI_aql_context_t* const context) { TRI_AQL_LOG("read-unlocking collection '%s'", collection->_name); + LOG_TRACE("before endRead"); documentCollection->endRead(documentCollection); + LOG_TRACE("after endRead"); collection->_readLocked = false; } } @@ -484,6 +490,7 @@ bool TRI_AddBarrierCollectionsAql (TRI_aql_context_t* const context) { if (!ce) { // couldn't create the barrier result = false; + LOG_WARNING("adding barrier for collection '%s' failed", collection->_name); TRI_SetErrorContextAql(context, TRI_ERROR_OUT_OF_MEMORY, NULL); break; } diff --git a/arangod/Ahuacatl/ahuacatl-context.c b/arangod/Ahuacatl/ahuacatl-context.c index b4a426c..b5e107a 100644 --- a/arangod/Ahuacatl/ahuacatl-context.c +++ b/arangod/Ahuacatl/ahuacatl-context.c @@ -279,21 +279,30 @@ void TRI_FreeContextAql (TRI_aql_context_t* const context) { //////////////////////////////////////////////////////////////////////////////// bool TRI_ValidateQueryContextAql (TRI_aql_context_t* const context) { + TRI_AQL_LOG("validating query context"); + if (context->_parser->_length == 0) { // query is empty, no need to parse it + TRI_AQL_LOG("validating query context failed"); TRI_SetErrorContextAql(context, TRI_ERROR_QUERY_EMPTY, NULL); return false; } + + TRI_AQL_LOG("validating query context parse"); // parse the query if (!TRI_ParseAql(context)) { // lexing/parsing failed + TRI_AQL_LOG("validating query context parse failed"); return false; } if (context->_error._code) { + TRI_AQL_LOG("validating query context failed with error %d", (int) context->_error._code); return false; } + + TRI_AQL_LOG("validating query context successful"); return true; } @@ -335,19 +344,26 @@ bool TRI_BindQueryContextAql (TRI_aql_context_t* const context, //////////////////////////////////////////////////////////////////////////////// bool TRI_OptimiseQueryContextAql (TRI_aql_context_t* const context) { + TRI_AQL_LOG("performing query optimisations"); + // do some basic optimisations in the AST if (!TRI_OptimiseAql(context)) { - // constant folding failed + // optimisations failed + TRI_AQL_LOG("performing query optimisations failed"); + return false; } if (context->_error._code) { + TRI_AQL_LOG("performing query optimisations failed with error code %d", (int) context->_error._code); return false; } + TRI_AQL_LOG("performing query optimisations compacting"); TRI_CompactStatementListAql(context->_statements); + TRI_AQL_LOG("performing query optimisations compacting successful"); - // TRI_DumpStatementsAql(context->_statements); + TRI_AQL_LOG("performing query optimisations successful"); return true; } @@ -357,24 +373,35 @@ bool TRI_OptimiseQueryContextAql (TRI_aql_context_t* const context) { //////////////////////////////////////////////////////////////////////////////// bool TRI_LockQueryContextAql (TRI_aql_context_t* const context) { + TRI_AQL_LOG("performing query locking"); + // mark all used collections as being used if (!TRI_LockCollectionsAql(context)) { return false; } + TRI_AQL_LOG("performing query locking read"); + // acquire read locks on all collections used if (!TRI_ReadLockCollectionsAql(context)) { + TRI_AQL_LOG("performing query locking read failed"); return false; } + TRI_AQL_LOG("performing query locking barrier"); + // add barriers for all collections used if (!TRI_AddBarrierCollectionsAql(context)) { + TRI_AQL_LOG("performing query locking barrier failed"); return false; } if (context->_error._code) { + TRI_AQL_LOG("performing query locking failed with error code %d", (int) context->_error._code); return false; } + + TRI_AQL_LOG("performing query locking successful"); return true; } diff --git a/arangod/Ahuacatl/ahuacatl-log.h b/arangod/Ahuacatl/ahuacatl-log.h index f8e92b7..aa5cf5e 100644 --- a/arangod/Ahuacatl/ahuacatl-log.h +++ b/arangod/Ahuacatl/ahuacatl-log.h @@ -35,7 +35,7 @@ extern "C" { #endif #undef TRI_DEBUG_AQL -//#define TRI_DEBUG_AQL 1 +#define TRI_DEBUG_AQL 1 // ----------------------------------------------------------------------------- // --SECTION-- public macros @@ -52,10 +52,8 @@ extern "C" { #ifdef TRI_DEBUG_AQL #define TRI_AQL_LOG(...) LOG_INFO(__VA_ARGS__); -#define TRI_AQL_DUMP(format, ...) printf(format, __VA_ARGS__); #else #define TRI_AQL_LOG(...) LOG_TRACE(__VA_ARGS__); -#define TRI_AQL_DUMP(...) { }; #endif //////////////////////////////////////////////////////////////////////////////// diff --git a/arangod/Ahuacatl/ahuacatl-optimiser.c b/arangod/Ahuacatl/ahuacatl-optimiser.c index 49437aa..43d905b 100644 --- a/arangod/Ahuacatl/ahuacatl-optimiser.c +++ b/arangod/Ahuacatl/ahuacatl-optimiser.c @@ -1076,14 +1076,22 @@ static bool DetermineIndexes (TRI_aql_context_t* const context) { //////////////////////////////////////////////////////////////////////////////// bool TRI_OptimiseAql (TRI_aql_context_t* const context) { + LOG_TRACE("optimising"); + + LOG_TRACE("optimising AST"); if (!OptimiseAst(context)) { + LOG_TRACE("optimising AST failed"); return false; } + LOG_TRACE("determining indexes"); if (!DetermineIndexes(context)) { + LOG_TRACE("determining indexes failed"); return false; } + LOG_TRACE("optimising successful"); + return true; } diff --git a/arangod/RestHandler/RestDocumentHandler.cpp b/arangod/RestHandler/RestDocumentHandler.cpp index e8fb467..026654d 100644 --- a/arangod/RestHandler/RestDocumentHandler.cpp +++ b/arangod/RestHandler/RestDocumentHandler.cpp @@ -270,13 +270,18 @@ bool RestDocumentHandler::createDocument () { // inside write transaction // ............................................................................. - _documentCollection->beginWrite(_documentCollection); + LOG_TRACE("before beginWrite"); + int lockResult = _documentCollection->beginWrite(_documentCollection); + LOG_TRACE("after beginWrite"); + LOGGER_TRACE << "result of beginWrite: " << lockResult; bool waitForSync = _documentCollection->base._waitForSync; TRI_voc_cid_t cid = _documentCollection->base._cid; // note: unlocked is performed by createJson() + LOGGER_TRACE << "creating document"; TRI_doc_mptr_t const mptr = _documentCollection->createJson(_documentCollection, TRI_DOC_MARKER_DOCUMENT, json, 0, reuseId, true); + LOGGER_TRACE << "finished creating document"; // ............................................................................. // outside write transaction @@ -284,6 +289,7 @@ bool RestDocumentHandler::createDocument () { // release collection and free json releaseCollection(); + LOGGER_TRACE << "released collection again"; // generate result if (mptr._did != 0) { @@ -415,12 +421,17 @@ bool RestDocumentHandler::readSingleDocument (bool generateBody) { // inside read transaction // ............................................................................. - _documentCollection->beginRead(_documentCollection); + LOG_TRACE("before beginRead"); + int lockResult = _documentCollection->beginRead(_documentCollection); + LOG_TRACE("after beginRead"); + LOGGER_TRACE << "result of beginRead: " << lockResult; TRI_voc_cid_t cid = _documentCollection->base._cid; TRI_doc_mptr_t const document = findDocument(did); + LOG_TRACE("before endRead"); _documentCollection->endRead(_documentCollection); + LOG_TRACE("after endRead"); // ............................................................................. // outside read transaction @@ -511,7 +522,10 @@ bool RestDocumentHandler::readAllDocuments () { vector ids; - _documentCollection->beginRead(_documentCollection); + LOG_DEBUG("before beginRead"); + int lockResult = _documentCollection->beginRead(_documentCollection); + LOG_DEBUG("after beginRead"); + LOGGER_TRACE << "result of beginRead: " << lockResult; TRI_voc_cid_t cid = _documentCollection->base._cid; @@ -537,7 +551,9 @@ bool RestDocumentHandler::readAllDocuments () { // necessary so we will always remove the read lock } + LOG_TRACE("before endRead"); _documentCollection->endRead(_documentCollection); + LOG_TRACE("after endRead"); // ............................................................................. // outside read transaction @@ -715,7 +731,10 @@ bool RestDocumentHandler::updateDocument () { // inside write transaction // ............................................................................. - _documentCollection->beginWrite(_documentCollection); + LOG_DEBUG("before beginWrite"); + int lockResult = _documentCollection->beginWrite(_documentCollection); + LOG_DEBUG("after beginWrite"); + LOGGER_TRACE << "result of beginWrite: " << lockResult; // unlocking is performed in updateJson() TRI_voc_rid_t rid = 0; @@ -860,7 +879,10 @@ bool RestDocumentHandler::deleteDocument () { // inside write transaction // ............................................................................. - _documentCollection->beginWrite(_documentCollection); + LOG_TRACE("before beginWrite"); + int lockResult = _documentCollection->beginWrite(_documentCollection); + LOG_TRACE("after beginWrite"); + LOGGER_TRACE << "result of beginWrite: " << lockResult; TRI_voc_rid_t rid = 0; TRI_voc_cid_t cid = _documentCollection->base._cid; diff --git a/arangod/RestHandler/RestVocbaseBaseHandler.cpp b/arangod/RestHandler/RestVocbaseBaseHandler.cpp index de40357..6e04545 100644 --- a/arangod/RestHandler/RestVocbaseBaseHandler.cpp +++ b/arangod/RestHandler/RestVocbaseBaseHandler.cpp @@ -600,7 +600,9 @@ TRI_doc_mptr_t const RestVocbaseBaseHandler::findDocument (string const& doc) { // inside read transaction // ............................................................................. + LOG_TRACE("before beginRead"); _documentCollection->beginRead(_documentCollection); + LOG_TRACE("after beginRead"); document = _documentCollection->read(_documentCollection, id); @@ -609,7 +611,9 @@ TRI_doc_mptr_t const RestVocbaseBaseHandler::findDocument (string const& doc) { _barrier = TRI_CreateBarrierElement(&_documentCollection->_barrierList); } + LOG_TRACE("before endRead"); _documentCollection->endRead(_documentCollection); + LOG_TRACE("after endRead"); // ............................................................................. // outside read transaction diff --git a/arangod/V8Server/ApplicationV8.cpp b/arangod/V8Server/ApplicationV8.cpp index d200d55..80185d3 100644 --- a/arangod/V8Server/ApplicationV8.cpp +++ b/arangod/V8Server/ApplicationV8.cpp @@ -1,5 +1,5 @@ //////////////////////////////////////////////////////////////////////////////// -/// @brief V8 enigne configuration +/// @brief V8 engine configuration /// /// @file /// @@ -28,6 +28,8 @@ #include "ApplicationV8.h" #include "Basics/ConditionLocker.h" +#include "Basics/ReadLocker.h" +#include "Basics/WriteLocker.h" #include "Logger/Logger.h" #include "V8/v8-conv.h" #include "V8/v8-shell.h" @@ -65,16 +67,43 @@ namespace { public: V8GcThread (ApplicationV8* applicationV8) : Thread("v8-gc"), - _applicationV8(applicationV8) { + _applicationV8(applicationV8), + _lock(), + _lastGcStamp(TRI_microtime()) { } public: + +//////////////////////////////////////////////////////////////////////////////// +/// @brief collect garbage in an endless loop (main functon of GC thread) +//////////////////////////////////////////////////////////////////////////////// + void run () { _applicationV8->collectGarbage(); } +//////////////////////////////////////////////////////////////////////////////// +/// @brief get the timestamp of the last GC +//////////////////////////////////////////////////////////////////////////////// + + double getLastGcStamp () { + READ_LOCKER(_lock); + return _lastGcStamp; + } + +//////////////////////////////////////////////////////////////////////////////// +/// @brief set the global GC timestamp +//////////////////////////////////////////////////////////////////////////////// + + void updateGcStamp (double value) { + WRITE_LOCKER(_lock); + _lastGcStamp = value; + } + private: ApplicationV8* _applicationV8; + ReadWriteLock _lock; + double _lastGcStamp; }; } @@ -106,6 +135,7 @@ ApplicationV8::ApplicationV8 (string const& binaryPath) _startupModules("js/modules"), _actionPath(), _gcInterval(1000), + _gcFrequency(10.0), _startupLoader(), _actionLoader(), _vocbase(0), @@ -230,21 +260,30 @@ ApplicationV8::V8Context* ApplicationV8::enterContext () { //////////////////////////////////////////////////////////////////////////////// void ApplicationV8::exitContext (V8Context* context) { + V8GcThread* gc = dynamic_cast(_gcThread); + assert(gc != 0); + double lastGc = gc->getLastGcStamp(); + context->_context->Exit(); context->_isolate->Exit(); delete context->_locker; - + ++context->_dirt; { CONDITION_LOCKER(guard, _contextCondition); - if (context->_dirt < _gcInterval) { - _freeContexts.push_back(context); + if (context->_lastGcStamp + _gcFrequency < lastGc) { + LOGGER_TRACE << "periodic gc interval reached"; + _dirtyContexts.push_back(context); } - else { + else if (context->_dirt >= _gcInterval) { + LOGGER_TRACE << "maximum number of requests reached"; _dirtyContexts.push_back(context); } + else { + _freeContexts.push_back(context); + } guard.broadcast(); } @@ -253,25 +292,112 @@ void ApplicationV8::exitContext (V8Context* context) { } //////////////////////////////////////////////////////////////////////////////// +/// @brief determine which of the free contexts should be picked for the GC +//////////////////////////////////////////////////////////////////////////////// + +ApplicationV8::V8Context* ApplicationV8::pickContextForGc () { + size_t n = _freeContexts.size(); + + if (n == 0) { + // this is easy... + return 0; + } + + V8GcThread* gc = dynamic_cast(_gcThread); + V8Context* context = 0; + + // we got more than 1 context to clean up, pick the one with the "oldest" GC stamp + size_t pickedContextNr = 0; // index of context with lowest GC stamp + + for (size_t i = 0; i < n; ++i) { + // compare last GC stamp + if (_freeContexts[i]->_lastGcStamp <= _freeContexts[pickedContextNr]->_lastGcStamp) { + pickedContextNr = i; + } + } + // we now have the context to clean up in pickedContextNr + + // this is the context to clean up + context = _freeContexts[pickedContextNr]; + assert(context != 0); + + // now compare its last GC timestamp with the last global GC stamp + if (context->_lastGcStamp + _gcFrequency >= gc->getLastGcStamp()) { + // no need yet to clean up the context + return 0; + } + + // we'll pop the context from the vector. the context might be at any position in the vector + // so we need to move the other elements around + if (n > 1) { + for (size_t i = pickedContextNr; i < n - 1; ++i) { + _freeContexts[i] = _freeContexts[i + 1]; + } + } + _freeContexts.pop_back(); + + return context; +} + +//////////////////////////////////////////////////////////////////////////////// /// @brief runs the garbage collection //////////////////////////////////////////////////////////////////////////////// void ApplicationV8::collectGarbage () { + V8GcThread* gc = dynamic_cast(_gcThread); + assert(gc != 0); + + // this flag will be set to true if we timed out waiting for a GC signal + // if set to true, the next cycle will use a reduced wait time so the GC + // can be performed more early for all dirty contexts. The flag is set + // to false again once all contexts have been cleaned up and there is nothing + // more to do + bool useReducedWait = false; + + // the time we'll wait for a signal + uint64_t regularWaitTime = (uint64_t) (_gcFrequency * 1000.0 * 1000.0); + + // the time we'll wait for a signal when the previous wait timed out + uint64_t reducedWaitTime = (uint64_t) (_gcFrequency * 1000.0 * 100.0); + while (_stopping == 0) { V8Context* context = 0; + bool gotSignal = false; { CONDITION_LOCKER(guard, _contextCondition); if (_dirtyContexts.empty()) { - guard.wait(); + uint64_t waitTime = useReducedWait ? reducedWaitTime : regularWaitTime; + // we'll wait for a signal or a timeout + gotSignal = guard.wait(waitTime); + + // use a reduced wait time in the next round because we seem to be idle + // the reduced wait time will allow use to perfom GC for more contexts + useReducedWait = ! gotSignal; } if (! _dirtyContexts.empty()) { context = _dirtyContexts.back(); _dirtyContexts.pop_back(); + useReducedWait = false; + } + else if (! gotSignal && ! _freeContexts.empty()) { + // we timed out waiting for a signal, so we have idle time that we can + // spend on running the GC pro-actively + // We'll pick one of the free contexts and clean it up + context = pickContextForGc(); + + // there is no context to clean up, probably they all have been cleaned up + // already. increase the wait time so we don't cycle to much in the GC loop + // and waste CPU unnecessary + useReducedWait = (context != 0); } } + + // update last gc time + double lastGc = TRI_microtime(); + gc->updateGcStamp(lastGc); if (context != 0) { LOGGER_TRACE << "collecting V8 garbage"; @@ -288,6 +414,7 @@ void ApplicationV8::collectGarbage () { delete context->_locker; context->_dirt = 0; + context->_lastGcStamp = lastGc; { CONDITION_LOCKER(guard, _contextCondition); @@ -326,7 +453,8 @@ void ApplicationV8::disableActions () { void ApplicationV8::setupOptions (map& options) { options["JAVASCRIPT Options:help-admin"] - ("javascript.gc-interval", &_gcInterval, "JavaScript garbage collection interval (each x requests)") + ("javascript.gc-interval", &_gcInterval, "JavaScript request-based garbage collection interval (each x requests)") + ("javascript.gc-frequency", &_gcFrequency, "JavaScript time-based garbage collection frequency (each x seconds)") ; options["JAVASCRIPT Options:help-admin"] @@ -341,6 +469,7 @@ void ApplicationV8::setupOptions (map //////////////////////////////////////////////////////////////////////////////// bool ApplicationV8::prepare () { + LOGGER_DEBUG << "V8 version: " << v8::V8::GetVersion(); LOGGER_INFO << "using JavaScript modules path '" << _startupModules << "'"; // set up the startup loader @@ -527,6 +656,8 @@ bool ApplicationV8::prepareV8Instance (size_t i) { context->_context->Exit(); context->_isolate->Exit(); delete context->_locker; + + context->_lastGcStamp = TRI_microtime(); LOGGER_TRACE << "initialised V8 context #" << i; diff --git a/arangod/V8Server/ApplicationV8.h b/arangod/V8Server/ApplicationV8.h index 62b0474..f6d485e 100644 --- a/arangod/V8Server/ApplicationV8.h +++ b/arangod/V8Server/ApplicationV8.h @@ -91,7 +91,19 @@ namespace triagens { v8::Persistent _context; v8::Isolate* _isolate; v8::Locker* _locker; + +//////////////////////////////////////////////////////////////////////////////// +/// @brief number of requests since last GC of the context +//////////////////////////////////////////////////////////////////////////////// + size_t _dirt; + +//////////////////////////////////////////////////////////////////////////////// +/// @brief timestamp of last GC for the context +//////////////////////////////////////////////////////////////////////////////// + + double _lastGcStamp; + }; //////////////////////////////////////////////////////////////////////////////// @@ -241,6 +253,12 @@ namespace triagens { //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// +/// @brief determine which of the free contexts should be picked for the GC +//////////////////////////////////////////////////////////////////////////////// + + V8Context* pickContextForGc (); + +//////////////////////////////////////////////////////////////////////////////// /// @brief prepares a V8 instance //////////////////////////////////////////////////////////////////////////////// @@ -312,6 +330,18 @@ namespace triagens { uint64_t _gcInterval; //////////////////////////////////////////////////////////////////////////////// +/// @brief JavaScript garbage collection frequency (each x seconds) +/// +/// @CMDOPT{--javascript.gc-frequency @CA{frequency}} +/// +/// Specifies the frequency in seconds for the automatic garbage collection of +/// JavaScript objects. This setting is useful to have the garbage collection +/// still work in periods with no or little numbers of requests. +//////////////////////////////////////////////////////////////////////////////// + + double _gcFrequency; + +//////////////////////////////////////////////////////////////////////////////// /// @brief V8 startup loader //////////////////////////////////////////////////////////////////////////////// diff --git a/arangod/V8Server/v8-query.cpp b/arangod/V8Server/v8-query.cpp index 0ca033a..0cad75a 100755 --- a/arangod/V8Server/v8-query.cpp +++ b/arangod/V8Server/v8-query.cpp @@ -35,6 +35,7 @@ #include "V8/v8-conv.h" #include "V8/v8-utils.h" #include "V8Server/v8-vocbase.h" +#include "Logger/Logger.h" // ----------------------------------------------------------------------------- // --SECTION-- HELPER FUNCTIONS @@ -639,20 +640,26 @@ static v8::Handle ExecuteSkiplistQuery (v8::Arguments const& argv, st // inside a read transaction // ............................................................................. + LOG_TRACE("before beginRead"); collection->_collection->beginRead(collection->_collection); + LOG_TRACE("after beginRead"); // extract the index TRI_index_t* idx = TRI_LookupIndexByHandle(sim->base.base._vocbase, collection, argv[0], false, &err); if (idx == 0) { + LOG_TRACE("before endRead"); collection->_collection->endRead(collection->_collection); + LOG_TRACE("after endRead"); TRI_ReleaseCollection(collection); return scope.Close(v8::ThrowException(err)); } if (idx->_type != TRI_IDX_TYPE_SKIPLIST_INDEX) { + LOG_TRACE("before endRead"); collection->_collection->endRead(collection->_collection); + LOG_TRACE("after endRead"); TRI_ReleaseCollection(collection); return scope.Close(v8::ThrowException(TRI_CreateErrorObject(TRI_ERROR_BAD_PARAMETER, "index must be a skiplist index"))); @@ -668,7 +675,9 @@ static v8::Handle ExecuteSkiplistQuery (v8::Arguments const& argv, st } if (!skiplistOperator) { + LOG_TRACE("before endRead"); collection->_collection->endRead(collection->_collection); + LOG_TRACE("after endRead"); TRI_ReleaseCollection(collection); return scope.Close(v8::ThrowException(TRI_CreateErrorObject(TRI_ERROR_BAD_PARAMETER, "setting up skiplist operator failed"))); @@ -699,7 +708,9 @@ static v8::Handle ExecuteSkiplistQuery (v8::Arguments const& argv, st } } + LOG_TRACE("before endRead"); collection->_collection->endRead(collection->_collection); + LOG_TRACE("after endRead"); // ............................................................................. // outside a write transaction @@ -836,7 +847,9 @@ static v8::Handle ExecuteBitarrayQuery (v8::Arguments const& argv, st // inside a read transaction // ............................................................................. + LOG_TRACE("before beginRead"); collection->_collection->beginRead(collection->_collection); + LOG_TRACE("after beginRead"); // ............................................................................. @@ -846,7 +859,9 @@ static v8::Handle ExecuteBitarrayQuery (v8::Arguments const& argv, st TRI_index_t* idx = TRI_LookupIndexByHandle(sim->base.base._vocbase, collection, argv[0], false, &err); if (idx == 0) { + LOG_TRACE("before endRead"); collection->_collection->endRead(collection->_collection); + LOG_TRACE("after endRead"); TRI_ReleaseCollection(collection); return scope.Close(v8::ThrowException(err)); @@ -854,7 +869,9 @@ static v8::Handle ExecuteBitarrayQuery (v8::Arguments const& argv, st if (idx->_type != TRI_IDX_TYPE_BITARRAY_INDEX) { + LOG_TRACE("before endRead"); collection->_collection->endRead(collection->_collection); + LOG_TRACE("after endRead"); TRI_ReleaseCollection(collection); return scope.Close(v8::ThrowException(TRI_CreateErrorObject(TRI_ERROR_BAD_PARAMETER, "index must be a skiplist index"))); @@ -872,7 +889,9 @@ static v8::Handle ExecuteBitarrayQuery (v8::Arguments const& argv, st if (indexOperator == 0) { // something wrong + LOG_TRACE("before endRead"); collection->_collection->endRead(collection->_collection); + LOG_TRACE("after endRead"); TRI_ReleaseCollection(collection); return scope.Close(v8::ThrowException(TRI_CreateErrorObject(TRI_ERROR_BAD_PARAMETER, "setting up skiplist operator failed"))); @@ -925,7 +944,9 @@ static v8::Handle ExecuteBitarrayQuery (v8::Arguments const& argv, st LOG_WARNING("index iterator returned with a NULL value in ExecuteBitarrayQuery"); } + LOG_TRACE("before endRead"); collection->_collection->endRead(collection->_collection); + LOG_TRACE("after endRead"); // ............................................................................. // outside a write transaction @@ -1097,7 +1118,9 @@ static v8::Handle EdgesQuery (TRI_edge_direction_e direction, v8::Arg // inside a read transaction // ............................................................................. + LOG_TRACE("before beginRead"); collection->_collection->beginRead(collection->_collection); + LOG_TRACE("after beginRead"); TRI_barrier_t* barrier = 0; uint32_t count = 0; @@ -1157,7 +1180,9 @@ static v8::Handle EdgesQuery (TRI_edge_direction_e direction, v8::Arg TRI_ReleaseCollection(vertexCollection); } + LOG_TRACE("before endRead"); collection->_collection->endRead(collection->_collection); + LOG_TRACE("after endRead"); TRI_ReleaseCollection(collection); return scope.Close(v8::ThrowException(errMsg)); @@ -1180,7 +1205,9 @@ static v8::Handle EdgesQuery (TRI_edge_direction_e direction, v8::Arg TRI_DestroyVectorPointer(&edges); } + LOG_TRACE("before endRead"); collection->_collection->endRead(collection->_collection); + LOG_TRACE("after endRead"); // ............................................................................. // outside a write transaction @@ -1210,6 +1237,7 @@ static v8::Handle EdgesQuery (TRI_edge_direction_e direction, v8::Arg static v8::Handle JS_AllQuery (v8::Arguments const& argv) { v8::HandleScope scope; + LOGGER_TRACE << "executing ALL query"; // extract and use the simple collection v8::Handle err; TRI_vocbase_col_t const* collection; @@ -1243,10 +1271,17 @@ static v8::Handle JS_AllQuery (v8::Arguments const& argv) { // inside a read transaction // ............................................................................. - collection->_collection->beginRead(collection->_collection); + LOGGER_TRACE << "acquiring lock in allquery"; + LOG_TRACE("before beginRead"); + int lockResult = collection->_collection->beginRead(collection->_collection); + LOG_TRACE("after beginRead"); + LOGGER_TRACE << "result of beginRead: " << lockResult; size_t total = sim->_primaryIndex._nrUsed; uint32_t count = 0; + LOGGER_TRACE << "number of documents: " << total; + LOGGER_TRACE << "skip: " << skip; + LOGGER_TRACE << "limit: " << limit; if (0 < total && 0 < limit) { TRI_barrier_t* barrier = 0; @@ -1254,6 +1289,9 @@ static v8::Handle JS_AllQuery (v8::Arguments const& argv) { void** beg = sim->_primaryIndex._table; void** end = sim->_primaryIndex._table + sim->_primaryIndex._nrAlloc; void** ptr = beg; + LOGGER_TRACE << "nrAlloc: " << sim->_primaryIndex._nrAlloc; + LOGGER_TRACE << "begin: " << beg; + LOGGER_TRACE << "end: " << beg; // skip from the beginning if (0 < skip) { @@ -1291,6 +1329,7 @@ static v8::Handle JS_AllQuery (v8::Arguments const& argv) { } } + LOGGER_TRACE << "beginning extraction. ptr: " << ptr << ", end: " << end << ", range: " << (end - ptr) << ", count: " << count << ", limit: " << limit; // limit for (; ptr < end && count < limit; ++ptr) { if (*ptr) { @@ -1307,9 +1346,12 @@ static v8::Handle JS_AllQuery (v8::Arguments const& argv) { } } } + LOGGER_TRACE << "finished extraction. count is " << count; } + LOG_TRACE("before endRead"); collection->_collection->endRead(collection->_collection); + LOG_TRACE("after endRead"); // ............................................................................. // outside a write transaction @@ -1386,7 +1428,9 @@ static v8::Handle JS_ByExampleQuery (v8::Arguments const& argv) { // inside a read transaction // ............................................................................. + LOG_TRACE("before beginRead"); collection->_collection->beginRead(collection->_collection); + LOG_TRACE("after beginRead"); // find documents by example TRI_vector_t filtered = TRI_SelectByExample(sim, n, pids, values); @@ -1418,7 +1462,9 @@ static v8::Handle JS_ByExampleQuery (v8::Arguments const& argv) { TRI_DestroyVector(&filtered); + LOG_TRACE("before endRead"); collection->_collection->endRead(collection->_collection); + LOG_TRACE("after endRead"); // ............................................................................. // outside a write transaction @@ -1483,20 +1529,26 @@ static v8::Handle JS_ByExampleHashIndex (v8::Arguments const& argv) { // inside a read transaction // ............................................................................. + LOG_TRACE("before beginRead"); collection->_collection->beginRead(collection->_collection); + LOG_TRACE("after beginRead"); // extract the index TRI_index_t* idx = TRI_LookupIndexByHandle(sim->base.base._vocbase, collection, argv[0], false, &err); if (idx == 0) { + LOG_TRACE("before endRead"); collection->_collection->endRead(collection->_collection); + LOG_TRACE("after endRead"); TRI_ReleaseCollection(collection); return scope.Close(v8::ThrowException(err)); } if (idx->_type != TRI_IDX_TYPE_HASH_INDEX) { + LOG_TRACE("before endRead"); collection->_collection->endRead(collection->_collection); + LOG_TRACE("after endRead"); TRI_ReleaseCollection(collection); return scope.Close(v8::ThrowException(TRI_CreateErrorObject(TRI_ERROR_BAD_PARAMETER, "index must be a hash index"))); @@ -1512,7 +1564,9 @@ static v8::Handle JS_ByExampleHashIndex (v8::Arguments const& argv) { int res = SetupExampleObjectIndex(hashIndex, example, shaper, n, values, &err); if (res != TRI_ERROR_NO_ERROR) { + LOG_TRACE("before endRead"); collection->_collection->endRead(collection->_collection); + LOG_TRACE("after endRead"); TRI_ReleaseCollection(collection); return scope.Close(v8::ThrowException(err)); @@ -1542,7 +1596,9 @@ static v8::Handle JS_ByExampleHashIndex (v8::Arguments const& argv) { } } + LOG_TRACE("before endRead"); collection->_collection->endRead(collection->_collection); + LOG_TRACE("after endRead"); // ............................................................................. // outside a write transaction @@ -1667,20 +1723,26 @@ static v8::Handle JS_NearQuery (v8::Arguments const& argv) { // inside a read transaction // ............................................................................. + LOG_TRACE("before beginRead"); collection->_collection->beginRead(collection->_collection); + LOG_TRACE("after beginRead"); // extract the index TRI_index_t* idx = TRI_LookupIndexByHandle(sim->base.base._vocbase, collection, argv[0], false, &err); if (idx == 0) { + LOG_TRACE("before endRead"); collection->_collection->endRead(collection->_collection); + LOG_TRACE("after endRead"); TRI_ReleaseCollection(collection); return scope.Close(v8::ThrowException(err)); } if (idx->_type != TRI_IDX_TYPE_GEO1_INDEX && idx->_type != TRI_IDX_TYPE_GEO2_INDEX) { + LOG_TRACE("before endRead"); collection->_collection->endRead(collection->_collection); + LOG_TRACE("after endRead"); TRI_ReleaseCollection(collection); return scope.Close(v8::ThrowException(TRI_CreateErrorObject(TRI_ERROR_BAD_PARAMETER, "index must be a geo-index"))); @@ -1708,7 +1770,9 @@ static v8::Handle JS_NearQuery (v8::Arguments const& argv) { StoreGeoResult(collection, cors, documents, distances); } + LOG_TRACE("before endRead"); collection->_collection->endRead(collection->_collection); + LOG_TRACE("after endRead"); // ............................................................................. // outside a write transaction @@ -1769,20 +1833,26 @@ static v8::Handle JS_WithinQuery (v8::Arguments const& argv) { // inside a read transaction // ............................................................................. + LOG_TRACE("before beginRead"); collection->_collection->beginRead(collection->_collection); + LOG_TRACE("after beginRead"); // extract the index TRI_index_t* idx = TRI_LookupIndexByHandle(sim->base.base._vocbase, collection, argv[0], false, &err); if (idx == 0) { + LOG_TRACE("before endRead"); collection->_collection->endRead(collection->_collection); + LOG_TRACE("after endRead"); TRI_ReleaseCollection(collection); return scope.Close(v8::ThrowException(err)); } if (idx->_type != TRI_IDX_TYPE_GEO1_INDEX && idx->_type != TRI_IDX_TYPE_GEO2_INDEX) { + LOG_TRACE("before endRead"); collection->_collection->endRead(collection->_collection); + LOG_TRACE("after endRead"); TRI_ReleaseCollection(collection); return scope.Close(v8::ThrowException(TRI_CreateErrorObject(TRI_ERROR_BAD_PARAMETER, "index must be a geo-index"))); @@ -1809,8 +1879,10 @@ static v8::Handle JS_WithinQuery (v8::Arguments const& argv) { if (cors != 0) { StoreGeoResult(collection, cors, documents, distances); } - + + LOG_TRACE("before endRead"); collection->_collection->endRead(collection->_collection); + LOG_TRACE("after endRead"); // ............................................................................. // outside a write transaction diff --git a/arangod/V8Server/v8-vocbase.cpp b/arangod/V8Server/v8-vocbase.cpp index 9bd6923..4b37d5d 100755 --- a/arangod/V8Server/v8-vocbase.cpp +++ b/arangod/V8Server/v8-vocbase.cpp @@ -48,6 +48,7 @@ #include "VocBase/general-cursor.h" #include "VocBase/simple-collection.h" #include "VocBase/voc-shaper.h" +#include "Logger/Logger.h" using namespace std; using namespace triagens::basics; @@ -119,6 +120,71 @@ static int32_t const WRP_SHAPED_JSON_TYPE = 4; //////////////////////////////////////////////////////////////////////////////// // ----------------------------------------------------------------------------- +// --SECTION-- HELPER CLASSES +// ----------------------------------------------------------------------------- + +// ----------------------------------------------------------------------------- +// --SECTION-- AhuacatlContextHolder +// ----------------------------------------------------------------------------- + +//////////////////////////////////////////////////////////////////////////////// +/// @addtogroup VocBase +/// @{ +//////////////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////////////// +/// @brief wraps a C++ into a v8::Object +//////////////////////////////////////////////////////////////////////////////// + +class AhuacatlContextGuard { + public: + AhuacatlContextGuard (TRI_vocbase_t* vocbase, const string& query) : + _context(0) { + LOGGER_DEBUG << "context guard created"; + _context = TRI_CreateContextAql(vocbase, query.c_str()); + + if (_context == 0) { + LOGGER_DEBUG << "failed to create context for query %s" << query; + } + } + + ~AhuacatlContextGuard () { + this->free(); + LOGGER_DEBUG << "context guard destroyed"; + } + + void free () { + if (_context != 0) { + TRI_FreeContextAql(_context); + _context = 0; + } + } + + inline TRI_aql_context_t* operator() () const { + return _context; + } + + inline TRI_aql_context_t* ptr () const { + return _context; + } + + inline const TRI_aql_context_t* const_ptr () const { + return _context; + } + + inline const bool valid () const { + return _context != 0; + } + + private: + TRI_aql_context_t* _context; +}; + +//////////////////////////////////////////////////////////////////////////////// +/// @} +//////////////////////////////////////////////////////////////////////////////// + +// ----------------------------------------------------------------------------- // --SECTION-- HELPER FUNCTIONS // ----------------------------------------------------------------------------- @@ -513,7 +579,9 @@ static v8::Handle DocumentVocbaseCol (TRI_vocbase_t* vocbase, // inside a read transaction // ............................................................................. + LOG_TRACE("before beginRead"); collection->_collection->beginRead(collection->_collection); + LOG_TRACE("after beginRead"); document = collection->_collection->read(collection->_collection, did); @@ -524,7 +592,9 @@ static v8::Handle DocumentVocbaseCol (TRI_vocbase_t* vocbase, result = TRI_WrapShapedJson(collection, &document, barrier); } + LOG_TRACE("before endRead"); collection->_collection->endRead(collection->_collection); + LOG_TRACE("after beginRead"); // ............................................................................. // outside a write transaction @@ -610,7 +680,9 @@ static v8::Handle ReplaceVocbaseCol (TRI_vocbase_t* vocbase, // inside a write transaction // ............................................................................. + LOG_TRACE("before beginWrite"); collection->_collection->beginWrite(collection->_collection); + LOG_TRACE("after beginWrite"); TRI_voc_rid_t oldRid = 0; TRI_doc_mptr_t mptr = doc->update(doc, shaped, did, rid, &oldRid, policy, true); @@ -1006,6 +1078,7 @@ static v8::Handle ExecuteQueryNativeAhuacatl (TRI_aql_context_t* cons const TRI_json_t* const parameters) { v8::HandleScope scope; + LOGGER_DEBUG << "executing native query"; // parse & validate // bind values // optimise @@ -1014,21 +1087,28 @@ static v8::Handle ExecuteQueryNativeAhuacatl (TRI_aql_context_t* cons !TRI_BindQueryContextAql(context, parameters) || !TRI_LockQueryContextAql(context) || !TRI_OptimiseQueryContextAql(context)) { + LOGGER_DEBUG << "executing native query failed"; v8::Handle errorObject = CreateErrorObjectAhuacatl(&context->_error); return scope.Close(v8::ThrowException(errorObject)); } + LOGGER_DEBUG << "generating code for native query"; // generate code char* code = TRI_GenerateCodeAql(context); + LOGGER_DEBUG << "generating code finished"; if (!code) { + LOGGER_DEBUG << "generating code failed"; v8::Handle errorObject = CreateErrorObjectAhuacatl(&context->_error); return scope.Close(v8::ThrowException(errorObject)); } + LOGGER_DEBUG << "generating code succeeded"; // execute code + LOGGER_DEBUG << "executing generated Javascript code"; v8::Handle result = TRI_ExecuteJavaScriptString(v8::Context::GetCurrent(), v8::String::New(code), v8::String::New("query"), false); + LOGGER_DEBUG << "finished executing generated Javascript code"; TRI_Free(TRI_UNKNOWN_MEM_ZONE, code); // return the result as a javascript array @@ -1048,14 +1128,18 @@ static v8::Handle ExecuteQueryCursorAhuacatl (TRI_vocbase_t* const vo v8::HandleScope scope; v8::TryCatch tryCatch; + LOGGER_DEBUG << "executing query cursor"; v8::Handle result = ExecuteQueryNativeAhuacatl(context, parameters); + LOGGER_DEBUG << "finished executing query cursor"; if (tryCatch.HasCaught()) { + LOGGER_DEBUG << "executing query cursor failed"; return scope.Close(v8::ThrowException(tryCatch.Exception())); } if (allowDirectReturn || !result->IsArray()) { // return the value we got as it is. this is a performance optimisation + LOGGER_DEBUG << "executing query cursor returned directly"; return scope.Close(result); } @@ -1063,6 +1147,7 @@ static v8::Handle ExecuteQueryCursorAhuacatl (TRI_vocbase_t* const vo TRI_json_t* json = TRI_JsonObject(result); if (!json) { + LOGGER_DEBUG << "executing query cursor failed with out-of-memory"; v8::Handle errorObject = TRI_CreateErrorObject(TRI_ERROR_OUT_OF_MEMORY, "out of memory"); return scope.Close(v8::ThrowException(errorObject)); @@ -1073,6 +1158,7 @@ static v8::Handle ExecuteQueryCursorAhuacatl (TRI_vocbase_t* const vo if (!cursorResult) { TRI_FreeJson(TRI_UNKNOWN_MEM_ZONE, json); + LOGGER_DEBUG << "executing query cursor failed with out-of-memory at cursor result"; v8::Handle errorObject = TRI_CreateErrorObject(TRI_ERROR_OUT_OF_MEMORY, "out of memory"); return scope.Close(v8::ThrowException(errorObject)); @@ -1083,6 +1169,7 @@ static v8::Handle ExecuteQueryCursorAhuacatl (TRI_vocbase_t* const vo TRI_Free(TRI_UNKNOWN_MEM_ZONE, cursorResult); TRI_FreeJson(TRI_UNKNOWN_MEM_ZONE, json); + LOGGER_DEBUG << "creating general cursor failed"; v8::Handle errorObject = TRI_CreateErrorObject(TRI_ERROR_OUT_OF_MEMORY, "out of memory"); return scope.Close(v8::ThrowException(errorObject)); @@ -1091,6 +1178,7 @@ static v8::Handle ExecuteQueryCursorAhuacatl (TRI_vocbase_t* const vo assert(cursor); TRI_StoreShadowData(vocbase->_cursors, (const void* const) cursor); + LOGGER_DEBUG << "returning general cursor"; return scope.Close(WrapGeneralCursor(cursor)); } @@ -1299,12 +1387,10 @@ static v8::Handle JS_DisposeGeneralCursor (v8::Arguments const& argv) bool found = TRI_DeleteDataShadowData(vocbase->_cursors, UnwrapGeneralCursor(argv.Holder())); if (found) { - return scope.Close(v8::True()); - } - - return scope.Close(v8::ThrowException( - TRI_CreateErrorObject(TRI_ERROR_CURSOR_NOT_FOUND, - "disposed or unknown cursor"))); + return scope.Close(v8::True()); + } + + return scope.Close(v8::False()); } //////////////////////////////////////////////////////////////////////////////// @@ -1365,7 +1451,7 @@ static v8::Handle JS_CountGeneralCursor (v8::Arguments const& argv) { cursor = (TRI_general_cursor_t*) TRI_BeginUsageDataShadowData(vocbase->_cursors, UnwrapGeneralCursor(argv.Holder())); if (cursor) { - size_t length = (size_t) cursor->_length; + size_t length = (size_t) cursor->_length; TRI_EndUsageDataShadowData(vocbase->_cursors, cursor); return scope.Close(v8::Number::New(length)); } @@ -1666,6 +1752,32 @@ static v8::Handle JS_HasNextGeneralCursor (v8::Arguments const& argv) } //////////////////////////////////////////////////////////////////////////////// +/// @brief unuse a general cursor +//////////////////////////////////////////////////////////////////////////////// + +static v8::Handle JS_UnuseGeneralCursor (v8::Arguments const& argv) { + v8::HandleScope scope; + + if (argv.Length() != 0) { + return scope.Close(v8::ThrowException( + TRI_CreateErrorObject(TRI_ERROR_ILLEGAL_OPTION, + "usage: unuse()"))); + } + + TRI_vocbase_t* vocbase = GetContextVocBase(); + + if (!vocbase) { + return scope.Close(v8::ThrowException( + TRI_CreateErrorObject(TRI_ERROR_INTERNAL, + "corrupted vocbase"))); + } + + TRI_EndUsageDataShadowData(vocbase->_cursors, UnwrapGeneralCursor(argv.Holder())); + + return scope.Close(v8::Undefined()); +} + +//////////////////////////////////////////////////////////////////////////////// /// @brief get a (persistent) cursor by its id //////////////////////////////////////////////////////////////////////////////// @@ -1707,7 +1819,7 @@ static v8::Handle JS_Cursor (v8::Arguments const& argv) { TRI_CreateErrorObject(TRI_ERROR_CURSOR_NOT_FOUND, "disposed or unknown cursor"))); } - + return scope.Close(WrapGeneralCursor(cursor)); } @@ -1776,16 +1888,16 @@ static v8::Handle JS_RunAhuacatl (v8::Arguments const& argv) { // bind parameters triagens::rest::JsonContainer parameters(TRI_UNKNOWN_MEM_ZONE, argc > 1 ? TRI_JsonObject(argv[1]) : 0); - TRI_aql_context_t* context = TRI_CreateContextAql(vocbase, queryString.c_str()); - if (!context) { + AhuacatlContextGuard context(vocbase, queryString); + if (! context.valid()) { v8::Handle errorObject = TRI_CreateErrorObject(TRI_ERROR_OUT_OF_MEMORY, "out of memory"); return scope.Close(v8::ThrowException(errorObject)); } v8::Handle result; - result = ExecuteQueryCursorAhuacatl(vocbase, context, parameters.ptr(), doCount, batchSize, allowDirectReturn); - TRI_FreeContextAql(context); + result = ExecuteQueryCursorAhuacatl(vocbase, context.ptr(), parameters.ptr(), doCount, batchSize, allowDirectReturn); + context.free(); if (tryCatch.HasCaught()) { if (tryCatch.Exception()->IsObject() && v8::Handle::Cast(tryCatch.Exception())->HasOwnProperty(v8::String::New("errorNum"))) { @@ -1798,6 +1910,8 @@ static v8::Handle JS_RunAhuacatl (v8::Arguments const& argv) { return scope.Close(v8::ThrowException(errorObject)); } + LOGGER_DEBUG << "returning cursor result"; + return scope.Close(result); } @@ -1834,8 +1948,8 @@ static v8::Handle JS_ExplainAhuacatl (v8::Arguments const& argv) { // bind parameters triagens::rest::JsonContainer parameters(TRI_UNKNOWN_MEM_ZONE, argc > 1 ? TRI_JsonObject(argv[1]) : 0); - TRI_aql_context_t* context = TRI_CreateContextAql(vocbase, queryString.c_str()); - if (!context) { + AhuacatlContextGuard context(vocbase, queryString); + if (! context.valid()) { v8::Handle errorObject = TRI_CreateErrorObject(TRI_ERROR_OUT_OF_MEMORY, "out of memory"); return scope.Close(v8::ThrowException(errorObject)); @@ -1849,13 +1963,12 @@ static v8::Handle JS_ExplainAhuacatl (v8::Arguments const& argv) { TRI_json_t* explain = 0; - if (!TRI_ValidateQueryContextAql(context) || - !TRI_BindQueryContextAql(context, parameters.ptr()) || - !TRI_LockQueryContextAql(context) || - (performOptimisations && !TRI_OptimiseQueryContextAql(context)) || - !(explain = TRI_ExplainAql(context))) { - v8::Handle errorObject = CreateErrorObjectAhuacatl(&context->_error); - TRI_FreeContextAql(context); + if (!TRI_ValidateQueryContextAql(context.ptr()) || + !TRI_BindQueryContextAql(context.ptr(), parameters.ptr()) || + !TRI_LockQueryContextAql(context.ptr()) || + (performOptimisations && !TRI_OptimiseQueryContextAql(context.ptr())) || + !(explain = TRI_ExplainAql(context.ptr()))) { + v8::Handle errorObject = CreateErrorObjectAhuacatl(&(context.ptr())->_error); return scope.Close(v8::ThrowException(errorObject)); } @@ -1863,8 +1976,8 @@ static v8::Handle JS_ExplainAhuacatl (v8::Arguments const& argv) { v8::Handle result; result = TRI_ObjectJson(explain); - TRI_FreeContextAql(context); TRI_FreeJson(TRI_UNKNOWN_MEM_ZONE, explain); + context.free(); if (tryCatch.HasCaught()) { if (tryCatch.Exception()->IsObject() && v8::Handle::Cast(tryCatch.Exception())->HasOwnProperty(v8::String::New("errorNum"))) { @@ -1906,17 +2019,16 @@ static v8::Handle JS_ParseAhuacatl (v8::Arguments const& argv) { } string queryString = TRI_ObjectToString(queryArg); - TRI_aql_context_t* context = TRI_CreateContextAql(vocbase, queryString.c_str()); - if (!context) { + AhuacatlContextGuard context(vocbase, queryString); + if (! context.valid()) { v8::Handle errorObject = TRI_CreateErrorObject(TRI_ERROR_OUT_OF_MEMORY, "out of memory"); return scope.Close(v8::ThrowException(errorObject)); } // parse & validate - if (!TRI_ValidateQueryContextAql(context)) { - v8::Handle errorObject = CreateErrorObjectAhuacatl(&context->_error); - TRI_FreeContextAql(context); + if (!TRI_ValidateQueryContextAql(context.ptr())) { + v8::Handle errorObject = CreateErrorObjectAhuacatl(&(context.ptr())->_error); return scope.Close(v8::ThrowException(errorObject)); } @@ -1927,11 +2039,10 @@ static v8::Handle JS_ParseAhuacatl (v8::Arguments const& argv) { result->Set(v8::String::New("parsed"), v8::True()); // return the bind parameter names - result->Set(v8::String::New("parameters"), TRI_ArrayAssociativePointer(&context->_parameters._names)); + result->Set(v8::String::New("parameters"), TRI_ArrayAssociativePointer(&(context.ptr())->_parameters._names)); // return the collection names - result->Set(v8::String::New("collections"), TRI_ArrayAssociativePointer(&context->_collectionNames)); + result->Set(v8::String::New("collections"), TRI_ArrayAssociativePointer(&(context.ptr())->_collectionNames)); - TRI_FreeContextAql(context); if (tryCatch.HasCaught()) { if (tryCatch.Exception()->IsObject() && v8::Handle::Cast(tryCatch.Exception())->HasOwnProperty(v8::String::New("errorNum"))) { // we already have an ArangoError object @@ -2987,9 +3098,13 @@ static v8::Handle JS_FiguresVocbaseCol (v8::Arguments const& argv) { TRI_doc_collection_t* doc = collection->_collection; + LOG_TRACE("before beginRead"); doc->beginRead(doc); + LOG_TRACE("after beginRead"); TRI_doc_collection_info_t* info = doc->figures(doc); + LOG_TRACE("before endRead"); doc->endRead(doc); + LOG_TRACE("after endRead"); if (info == NULL) { TRI_READ_UNLOCK_STATUS_VOCBASE_COL(collection); @@ -3464,7 +3579,9 @@ static v8::Handle JS_SaveVocbaseCol (v8::Arguments const& argv) { // inside a write transaction // ............................................................................. + LOG_TRACE("before beginWrite"); collection->_collection->beginWrite(collection->_collection); + LOG_TRACE("after beginWrite"); // the lock is freed in create TRI_doc_mptr_t mptr = doc->create(doc, TRI_DOC_MARKER_DOCUMENT, shaped, 0, did, rid, true); @@ -3705,7 +3822,9 @@ static v8::Handle JS_SaveEdgesCol (v8::Arguments const& argv) { // inside a write transaction // ............................................................................. + LOG_TRACE("before beginWrite"); collection->_collection->beginWrite(collection->_collection); + LOG_TRACE("after beginWrite"); TRI_doc_mptr_t mptr = doc->create(doc, TRI_DOC_MARKER_EDGE, shaped, &edge, did, rid, true); @@ -4907,6 +5026,7 @@ TRI_v8_global_t* TRI_InitV8VocBridge (v8::Handle context, TRI_vocba v8::Handle StatusFuncName = v8::Persistent::New(v8::String::New("status")); v8::Handle TruncateDatafileFuncName = v8::Persistent::New(v8::String::New("truncateDatafile")); v8::Handle UnloadFuncName = v8::Persistent::New(v8::String::New("unload")); + v8::Handle UnuseFuncName = v8::Persistent::New(v8::String::New("unuse")); v8::Handle _CollectionFuncName = v8::Persistent::New(v8::String::New("_collection")); v8::Handle _CollectionsFuncName = v8::Persistent::New(v8::String::New("_collections")); @@ -5165,12 +5285,17 @@ TRI_v8_global_t* TRI_InitV8VocBridge (v8::Handle context, TRI_vocba rt->Set(IdFuncName, v8::FunctionTemplate::New(JS_IdGeneralCursor)); rt->Set(NextFuncName, v8::FunctionTemplate::New(JS_NextGeneralCursor)); rt->Set(PersistFuncName, v8::FunctionTemplate::New(JS_PersistGeneralCursor)); + rt->Set(UnuseFuncName, v8::FunctionTemplate::New(JS_UnuseGeneralCursor)); v8g->GeneralCursorTempl = v8::Persistent::New(rt); // must come after SetInternalFieldCount context->Global()->Set(v8::String::New("ArangoCursor"), ft->GetFunction()); + // ............................................................................. + // create some global functions + // ............................................................................. + context->Global()->Set(v8::String::New("CURSOR"), v8::FunctionTemplate::New(JS_Cursor)->GetFunction(), v8::ReadOnly); diff --git a/arangod/VocBase/compactor.c b/arangod/VocBase/compactor.c index 2809b17..206d9ec 100644 --- a/arangod/VocBase/compactor.c +++ b/arangod/VocBase/compactor.c @@ -538,8 +538,6 @@ static void CleanupSimCollection (TRI_sim_collection_t* sim) { //////////////////////////////////////////////////////////////////////////////// static void CleanupShadows (TRI_vocbase_t* const vocbase, bool force) { - LOG_TRACE("cleaning shadows"); - // clean unused cursors TRI_CleanupShadowData(vocbase->_cursors, SHADOW_CURSOR_MAX_AGE, force); } diff --git a/arangod/VocBase/shadow-data.c b/arangod/VocBase/shadow-data.c index b664156..1d08927 100644 --- a/arangod/VocBase/shadow-data.c +++ b/arangod/VocBase/shadow-data.c @@ -82,13 +82,14 @@ static TRI_shadow_t* CreateShadow (const void* const data) { //////////////////////////////////////////////////////////////////////////////// static void DecreaseRefCount (TRI_shadow_store_t* const store, TRI_shadow_t* const shadow) { - LOG_TRACE("decreasing refcount for shadow %p with data ptr %p and id %lu", + LOG_TRACE("decreasing refcount for shadow %p with data ptr %p and id %lu to %d", shadow, shadow->_data, - (unsigned long) shadow->_id); + (unsigned long) shadow->_id, + (int) (shadow->_rc - 1)); if (--shadow->_rc <= 0 && shadow->_type == SHADOW_TRANSIENT) { - LOG_TRACE("deleting shadow %p", shadow); + LOG_TRACE("deleting transient shadow %p", shadow); TRI_RemoveKeyAssociativePointer(&store->_ids, &shadow->_id); TRI_RemoveKeyAssociativePointer(&store->_pointers, shadow->_data); @@ -102,12 +103,15 @@ static void DecreaseRefCount (TRI_shadow_store_t* const store, TRI_shadow_t* con //////////////////////////////////////////////////////////////////////////////// static void IncreaseRefCount (TRI_shadow_store_t* const store, TRI_shadow_t* const shadow) { - LOG_TRACE("increasing refcount for shadow %p with data ptr %p and id %lu", + LOG_TRACE("increasing refcount for shadow %p with data ptr %p and id %lu to %d", shadow, shadow->_data, - (unsigned long) shadow->_id); + (unsigned long) shadow->_id, + (int) (shadow->_rc + 1)); - ++shadow->_rc; + if (++shadow->_rc <= 0) { + shadow->_rc = 1; + } UpdateTimestampShadow(shadow); } @@ -255,7 +259,7 @@ void TRI_FreeShadowStore (TRI_shadow_store_t* const store) { assert(store); // force deletion of all remaining shadows - TRI_CleanupShadowData(store, 0, true); + TRI_CleanupShadowData(store, 0.0, true); TRI_DestroyMutex(&store->_lock); TRI_DestroyAssociativePointer(&store->_ids); @@ -376,7 +380,7 @@ void TRI_EndUsageDataShadowData (TRI_shadow_store_t* const store, TRI_LockMutex(&store->_lock); shadow = (TRI_shadow_t*) TRI_LookupByKeyAssociativePointer(&store->_pointers, data); - if (shadow && !shadow->_deleted) { + if (shadow) { DecreaseRefCount(store, shadow); // this might delete the shadow } @@ -523,6 +527,14 @@ void TRI_CleanupShadowData (TRI_shadow_store_t* const store, // we need an exclusive lock on the index TRI_LockMutex(&store->_lock); + if (store->_ids._nrUsed == 0) { + // store is empty, nothing to do! + TRI_UnlockMutex(&store->_lock); + return; + } + + LOG_TRACE("cleaning shadows. in store: %ld", (unsigned long) store->_ids._nrUsed); + // loop until there's nothing to delete or // we have deleted SHADOW_MAX_DELETE elements while (deleteCount++ < SHADOW_MAX_DELETE || force) { @@ -539,9 +551,14 @@ void TRI_CleanupShadowData (TRI_shadow_store_t* const store, // check if shadow is unused and expired if (shadow->_rc < 1 || force) { if (shadow->_type == SHADOW_TRANSIENT || - shadow->_timestamp < compareStamp || + shadow->_timestamp < compareStamp || + shadow->_deleted || force) { - LOG_TRACE("cleaning expired shadow %p", shadow); + LOG_TRACE("cleaning shadow %p, rc: %d, expired: %d, deleted: %d", + shadow, + (int) shadow->_rc, + (int) (shadow->_timestamp < compareStamp), + (int) shadow->_deleted); TRI_RemoveKeyAssociativePointer(&store->_ids, &shadow->_id); TRI_RemoveKeyAssociativePointer(&store->_pointers, shadow->_data); diff --git a/arangod/VocBase/simple-collection.c b/arangod/VocBase/simple-collection.c index 3e28a19..d4c44e3 100644 --- a/arangod/VocBase/simple-collection.c +++ b/arangod/VocBase/simple-collection.c @@ -323,6 +323,8 @@ static TRI_doc_mptr_t CreateDocument (TRI_sim_collection_t* sim, TRI_voc_size_t total; TRI_doc_datafile_info_t* dfi; int res; + + LOG_TRACE("CreateDocument. release flag is: %d", (int) release); // ............................................................................. // create header @@ -349,7 +351,9 @@ static TRI_doc_mptr_t CreateDocument (TRI_sim_collection_t* sim, if (journal == NULL) { if (release) { + LOG_DEBUG("before endWrite"); sim->base.endWrite(&sim->base); + LOG_DEBUG("after endWrite"); } memset(&mptr, 0, sizeof(mptr)); @@ -440,7 +444,9 @@ static TRI_doc_mptr_t CreateDocument (TRI_sim_collection_t* sim, // release lock, header might be invalid after this if (release) { + LOG_DEBUG("before endWrite"); sim->base.endWrite(&sim->base); + LOG_DEBUG("after endWrite"); } // wait for sync @@ -451,7 +457,9 @@ static TRI_doc_mptr_t CreateDocument (TRI_sim_collection_t* sim, } else { if (release) { + LOG_DEBUG("before endWrite"); sim->base.endWrite(&sim->base); + LOG_DEBUG("after endWrite"); } mptr._did = 0; @@ -461,7 +469,9 @@ static TRI_doc_mptr_t CreateDocument (TRI_sim_collection_t* sim, } else { if (release) { + LOG_DEBUG("before endWrite"); sim->base.endWrite(&sim->base); + LOG_DEBUG("after endWrite"); } LOG_ERROR("cannot write element: %s", TRI_last_error()); @@ -565,6 +575,8 @@ static TRI_doc_mptr_t UpdateDocument (TRI_sim_collection_t* collection, TRI_doc_mptr_t resUpd; TRI_voc_size_t total; int res; + + LOG_TRACE("UpdateDocument. release flag is: %d", (int) release); originalMarker = header->_data; @@ -581,7 +593,9 @@ static TRI_doc_mptr_t UpdateDocument (TRI_sim_collection_t* collection, if (rid != 0) { if (rid != header->_rid) { if (release) { + LOG_DEBUG("before endWrite"); collection->base.endWrite(&collection->base); + LOG_DEBUG("after endWrite"); } TRI_set_errno(TRI_ERROR_ARANGO_CONFLICT); @@ -597,7 +611,9 @@ static TRI_doc_mptr_t UpdateDocument (TRI_sim_collection_t* collection, case TRI_DOC_UPDATE_CONFLICT: if (release) { + LOG_DEBUG("before endWrite"); collection->base.endWrite(&collection->base); + LOG_DEBUG("after endWrite"); } TRI_set_errno(TRI_ERROR_NOT_IMPLEMENTED); @@ -606,7 +622,9 @@ static TRI_doc_mptr_t UpdateDocument (TRI_sim_collection_t* collection, case TRI_DOC_UPDATE_ILLEGAL: if (release) { + LOG_DEBUG("before endWrite"); collection->base.endWrite(&collection->base); + LOG_DEBUG("after endWrite"); } TRI_set_errno(TRI_ERROR_INTERNAL); @@ -629,7 +647,9 @@ static TRI_doc_mptr_t UpdateDocument (TRI_sim_collection_t* collection, collection->base.base._lastError = TRI_set_errno(TRI_ERROR_ARANGO_NO_JOURNAL); if (release) { + LOG_DEBUG("before endWrite"); collection->base.endWrite(&collection->base); + LOG_DEBUG("after endWrite"); } mptr._did = 0; @@ -699,7 +719,9 @@ static TRI_doc_mptr_t UpdateDocument (TRI_sim_collection_t* collection, // release lock, header might be invalid after this if (release) { + LOG_DEBUG("before endWrite"); collection->base.endWrite(&collection->base); + LOG_DEBUG("after endWrite"); } // wait for sync @@ -710,7 +732,9 @@ static TRI_doc_mptr_t UpdateDocument (TRI_sim_collection_t* collection, } else { if (release) { + LOG_DEBUG("before endWrite"); collection->base.endWrite(&collection->base); + LOG_DEBUG("after endWrite"); } mptr._did = 0; @@ -720,7 +744,9 @@ static TRI_doc_mptr_t UpdateDocument (TRI_sim_collection_t* collection, } else { if (release) { + LOG_DEBUG("before endWrite"); collection->base.endWrite(&collection->base); + LOG_DEBUG("after endWrite"); } LOG_ERROR("cannot write element"); @@ -744,13 +770,17 @@ static int DeleteDocument (TRI_sim_collection_t* collection, TRI_doc_mptr_t const* header; TRI_voc_size_t total; int res; + + LOG_TRACE("DeleteDocument. release flag is: %d", (int) release); // get an existing header pointer header = TRI_LookupByKeyAssociativePointer(&collection->_primaryIndex, &marker->_did); if (header == NULL || header->_deletion != 0) { if (release) { + LOG_DEBUG("before endWrite"); collection->base.endWrite(&collection->base); + LOG_DEBUG("after endWrite"); } return TRI_set_errno(TRI_ERROR_ARANGO_DOCUMENT_NOT_FOUND); @@ -766,7 +796,9 @@ static int DeleteDocument (TRI_sim_collection_t* collection, if (rid != 0) { if (rid != header->_rid) { if (release) { + LOG_DEBUG("before endWrite"); collection->base.endWrite(&collection->base); + LOG_DEBUG("after endWrite"); } return TRI_set_errno(TRI_ERROR_ARANGO_CONFLICT); @@ -780,14 +812,18 @@ static int DeleteDocument (TRI_sim_collection_t* collection, case TRI_DOC_UPDATE_CONFLICT: if (release) { + LOG_DEBUG("before endWrite"); collection->base.endWrite(&collection->base); + LOG_DEBUG("after endWrite"); } return TRI_set_errno(TRI_ERROR_NOT_IMPLEMENTED); case TRI_DOC_UPDATE_ILLEGAL: if (release) { + LOG_DEBUG("before endWrite"); collection->base.endWrite(&collection->base); + LOG_DEBUG("after endWrite"); } return TRI_set_errno(TRI_ERROR_INTERNAL); @@ -804,7 +840,9 @@ static int DeleteDocument (TRI_sim_collection_t* collection, collection->base.base._lastError = TRI_set_errno(TRI_ERROR_ARANGO_NO_JOURNAL); if (release) { + LOG_DEBUG("before endWrite"); collection->base.endWrite(&collection->base); + LOG_DEBUG("after endWrite"); } return TRI_ERROR_ARANGO_NO_JOURNAL; @@ -840,7 +878,9 @@ static int DeleteDocument (TRI_sim_collection_t* collection, // release lock if (release) { + LOG_DEBUG("before endWrite"); collection->base.endWrite(&collection->base); + LOG_DEBUG("after endWrite"); } // wait for sync @@ -848,7 +888,9 @@ static int DeleteDocument (TRI_sim_collection_t* collection, } else { if (release) { + LOG_DEBUG("before endWrite"); collection->base.endWrite(&collection->base); + LOG_DEBUG("after endWrite"); } LOG_ERROR("cannot delete element"); @@ -973,6 +1015,8 @@ static TRI_doc_mptr_t CreateShapedJson (TRI_doc_collection_t* document, bool release) { TRI_df_marker_t* result; TRI_sim_collection_t* collection; + + LOG_TRACE("CreateShapedJson. release flag is: %d", (int) release); collection = (TRI_sim_collection_t*) document; @@ -1069,6 +1113,8 @@ static TRI_doc_mptr_t UpdateShapedJson (TRI_doc_collection_t* document, TRI_doc_mptr_t mptr; TRI_doc_mptr_t const* header; TRI_sim_collection_t* collection; + + LOG_TRACE("UpdateShapedJson. release flag is: %d", (int) release); collection = (TRI_sim_collection_t*) document; @@ -1077,7 +1123,9 @@ static TRI_doc_mptr_t UpdateShapedJson (TRI_doc_collection_t* document, if (header == NULL || header->_deletion != 0) { if (release) { + LOG_DEBUG("before endWrite"); document->endWrite(&collection->base); + LOG_DEBUG("after endWrite"); } TRI_set_errno(TRI_ERROR_ARANGO_DOCUMENT_NOT_FOUND); @@ -1150,7 +1198,9 @@ static TRI_doc_mptr_t UpdateShapedJson (TRI_doc_collection_t* document, // do not know else { if (release) { + LOG_DEBUG("before endWrite"); document->endWrite(&collection->base); + LOG_DEBUG("after endWrite"); } LOG_FATAL("unknown marker type %lu", (unsigned long) original->_type); @@ -1170,6 +1220,8 @@ static int DeleteShapedJson (TRI_doc_collection_t* doc, bool release) { TRI_sim_collection_t* sim; TRI_doc_deletion_marker_t marker; + + LOG_TRACE("DeleteShapedJson. release flag is: %d", (int) release); sim = (TRI_sim_collection_t*) doc; @@ -1191,6 +1243,7 @@ static int DeleteShapedJson (TRI_doc_collection_t* doc, static int BeginRead (TRI_doc_collection_t* doc) { TRI_sim_collection_t* sim; + LOG_TRACE("BeginRead %p", doc); sim = (TRI_sim_collection_t*) doc; TRI_READ_LOCK_DOCUMENTS_INDEXES_SIM_COLLECTION(sim); @@ -1204,6 +1257,7 @@ static int BeginRead (TRI_doc_collection_t* doc) { static int EndRead (TRI_doc_collection_t* doc) { TRI_sim_collection_t* sim; + LOG_TRACE("EndRead %p", doc); sim = (TRI_sim_collection_t*) doc; TRI_READ_UNLOCK_DOCUMENTS_INDEXES_SIM_COLLECTION(sim); @@ -1217,6 +1271,7 @@ static int EndRead (TRI_doc_collection_t* doc) { static int BeginWrite (TRI_doc_collection_t* doc) { TRI_sim_collection_t* sim; + LOG_TRACE("BeginWrite %p", doc); sim = (TRI_sim_collection_t*) doc; TRI_WRITE_LOCK_DOCUMENTS_INDEXES_SIM_COLLECTION(sim); @@ -1230,6 +1285,7 @@ static int BeginWrite (TRI_doc_collection_t* doc) { static int EndWrite (TRI_doc_collection_t* document) { TRI_sim_collection_t* sim; + LOG_TRACE("EndWrite %p", document); sim = (TRI_sim_collection_t*) document; TRI_WRITE_UNLOCK_DOCUMENTS_INDEXES_SIM_COLLECTION(sim); diff --git a/arangod/VocBase/simple-collection.h b/arangod/VocBase/simple-collection.h index 1a8c2e7..5385cc8 100644 --- a/arangod/VocBase/simple-collection.h +++ b/arangod/VocBase/simple-collection.h @@ -85,28 +85,36 @@ extern "C" { //////////////////////////////////////////////////////////////////////////////// #define TRI_READ_LOCK_DOCUMENTS_INDEXES_SIM_COLLECTION(a) \ - TRI_ReadLockReadWriteLock(&(a)->_lock) + LOG_TRACE("RWLock READ-LOCK %p", a); \ + TRI_ReadLockReadWriteLock(&(a)->_lock); \ + LOG_TRACE("RWLock READ-LOCK %p SUCCESS", a) //////////////////////////////////////////////////////////////////////////////// /// @brief read unlocks the documents and indexes //////////////////////////////////////////////////////////////////////////////// #define TRI_READ_UNLOCK_DOCUMENTS_INDEXES_SIM_COLLECTION(a) \ - TRI_ReadUnlockReadWriteLock(&(a)->_lock) + LOG_TRACE("RWLock READ-UNLOCK %p", a); \ + TRI_ReadUnlockReadWriteLock(&(a)->_lock); \ + LOG_TRACE("RWLock READ-UNLOCK %p SUCCESS", a) //////////////////////////////////////////////////////////////////////////////// /// @brief write locks the documents and indexes //////////////////////////////////////////////////////////////////////////////// #define TRI_WRITE_LOCK_DOCUMENTS_INDEXES_SIM_COLLECTION(a) \ - TRI_WriteLockReadWriteLock(&(a)->_lock) + LOG_TRACE("RWLock WRITE-LOCK %p", a); \ + TRI_WriteLockReadWriteLock(&(a)->_lock); \ + LOG_TRACE("RWLock WRITE-LOCK %p SUCCESS", a) //////////////////////////////////////////////////////////////////////////////// /// @brief write unlocks the documents and indexes //////////////////////////////////////////////////////////////////////////////// #define TRI_WRITE_UNLOCK_DOCUMENTS_INDEXES_SIM_COLLECTION(a) \ - TRI_WriteUnlockReadWriteLock(&(a)->_lock) + LOG_TRACE("RWLock WRITE-UNLOCK %p", a); \ + TRI_WriteUnlockReadWriteLock(&(a)->_lock); \ + LOG_TRACE("RWLock WRITE-UNLOCK %p SUCCESS", a) //////////////////////////////////////////////////////////////////////////////// /// @brief locks the journal entries diff --git a/html/admin/js/modules/simple-query-basics.js b/html/admin/js/modules/simple-query-basics.js index fcbe6f4..5f6b0ca 100644 --- a/html/admin/js/modules/simple-query-basics.js +++ b/html/admin/js/modules/simple-query-basics.js @@ -151,6 +151,7 @@ GeneralArrayCursor.prototype._PRINT = function () { //////////////////////////////////////////////////////////////////////////////// GeneralArrayCursor.prototype.hasNext = function () { + require("console").log("hasNext called. current: " + this._current + ", stop: " + this._stop); return this._current < this._stop; } @@ -177,7 +178,8 @@ GeneralArrayCursor.prototype.dispose = function() { this._limit = null; this._countTotal = null; this._countQuery = null; - this.current = null; + this._current = null; + this._stop = null; } //////////////////////////////////////////////////////////////////////////////// @@ -664,7 +666,9 @@ SimpleQueryArray.prototype.execute = function () { this._skip = 0; } + require("console").log("creating GeneralArrayCursor with " + this._documents.length + " docs, skip: " + this._skip + ", limit: " + this._limit); this._execution = new GeneralArrayCursor(this._documents, this._skip, this._limit); + require("console").log("created GeneralArrayCursor"); } } diff --git a/js/actions/system/api-cursor.js b/js/actions/system/api-cursor.js index c2d1107..6b1ec42 100644 --- a/js/actions/system/api-cursor.js +++ b/js/actions/system/api-cursor.js @@ -216,8 +216,15 @@ function PUT_api_cursor(req, res) { return; } - // note: this might dispose or persist the cursor - actions.resultCursor(req, res, cursor, actions.HTTP_OK); + try { + // note: this might dispose or persist the cursor + actions.resultCursor(req, res, cursor, actions.HTTP_OK); + } + catch (e) { + } + cursor.unuse(); + cursor = null; + internal.wait(0.0); } catch (err) { actions.resultException(req, res, err); @@ -269,7 +276,9 @@ function DELETE_api_cursor(req, res) { } cursor.dispose(); + cursor = null; actions.resultOk(req, res, actions.HTTP_ACCEPTED, { "id" : cursorId }); + internal.wait(0.0); } catch (err) { actions.resultException(req, res, err); diff --git a/js/common/modules/simple-query-basics.js b/js/common/modules/simple-query-basics.js index 0840c2c..ed5daa1 100644 --- a/js/common/modules/simple-query-basics.js +++ b/js/common/modules/simple-query-basics.js @@ -150,6 +150,7 @@ GeneralArrayCursor.prototype._PRINT = function () { //////////////////////////////////////////////////////////////////////////////// GeneralArrayCursor.prototype.hasNext = function () { + require("console").log("hasNext called. current: " + this._current + ", stop: " + this._stop); return this._current < this._stop; } @@ -176,7 +177,8 @@ GeneralArrayCursor.prototype.dispose = function() { this._limit = null; this._countTotal = null; this._countQuery = null; - this.current = null; + this._current = null; + this._stop = null; } //////////////////////////////////////////////////////////////////////////////// @@ -663,7 +665,9 @@ SimpleQueryArray.prototype.execute = function () { this._skip = 0; } + require("console").log("creating GeneralArrayCursor with " + this._documents.length + " docs, skip: " + this._skip + ", limit: " + this._limit); this._execution = new GeneralArrayCursor(this._documents, this._skip, this._limit); + require("console").log("created GeneralArrayCursor"); } } diff --git a/js/server/ahuacatl.js b/js/server/ahuacatl.js index 665f723..1707ab4 100644 --- a/js/server/ahuacatl.js +++ b/js/server/ahuacatl.js @@ -385,7 +385,11 @@ function AHUACATL_LIST (value) { //////////////////////////////////////////////////////////////////////////////// function AHUACATL_GET_DOCUMENTS (collection) { - return internal.db[collection].all().toArray(); + require("console").log("AHUACATL_GET_DOCUMENTS called for " + collection); + var result = internal.db[collection].ALL(0, null).documents; + require("console").log("AHUACATL_GET_DOCUMENTS finished for " + collection); + + return result; } //////////////////////////////////////////////////////////////////////////////// diff --git a/js/server/js-ahuacatl.h b/js/server/js-ahuacatl.h index 7bf2dd5..7ab98bc 100644 --- a/js/server/js-ahuacatl.h +++ b/js/server/js-ahuacatl.h @@ -386,7 +386,11 @@ static string JS_server_ahuacatl = "////////////////////////////////////////////////////////////////////////////////\n" "\n" "function AHUACATL_GET_DOCUMENTS (collection) {\n" - " return internal.db[collection].all().toArray();\n" + " require(\"console\").log(\"AHUACATL_GET_DOCUMENTS called for \" + collection);\n" + " var result = internal.db[collection].ALL(0, null).documents;\n" + " require(\"console\").log(\"AHUACATL_GET_DOCUMENTS finished for \" + collection);\n" + "\n" + " return result;\n" "}\n" "\n" "////////////////////////////////////////////////////////////////////////////////\n" diff --git a/lib/Basics/ConditionLocker.cpp b/lib/Basics/ConditionLocker.cpp index e9469e6..097ef7b 100644 --- a/lib/Basics/ConditionLocker.cpp +++ b/lib/Basics/ConditionLocker.cpp @@ -96,6 +96,14 @@ void ConditionLocker::wait () { } //////////////////////////////////////////////////////////////////////////////// +/// @brief waits for an event to occur, with a timeout +//////////////////////////////////////////////////////////////////////////////// + +bool ConditionLocker::wait (uint64_t delay) { + return _conditionVariable->wait(delay); +} + +//////////////////////////////////////////////////////////////////////////////// /// @brief broadcasts an event //////////////////////////////////////////////////////////////////////////////// diff --git a/lib/Basics/ConditionLocker.h b/lib/Basics/ConditionLocker.h index 4114fe3..543f389 100644 --- a/lib/Basics/ConditionLocker.h +++ b/lib/Basics/ConditionLocker.h @@ -139,6 +139,12 @@ namespace triagens { void wait (); //////////////////////////////////////////////////////////////////////////////// +/// @brief waits for an event to occur, using a timeout +//////////////////////////////////////////////////////////////////////////////// + + bool wait (uint64_t); + +//////////////////////////////////////////////////////////////////////////////// /// @brief broadcasts an event //////////////////////////////////////////////////////////////////////////////// diff --git a/lib/BasicsC/memory.c b/lib/BasicsC/memory.c index 71b4d69..12b53a6 100644 --- a/lib/BasicsC/memory.c +++ b/lib/BasicsC/memory.c @@ -181,6 +181,7 @@ void* TRI_Allocate (TRI_memory_zone_t* zone, uint64_t n, bool set) { if (m == NULL) { if (zone->_failable) { + LOG_TRACE("malloc returned 0"); return NULL; }