diff --git a/arangod/V8Server/ApplicationV8.cpp b/arangod/V8Server/ApplicationV8.cpp index d200d55..629fe3f 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,6 +260,10 @@ 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; @@ -239,12 +273,17 @@ void ApplicationV8::exitContext (V8Context* context) { { 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 @@ -528,6 +657,8 @@ bool ApplicationV8::prepareV8Instance (size_t i) { context->_isolate->Exit(); delete context->_locker; + context->_lastGcStamp = TRI_microtime(); + LOGGER_TRACE << "initialised V8 context #" << i; _freeContexts.push_back(context); diff --git a/arangod/V8Server/ApplicationV8.h b/arangod/V8Server/ApplicationV8.h index 62b0474..0ee5356 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; - size_t _dirt; + +//////////////////////////////////////////////////////////////////////////////// +/// @brief number of requests since last GC of the context +//////////////////////////////////////////////////////////////////////////////// + + size_t _dirt; + +//////////////////////////////////////////////////////////////////////////////// +/// @brief timestamp of last GC for the context +//////////////////////////////////////////////////////////////////////////////// + + double _lastGcStamp; + }; //////////////////////////////////////////////////////////////////////////////// @@ -239,6 +251,14 @@ namespace triagens { /// @addtogroup ArangoDB /// @{ //////////////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////////////// +/// @brief determine which of the free contexts should be picked for the GC +//////////////////////////////////////////////////////////////////////////////// + + V8Context* pickContextForGc (); + +//////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// /// @brief prepares a V8 instance @@ -312,6 +332,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..e567349 100755 --- a/arangod/V8Server/v8-query.cpp +++ b/arangod/V8Server/v8-query.cpp @@ -584,13 +584,23 @@ static int SetupExampleObjectIndex (TRI_hash_index_t* hashIndex, /// @brief execute a skiplist query (by condition or by example) //////////////////////////////////////////////////////////////////////////////// -static v8::Handle ExecuteSkiplistQuery (v8::Arguments const& argv, std::string const& signature, const query_t type) { +static v8::Handle ExecuteSkiplistQuery (v8::Arguments const& argv, + std::string const& signature, + const query_t type, + const bool lock) { v8::HandleScope scope; // extract and use the simple collection v8::Handle err; TRI_vocbase_col_t const* collection; - TRI_sim_collection_t* sim = TRI_ExtractAndUseSimpleCollection(argv, collection, &err); + TRI_sim_collection_t* sim = 0; + + if (lock) { + sim = TRI_ExtractAndUseSimpleCollection(argv, collection, &err); + } + else { + sim = TRI_ExtractSimpleCollection(argv, collection, &err); + } if (sim == 0) { return scope.Close(v8::ThrowException(err)); @@ -598,7 +608,9 @@ static v8::Handle ExecuteSkiplistQuery (v8::Arguments const& argv, st // expecting index, example, skip, and limit if (argv.Length() < 2) { - TRI_ReleaseCollection(collection); + if (lock) { + TRI_ReleaseCollection(collection); + } std::string usage("Usage: "); usage += signature; @@ -608,7 +620,9 @@ static v8::Handle ExecuteSkiplistQuery (v8::Arguments const& argv, st } if (! argv[1]->IsObject()) { - TRI_ReleaseCollection(collection); + if (lock) { + TRI_ReleaseCollection(collection); + } std::string msg; if (type == QUERY_EXAMPLE) { @@ -639,7 +653,9 @@ static v8::Handle ExecuteSkiplistQuery (v8::Arguments const& argv, st // inside a read transaction // ............................................................................. - collection->_collection->beginRead(collection->_collection); + if (lock) { + collection->_collection->beginRead(collection->_collection); + } // extract the index TRI_index_t* idx = TRI_LookupIndexByHandle(sim->base.base._vocbase, collection, argv[0], false, &err); @@ -647,14 +663,18 @@ static v8::Handle ExecuteSkiplistQuery (v8::Arguments const& argv, st if (idx == 0) { collection->_collection->endRead(collection->_collection); - TRI_ReleaseCollection(collection); + if (lock) { + TRI_ReleaseCollection(collection); + } return scope.Close(v8::ThrowException(err)); } if (idx->_type != TRI_IDX_TYPE_SKIPLIST_INDEX) { - collection->_collection->endRead(collection->_collection); + if (lock) { + collection->_collection->endRead(collection->_collection); - TRI_ReleaseCollection(collection); + TRI_ReleaseCollection(collection); + } return scope.Close(v8::ThrowException(TRI_CreateErrorObject(TRI_ERROR_BAD_PARAMETER, "index must be a skiplist index"))); } @@ -668,9 +688,11 @@ static v8::Handle ExecuteSkiplistQuery (v8::Arguments const& argv, st } if (!skiplistOperator) { - collection->_collection->endRead(collection->_collection); + if (lock) { + collection->_collection->endRead(collection->_collection); - TRI_ReleaseCollection(collection); + TRI_ReleaseCollection(collection); + } return scope.Close(v8::ThrowException(TRI_CreateErrorObject(TRI_ERROR_BAD_PARAMETER, "setting up skiplist operator failed"))); } @@ -699,7 +721,9 @@ static v8::Handle ExecuteSkiplistQuery (v8::Arguments const& argv, st } } - collection->_collection->endRead(collection->_collection); + if (lock) { + collection->_collection->endRead(collection->_collection); + } // ............................................................................. // outside a write transaction @@ -711,18 +735,17 @@ static v8::Handle ExecuteSkiplistQuery (v8::Arguments const& argv, st result->Set(v8::String::New("total"), v8::Number::New((double) total)); result->Set(v8::String::New("count"), v8::Number::New(count)); - TRI_ReleaseCollection(collection); + if (lock) { + TRI_ReleaseCollection(collection); + } return scope.Close(result); } - - //////////////////////////////////////////////////////////////////////////////// /// @brief execute a bitarray index query (by condition or by example) //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// // Example of a filter associated with an interator //////////////////////////////////////////////////////////////////////////////// @@ -755,7 +778,10 @@ static bool BitarrayFilterExample(TRI_index_iterator_t* indexIterator) { return true; } -static v8::Handle ExecuteBitarrayQuery (v8::Arguments const& argv, std::string const& signature, const query_t type) { +static v8::Handle ExecuteBitarrayQuery (v8::Arguments const& argv, + std::string const& signature, + const query_t type, + const bool lock) { v8::HandleScope scope; v8::Handle err; const TRI_vocbase_col_t* collection; @@ -766,8 +792,14 @@ static v8::Handle ExecuteBitarrayQuery (v8::Arguments const& argv, st // extract and use the simple collection // ........................................................................... - - TRI_sim_collection_t* sim = TRI_ExtractAndUseSimpleCollection(argv, collection, &err); + + TRI_sim_collection_t* sim = 0; + if (lock) { + sim = TRI_ExtractAndUseSimpleCollection(argv, collection, &err); + } + else { + sim = TRI_ExtractSimpleCollection(argv, collection, &err); + } if (sim == 0) { return scope.Close(v8::ThrowException(err)); @@ -781,7 +813,9 @@ static v8::Handle ExecuteBitarrayQuery (v8::Arguments const& argv, st // ........................................................................... if (argv.Length() < 2) { - TRI_ReleaseCollection(collection); + if (lock) { + TRI_ReleaseCollection(collection); + } std::string usage("Usage: "); usage += signature; @@ -794,7 +828,9 @@ static v8::Handle ExecuteBitarrayQuery (v8::Arguments const& argv, st // ........................................................................... if (! argv[1]->IsObject()) { - TRI_ReleaseCollection(collection); + if (lock) { + TRI_ReleaseCollection(collection); + } std::string msg; if (type == QUERY_EXAMPLE) { @@ -836,8 +872,9 @@ static v8::Handle ExecuteBitarrayQuery (v8::Arguments const& argv, st // inside a read transaction // ............................................................................. - collection->_collection->beginRead(collection->_collection); - + if (lock) { + collection->_collection->beginRead(collection->_collection); + } // ............................................................................. // extract the index @@ -848,15 +885,19 @@ static v8::Handle ExecuteBitarrayQuery (v8::Arguments const& argv, st if (idx == 0) { collection->_collection->endRead(collection->_collection); - TRI_ReleaseCollection(collection); + if (lock) { + TRI_ReleaseCollection(collection); + } return scope.Close(v8::ThrowException(err)); } if (idx->_type != TRI_IDX_TYPE_BITARRAY_INDEX) { - collection->_collection->endRead(collection->_collection); + if (lock) { + collection->_collection->endRead(collection->_collection); - TRI_ReleaseCollection(collection); + TRI_ReleaseCollection(collection); + } return scope.Close(v8::ThrowException(TRI_CreateErrorObject(TRI_ERROR_BAD_PARAMETER, "index must be a skiplist index"))); } @@ -872,9 +913,11 @@ static v8::Handle ExecuteBitarrayQuery (v8::Arguments const& argv, st if (indexOperator == 0) { // something wrong - collection->_collection->endRead(collection->_collection); + if (lock) { + collection->_collection->endRead(collection->_collection); - TRI_ReleaseCollection(collection); + TRI_ReleaseCollection(collection); + } return scope.Close(v8::ThrowException(TRI_CreateErrorObject(TRI_ERROR_BAD_PARAMETER, "setting up skiplist operator failed"))); } @@ -924,8 +967,10 @@ static v8::Handle ExecuteBitarrayQuery (v8::Arguments const& argv, st else { LOG_WARNING("index iterator returned with a NULL value in ExecuteBitarrayQuery"); } - - collection->_collection->endRead(collection->_collection); + + if (lock) { + collection->_collection->endRead(collection->_collection); + } // ............................................................................. // outside a write transaction @@ -935,12 +980,13 @@ static v8::Handle ExecuteBitarrayQuery (v8::Arguments const& argv, st result->Set(v8::String::New("total"), v8::Number::New((double) total)); result->Set(v8::String::New("count"), v8::Number::New(count)); - TRI_ReleaseCollection(collection); + if (lock) { + TRI_ReleaseCollection(collection); + } return scope.Close(result); } - //////////////////////////////////////////////////////////////////////////////// /// @brief sorts geo coordinates //////////////////////////////////////////////////////////////////////////////// @@ -1114,7 +1160,7 @@ static v8::Handle EdgesQuery (TRI_edge_direction_e direction, v8::Arg TRI_voc_rid_t rid; TRI_vocbase_col_t const* vertexCollection = 0; - v8::Handle errMsg = TRI_ParseDocumentOrDocumentHandle(collection->_vocbase, vertexCollection, did, rid, vertices->Get(i)); + v8::Handle errMsg = TRI_ParseDocumentOrDocumentHandle(collection->_vocbase, vertexCollection, did, rid, true, vertices->Get(i)); if (! errMsg.IsEmpty()) { if (vertexCollection != 0) { @@ -1150,7 +1196,7 @@ static v8::Handle EdgesQuery (TRI_edge_direction_e direction, v8::Arg TRI_voc_rid_t rid; TRI_vocbase_col_t const* vertexCollection = 0; - v8::Handle errMsg = TRI_ParseDocumentOrDocumentHandle(collection->_vocbase, vertexCollection, did, rid, argv[0]); + v8::Handle errMsg = TRI_ParseDocumentOrDocumentHandle(collection->_vocbase, vertexCollection, did, rid, true, argv[0]); if (! errMsg.IsEmpty()) { if (vertexCollection != 0) { @@ -1204,24 +1250,18 @@ static v8::Handle EdgesQuery (TRI_edge_direction_e direction, v8::Arg //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// -/// @brief selects all elements +/// @brief executes an ALL query, without any locking +/// +/// the caller must ensure all relevant locks are acquired and freed //////////////////////////////////////////////////////////////////////////////// -static v8::Handle JS_AllQuery (v8::Arguments const& argv) { +static v8::Handle AllQuery (TRI_sim_collection_t* sim, + TRI_vocbase_col_t const* collection, + v8::Arguments const& argv) { v8::HandleScope scope; - - // extract and use the simple collection - v8::Handle err; - TRI_vocbase_col_t const* collection; - TRI_sim_collection_t* sim = TRI_ExtractAndUseSimpleCollection(argv, collection, &err); - - if (sim == 0) { - return scope.Close(v8::ThrowException(err)); - } - + // expecting two arguments if (argv.Length() != 2) { - TRI_ReleaseCollection(collection); return scope.Close(v8::ThrowException( TRI_CreateErrorObject(TRI_ERROR_BAD_PARAMETER, "usage: ALL(, )"))); @@ -1239,12 +1279,6 @@ static v8::Handle JS_AllQuery (v8::Arguments const& argv) { v8::Handle documents = v8::Array::New(); result->Set(v8::String::New("documents"), documents); - // ............................................................................. - // inside a read transaction - // ............................................................................. - - collection->_collection->beginRead(collection->_collection); - size_t total = sim->_primaryIndex._nrUsed; uint32_t count = 0; @@ -1309,8 +1343,6 @@ static v8::Handle JS_AllQuery (v8::Arguments const& argv) { } } - collection->_collection->endRead(collection->_collection); - // ............................................................................. // outside a write transaction // ............................................................................. @@ -1318,7 +1350,64 @@ static v8::Handle JS_AllQuery (v8::Arguments const& argv) { result->Set(v8::String::New("total"), v8::Number::New((double) total)); result->Set(v8::String::New("count"), v8::Number::New(count)); + return scope.Close(result); +} + +//////////////////////////////////////////////////////////////////////////////// +/// @brief selects all elements, acquiring all required locks +//////////////////////////////////////////////////////////////////////////////// + +static v8::Handle JS_AllQuery (v8::Arguments const& argv) { + v8::HandleScope scope; + + // extract and use the simple collection + v8::Handle err; + TRI_vocbase_col_t const* collection; + TRI_sim_collection_t* sim = TRI_ExtractAndUseSimpleCollection(argv, collection, &err); + + if (sim == 0) { + return scope.Close(v8::ThrowException(err)); + } + + // ............................................................................. + // inside a read transaction + // ............................................................................. + + collection->_collection->beginRead(collection->_collection); + + v8::Handle result = AllQuery(sim, collection, argv); + + collection->_collection->endRead(collection->_collection); + + // ............................................................................. + // outside a write transaction + // ............................................................................. + TRI_ReleaseCollection(collection); + + return scope.Close(result); +} + +//////////////////////////////////////////////////////////////////////////////// +/// @brief selects all elements, without acquiring any locks +/// +/// It is the callers responsibility to acquire and free the required locks +//////////////////////////////////////////////////////////////////////////////// + +static v8::Handle JS_AllNLQuery (v8::Arguments const& argv) { + v8::HandleScope scope; + + // extract and use the simple collection + v8::Handle err; + TRI_vocbase_col_t const* collection; + TRI_sim_collection_t* sim = TRI_ExtractSimpleCollection(argv, collection, &err); + + if (sim == 0) { + return scope.Close(v8::ThrowException(err)); + } + + v8::Handle result = AllQuery(sim, collection, argv); + return scope.Close(result); } @@ -1435,23 +1524,18 @@ static v8::Handle JS_ByExampleQuery (v8::Arguments const& argv) { //////////////////////////////////////////////////////////////////////////////// /// @brief selects elements by example using a hash index +/// +/// It is the callers responsibility to acquire and free the required locks //////////////////////////////////////////////////////////////////////////////// -static v8::Handle JS_ByExampleHashIndex (v8::Arguments const& argv) { +static v8::Handle ByExampleHashIndexQuery (TRI_sim_collection_t* sim, + TRI_vocbase_col_t const* collection, + v8::Handle* err, + v8::Arguments const& argv) { v8::HandleScope scope; - // extract and use the simple collection - v8::Handle err; - TRI_vocbase_col_t const* collection; - TRI_sim_collection_t* sim = TRI_ExtractAndUseSimpleCollection(argv, collection, &err); - - if (sim == 0) { - return scope.Close(v8::ThrowException(err)); - } - // expecting index, example, skip, and limit if (argv.Length() < 2) { - TRI_ReleaseCollection(collection); return scope.Close(v8::ThrowException( TRI_CreateErrorObject(TRI_ERROR_BAD_PARAMETER, "usage: BY_EXAMPLE_HASH(, , , )"))); @@ -1459,7 +1543,6 @@ static v8::Handle JS_ByExampleHashIndex (v8::Arguments const& argv) { // extract the example if (! argv[1]->IsObject()) { - TRI_ReleaseCollection(collection); return scope.Close(v8::ThrowException( TRI_CreateErrorObject(TRI_ERROR_BAD_PARAMETER, " must be an object"))); @@ -1479,26 +1562,14 @@ static v8::Handle JS_ByExampleHashIndex (v8::Arguments const& argv) { v8::Handle documents = v8::Array::New(); result->Set(v8::String::New("documents"), documents); - // ............................................................................. - // inside a read transaction - // ............................................................................. - - collection->_collection->beginRead(collection->_collection); - // extract the index - TRI_index_t* idx = TRI_LookupIndexByHandle(sim->base.base._vocbase, collection, argv[0], false, &err); + TRI_index_t* idx = TRI_LookupIndexByHandle(sim->base.base._vocbase, collection, argv[0], false, err); if (idx == 0) { - collection->_collection->endRead(collection->_collection); - - TRI_ReleaseCollection(collection); - return scope.Close(v8::ThrowException(err)); + return scope.Close(v8::ThrowException(*err)); } if (idx->_type != TRI_IDX_TYPE_HASH_INDEX) { - collection->_collection->endRead(collection->_collection); - - TRI_ReleaseCollection(collection); return scope.Close(v8::ThrowException(TRI_CreateErrorObject(TRI_ERROR_BAD_PARAMETER, "index must be a hash index"))); } @@ -1509,13 +1580,10 @@ static v8::Handle JS_ByExampleHashIndex (v8::Arguments const& argv) { TRI_shaped_json_t** values; TRI_shaper_t* shaper = sim->base._shaper; - int res = SetupExampleObjectIndex(hashIndex, example, shaper, n, values, &err); + int res = SetupExampleObjectIndex(hashIndex, example, shaper, n, values, err); if (res != TRI_ERROR_NO_ERROR) { - collection->_collection->endRead(collection->_collection); - - TRI_ReleaseCollection(collection); - return scope.Close(v8::ThrowException(err)); + return scope.Close(v8::ThrowException(*err)); } // find the matches @@ -1542,12 +1610,6 @@ static v8::Handle JS_ByExampleHashIndex (v8::Arguments const& argv) { } } - collection->_collection->endRead(collection->_collection); - - // ............................................................................. - // outside a write transaction - // ............................................................................. - // free data allocated by hash index result TRI_FreeResultHashIndex(idx, list); @@ -1556,19 +1618,85 @@ static v8::Handle JS_ByExampleHashIndex (v8::Arguments const& argv) { CleanupExampleObject(shaper, n, 0, values); + return scope.Close(result); +} + +//////////////////////////////////////////////////////////////////////////////// +/// @brief selects elements by example using a hash index +//////////////////////////////////////////////////////////////////////////////// + +static v8::Handle JS_ByExampleHashIndex (v8::Arguments const& argv) { + v8::HandleScope scope; + + // extract and use the simple collection + v8::Handle err; + TRI_vocbase_col_t const* collection; + TRI_sim_collection_t* sim = TRI_ExtractAndUseSimpleCollection(argv, collection, &err); + + if (sim == 0) { + return scope.Close(v8::ThrowException(err)); + } + + // ............................................................................. + // inside a read transaction + // ............................................................................. + + collection->_collection->beginRead(collection->_collection); + + v8::Handle result = ByExampleHashIndexQuery(sim, collection, &err, argv); + + collection->_collection->endRead(collection->_collection); + + // ............................................................................. + // outside a write transaction + // ............................................................................. + TRI_ReleaseCollection(collection); return scope.Close(result); } //////////////////////////////////////////////////////////////////////////////// +/// @brief selects elements by example using a hash index +/// +/// It is the callers responsibility to acquire and free the required locks +//////////////////////////////////////////////////////////////////////////////// + +static v8::Handle JS_ByExampleNLHashIndex (v8::Arguments const& argv) { + v8::HandleScope scope; + + // extract and use the simple collection + v8::Handle err; + TRI_vocbase_col_t const* collection; + TRI_sim_collection_t* sim = TRI_ExtractSimpleCollection(argv, collection, &err); + + if (sim == 0) { + return scope.Close(v8::ThrowException(err)); + } + + v8::Handle result = ByExampleHashIndexQuery(sim, collection, &err, argv); + + return scope.Close(result); +} + +//////////////////////////////////////////////////////////////////////////////// /// @brief selects elements by condition using a skiplist index //////////////////////////////////////////////////////////////////////////////// static v8::Handle JS_ByConditionSkiplist (v8::Arguments const& argv) { std::string signature("BY_CONDITION_SKIPLIST(, , , )"); - return ExecuteSkiplistQuery(argv, signature, QUERY_CONDITION); + return ExecuteSkiplistQuery(argv, signature, QUERY_CONDITION, true); +} + +//////////////////////////////////////////////////////////////////////////////// +/// @brief selects elements by condition using a skiplist index +//////////////////////////////////////////////////////////////////////////////// + +static v8::Handle JS_ByConditionNLSkiplist (v8::Arguments const& argv) { + std::string signature("BY_CONDITION_SKIPLIST_NL(, , , )"); + + return ExecuteSkiplistQuery(argv, signature, QUERY_CONDITION, false); } //////////////////////////////////////////////////////////////////////////////// @@ -1578,9 +1706,18 @@ static v8::Handle JS_ByConditionSkiplist (v8::Arguments const& argv) static v8::Handle JS_ByExampleSkiplist (v8::Arguments const& argv) { std::string signature("BY_EXAMPLE_SKIPLIST(, , , )"); - return ExecuteSkiplistQuery(argv, signature, QUERY_EXAMPLE); + return ExecuteSkiplistQuery(argv, signature, QUERY_EXAMPLE, true); } +//////////////////////////////////////////////////////////////////////////////// +/// @brief selects elements by example using a skiplist index +//////////////////////////////////////////////////////////////////////////////// + +static v8::Handle JS_ByExampleNLSkiplist (v8::Arguments const& argv) { + std::string signature("BY_EXAMPLE_SKIPLIST_NL(, , , )"); + + return ExecuteSkiplistQuery(argv, signature, QUERY_EXAMPLE, false); +} //////////////////////////////////////////////////////////////////////////////// /// @brief selects elements by example using a bitarray index @@ -1589,11 +1726,18 @@ static v8::Handle JS_ByExampleSkiplist (v8::Arguments const& argv) { static v8::Handle JS_ByExampleBitarray (v8::Arguments const& argv) { std::string signature("BY_EXAMPLE_BITARRAY(, , , )"); - return ExecuteBitarrayQuery(argv, signature, QUERY_EXAMPLE); + return ExecuteBitarrayQuery(argv, signature, QUERY_EXAMPLE, true); } +//////////////////////////////////////////////////////////////////////////////// +/// @brief selects elements by example using a bitarray index +//////////////////////////////////////////////////////////////////////////////// +static v8::Handle JS_ByExampleNLBitarray (v8::Arguments const& argv) { + std::string signature("BY_EXAMPLE_BITARRAY_NL(, , , )"); + return ExecuteBitarrayQuery(argv, signature, QUERY_EXAMPLE, false); +} //////////////////////////////////////////////////////////////////////////////// /// @brief selects all edges for a set of vertices @@ -1641,48 +1785,31 @@ static v8::Handle JS_InEdgesQuery (v8::Arguments const& argv) { //////////////////////////////////////////////////////////////////////////////// /// @brief selects points near a given coordinate +/// +/// the caller must ensure all relevant locks are acquired and freed //////////////////////////////////////////////////////////////////////////////// -static v8::Handle JS_NearQuery (v8::Arguments const& argv) { +static v8::Handle NearQuery (TRI_sim_collection_t* sim, + TRI_vocbase_col_t const* collection, + v8::Handle* err, + v8::Arguments const& argv) { v8::HandleScope scope; - // extract and use the simple collection - v8::Handle err; - TRI_vocbase_col_t const* collection; - TRI_sim_collection_t* sim = TRI_ExtractAndUseSimpleCollection(argv, collection, &err); - - if (sim == 0) { - return scope.Close(v8::ThrowException(err)); - } - // expect: NEAR(, , , ) if (argv.Length() != 4) { - TRI_ReleaseCollection(collection); return scope.Close(v8::ThrowException( TRI_CreateErrorObject(TRI_ERROR_BAD_PARAMETER, "usage: NEAR(, , , )"))); } - // ............................................................................. - // inside a read transaction - // ............................................................................. - - collection->_collection->beginRead(collection->_collection); - // extract the index - TRI_index_t* idx = TRI_LookupIndexByHandle(sim->base.base._vocbase, collection, argv[0], false, &err); + TRI_index_t* idx = TRI_LookupIndexByHandle(sim->base.base._vocbase, collection, argv[0], false, err); if (idx == 0) { - collection->_collection->endRead(collection->_collection); - - TRI_ReleaseCollection(collection); - return scope.Close(v8::ThrowException(err)); + return scope.Close(v8::ThrowException(*err)); } if (idx->_type != TRI_IDX_TYPE_GEO1_INDEX && idx->_type != TRI_IDX_TYPE_GEO2_INDEX) { - collection->_collection->endRead(collection->_collection); - - TRI_ReleaseCollection(collection); return scope.Close(v8::ThrowException(TRI_CreateErrorObject(TRI_ERROR_BAD_PARAMETER, "index must be a geo-index"))); } @@ -1708,6 +1835,33 @@ static v8::Handle JS_NearQuery (v8::Arguments const& argv) { StoreGeoResult(collection, cors, documents, distances); } + return scope.Close(result); +} + +//////////////////////////////////////////////////////////////////////////////// +/// @brief selects points near a given coordinate +//////////////////////////////////////////////////////////////////////////////// + +static v8::Handle JS_NearQuery (v8::Arguments const& argv) { + v8::HandleScope scope; + + // extract and use the simple collection + v8::Handle err; + TRI_vocbase_col_t const* collection; + TRI_sim_collection_t* sim = TRI_ExtractAndUseSimpleCollection(argv, collection, &err); + + if (sim == 0) { + return scope.Close(v8::ThrowException(err)); + } + + // ............................................................................. + // inside a read transaction + // ............................................................................. + + collection->_collection->beginRead(collection->_collection); + + v8::Handle result = NearQuery(sim, collection, &err, argv); + collection->_collection->endRead(collection->_collection); // ............................................................................. @@ -1720,6 +1874,29 @@ static v8::Handle JS_NearQuery (v8::Arguments const& argv) { } //////////////////////////////////////////////////////////////////////////////// +/// @brief selects points near a given coordinate +/// +/// It is the callers responsibility to acquire and free the required locks +//////////////////////////////////////////////////////////////////////////////// + +static v8::Handle JS_NearNLQuery (v8::Arguments const& argv) { + v8::HandleScope scope; + + // extract and use the simple collection + v8::Handle err; + TRI_vocbase_col_t const* collection; + TRI_sim_collection_t* sim = TRI_ExtractSimpleCollection(argv, collection, &err); + + if (sim == 0) { + return scope.Close(v8::ThrowException(err)); + } + + v8::Handle result = NearQuery(sim, collection, &err, argv); + + return scope.Close(result); +} + +//////////////////////////////////////////////////////////////////////////////// /// @brief selects all outbound edges /// /// @FUN{@FA{edge-collection}.outEdges(@FA{vertex})} @@ -1743,48 +1920,31 @@ static v8::Handle JS_OutEdgesQuery (v8::Arguments const& argv) { //////////////////////////////////////////////////////////////////////////////// /// @brief selects points within a given radius +/// +/// the caller must ensure all relevant locks are acquired and freed //////////////////////////////////////////////////////////////////////////////// -static v8::Handle JS_WithinQuery (v8::Arguments const& argv) { +static v8::Handle WithinQuery (TRI_sim_collection_t* sim, + TRI_vocbase_col_t const* collection, + v8::Handle* err, + v8::Arguments const& argv) { v8::HandleScope scope; - // extract and use the simple collection - v8::Handle err; - TRI_vocbase_col_t const* collection; - TRI_sim_collection_t* sim = TRI_ExtractAndUseSimpleCollection(argv, collection, &err); - - if (sim == 0) { - return scope.Close(v8::ThrowException(err)); - } - // expect: WITHIN(, , , ) if (argv.Length() != 4) { - TRI_ReleaseCollection(collection); return scope.Close(v8::ThrowException( TRI_CreateErrorObject(TRI_ERROR_BAD_PARAMETER, "usage: WITHIN(, , , )"))); } - // ............................................................................. - // inside a read transaction - // ............................................................................. - - collection->_collection->beginRead(collection->_collection); - // extract the index - TRI_index_t* idx = TRI_LookupIndexByHandle(sim->base.base._vocbase, collection, argv[0], false, &err); + TRI_index_t* idx = TRI_LookupIndexByHandle(sim->base.base._vocbase, collection, argv[0], false, err); if (idx == 0) { - collection->_collection->endRead(collection->_collection); - - TRI_ReleaseCollection(collection); - return scope.Close(v8::ThrowException(err)); + return scope.Close(v8::ThrowException(*err)); } if (idx->_type != TRI_IDX_TYPE_GEO1_INDEX && idx->_type != TRI_IDX_TYPE_GEO2_INDEX) { - collection->_collection->endRead(collection->_collection); - - TRI_ReleaseCollection(collection); return scope.Close(v8::ThrowException(TRI_CreateErrorObject(TRI_ERROR_BAD_PARAMETER, "index must be a geo-index"))); } @@ -1810,6 +1970,33 @@ static v8::Handle JS_WithinQuery (v8::Arguments const& argv) { StoreGeoResult(collection, cors, documents, distances); } + return scope.Close(result); +} + +//////////////////////////////////////////////////////////////////////////////// +/// @brief selects points within a given radius +//////////////////////////////////////////////////////////////////////////////// + +static v8::Handle JS_WithinQuery (v8::Arguments const& argv) { + v8::HandleScope scope; + + // extract and use the simple collection + v8::Handle err; + TRI_vocbase_col_t const* collection; + TRI_sim_collection_t* sim = TRI_ExtractAndUseSimpleCollection(argv, collection, &err); + + if (sim == 0) { + return scope.Close(v8::ThrowException(err)); + } + + // ............................................................................. + // inside a read transaction + // ............................................................................. + + collection->_collection->beginRead(collection->_collection); + + v8::Handle result = WithinQuery(sim, collection, &err, argv); + collection->_collection->endRead(collection->_collection); // ............................................................................. @@ -1817,6 +2004,30 @@ static v8::Handle JS_WithinQuery (v8::Arguments const& argv) { // ............................................................................. TRI_ReleaseCollection(collection); + + return scope.Close(result); +} + +//////////////////////////////////////////////////////////////////////////////// +/// @brief selects points within a given radius +/// +/// It is the callers responsibility to acquire and free the required locks +//////////////////////////////////////////////////////////////////////////////// + +static v8::Handle JS_WithinNLQuery (v8::Arguments const& argv) { + v8::HandleScope scope; + + // extract the simple collection + v8::Handle err; + TRI_vocbase_col_t const* collection; + TRI_sim_collection_t* sim = TRI_ExtractSimpleCollection(argv, collection, &err); + + if (sim == 0) { + return scope.Close(v8::ThrowException(err)); + } + + v8::Handle result = WithinQuery(sim, collection, &err, argv); + return scope.Close(result); } @@ -1860,16 +2071,23 @@ void TRI_InitV8Queries (v8::Handle context) { // ............................................................................. v8::Handle AllFuncName = v8::Persistent::New(v8::String::New("ALL")); + v8::Handle AllNLFuncName = v8::Persistent::New(v8::String::New("ALL_NL")); v8::Handle ByConditionSkiplistFuncName = v8::Persistent::New(v8::String::New("BY_CONDITION_SKIPLIST")); + v8::Handle ByConditionSkiplistNLFuncName = v8::Persistent::New(v8::String::New("BY_CONDITION_SKIPLIST_NL")); v8::Handle ByExampleFuncName = v8::Persistent::New(v8::String::New("BY_EXAMPLE")); v8::Handle ByExampleBitarrayFuncName = v8::Persistent::New(v8::String::New("BY_EXAMPLE_BITARRAY")); + v8::Handle ByExampleBitarrayNLFuncName = v8::Persistent::New(v8::String::New("BY_EXAMPLE_BITARRAY_NL")); v8::Handle ByExampleHashFuncName = v8::Persistent::New(v8::String::New("BY_EXAMPLE_HASH")); + v8::Handle ByExampleHashNLFuncName = v8::Persistent::New(v8::String::New("BY_EXAMPLE_HASH_NL")); v8::Handle ByExampleSkiplistFuncName = v8::Persistent::New(v8::String::New("BY_EXAMPLE_SKIPLIST")); + v8::Handle ByExampleSkiplistNLFuncName = v8::Persistent::New(v8::String::New("BY_EXAMPLE_SKIPLIST_NL")); v8::Handle EdgesFuncName = v8::Persistent::New(v8::String::New("edges")); v8::Handle InEdgesFuncName = v8::Persistent::New(v8::String::New("inEdges")); v8::Handle NearFuncName = v8::Persistent::New(v8::String::New("NEAR")); + v8::Handle NearNLFuncName = v8::Persistent::New(v8::String::New("NEAR_NL")); v8::Handle OutEdgesFuncName = v8::Persistent::New(v8::String::New("outEdges")); v8::Handle WithinFuncName = v8::Persistent::New(v8::String::New("WITHIN")); + v8::Handle WithinNLFuncName = v8::Persistent::New(v8::String::New("WITHIN_NL")); // ............................................................................. // generate the TRI_vocbase_col_t template @@ -1878,13 +2096,20 @@ void TRI_InitV8Queries (v8::Handle context) { rt = v8g->VocbaseColTempl; rt->Set(AllFuncName, v8::FunctionTemplate::New(JS_AllQuery)); + rt->Set(AllNLFuncName, v8::FunctionTemplate::New(JS_AllNLQuery)); rt->Set(ByConditionSkiplistFuncName, v8::FunctionTemplate::New(JS_ByConditionSkiplist)); + rt->Set(ByConditionSkiplistNLFuncName, v8::FunctionTemplate::New(JS_ByConditionNLSkiplist)); rt->Set(ByExampleBitarrayFuncName, v8::FunctionTemplate::New(JS_ByExampleBitarray)); + rt->Set(ByExampleBitarrayNLFuncName, v8::FunctionTemplate::New(JS_ByExampleNLBitarray)); rt->Set(ByExampleFuncName, v8::FunctionTemplate::New(JS_ByExampleQuery)); rt->Set(ByExampleHashFuncName, v8::FunctionTemplate::New(JS_ByExampleHashIndex)); + rt->Set(ByExampleHashNLFuncName, v8::FunctionTemplate::New(JS_ByExampleNLHashIndex)); rt->Set(ByExampleSkiplistFuncName, v8::FunctionTemplate::New(JS_ByExampleSkiplist)); + rt->Set(ByExampleSkiplistNLFuncName, v8::FunctionTemplate::New(JS_ByExampleNLSkiplist)); rt->Set(NearFuncName, v8::FunctionTemplate::New(JS_NearQuery)); + rt->Set(NearNLFuncName, v8::FunctionTemplate::New(JS_NearNLQuery)); rt->Set(WithinFuncName, v8::FunctionTemplate::New(JS_WithinQuery)); + rt->Set(WithinNLFuncName, v8::FunctionTemplate::New(JS_WithinNLQuery)); // ............................................................................. // generate the TRI_vocbase_col_t template for edges @@ -1893,16 +2118,23 @@ void TRI_InitV8Queries (v8::Handle context) { rt = v8g->EdgesColTempl; rt->Set(AllFuncName, v8::FunctionTemplate::New(JS_AllQuery)); + rt->Set(AllNLFuncName, v8::FunctionTemplate::New(JS_AllNLQuery)); rt->Set(ByConditionSkiplistFuncName, v8::FunctionTemplate::New(JS_ByConditionSkiplist)); + rt->Set(ByConditionSkiplistNLFuncName, v8::FunctionTemplate::New(JS_ByConditionNLSkiplist)); rt->Set(ByExampleBitarrayFuncName, v8::FunctionTemplate::New(JS_ByExampleBitarray)); + rt->Set(ByExampleBitarrayNLFuncName, v8::FunctionTemplate::New(JS_ByExampleNLBitarray)); rt->Set(ByExampleFuncName, v8::FunctionTemplate::New(JS_ByExampleQuery)); rt->Set(ByExampleHashFuncName, v8::FunctionTemplate::New(JS_ByExampleHashIndex)); + rt->Set(ByExampleHashNLFuncName, v8::FunctionTemplate::New(JS_ByExampleNLHashIndex)); rt->Set(ByExampleSkiplistFuncName, v8::FunctionTemplate::New(JS_ByExampleSkiplist)); + rt->Set(ByExampleSkiplistNLFuncName, v8::FunctionTemplate::New(JS_ByExampleNLSkiplist)); rt->Set(EdgesFuncName, v8::FunctionTemplate::New(JS_EdgesQuery)); rt->Set(InEdgesFuncName, v8::FunctionTemplate::New(JS_InEdgesQuery)); rt->Set(NearFuncName, v8::FunctionTemplate::New(JS_NearQuery)); + rt->Set(NearNLFuncName, v8::FunctionTemplate::New(JS_NearNLQuery)); rt->Set(OutEdgesFuncName, v8::FunctionTemplate::New(JS_OutEdgesQuery)); rt->Set(WithinFuncName, v8::FunctionTemplate::New(JS_WithinQuery)); + rt->Set(WithinNLFuncName, v8::FunctionTemplate::New(JS_WithinNLQuery)); } //////////////////////////////////////////////////////////////////////////////// diff --git a/arangod/V8Server/v8-vocbase.cpp b/arangod/V8Server/v8-vocbase.cpp index 9bd6923..c92f67d 100755 --- a/arangod/V8Server/v8-vocbase.cpp +++ b/arangod/V8Server/v8-vocbase.cpp @@ -117,6 +117,65 @@ static int32_t const WRP_SHAPED_JSON_TYPE = 4; //////////////////////////////////////////////////////////////////////////////// /// @} //////////////////////////////////////////////////////////////////////////////// + +// ----------------------------------------------------------------------------- +// --SECTION-- HELPER CLASSES +// ----------------------------------------------------------------------------- + +// ----------------------------------------------------------------------------- +// --SECTION-- AhuacatlContextGuard +// ----------------------------------------------------------------------------- + +//////////////////////////////////////////////////////////////////////////////// +/// @addtogroup VocBase +/// @{ +//////////////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////////////// +/// @brief scope guard for AQL queries +//////////////////////////////////////////////////////////////////////////////// + +class AhuacatlContextGuard { + public: + AhuacatlContextGuard (TRI_vocbase_t* vocbase, const string& query) : + _context(0) { + _context = TRI_CreateContextAql(vocbase, query.c_str()); + } + + ~AhuacatlContextGuard () { + this->free(); + } + + 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 @@ -476,14 +535,19 @@ static v8::Handle EnsurePathIndex (string const& cmd, //////////////////////////////////////////////////////////////////////////////// /// @brief looks up a document +/// +/// it is the caller's responsibility to acquire and release the required locks +/// the collection must also have the correct status already. don't use this +/// function if you're unsure about it! //////////////////////////////////////////////////////////////////////////////// static v8::Handle DocumentVocbaseCol (TRI_vocbase_t* vocbase, TRI_vocbase_col_t const* collection, - v8::Arguments const& argv) { + v8::Arguments const& argv, + const bool lock) { v8::HandleScope scope; - // first and only argument schould be a document idenfifier + // first and only argument should be a document idenfifier if (argv.Length() != 1) { return scope.Close(v8::ThrowException( TRI_CreateErrorObject(TRI_ERROR_BAD_PARAMETER, @@ -492,10 +556,10 @@ static v8::Handle DocumentVocbaseCol (TRI_vocbase_t* vocbase, TRI_voc_did_t did; TRI_voc_rid_t rid; - v8::Handle err = TRI_ParseDocumentOrDocumentHandle(vocbase, collection, did, rid, argv[0]); + v8::Handle err = TRI_ParseDocumentOrDocumentHandle(vocbase, collection, did, rid, lock, argv[0]); if (! err.IsEmpty()) { - if (collection != 0) { + if (collection != 0 && lock) { TRI_ReleaseCollection(collection); } @@ -513,7 +577,9 @@ static v8::Handle DocumentVocbaseCol (TRI_vocbase_t* vocbase, // inside a read transaction // ............................................................................. - collection->_collection->beginRead(collection->_collection); + if (lock) { + collection->_collection->beginRead(collection->_collection); + } document = collection->_collection->read(collection->_collection, did); @@ -524,13 +590,17 @@ static v8::Handle DocumentVocbaseCol (TRI_vocbase_t* vocbase, result = TRI_WrapShapedJson(collection, &document, barrier); } - collection->_collection->endRead(collection->_collection); + if (lock) { + collection->_collection->endRead(collection->_collection); + } // ............................................................................. // outside a write transaction // ............................................................................. - TRI_ReleaseCollection(collection); + if (lock) { + TRI_ReleaseCollection(collection); + } if (document._did == 0) { return scope.Close(v8::ThrowException( @@ -571,7 +641,7 @@ static v8::Handle ReplaceVocbaseCol (TRI_vocbase_t* vocbase, TRI_voc_did_t did; TRI_voc_rid_t rid; - v8::Handle err = TRI_ParseDocumentOrDocumentHandle(vocbase, collection, did, rid, argv[0]); + v8::Handle err = TRI_ParseDocumentOrDocumentHandle(vocbase, collection, did, rid, true, argv[0]); if (! err.IsEmpty()) { if (collection != 0) { @@ -658,7 +728,7 @@ static v8::Handle DeleteVocbaseCol (TRI_vocbase_t* vocbase, TRI_voc_did_t did; TRI_voc_rid_t rid; - v8::Handle err = TRI_ParseDocumentOrDocumentHandle(vocbase, collection, did, rid, argv[0]); + v8::Handle err = TRI_ParseDocumentOrDocumentHandle(vocbase, collection, did, rid, true, argv[0]); if (! err.IsEmpty()) { if (collection != 0) { @@ -1302,9 +1372,7 @@ static v8::Handle JS_DisposeGeneralCursor (v8::Arguments const& argv) 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::False()); } //////////////////////////////////////////////////////////////////////////////// @@ -1664,6 +1732,32 @@ static v8::Handle JS_HasNextGeneralCursor (v8::Arguments const& argv) TRI_CreateErrorObject(TRI_ERROR_CURSOR_NOT_FOUND, "disposed or unknown cursor"))); } + +//////////////////////////////////////////////////////////////////////////////// +/// @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 @@ -1776,16 +1870,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"))) { @@ -1834,8 +1928,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 +1943,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 +1956,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,18 +1999,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 +2018,11 @@ 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)); + context.free(); - 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 @@ -2188,7 +2279,27 @@ static v8::Handle JS_DocumentVocbaseCol (v8::Arguments const& argv) { return scope.Close(v8::ThrowException(err)); } - return DocumentVocbaseCol(collection->_vocbase, collection, argv); + return DocumentVocbaseCol(collection->_vocbase, collection, argv, true); +} + +//////////////////////////////////////////////////////////////////////////////// +/// @brief looks up a document +/// +/// it is the caller's responsibility to acquire and release the required locks +//////////////////////////////////////////////////////////////////////////////// + +static v8::Handle JS_DocumentNLVocbaseCol (v8::Arguments const& argv) { + v8::HandleScope scope; + + // extract the collection + TRI_vocbase_col_t* col = TRI_UnwrapClass(argv.Holder(), WRP_VOCBASE_COL_TYPE); + if (col == 0 || col->_collection == 0) { + return scope.Close(v8::ThrowException(TRI_CreateErrorObject(TRI_ERROR_INTERNAL, "cannot use/load collection"))); + } + + TRI_vocbase_col_t const* collection = col; + + return DocumentVocbaseCol(collection->_vocbase, collection, argv, false); } //////////////////////////////////////////////////////////////////////////////// @@ -3034,20 +3145,27 @@ static v8::Handle JS_FiguresVocbaseCol (v8::Arguments const& argv) { //////////////////////////////////////////////////////////////////////////////// /// @brief returns information about the indexes /// -/// @FUN{getIndexes()} -/// -/// Returns a list of all indexes defined for the collection. -/// -/// @EXAMPLES -/// -/// @verbinclude shell_index-read-all +/// it is the caller's responsibility to acquire and release all required locks //////////////////////////////////////////////////////////////////////////////// -static v8::Handle JS_GetIndexesVocbaseCol (v8::Arguments const& argv) { +static v8::Handle GetIndexesVocbaseCol (v8::Arguments const& argv, + const bool lock) { v8::HandleScope scope; v8::Handle err; - TRI_vocbase_col_t const* collection = UseCollection(argv.Holder(), &err); + TRI_vocbase_col_t const* collection = 0; + + if (lock) { + collection = UseCollection(argv.Holder(), &err); + } + else { + TRI_vocbase_col_t const* col = TRI_UnwrapClass(argv.Holder(), WRP_VOCBASE_COL_TYPE); + if (col == 0 || col->_collection == 0) { + return scope.Close(TRI_CreateErrorObject(TRI_ERROR_INTERNAL, "cannot use/load collection"));; + } + + collection = col; + } if (collection == 0) { return scope.Close(v8::ThrowException(err)); @@ -3056,14 +3174,20 @@ static v8::Handle JS_GetIndexesVocbaseCol (v8::Arguments const& argv) TRI_doc_collection_t* doc = collection->_collection; if (doc->base._type != TRI_COL_TYPE_SIMPLE_DOCUMENT) { - TRI_ReleaseCollection(collection); + if (lock) { + TRI_ReleaseCollection(collection); + } return scope.Close(v8::ThrowException(v8::String::New("unknown collection type"))); } TRI_sim_collection_t* sim = (TRI_sim_collection_t*) doc; // get a list of indexes - TRI_vector_pointer_t* indexes = TRI_IndexesSimCollection(sim); + TRI_vector_pointer_t* indexes = TRI_IndexesSimCollection(sim, lock); + + if (lock) { + TRI_ReleaseCollection(collection); + } if (!indexes) { return scope.Close(v8::ThrowException(v8::String::New("out of memory"))); @@ -3082,12 +3206,36 @@ static v8::Handle JS_GetIndexesVocbaseCol (v8::Arguments const& argv) } } - TRI_ReleaseCollection(collection); - TRI_FreeVectorPointer(TRI_UNKNOWN_MEM_ZONE, indexes); return scope.Close(result); } + +//////////////////////////////////////////////////////////////////////////////// +/// @brief returns information about the indexes +/// +/// @FUN{getIndexes()} +/// +/// Returns a list of all indexes defined for the collection. +/// +/// @EXAMPLES +/// +/// @verbinclude shell_index-read-all +//////////////////////////////////////////////////////////////////////////////// + +static v8::Handle JS_GetIndexesVocbaseCol (v8::Arguments const& argv) { + return GetIndexesVocbaseCol(argv, true); +} + +//////////////////////////////////////////////////////////////////////////////// +/// @brief returns information about the indexes +/// +/// it is the caller's responsibility to acquire and release all required locks +//////////////////////////////////////////////////////////////////////////////// + +static v8::Handle JS_GetIndexesNLVocbaseCol (v8::Arguments const& argv) { + return GetIndexesVocbaseCol(argv, false); +} //////////////////////////////////////////////////////////////////////////////// /// @brief loads a collection @@ -3657,7 +3805,7 @@ static v8::Handle JS_SaveEdgesCol (v8::Arguments const& argv) { TRI_vocbase_col_t const* fromCollection = 0; TRI_voc_rid_t fromRid; - errMsg = TRI_ParseDocumentOrDocumentHandle(collection->_vocbase, fromCollection, edge._fromDid, fromRid, argv[0]); + errMsg = TRI_ParseDocumentOrDocumentHandle(collection->_vocbase, fromCollection, edge._fromDid, fromRid, true, argv[0]); if (! errMsg.IsEmpty()) { TRI_ReleaseCollection(collection); @@ -3676,7 +3824,7 @@ static v8::Handle JS_SaveEdgesCol (v8::Arguments const& argv) { TRI_vocbase_col_t const* toCollection = 0; TRI_voc_rid_t toRid; - errMsg = TRI_ParseDocumentOrDocumentHandle(collection->_vocbase, toCollection, edge._toDid, toRid, argv[1]); + errMsg = TRI_ParseDocumentOrDocumentHandle(collection->_vocbase, toCollection, edge._toDid, toRid, true, argv[1]); if (! errMsg.IsEmpty()) { TRI_ReleaseCollection(collection); @@ -4024,7 +4172,27 @@ static v8::Handle JS_DocumentVocbase (v8::Arguments const& argv) { return scope.Close(v8::ThrowException(v8::String::New("corrupted vocbase"))); } - return DocumentVocbaseCol(vocbase, 0, argv); + return DocumentVocbaseCol(vocbase, 0, argv, true); +} + +//////////////////////////////////////////////////////////////////////////////// +/// @brief looks up a document +/// +/// it is the caller's responsibility to acquire and release the required locks +/// the collection must also have the correct status already. don't use this +/// function if you're unsure about it! +//////////////////////////////////////////////////////////////////////////////// + +static v8::Handle JS_DocumentNLVocbase (v8::Arguments const& argv) { + v8::HandleScope scope; + + TRI_vocbase_t* vocbase = TRI_UnwrapClass(argv.Holder(), WRP_VOCBASE_TYPE); + + if (vocbase == 0) { + return scope.Close(v8::ThrowException(v8::String::New("corrupted vocbase"))); + } + + return DocumentVocbaseCol(vocbase, 0, argv, false); } //////////////////////////////////////////////////////////////////////////////// @@ -4470,6 +4638,38 @@ static v8::Handle PropertyQueryShapedJson (v8::Local na //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// +/// @brief extracts the collection, but doesn't lock it +/// +/// it is the caller's responsibility to acquire and release the required locks +/// the collection must also have the correct status already. don't use this +/// function if you're unsure about it! +//////////////////////////////////////////////////////////////////////////////// + +TRI_sim_collection_t* TRI_ExtractSimpleCollection (v8::Arguments const& argv, + TRI_vocbase_col_t const*& collection, + v8::Handle* err) { + // extract the collection + v8::Handle operand = argv.Holder(); + + TRI_vocbase_col_t* col = TRI_UnwrapClass(argv.Holder(), WRP_VOCBASE_COL_TYPE); + if (col == 0 || col->_collection == 0) { + return 0; + } + + collection = col; + + // handle various collection types + TRI_doc_collection_t* doc = collection->_collection; + + if (doc->base._type != TRI_COL_TYPE_SIMPLE_DOCUMENT) { + *err = TRI_CreateErrorObject(TRI_ERROR_INTERNAL, "unknown collection type"); + return 0; + } + + return (TRI_sim_collection_t*) doc; +} + +//////////////////////////////////////////////////////////////////////////////// /// @brief extracts and locks the collection //////////////////////////////////////////////////////////////////////////////// @@ -4516,6 +4716,7 @@ v8::Handle TRI_ParseDocumentOrDocumentHandle (TRI_vocbase_t* vocbase, TRI_vocbase_col_t const*& collection, TRI_voc_did_t& did, TRI_voc_rid_t& rid, + const bool lock, v8::Handle val) { v8::HandleScope scope; TRI_v8_global_t* v8g; @@ -4567,11 +4768,13 @@ v8::Handle TRI_ParseDocumentOrDocumentHandle (TRI_vocbase_t* vocbase, "collection of is unknown"));; } - // use the collection - int res = TRI_UseCollectionVocBase(vocbase, vc); + if (lock) { + // use the collection + int res = TRI_UseCollectionVocBase(vocbase, vc); - if (res != TRI_ERROR_NO_ERROR) { - return scope.Close(TRI_CreateErrorObject(res, "cannot use/load collection"));; + if (res != TRI_ERROR_NO_ERROR) { + return scope.Close(TRI_CreateErrorObject(res, "cannot use/load collection"));; + } } collection = vc; @@ -4872,6 +5075,7 @@ TRI_v8_global_t* TRI_InitV8VocBridge (v8::Handle context, TRI_vocba v8::Handle DatafilesFuncName = v8::Persistent::New(v8::String::New("datafiles")); v8::Handle DisposeFuncName = v8::Persistent::New(v8::String::New("dispose")); v8::Handle DocumentFuncName = v8::Persistent::New(v8::String::New("document")); + v8::Handle DocumentNLFuncName = v8::Persistent::New(v8::String::New("document_nl")); v8::Handle DropFuncName = v8::Persistent::New(v8::String::New("drop")); v8::Handle DropIndexFuncName = v8::Persistent::New(v8::String::New("dropIndex")); v8::Handle EnsureBitarrayFuncName = v8::Persistent::New(v8::String::New("ensureBitarray")); @@ -4887,6 +5091,7 @@ TRI_v8_global_t* TRI_InitV8VocBridge (v8::Handle context, TRI_vocba v8::Handle FiguresFuncName = v8::Persistent::New(v8::String::New("figures")); v8::Handle GetBatchSizeFuncName = v8::Persistent::New(v8::String::New("getBatchSize")); v8::Handle GetIndexesFuncName = v8::Persistent::New(v8::String::New("getIndexes")); + v8::Handle GetIndexesNLFuncName = v8::Persistent::New(v8::String::New("getIndexesNL")); v8::Handle GetRowsFuncName = v8::Persistent::New(v8::String::New("getRows")); v8::Handle HasCountFuncName = v8::Persistent::New(v8::String::New("hasCount")); v8::Handle HasNextFuncName = v8::Persistent::New(v8::String::New("hasNext")); @@ -4907,6 +5112,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")); @@ -4914,6 +5120,7 @@ TRI_v8_global_t* TRI_InitV8VocBridge (v8::Handle context, TRI_vocba v8::Handle _CreateFuncName = v8::Persistent::New(v8::String::New("_create")); v8::Handle _RemoveFuncName = v8::Persistent::New(v8::String::New("_remove")); v8::Handle _DocumentFuncName = v8::Persistent::New(v8::String::New("_document")); + v8::Handle _DocumentNLFuncName = v8::Persistent::New(v8::String::New("_document_nl")); v8::Handle _ReplaceFuncName = v8::Persistent::New(v8::String::New("_replace")); // ............................................................................. @@ -4972,6 +5179,7 @@ TRI_v8_global_t* TRI_InitV8VocBridge (v8::Handle context, TRI_vocba rt->Set(_RemoveFuncName, v8::FunctionTemplate::New(JS_RemoveVocbase)); rt->Set(_DocumentFuncName, v8::FunctionTemplate::New(JS_DocumentVocbase)); + rt->Set(_DocumentNLFuncName, v8::FunctionTemplate::New(JS_DocumentNLVocbase)); rt->Set(_ReplaceFuncName, v8::FunctionTemplate::New(JS_ReplaceVocbase)); v8g->VocbaseTempl = v8::Persistent::New(rt); @@ -4999,6 +5207,7 @@ TRI_v8_global_t* TRI_InitV8VocBridge (v8::Handle context, TRI_vocba rt->Set(_RemoveFuncName, v8::FunctionTemplate::New(JS_RemoveVocbase)); rt->Set(_DocumentFuncName, v8::FunctionTemplate::New(JS_DocumentVocbase)); + rt->Set(_DocumentNLFuncName, v8::FunctionTemplate::New(JS_DocumentNLVocbase)); rt->Set(_ReplaceFuncName, v8::FunctionTemplate::New(JS_ReplaceVocbase)); v8g->EdgesTempl = v8::Persistent::New(rt); @@ -5046,6 +5255,7 @@ TRI_v8_global_t* TRI_InitV8VocBridge (v8::Handle context, TRI_vocba rt->Set(CountFuncName, v8::FunctionTemplate::New(JS_CountVocbaseCol)); rt->Set(DocumentFuncName, v8::FunctionTemplate::New(JS_DocumentVocbaseCol)); + rt->Set(DocumentNLFuncName, v8::FunctionTemplate::New(JS_DocumentNLVocbaseCol)); rt->Set(DropFuncName, v8::FunctionTemplate::New(JS_DropVocbaseCol)); rt->Set(DropIndexFuncName, v8::FunctionTemplate::New(JS_DropIndexVocbaseCol)); rt->Set(EnsureBitarrayFuncName, v8::FunctionTemplate::New(JS_EnsureBitarrayVocbaseCol)); @@ -5062,6 +5272,7 @@ TRI_v8_global_t* TRI_InitV8VocBridge (v8::Handle context, TRI_vocba rt->Set(DatafilesFuncName, v8::FunctionTemplate::New(JS_DatafilesVocbaseCol)); rt->Set(FiguresFuncName, v8::FunctionTemplate::New(JS_FiguresVocbaseCol)); rt->Set(GetIndexesFuncName, v8::FunctionTemplate::New(JS_GetIndexesVocbaseCol)); + rt->Set(GetIndexesNLFuncName, v8::FunctionTemplate::New(JS_GetIndexesNLVocbaseCol)); rt->Set(LoadFuncName, v8::FunctionTemplate::New(JS_LoadVocbaseCol)); rt->Set(LookupHashIndexFuncName, v8::FunctionTemplate::New(JS_LookupHashIndexVocbaseCol)); rt->Set(LookupSkiplistFuncName, v8::FunctionTemplate::New(JS_LookupSkiplistVocbaseCol)); @@ -5112,6 +5323,7 @@ TRI_v8_global_t* TRI_InitV8VocBridge (v8::Handle context, TRI_vocba rt->Set(DatafilesFuncName, v8::FunctionTemplate::New(JS_DatafilesVocbaseCol)); rt->Set(FiguresFuncName, v8::FunctionTemplate::New(JS_FiguresVocbaseCol)); rt->Set(GetIndexesFuncName, v8::FunctionTemplate::New(JS_GetIndexesVocbaseCol)); + rt->Set(GetIndexesNLFuncName, v8::FunctionTemplate::New(JS_GetIndexesNLVocbaseCol)); rt->Set(LoadFuncName, v8::FunctionTemplate::New(JS_LoadVocbaseCol)); rt->Set(LookupHashIndexFuncName, v8::FunctionTemplate::New(JS_LookupHashIndexVocbaseCol)); rt->Set(LookupSkiplistFuncName, v8::FunctionTemplate::New(JS_LookupSkiplistVocbaseCol)); @@ -5165,6 +5377,7 @@ 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); diff --git a/arangod/V8Server/v8-vocbase.h b/arangod/V8Server/v8-vocbase.h index fd28c1a..0057617 100644 --- a/arangod/V8Server/v8-vocbase.h +++ b/arangod/V8Server/v8-vocbase.h @@ -42,6 +42,18 @@ //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// +/// @brief extracts the collection, but doesn't lock it +/// +/// it is the caller's responsibility to acquire and release the required locks +/// the collection must also have the correct status already. don't use this +/// function if you're unsure about it! +//////////////////////////////////////////////////////////////////////////////// + +TRI_sim_collection_t* TRI_ExtractSimpleCollection (v8::Arguments const& argv, + TRI_vocbase_col_t const*& collection, + v8::Handle* err); + +//////////////////////////////////////////////////////////////////////////////// /// @brief extracts and locks the collection //////////////////////////////////////////////////////////////////////////////// @@ -63,6 +75,7 @@ v8::Handle TRI_ParseDocumentOrDocumentHandle (TRI_vocbase_t* vocbase, TRI_vocbase_col_t const*& collection, TRI_voc_did_t& did, TRI_voc_rid_t& rid, + const bool lock, v8::Handle val); //////////////////////////////////////////////////////////////////////////////// diff --git a/arangod/VocBase/compactor.c b/arangod/VocBase/compactor.c index 2809b17..13ef8fb 100644 --- a/arangod/VocBase/compactor.c +++ b/arangod/VocBase/compactor.c @@ -538,10 +538,8 @@ 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); + TRI_CleanupShadowData(vocbase->_cursors, (double) SHADOW_CURSOR_MAX_AGE, force); } //////////////////////////////////////////////////////////////////////////////// diff --git a/arangod/VocBase/shadow-data.c b/arangod/VocBase/shadow-data.c index b664156..873fc66 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 } @@ -400,7 +404,7 @@ void TRI_EndUsageIdShadowData (TRI_shadow_store_t* const store, TRI_LockMutex(&store->_lock); shadow = (TRI_shadow_t*) TRI_LookupByKeyAssociativePointer(&store->_ids, &id); - 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) { @@ -540,8 +552,13 @@ void TRI_CleanupShadowData (TRI_shadow_store_t* const store, if (shadow->_rc < 1 || force) { if (shadow->_type == SHADOW_TRANSIENT || 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..fa13261 100644 --- a/arangod/VocBase/simple-collection.c +++ b/arangod/VocBase/simple-collection.c @@ -2868,7 +2868,8 @@ static int ComparePidName (void const* left, void const* right) { /// @brief returns a description of all indexes //////////////////////////////////////////////////////////////////////////////// -TRI_vector_pointer_t* TRI_IndexesSimCollection (TRI_sim_collection_t* sim) { +TRI_vector_pointer_t* TRI_IndexesSimCollection (TRI_sim_collection_t* sim, + const bool lock) { TRI_vector_pointer_t* vector; size_t n; size_t i; @@ -2884,7 +2885,9 @@ TRI_vector_pointer_t* TRI_IndexesSimCollection (TRI_sim_collection_t* sim) { // inside read-lock // ............................................................................. - TRI_READ_LOCK_DOCUMENTS_INDEXES_SIM_COLLECTION(sim); + if (lock) { + TRI_READ_LOCK_DOCUMENTS_INDEXES_SIM_COLLECTION(sim); + } n = sim->_indexes._length; @@ -2901,7 +2904,9 @@ TRI_vector_pointer_t* TRI_IndexesSimCollection (TRI_sim_collection_t* sim) { } } - TRI_READ_UNLOCK_DOCUMENTS_INDEXES_SIM_COLLECTION(sim); + if (lock) { + TRI_READ_UNLOCK_DOCUMENTS_INDEXES_SIM_COLLECTION(sim); + } // ............................................................................. // outside read-lock diff --git a/arangod/VocBase/simple-collection.h b/arangod/VocBase/simple-collection.h index 1a8c2e7..c5cce0a 100644 --- a/arangod/VocBase/simple-collection.h +++ b/arangod/VocBase/simple-collection.h @@ -349,7 +349,8 @@ int TRI_CloseSimCollection (TRI_sim_collection_t* collection); /// @brief returns a description of all indexes //////////////////////////////////////////////////////////////////////////////// -TRI_vector_pointer_t* TRI_IndexesSimCollection (TRI_sim_collection_t*); +TRI_vector_pointer_t* TRI_IndexesSimCollection (TRI_sim_collection_t*, + const bool); //////////////////////////////////////////////////////////////////////////////// /// @brief drops an index 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/server/ahuacatl.js b/js/server/ahuacatl.js index 665f723..9c6de31 100644 --- a/js/server/ahuacatl.js +++ b/js/server/ahuacatl.js @@ -70,7 +70,7 @@ function AHUACATL_THROW (error, data) { //////////////////////////////////////////////////////////////////////////////// function AHUACATL_INDEX (collection, indexTypes) { - var indexes = collection.getIndexes(); + var indexes = collection.getIndexesNL(); for (var i = 0; i < indexes.length; ++i) { var index = indexes[i]; @@ -385,7 +385,7 @@ function AHUACATL_LIST (value) { //////////////////////////////////////////////////////////////////////////////// function AHUACATL_GET_DOCUMENTS (collection) { - return internal.db[collection].all().toArray(); + return internal.db[collection].ALL_NL(0, null).documents; } //////////////////////////////////////////////////////////////////////////////// @@ -395,7 +395,7 @@ function AHUACATL_GET_DOCUMENTS (collection) { function AHUACATL_GET_DOCUMENTS_PRIMARY (collection, idx, id) { try { - return [ internal.db[collection].document(id) ]; + return [ internal.db[collection].document_nl(id) ]; } catch (e) { return [ ]; @@ -413,7 +413,7 @@ function AHUACATL_GET_DOCUMENTS_PRIMARY_LIST (collection, idx, values) { for (var i in values) { var id = values[i]; try { - var d = internal.db[collection].document(id); + var d = internal.db[collection].document_nl(id); result.push(d); } catch (e) { @@ -429,7 +429,7 @@ function AHUACATL_GET_DOCUMENTS_PRIMARY_LIST (collection, idx, values) { //////////////////////////////////////////////////////////////////////////////// function AHUACATL_GET_DOCUMENTS_HASH (collection, idx, example) { - return internal.db[collection].BY_EXAMPLE_HASH(idx, example).documents; + return internal.db[collection].BY_EXAMPLE_HASH_NL(idx, example).documents; } //////////////////////////////////////////////////////////////////////////////// @@ -446,7 +446,7 @@ function AHUACATL_GET_DOCUMENTS_HASH_LIST (collection, idx, attribute, values) { example[attribute] = value; - var documents = internal.db[collection].BY_EXAMPLE_HASH(idx, example).documents; + var documents = internal.db[collection].BY_EXAMPLE_HASH_NL(idx, example).documents; for (var j in documents) { result.push(documents[j]); } @@ -460,7 +460,7 @@ function AHUACATL_GET_DOCUMENTS_HASH_LIST (collection, idx, attribute, values) { //////////////////////////////////////////////////////////////////////////////// function AHUACATL_GET_DOCUMENTS_SKIPLIST (collection, idx, example) { - return internal.db[collection].BY_CONDITION_SKIPLIST(idx, example).documents; + return internal.db[collection].BY_CONDITION_SKIPLIST_NL(idx, example).documents; } //////////////////////////////////////////////////////////////////////////////// @@ -477,7 +477,7 @@ function AHUACATL_GET_DOCUMENTS_SKIPLIST_LIST (collection, idx, attribute, value example[attribute] = value; - var documents = internal.db[collection].BY_EXAMPLE_SKIPLIST(idx, example).documents; + var documents = internal.db[collection].BY_EXAMPLE_SKIPLIST_NL(idx, example).documents; for (var j in documents) { result.push(documents[j]); } @@ -1850,7 +1850,7 @@ function AHUACATL_GEO_NEAR () { AHUACATL_THROW(internal.errors.ERROR_QUERY_GEO_INDEX_MISSING, collection); } - var result = internal.db[collection].NEAR(idx, latitude, longitude, limit); + var result = internal.db[collection].NEAR_NL(idx, latitude, longitude, limit); if (distanceAttribute == null) { return result.documents; } @@ -1882,7 +1882,7 @@ function AHUACATL_GEO_WITHIN () { AHUACATL_THROW(internal.errors.ERROR_QUERY_GEO_INDEX_MISSING, collection); } - var result = internal.db[collection].WITHIN(idx, latitude, longitude, radius); + var result = internal.db[collection].WITHIN_NL(idx, latitude, longitude, radius); if (distanceAttribute == null) { return result.documents; } @@ -2027,7 +2027,7 @@ function AHUACATL_GRAPH_SUBNODES (searchAttributes, vertexId, visited, edges, ve var clonedEdges = AHUACATL_CLONE(edges); var clonedVertices = AHUACATL_CLONE(vertices); clonedEdges.push(subEdge); - clonedVertices.push(internal.db._document(targetId)); + clonedVertices.push(internal.db._document_nl(targetId)); var connected = AHUACATL_GRAPH_SUBNODES(searchAttributes, targetId, AHUACATL_CLONE(visited), clonedEdges, clonedVertices, level + 1); for (k = 0; k < connected.length; ++k) { diff --git a/js/server/js-ahuacatl.h b/js/server/js-ahuacatl.h index 7bf2dd5..2115bad 100644 --- a/js/server/js-ahuacatl.h +++ b/js/server/js-ahuacatl.h @@ -71,7 +71,7 @@ static string JS_server_ahuacatl = "////////////////////////////////////////////////////////////////////////////////\n" "\n" "function AHUACATL_INDEX (collection, indexTypes) {\n" - " var indexes = collection.getIndexes();\n" + " var indexes = collection.getIndexesNL();\n" "\n" " for (var i = 0; i < indexes.length; ++i) {\n" " var index = indexes[i];\n" @@ -386,7 +386,7 @@ static string JS_server_ahuacatl = "////////////////////////////////////////////////////////////////////////////////\n" "\n" "function AHUACATL_GET_DOCUMENTS (collection) {\n" - " return internal.db[collection].all().toArray();\n" + " return internal.db[collection].ALL_NL(0, null).documents;\n" "}\n" "\n" "////////////////////////////////////////////////////////////////////////////////\n" @@ -396,7 +396,7 @@ static string JS_server_ahuacatl = "\n" "function AHUACATL_GET_DOCUMENTS_PRIMARY (collection, idx, id) {\n" " try {\n" - " return [ internal.db[collection].document(id) ];\n" + " return [ internal.db[collection].document_nl(id) ];\n" " }\n" " catch (e) {\n" " return [ ];\n" @@ -414,7 +414,7 @@ static string JS_server_ahuacatl = " for (var i in values) {\n" " var id = values[i];\n" " try {\n" - " var d = internal.db[collection].document(id);\n" + " var d = internal.db[collection].document_nl(id);\n" " result.push(d);\n" " }\n" " catch (e) {\n" @@ -430,7 +430,7 @@ static string JS_server_ahuacatl = "////////////////////////////////////////////////////////////////////////////////\n" "\n" "function AHUACATL_GET_DOCUMENTS_HASH (collection, idx, example) {\n" - " return internal.db[collection].BY_EXAMPLE_HASH(idx, example).documents;\n" + " return internal.db[collection].BY_EXAMPLE_HASH_NL(idx, example).documents;\n" "}\n" "\n" "////////////////////////////////////////////////////////////////////////////////\n" @@ -447,7 +447,7 @@ static string JS_server_ahuacatl = "\n" " example[attribute] = value;\n" "\n" - " var documents = internal.db[collection].BY_EXAMPLE_HASH(idx, example).documents;\n" + " var documents = internal.db[collection].BY_EXAMPLE_HASH_NL(idx, example).documents;\n" " for (var j in documents) {\n" " result.push(documents[j]);\n" " }\n" @@ -461,7 +461,7 @@ static string JS_server_ahuacatl = "////////////////////////////////////////////////////////////////////////////////\n" "\n" "function AHUACATL_GET_DOCUMENTS_SKIPLIST (collection, idx, example) {\n" - " return internal.db[collection].BY_CONDITION_SKIPLIST(idx, example).documents;\n" + " return internal.db[collection].BY_CONDITION_SKIPLIST_NL(idx, example).documents;\n" "}\n" "\n" "////////////////////////////////////////////////////////////////////////////////\n" @@ -478,7 +478,7 @@ static string JS_server_ahuacatl = "\n" " example[attribute] = value;\n" "\n" - " var documents = internal.db[collection].BY_EXAMPLE_SKIPLIST(idx, example).documents;\n" + " var documents = internal.db[collection].BY_EXAMPLE_SKIPLIST_NL(idx, example).documents;\n" " for (var j in documents) {\n" " result.push(documents[j]);\n" " }\n" @@ -1851,7 +1851,7 @@ static string JS_server_ahuacatl = " AHUACATL_THROW(internal.errors.ERROR_QUERY_GEO_INDEX_MISSING, collection);\n" " }\n" "\n" - " var result = internal.db[collection].NEAR(idx, latitude, longitude, limit);\n" + " var result = internal.db[collection].NEAR_NL(idx, latitude, longitude, limit);\n" " if (distanceAttribute == null) {\n" " return result.documents;\n" " }\n" @@ -1883,7 +1883,7 @@ static string JS_server_ahuacatl = " AHUACATL_THROW(internal.errors.ERROR_QUERY_GEO_INDEX_MISSING, collection);\n" " }\n" "\n" - " var result = internal.db[collection].WITHIN(idx, latitude, longitude, radius);\n" + " var result = internal.db[collection].WITHIN_NL(idx, latitude, longitude, radius);\n" " if (distanceAttribute == null) {\n" " return result.documents;\n" " }\n" @@ -2028,7 +2028,7 @@ static string JS_server_ahuacatl = " var clonedEdges = AHUACATL_CLONE(edges);\n" " var clonedVertices = AHUACATL_CLONE(vertices);\n" " clonedEdges.push(subEdge);\n" - " clonedVertices.push(internal.db._document(targetId));\n" + " clonedVertices.push(internal.db._document_nl(targetId));\n" " \n" " var connected = AHUACATL_GRAPH_SUBNODES(searchAttributes, targetId, AHUACATL_CLONE(visited), clonedEdges, clonedVertices, level + 1);\n" " for (k = 0; k < connected.length; ++k) {\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..006f8cc 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, with a timeout +//////////////////////////////////////////////////////////////////////////////// + + bool wait (uint64_t); + +//////////////////////////////////////////////////////////////////////////////// /// @brief broadcasts an event ////////////////////////////////////////////////////////////////////////////////