mirror of https://gitee.com/bigwinds/arangodb
2058 lines
78 KiB
Diff
2058 lines
78 KiB
Diff
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<TRI_voc_did_t> 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<V8GcThread*>(_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<V8GcThread*>(_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<V8GcThread*>(_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<string, basics::ProgramOptionsDescription>& 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<string, basics::ProgramOptionsDescription>
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
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<v8::Context> _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<v8::Value> 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<v8::Value> 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<v8::Value> 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<v8::Value> 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<v8::Value> 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<v8::Value> 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<v8::Value> 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<v8::Value> 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<v8::Value> 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<v8::Value> 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<v8::Value> 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<v8::Value> EdgesQuery (TRI_edge_direction_e direction, v8::Arg
|
|
static v8::Handle<v8::Value> JS_AllQuery (v8::Arguments const& argv) {
|
|
v8::HandleScope scope;
|
|
|
|
+ LOGGER_TRACE << "executing ALL query";
|
|
// extract and use the simple collection
|
|
v8::Handle<v8::Object> err;
|
|
TRI_vocbase_col_t const* collection;
|
|
@@ -1243,10 +1271,17 @@ static v8::Handle<v8::Value> 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<v8::Value> 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<v8::Value> 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<v8::Value> 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<v8::Value> 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<v8::Value> 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<v8::Value> 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<v8::Value> 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<v8::Value> 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<v8::Value> 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<v8::Value> 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<v8::Value> 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<v8::Value> 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<v8::Value> 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<v8::Value> 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<v8::Value> 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<v8::Value> 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<v8::Value> ExecuteQueryNativeAhuacatl (TRI_aql_context_t* cons
|
|
!TRI_BindQueryContextAql(context, parameters) ||
|
|
!TRI_LockQueryContextAql(context) ||
|
|
!TRI_OptimiseQueryContextAql(context)) {
|
|
+ LOGGER_DEBUG << "executing native query failed";
|
|
v8::Handle<v8::Object> 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<v8::Object> 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<v8::Value> 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<v8::Value> ExecuteQueryCursorAhuacatl (TRI_vocbase_t* const vo
|
|
v8::HandleScope scope;
|
|
v8::TryCatch tryCatch;
|
|
|
|
+ LOGGER_DEBUG << "executing query cursor";
|
|
v8::Handle<v8::Value> 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<v8::Value> 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<v8::Object> errorObject = TRI_CreateErrorObject(TRI_ERROR_OUT_OF_MEMORY, "out of memory");
|
|
|
|
return scope.Close(v8::ThrowException(errorObject));
|
|
@@ -1073,6 +1158,7 @@ static v8::Handle<v8::Value> 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<v8::Object> errorObject = TRI_CreateErrorObject(TRI_ERROR_OUT_OF_MEMORY, "out of memory");
|
|
|
|
return scope.Close(v8::ThrowException(errorObject));
|
|
@@ -1083,6 +1169,7 @@ static v8::Handle<v8::Value> 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<v8::Object> errorObject = TRI_CreateErrorObject(TRI_ERROR_OUT_OF_MEMORY, "out of memory");
|
|
|
|
return scope.Close(v8::ThrowException(errorObject));
|
|
@@ -1091,6 +1178,7 @@ static v8::Handle<v8::Value> 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<v8::Value> 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<v8::Value> 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<v8::Value> JS_HasNextGeneralCursor (v8::Arguments const& argv)
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
+/// @brief unuse a general cursor
|
|
+////////////////////////////////////////////////////////////////////////////////
|
|
+
|
|
+static v8::Handle<v8::Value> 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<v8::Value> 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<v8::Value> 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<v8::Object> errorObject = TRI_CreateErrorObject(TRI_ERROR_OUT_OF_MEMORY, "out of memory");
|
|
|
|
return scope.Close(v8::ThrowException(errorObject));
|
|
}
|
|
|
|
v8::Handle<v8::Value> 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<v8::Array>::Cast(tryCatch.Exception())->HasOwnProperty(v8::String::New("errorNum"))) {
|
|
@@ -1798,6 +1910,8 @@ static v8::Handle<v8::Value> 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<v8::Value> 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<v8::Object> errorObject = TRI_CreateErrorObject(TRI_ERROR_OUT_OF_MEMORY, "out of memory");
|
|
|
|
return scope.Close(v8::ThrowException(errorObject));
|
|
@@ -1849,13 +1963,12 @@ static v8::Handle<v8::Value> 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<v8::Object> 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<v8::Object> errorObject = CreateErrorObjectAhuacatl(&(context.ptr())->_error);
|
|
return scope.Close(v8::ThrowException(errorObject));
|
|
}
|
|
|
|
@@ -1863,8 +1976,8 @@ static v8::Handle<v8::Value> JS_ExplainAhuacatl (v8::Arguments const& argv) {
|
|
|
|
v8::Handle<v8::Value> 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<v8::Array>::Cast(tryCatch.Exception())->HasOwnProperty(v8::String::New("errorNum"))) {
|
|
@@ -1906,17 +2019,16 @@ static v8::Handle<v8::Value> 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<v8::Object> 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<v8::Object> errorObject = CreateErrorObjectAhuacatl(&context->_error);
|
|
- TRI_FreeContextAql(context);
|
|
+ if (!TRI_ValidateQueryContextAql(context.ptr())) {
|
|
+ v8::Handle<v8::Object> errorObject = CreateErrorObjectAhuacatl(&(context.ptr())->_error);
|
|
|
|
return scope.Close(v8::ThrowException(errorObject));
|
|
}
|
|
@@ -1927,11 +2039,10 @@ static v8::Handle<v8::Value> 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<v8::Array>::Cast(tryCatch.Exception())->HasOwnProperty(v8::String::New("errorNum"))) {
|
|
// we already have an ArangoError object
|
|
@@ -2987,9 +3098,13 @@ static v8::Handle<v8::Value> 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<v8::Value> 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<v8::Value> 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<v8::Context> context, TRI_vocba
|
|
v8::Handle<v8::String> StatusFuncName = v8::Persistent<v8::String>::New(v8::String::New("status"));
|
|
v8::Handle<v8::String> TruncateDatafileFuncName = v8::Persistent<v8::String>::New(v8::String::New("truncateDatafile"));
|
|
v8::Handle<v8::String> UnloadFuncName = v8::Persistent<v8::String>::New(v8::String::New("unload"));
|
|
+ v8::Handle<v8::String> UnuseFuncName = v8::Persistent<v8::String>::New(v8::String::New("unuse"));
|
|
|
|
v8::Handle<v8::String> _CollectionFuncName = v8::Persistent<v8::String>::New(v8::String::New("_collection"));
|
|
v8::Handle<v8::String> _CollectionsFuncName = v8::Persistent<v8::String>::New(v8::String::New("_collections"));
|
|
@@ -5165,12 +5285,17 @@ TRI_v8_global_t* TRI_InitV8VocBridge (v8::Handle<v8::Context> 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<v8::ObjectTemplate>::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;
|
|
}
|
|
|