From ab6ca2f0172f2a2cb90c2938441add9965ea705b Mon Sep 17 00:00:00 2001 From: Jan Steemann Date: Thu, 16 May 2013 21:04:04 +0200 Subject: [PATCH] ported all recent fixes from 1.3 --- CHANGELOG | 16 +- UnitTests/Makefile.unittests | 1 + arangod/Utils/Transaction.h | 29 +-- arangod/V8Server/v8-query.cpp | 133 ++++++---- arangod/V8Server/v8-vocbase.cpp | 75 ++++-- arangod/V8Server/v8-vocbase.h | 5 +- arangod/VocBase/barrier.c | 25 ++ arangod/VocBase/barrier.h | 2 + arangod/VocBase/document-collection.c | 6 +- arangod/VocBase/headers.c | 68 +++++- js/server/modules/org/arangodb/graph.js | 1 + js/server/tests/transactions.js | 312 +++++++++++++++++++++++- js/server/version-check.js | 3 +- 13 files changed, 578 insertions(+), 98 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index b807dda859..1856be8e02 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -12,7 +12,21 @@ v1.4 * issue #512: Binded Parameters for LIMIT -v1.3.0 (2013-05-XX) +v1.3.1 (2013-XX-XX) +------------------- + +* set --javascript.app-path for test execution to prevent startup error + +* issue #532: Graph _edgesCache returns invalid data? + +* issue #531: Arangod errors + +* issue #529: Really weird transaction issue + +* fixed usage of --temp-path in aranogd and arangosh + + +v1.3.0 (2013-05-10) ------------------- * fixed problem on restart ("datafile-xxx is not sealed") when server was killed diff --git a/UnitTests/Makefile.unittests b/UnitTests/Makefile.unittests index 1fdd2c3890..45f4524483 100755 --- a/UnitTests/Makefile.unittests +++ b/UnitTests/Makefile.unittests @@ -80,6 +80,7 @@ SERVER_OPT := \ --database.force-sync-shapes false \ --database.force-sync-properties false \ --javascript.action-directory @top_srcdir@/js/actions \ + --javascript.app-path @top_srcdir@/js/apps \ --javascript.gc-interval 1 \ --javascript.modules-path @top_srcdir@/js/server/modules\;@top_srcdir@/js/common/modules\;@top_srcdir@/js/node \ --javascript.package-path @top_srcdir@/js/npm\;@top_srcdir@/js/common/test-data/modules \ diff --git a/arangod/Utils/Transaction.h b/arangod/Utils/Transaction.h index 14ce4c8ae1..ac65c0cd94 100644 --- a/arangod/Utils/Transaction.h +++ b/arangod/Utils/Transaction.h @@ -90,7 +90,6 @@ namespace triagens { T(), _setupState(TRI_ERROR_NO_ERROR), _nestingLevel(0), - _readOnly(true), _hints(0), _timeout(0.0), _waitForSync(false), @@ -141,6 +140,14 @@ namespace triagens { public: +//////////////////////////////////////////////////////////////////////////////// +/// @brief return the collection name resolver +//////////////////////////////////////////////////////////////////////////////// + + const CollectionNameResolver& resolver () const { + return _resolver; + } + //////////////////////////////////////////////////////////////////////////////// /// @brief whether or not the transaction is embedded //////////////////////////////////////////////////////////////////////////////// @@ -150,11 +157,15 @@ namespace triagens { } //////////////////////////////////////////////////////////////////////////////// -/// @brief return whether or not the transaction is read-only +/// @brief whether or not shaped json in this trx should be copied //////////////////////////////////////////////////////////////////////////////// - inline bool isReadOnlyTransaction () const { - return _readOnly; + inline bool mustCopyShapedJson () const { + if (_trx != 0 && _trx->_hasOperations) { + return true; + } + + return false; } //////////////////////////////////////////////////////////////////////////////// @@ -312,10 +323,6 @@ namespace triagens { res = this->addCollectionToplevel(cid, type); } - if (type == TRI_TRANSACTION_WRITE) { - _readOnly = false; - } - return res; } @@ -992,12 +999,6 @@ namespace triagens { int _nestingLevel; -//////////////////////////////////////////////////////////////////////////////// -/// @brief whether or not the transaction is read-only -//////////////////////////////////////////////////////////////////////////////// - - bool _readOnly; - //////////////////////////////////////////////////////////////////////////////// /// @brief transaction hints //////////////////////////////////////////////////////////////////////////////// diff --git a/arangod/V8Server/v8-query.cpp b/arangod/V8Server/v8-query.cpp index 5bdcd34634..4f0ce7b277 100644 --- a/arangod/V8Server/v8-query.cpp +++ b/arangod/V8Server/v8-query.cpp @@ -50,6 +50,31 @@ using namespace std; using namespace triagens::basics; using namespace triagens::arango; +// ----------------------------------------------------------------------------- +// --SECTION-- private defines +// ----------------------------------------------------------------------------- + +//////////////////////////////////////////////////////////////////////////////// +/// @addtogroup VocBase +/// @{ +//////////////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////////////// +/// @brief shortcut for read-only transaction class type +//////////////////////////////////////////////////////////////////////////////// + +#define ReadTransactionType SingleCollectionReadOnlyTransaction > + +//////////////////////////////////////////////////////////////////////////////// +/// @brief shortcut to wrap a shaped-json object in a read-only transaction +//////////////////////////////////////////////////////////////////////////////// + +#define WRAP_SHAPED_JSON(...) TRI_WrapShapedJson(__VA_ARGS__) + +//////////////////////////////////////////////////////////////////////////////// +/// @} +//////////////////////////////////////////////////////////////////////////////// + // ----------------------------------------------------------------------------- // --SECTION-- HELPER FUNCTIONS // ----------------------------------------------------------------------------- @@ -985,7 +1010,7 @@ static v8::Handle ExecuteSkiplistQuery (v8::Arguments const& argv, } CollectionNameResolver resolver(col->_vocbase); - SingleCollectionReadOnlyTransaction > trx(col->_vocbase, resolver, col->_cid); + ReadTransactionType trx(col->_vocbase, resolver, col->_cid); int res = trx.begin(); if (res != TRI_ERROR_NO_ERROR) { @@ -1065,7 +1090,7 @@ static v8::Handle ExecuteSkiplistQuery (v8::Arguments const& argv, } } - v8::Handle doc = TRI_WrapShapedJson(resolver, col, (TRI_doc_mptr_t const*) indexElement->_document, barrier); + v8::Handle doc = WRAP_SHAPED_JSON(trx, col->_cid, (TRI_doc_mptr_t const*) indexElement->_document, barrier); if (doc.IsEmpty()) { error = true; @@ -1175,7 +1200,7 @@ static v8::Handle ExecuteBitarrayQuery (v8::Arguments const& argv, } CollectionNameResolver resolver(col->_vocbase); - SingleCollectionReadOnlyTransaction > trx(col->_vocbase, resolver, col->_cid); + ReadTransactionType trx(col->_vocbase, resolver, col->_cid); int res = trx.begin(); @@ -1267,13 +1292,14 @@ static v8::Handle ExecuteBitarrayQuery (v8::Arguments const& argv, if (total > skip && count < limit) { if (barrier == 0) { barrier = TRI_CreateBarrierElement(&primary->_barrierList); + if (barrier == 0) { error = true; break; } } - v8::Handle doc = TRI_WrapShapedJson(resolver, col, data, barrier); + v8::Handle doc = WRAP_SHAPED_JSON(trx, col->_cid, data, barrier); if (doc.IsEmpty()) { error = true; @@ -1355,7 +1381,8 @@ static uint32_t SortGeoRandomGenerator (void) { /// @brief creates a geo result //////////////////////////////////////////////////////////////////////////////// -static int StoreGeoResult (TRI_vocbase_col_t const* collection, +static int StoreGeoResult (ReadTransactionType& trx, + TRI_vocbase_col_t const* collection, GeoCoordinates* cors, v8::Handle& documents, v8::Handle& distances) { @@ -1408,11 +1435,9 @@ static int StoreGeoResult (TRI_vocbase_col_t const* collection, return TRI_ERROR_OUT_OF_MEMORY; } - CollectionNameResolver resolver(collection->_vocbase); - // copy the documents for (gtr = tmp, i = 0; gtr < gnd; ++gtr, ++i) { - documents->Set(i, TRI_WrapShapedJson(resolver, collection, (TRI_doc_mptr_t const*) gtr->_data, barrier)); + documents->Set(i, WRAP_SHAPED_JSON(trx, collection->_cid, (TRI_doc_mptr_t const*) gtr->_data, barrier)); distances->Set(i, v8::Number::New(gtr->_distance)); } @@ -1457,7 +1482,7 @@ static v8::Handle EdgesQuery (TRI_edge_direction_e direction, v8::Arg } CollectionNameResolver resolver(col->_vocbase); - SingleCollectionReadOnlyTransaction > trx(col->_vocbase, resolver, col->_cid); + ReadTransactionType trx(col->_vocbase, resolver, col->_cid); int res = trx.begin(); @@ -1538,7 +1563,7 @@ static v8::Handle EdgesQuery (TRI_edge_direction_e direction, v8::Arg } } - v8::Handle doc = TRI_WrapShapedJson(resolver, col, (TRI_doc_mptr_t const*) edges._buffer[j], barrier); + v8::Handle doc = WRAP_SHAPED_JSON(trx, col->_cid, (TRI_doc_mptr_t const*) edges._buffer[j], barrier); if (doc.IsEmpty()) { // error @@ -1596,7 +1621,7 @@ static v8::Handle EdgesQuery (TRI_edge_direction_e direction, v8::Arg } } - v8::Handle doc = TRI_WrapShapedJson(resolver, col, (TRI_doc_mptr_t const*) edges._buffer[j], barrier); + v8::Handle doc = WRAP_SHAPED_JSON(trx, col->_cid, (TRI_doc_mptr_t const*) edges._buffer[j], barrier); if (doc.IsEmpty()) { error = true; @@ -1666,7 +1691,7 @@ static v8::Handle JS_AllQuery (v8::Arguments const& argv) { vector docs; CollectionNameResolver resolver(col->_vocbase); - SingleCollectionReadOnlyTransaction > trx(col->_vocbase, resolver, col->_cid); + ReadTransactionType trx(col->_vocbase, resolver, col->_cid); int res = trx.begin(); @@ -1691,11 +1716,11 @@ static v8::Handle JS_AllQuery (v8::Arguments const& argv) { // setup result v8::Handle result = v8::Object::New(); v8::Handle documents = v8::Array::New(n); - // reserver full capacity in one go + // reserve full capacity in one go result->Set(v8::String::New("documents"), documents); for (size_t i = 0; i < n; ++i) { - v8::Handle document = TRI_WrapShapedJson(resolver, col, &docs[i], barrier); + v8::Handle document = WRAP_SHAPED_JSON(trx, col->_cid, &docs[i], barrier); if (document.IsEmpty()) { TRI_V8_EXCEPTION_MEMORY(scope); @@ -1743,7 +1768,8 @@ static v8::Handle JS_AnyQuery (v8::Arguments const& argv) { document._key = 0; CollectionNameResolver resolver(col->_vocbase); - SingleCollectionReadOnlyTransaction > trx(col->_vocbase, resolver, col->_cid); + ReadTransactionType trx(col->_vocbase, resolver, col->_cid); + int res = trx.begin(); if (res != TRI_ERROR_NO_ERROR) { @@ -1768,7 +1794,7 @@ static v8::Handle JS_AnyQuery (v8::Arguments const& argv) { return scope.Close(v8::Null()); } - return scope.Close(TRI_WrapShapedJson(resolver, col, &document, barrier)); + return scope.Close(WRAP_SHAPED_JSON(trx, col->_cid, &document, barrier)); } //////////////////////////////////////////////////////////////////////////////// @@ -1797,7 +1823,7 @@ static v8::Handle JS_ByExampleQuery (v8::Arguments const& argv) { } CollectionNameResolver resolver(col->_vocbase); - SingleCollectionReadOnlyTransaction > trx(col->_vocbase, resolver, col->_cid); + ReadTransactionType trx(col->_vocbase, resolver, col->_cid); int res = trx.begin(); @@ -1861,7 +1887,8 @@ static v8::Handle JS_ByExampleQuery (v8::Arguments const& argv) { else { for (size_t j = s; j < e; ++j) { TRI_doc_mptr_t* mptr = (TRI_doc_mptr_t*) TRI_AtVector(&filtered, j); - v8::Handle doc = TRI_WrapShapedJson(resolver, col, mptr, barrier); + + v8::Handle doc = WRAP_SHAPED_JSON(trx, col->_cid, mptr, barrier); if (doc.IsEmpty()) { error = true; @@ -1903,7 +1930,7 @@ static v8::Handle JS_ByExampleQuery (v8::Arguments const& argv) { /// It is the callers responsibility to acquire and free the required locks //////////////////////////////////////////////////////////////////////////////// -static v8::Handle ByExampleHashIndexQuery (TRI_document_collection_t* document, +static v8::Handle ByExampleHashIndexQuery (ReadTransactionType& trx, TRI_vocbase_col_t const* collection, v8::Handle* err, v8::Arguments const& argv) { @@ -1934,8 +1961,7 @@ static v8::Handle ByExampleHashIndexQuery (TRI_document_collection_t* result->Set(v8::String::New("documents"), documents); // extract the index - CollectionNameResolver resolver(collection->_vocbase); - TRI_index_t* idx = TRI_LookupIndexByHandle(resolver, collection, argv[0], false, err); + TRI_index_t* idx = TRI_LookupIndexByHandle(trx.resolver(), collection, argv[0], false, err); if (idx == 0) { return scope.Close(v8::ThrowException(*err)); @@ -1949,8 +1975,9 @@ static v8::Handle ByExampleHashIndexQuery (TRI_document_collection_t* // convert the example (index is locked by lockRead) TRI_index_search_value_t searchValue; - - TRI_shaper_t* shaper = document->base._shaper; + + TRI_primary_collection_t* primary = trx.primaryCollection(); + TRI_shaper_t* shaper = primary->_shaper; int res = SetupSearchValue(&hashIndex->_paths, example, shaper, searchValue, err); if (res != TRI_ERROR_NO_ERROR) { @@ -1973,13 +2000,13 @@ static v8::Handle ByExampleHashIndexQuery (TRI_document_collection_t* CalculateSkipLimitSlice(total, skip, limit, s, e); if (s < e) { - TRI_barrier_t* barrier = TRI_CreateBarrierElement(&document->base._barrierList); + TRI_barrier_t* barrier = TRI_CreateBarrierElement(&primary->_barrierList); if (barrier == 0) { error = true; } else { for (size_t i = s; i < e; ++i) { - v8::Handle doc = TRI_WrapShapedJson(resolver, collection, list._documents[i], barrier); + v8::Handle doc = WRAP_SHAPED_JSON(trx, collection->_cid, list._documents[i], barrier); if (doc.IsEmpty()) { error = true; @@ -2023,7 +2050,8 @@ static v8::Handle JS_ByExampleHashIndex (v8::Arguments const& argv) { } CollectionNameResolver resolver(col->_vocbase); - SingleCollectionReadOnlyTransaction > trx(col->_vocbase, resolver, col->_cid); + ReadTransactionType trx(col->_vocbase, resolver, col->_cid); + int res = trx.begin(); if (res != TRI_ERROR_NO_ERROR) { @@ -2038,7 +2066,7 @@ static v8::Handle JS_ByExampleHashIndex (v8::Arguments const& argv) { trx.lockRead(); - v8::Handle result = ByExampleHashIndexQuery((TRI_document_collection_t*) trx.primaryCollection(), col, &err, argv); + v8::Handle result = ByExampleHashIndexQuery(trx, col, &err, argv); trx.finish(res); @@ -2139,7 +2167,7 @@ static v8::Handle JS_InEdgesQuery (v8::Arguments const& argv) { /// the caller must ensure all relevant locks are acquired and freed //////////////////////////////////////////////////////////////////////////////// -static v8::Handle FulltextQuery (TRI_document_collection_t* document, +static v8::Handle FulltextQuery (ReadTransactionType& trx, TRI_vocbase_col_t const* collection, v8::Handle* err, v8::Arguments const& argv) { @@ -2151,8 +2179,7 @@ static v8::Handle FulltextQuery (TRI_document_collection_t* document, } // extract the index - CollectionNameResolver resolver(collection->_vocbase); - TRI_index_t* idx = TRI_LookupIndexByHandle(resolver, collection, argv[0], false, err); + TRI_index_t* idx = TRI_LookupIndexByHandle(trx.resolver(), collection, argv[0], false, err); if (idx == 0) { return scope.Close(v8::ThrowException(*err)); @@ -2206,7 +2233,7 @@ static v8::Handle FulltextQuery (TRI_document_collection_t* document, result->Set(v8::String::New("documents"), documents); for (uint32_t i = 0; i < queryResult->_numDocuments; ++i) { - documents->Set(i, TRI_WrapShapedJson(resolver, collection, (TRI_doc_mptr_t const*) queryResult->_documents[i], barrier)); + documents->Set(i, WRAP_SHAPED_JSON(trx, collection->_cid, (TRI_doc_mptr_t const*) queryResult->_documents[i], barrier)); } TRI_FreeResultFulltextIndex(queryResult); @@ -2247,7 +2274,8 @@ static v8::Handle JS_FulltextQuery (v8::Arguments const& argv) { } CollectionNameResolver resolver(col->_vocbase); - SingleCollectionReadOnlyTransaction > trx(col->_vocbase, resolver, col->_cid); + ReadTransactionType trx(col->_vocbase, resolver, col->_cid); + int res = trx.begin(); if (res != TRI_ERROR_NO_ERROR) { @@ -2262,7 +2290,7 @@ static v8::Handle JS_FulltextQuery (v8::Arguments const& argv) { trx.lockRead(); - v8::Handle result = FulltextQuery((TRI_document_collection_t*) trx.primaryCollection(), col, &err, argv); + v8::Handle result = FulltextQuery(trx, col, &err, argv); trx.finish(res); @@ -2279,7 +2307,7 @@ static v8::Handle JS_FulltextQuery (v8::Arguments const& argv) { /// the caller must ensure all relevant locks are acquired and freed //////////////////////////////////////////////////////////////////////////////// -static v8::Handle NearQuery (TRI_document_collection_t* document, +static v8::Handle NearQuery (ReadTransactionType& trx, TRI_vocbase_col_t const* collection, v8::Handle* err, v8::Arguments const& argv) { @@ -2291,8 +2319,7 @@ static v8::Handle NearQuery (TRI_document_collection_t* document, } // extract the index - CollectionNameResolver resolver(collection->_vocbase); - TRI_index_t* idx = TRI_LookupIndexByHandle(resolver, collection, argv[0], false, err); + TRI_index_t* idx = TRI_LookupIndexByHandle(trx.resolver(), collection, argv[0], false, err); if (idx == 0) { return scope.Close(v8::ThrowException(*err)); @@ -2321,7 +2348,7 @@ static v8::Handle NearQuery (TRI_document_collection_t* document, GeoCoordinates* cors = TRI_NearestGeoIndex(idx, latitude, longitude, limit); if (cors != 0) { - int res = StoreGeoResult(collection, cors, documents, distances); + int res = StoreGeoResult(trx, collection, cors, documents, distances); if (res != TRI_ERROR_NO_ERROR) { TRI_V8_EXCEPTION_MESSAGE(scope, res, "cannot add document to geo-index"); @@ -2346,7 +2373,8 @@ static v8::Handle JS_NearQuery (v8::Arguments const& argv) { } CollectionNameResolver resolver(col->_vocbase); - SingleCollectionReadOnlyTransaction > trx(col->_vocbase, resolver, col->_cid); + ReadTransactionType trx(col->_vocbase, resolver, col->_cid); + int res = trx.begin(); if (res != TRI_ERROR_NO_ERROR) { @@ -2361,7 +2389,7 @@ static v8::Handle JS_NearQuery (v8::Arguments const& argv) { trx.lockRead(); - v8::Handle result = NearQuery((TRI_document_collection_t*) trx.primaryCollection(), col, &err, argv); + v8::Handle result = NearQuery(trx, col, &err, argv); trx.finish(res); @@ -2413,7 +2441,7 @@ static v8::Handle JS_TopQuery (v8::Arguments const& argv) { } CollectionNameResolver resolver(col->_vocbase); - SingleCollectionReadOnlyTransaction > trx(col->_vocbase, resolver, col->_cid); + ReadTransactionType trx(col->_vocbase, resolver, col->_cid); int res = trx.begin(); @@ -2442,10 +2470,15 @@ static v8::Handle JS_TopQuery (v8::Arguments const& argv) { } TRI_barrier_t* barrier = TRI_CreateBarrierElement(&((TRI_primary_collection_t*) col->_collection)->_barrierList); - v8::Handle result = TRI_WrapShapedJson(resolver, - col, - (TRI_doc_mptr_t const*) elms->_elements[0]._document, - barrier); + + if (barrier == 0) { + TRI_V8_EXCEPTION_MEMORY(scope); + } + + v8::Handle result = WRAP_SHAPED_JSON(trx, + col->_cid, + (TRI_doc_mptr_t const*) elms->_elements[0]._document, + barrier); TRI_Free(TRI_UNKNOWN_MEM_ZONE, elms->_elements); @@ -2459,7 +2492,7 @@ static v8::Handle JS_TopQuery (v8::Arguments const& argv) { /// the caller must ensure all relevant locks are acquired and freed //////////////////////////////////////////////////////////////////////////////// -static v8::Handle WithinQuery (TRI_document_collection_t* document, +static v8::Handle WithinQuery (ReadTransactionType& trx, TRI_vocbase_col_t const* collection, v8::Handle* err, v8::Arguments const& argv) { @@ -2471,8 +2504,7 @@ static v8::Handle WithinQuery (TRI_document_collection_t* document, } // extract the index - CollectionNameResolver resolver(collection->_vocbase); - TRI_index_t* idx = TRI_LookupIndexByHandle(resolver, collection, argv[0], false, err); + TRI_index_t* idx = TRI_LookupIndexByHandle(trx.resolver(), collection, argv[0], false, err); if (idx == 0) { return scope.Close(v8::ThrowException(*err)); @@ -2501,7 +2533,7 @@ static v8::Handle WithinQuery (TRI_document_collection_t* document, GeoCoordinates* cors = TRI_WithinGeoIndex(idx, latitude, longitude, radius); if (cors != 0) { - int res = StoreGeoResult(collection, cors, documents, distances); + int res = StoreGeoResult(trx, collection, cors, documents, distances); if (res != TRI_ERROR_NO_ERROR) { TRI_V8_EXCEPTION_MESSAGE(scope, res, "cannot add document to geo-index"); @@ -2526,7 +2558,8 @@ static v8::Handle JS_WithinQuery (v8::Arguments const& argv) { } CollectionNameResolver resolver(col->_vocbase); - SingleCollectionReadOnlyTransaction > trx(col->_vocbase, resolver, col->_cid); + ReadTransactionType trx(col->_vocbase, resolver, col->_cid); + int res = trx.begin(); if (res != TRI_ERROR_NO_ERROR) { @@ -2541,7 +2574,7 @@ static v8::Handle JS_WithinQuery (v8::Arguments const& argv) { trx.lockRead(); - v8::Handle result = WithinQuery((TRI_document_collection_t*) trx.primaryCollection(), col, &err, argv); + v8::Handle result = WithinQuery(trx, col, &err, argv); trx.finish(res); diff --git a/arangod/V8Server/v8-vocbase.cpp b/arangod/V8Server/v8-vocbase.cpp index cc1beba57e..dbe041ae76 100644 --- a/arangod/V8Server/v8-vocbase.cpp +++ b/arangod/V8Server/v8-vocbase.cpp @@ -89,6 +89,12 @@ static v8::Handle WrapGeneralCursor (void* cursor); /// @{ //////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +/// @brief shortcut for read-only transaction class type +//////////////////////////////////////////////////////////////////////////////// + +#define ReadTransactionType SingleCollectionReadOnlyTransaction > + //////////////////////////////////////////////////////////////////////////////// /// @brief macro to make sure we won't continue if we are inside a transaction //////////////////////////////////////////////////////////////////////////////// @@ -871,7 +877,8 @@ static v8::Handle DocumentVocbaseCol (const bool useCollection, assert(col); assert(key); - SingleCollectionReadOnlyTransaction > trx(vocbase, resolver, col->_cid); + ReadTransactionType trx(vocbase, resolver, col->_cid); + int res = trx.begin(); if (res != TRI_ERROR_NO_ERROR) { @@ -893,7 +900,8 @@ static v8::Handle DocumentVocbaseCol (const bool useCollection, res = trx.read(&document, key); if (res == TRI_ERROR_NO_ERROR) { - result = TRI_WrapShapedJson(resolver, col, &document, barrier); + result = TRI_WrapShapedJson(trx, col->_cid, &document, barrier); + if (! result.IsEmpty()) { freeBarrier = false; } @@ -3438,7 +3446,8 @@ static v8::Handle JS_CountVocbaseCol (v8::Arguments const& argv) { } CollectionNameResolver resolver(collection->_vocbase); - SingleCollectionReadOnlyTransaction > trx(collection->_vocbase, resolver, collection->_cid); + ReadTransactionType trx(collection->_vocbase, resolver, collection->_cid); + int res = trx.begin(); if (res != TRI_ERROR_NO_ERROR) { @@ -4515,7 +4524,8 @@ static v8::Handle JS_FiguresVocbaseCol (v8::Arguments const& argv) { v8::Handle result = v8::Object::New(); CollectionNameResolver resolver(collection->_vocbase); - SingleCollectionReadOnlyTransaction > trx(collection->_vocbase, resolver, collection->_cid); + ReadTransactionType trx(collection->_vocbase, resolver, collection->_cid); + int res = trx.begin(); if (res != TRI_ERROR_NO_ERROR) { @@ -4603,7 +4613,8 @@ static v8::Handle JS_GetIndexesVocbaseCol (v8::Arguments const& argv) } CollectionNameResolver resolver(collection->_vocbase); - SingleCollectionReadOnlyTransaction > trx(collection->_vocbase, resolver, collection->_cid); + ReadTransactionType trx(collection->_vocbase, resolver, collection->_cid); + int res = trx.begin(); if (res != TRI_ERROR_NO_ERROR) { @@ -5441,7 +5452,8 @@ static v8::Handle JS_RevisionVocbaseCol (v8::Arguments const& argv) { } CollectionNameResolver resolver(collection->_vocbase); - SingleCollectionReadOnlyTransaction > trx(collection->_vocbase, resolver, collection->_cid); + ReadTransactionType trx(collection->_vocbase, resolver, collection->_cid); + int res = trx.begin(); if (res != TRI_ERROR_NO_ERROR) { @@ -6257,9 +6269,8 @@ static v8::Handle JS_VersionVocbase (v8::Arguments const& argv) { static void WeakBarrierCallback (v8::Isolate* isolate, v8::Persistent object, void* parameter) { - TRI_barrier_t* barrier; TRI_v8_global_t* v8g = (TRI_v8_global_t*) isolate->GetData(); - barrier = (TRI_barrier_t*) parameter; + TRI_barrier_blocker_t* barrier = (TRI_barrier_blocker_t*) parameter; LOG_TRACE("weak-callback for barrier called"); @@ -6267,13 +6278,12 @@ static void WeakBarrierCallback (v8::Isolate* isolate, v8::Persistent persistent = v8g->JSBarriers[barrier]; v8g->JSBarriers.erase(barrier); - // dispose and clear the persistent handle persistent.Dispose(isolate); persistent.Clear(); // free the barrier - TRI_FreeBarrier(barrier); + TRI_FreeBarrier(&barrier->base); } //////////////////////////////////////////////////////////////////////////////// @@ -6705,15 +6715,16 @@ v8::Handle TRI_WrapCollection (TRI_vocbase_col_t const* collection) /// @brief wraps a TRI_shaped_json_t //////////////////////////////////////////////////////////////////////////////// -v8::Handle TRI_WrapShapedJson (const CollectionNameResolver& resolver, - TRI_vocbase_col_t const* collection, +template +v8::Handle TRI_WrapShapedJson (T& trx, + TRI_voc_cid_t cid, TRI_doc_mptr_t const* document, TRI_barrier_t* barrier) { v8::HandleScope scope; TRI_ASSERT_MAINTAINER(document != 0); TRI_ASSERT_MAINTAINER(document->_key != 0); - TRI_ASSERT_MAINTAINER(collection != 0); + TRI_ASSERT_MAINTAINER(document->_data != 0); TRI_ASSERT_MAINTAINER(barrier != 0); v8::Isolate* isolate = v8::Isolate::GetCurrent(); @@ -6728,9 +6739,39 @@ v8::Handle TRI_WrapShapedJson (const CollectionNameResolver& resolver return scope.Close(result); } + TRI_barrier_blocker_t* blocker = (TRI_barrier_blocker_t*) barrier; + bool doCopy = trx.mustCopyShapedJson(); + + if (doCopy) { + // we'll create our own copy of the data + TRI_df_marker_t const* m = static_cast(document->_data); + + if (blocker->_data != NULL && blocker->_mustFree) { + TRI_Free(TRI_UNKNOWN_MEM_ZONE, blocker->_data); + blocker->_data = NULL; + blocker->_mustFree = false; + } + + blocker->_data = TRI_Allocate(TRI_UNKNOWN_MEM_ZONE, m->_size, false); + + if (blocker->_data == 0) { + // out of memory + return scope.Close(result); + } + + memcpy(blocker->_data, m, m->_size); + + blocker->_mustFree = true; + } + else { + // we'll use the pointer into the datafile + blocker->_data = const_cast(document->_data); + } + + // point the 0 index Field to the c++ pointer for unwrapping later result->SetInternalField(SLOT_CLASS_TYPE, v8::Integer::New(WRP_SHAPED_JSON_TYPE)); - result->SetInternalField(SLOT_CLASS, v8::External::New(const_cast(document->_data))); + result->SetInternalField(SLOT_CLASS, v8::External::New(blocker->_data)); map< void*, v8::Persistent >::iterator i = v8g->JSBarriers.find(barrier); @@ -6748,7 +6789,7 @@ v8::Handle TRI_WrapShapedJson (const CollectionNameResolver& resolver // store the document reference TRI_voc_rid_t rid = document->_rid; - result->Set(v8g->_IdKey, V8DocumentId(resolver.getCollectionName(collection->_cid), document->_key), v8::ReadOnly); + result->Set(v8g->_IdKey, V8DocumentId(trx.resolver().getCollectionName(cid), document->_key), v8::ReadOnly); result->Set(v8g->_RevKey, V8RevisionId(rid), v8::ReadOnly); result->Set(v8g->_KeyKey, v8::String::New(document->_key), v8::ReadOnly); @@ -6757,8 +6798,8 @@ v8::Handle TRI_WrapShapedJson (const CollectionNameResolver& resolver if (type == TRI_DOC_MARKER_KEY_EDGE) { TRI_doc_edge_key_marker_t* marker = (TRI_doc_edge_key_marker_t*) document->_data; - result->Set(v8g->_FromKey, V8DocumentId(resolver.getCollectionName(marker->_fromCid), ((char*) marker) + marker->_offsetFromKey)); - result->Set(v8g->_ToKey, V8DocumentId(resolver.getCollectionName(marker->_toCid), ((char*) marker) + marker->_offsetToKey)); + result->Set(v8g->_FromKey, V8DocumentId(trx.resolver().getCollectionName(marker->_fromCid), ((char*) marker) + marker->_offsetFromKey)); + result->Set(v8g->_ToKey, V8DocumentId(trx.resolver().getCollectionName(marker->_toCid), ((char*) marker) + marker->_offsetToKey)); } // and return diff --git a/arangod/V8Server/v8-vocbase.h b/arangod/V8Server/v8-vocbase.h index ebe4586ea9..c25e886ff6 100644 --- a/arangod/V8Server/v8-vocbase.h +++ b/arangod/V8Server/v8-vocbase.h @@ -78,8 +78,9 @@ v8::Handle TRI_WrapCollection (TRI_vocbase_col_t const*); /// @brief wraps a TRI_shaped_json_t //////////////////////////////////////////////////////////////////////////////// -v8::Handle TRI_WrapShapedJson (const triagens::arango::CollectionNameResolver&, - TRI_vocbase_col_t const*, +template +v8::Handle TRI_WrapShapedJson (T&, + TRI_voc_cid_t, TRI_doc_mptr_t const*, TRI_barrier_t*); diff --git a/arangod/VocBase/barrier.c b/arangod/VocBase/barrier.c index ee57e7617b..4f54c3a362 100644 --- a/arangod/VocBase/barrier.c +++ b/arangod/VocBase/barrier.c @@ -43,6 +43,22 @@ /// @{ //////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +/// @brief free data associated with a barrier +/// currently only barriers pointing to shaped json might contain data that +/// needs to be freed. +//////////////////////////////////////////////////////////////////////////////// + +static void FreeDataBarrier (TRI_barrier_t* element) { + if (element->_type == TRI_BARRIER_ELEMENT) { + TRI_barrier_blocker_t* b = (TRI_barrier_blocker_t*) element; + + if (b->_data != NULL && b->_mustFree) { + TRI_Free(TRI_UNKNOWN_MEM_ZONE, b->_data); + } + } +} + //////////////////////////////////////////////////////////////////////////////// /// @brief inserts the barrier element into the linked list of barrier elemnents /// of the collection @@ -121,6 +137,8 @@ void TRI_DestroyBarrierList (TRI_barrier_list_t* container) { ptr->_type == TRI_BARRIER_COLLECTION_COMPACTION) { // free data still allocated in barrier elements + FreeDataBarrier(ptr); + TRI_Free(TRI_UNKNOWN_MEM_ZONE, ptr); } else if (ptr->_type == TRI_BARRIER_ELEMENT) { @@ -150,6 +168,7 @@ bool TRI_ContainsBarrierList (TRI_barrier_list_t* container, TRI_barrier_type_e while (ptr != NULL) { if (ptr->_type == type) { TRI_UnlockSpin(&container->_lock); + return true; } ptr = ptr->_next; @@ -176,6 +195,8 @@ TRI_barrier_t* TRI_CreateBarrierElementZ (TRI_barrier_list_t* container, } element->base._type = TRI_BARRIER_ELEMENT; + element->_data = NULL; + element->_mustFree = false; element->_line = line; element->_filename = filename; @@ -350,6 +371,10 @@ void TRI_FreeBarrier (TRI_barrier_t* element) { } TRI_UnlockSpin(&container->_lock); + + // free data contained in the element + // currently, only barriers of type ELEMENT contain data that needs freeing + FreeDataBarrier(element); // free the element TRI_Free(TRI_UNKNOWN_MEM_ZONE, element); diff --git a/arangod/VocBase/barrier.h b/arangod/VocBase/barrier.h index 984866559c..8fd22a63f3 100644 --- a/arangod/VocBase/barrier.h +++ b/arangod/VocBase/barrier.h @@ -86,6 +86,8 @@ TRI_barrier_t; typedef struct TRI_barrier_blocker_s { TRI_barrier_t base; + void* _data; + bool _mustFree; size_t _line; char const* _filename; } diff --git a/arangod/VocBase/document-collection.c b/arangod/VocBase/document-collection.c index 788118bf86..18db7b1eef 100644 --- a/arangod/VocBase/document-collection.c +++ b/arangod/VocBase/document-collection.c @@ -758,6 +758,9 @@ static int RollbackUpdate (TRI_document_collection_t* document, // ignore any errors we're getting from this DeleteSecondaryIndexes(document, newHeader, true); + + // put back the header into its old position + document->_headers->move(document->_headers, newHeader, oldHeader); *newHeader = *oldHeader; @@ -767,9 +770,6 @@ static int RollbackUpdate (TRI_document_collection_t* document, LOG_ERROR("error rolling back update operation"); } - // put back the header into its old position - document->_headers->move(document->_headers, newHeader, newHeader); - return res; } diff --git a/arangod/VocBase/headers.c b/arangod/VocBase/headers.c index 1507a2a416..40b130bea2 100644 --- a/arangod/VocBase/headers.c +++ b/arangod/VocBase/headers.c @@ -128,6 +128,8 @@ static void MoveBackHeader (TRI_headers_t* h, // we have at least one element in the list TRI_ASSERT_MAINTAINER(headers->_begin != NULL); TRI_ASSERT_MAINTAINER(headers->_end != NULL); + TRI_ASSERT_MAINTAINER(header->_prev != header); + TRI_ASSERT_MAINTAINER(header->_next != header); if (headers->_end == header) { // header is already at the end @@ -157,6 +159,8 @@ static void MoveBackHeader (TRI_headers_t* h, TRI_ASSERT_MAINTAINER(headers->_begin != NULL); TRI_ASSERT_MAINTAINER(headers->_end != NULL); + TRI_ASSERT_MAINTAINER(header->_prev != header); + TRI_ASSERT_MAINTAINER(header->_next != header); } //////////////////////////////////////////////////////////////////////////////// @@ -167,6 +171,12 @@ static void UnlinkHeader (TRI_headers_t* h, TRI_doc_mptr_t* header) { simple_headers_t* headers = (simple_headers_t*) h; +//printf("UNLINK\n-------------\n"); +//h->dump(h); + TRI_ASSERT_MAINTAINER(header != NULL); + TRI_ASSERT_MAINTAINER(header->_prev != header); + TRI_ASSERT_MAINTAINER(header->_next != header); + // unlink the header if (header->_prev != NULL) { header->_prev->_next = header->_next; @@ -199,10 +209,13 @@ static void UnlinkHeader (TRI_headers_t* h, TRI_ASSERT_MAINTAINER(headers->_begin != NULL); TRI_ASSERT_MAINTAINER(headers->_end != NULL); } + + TRI_ASSERT_MAINTAINER(header->_prev != header); + TRI_ASSERT_MAINTAINER(header->_next != header); } //////////////////////////////////////////////////////////////////////////////// -/// @brief moves a header back into the list, using its previous position +/// @brief moves a header around in the list, using its previous position /// (specified in "old") //////////////////////////////////////////////////////////////////////////////// @@ -216,26 +229,61 @@ static void MoveHeader (TRI_headers_t* h, } TRI_ASSERT_MAINTAINER(headers->_nrAllocated > 0); + TRI_ASSERT_MAINTAINER(header->_prev != header); + TRI_ASSERT_MAINTAINER(header->_next != header); + + // adjust list start and end pointers + if (old->_prev == NULL) { + headers->_begin = header; + } + if (old->_next == NULL) { + headers->_end = header; + } + if (header->_prev != NULL && header->_prev == old->_next) { + header->_prev->_next = NULL; + headers->_end = header->_prev; + } + else if (header->_next != NULL && header->_next == old->_prev) { + header->_next->_prev = NULL; + headers->_begin = header->_next; + } +/* if (headers->_begin == old->_next) { // adjust list start pointer headers->_begin = header; } - +*/ +/* if (old->_next == NULL) { // adjust list end pointer headers->_end = header; } +*/ if (old->_prev != NULL) { old->_prev->_next = header; + header->_prev = old->_prev; + } + else { + header->_prev = NULL; } if (old->_next != NULL) { old->_next->_prev = header; + header->_next = old->_next; } - + else { + header->_next = NULL; + } +/* header->_prev = old->_prev; header->_next = old->_next; + */ + + TRI_ASSERT_MAINTAINER(headers->_begin != NULL); + TRI_ASSERT_MAINTAINER(headers->_end != NULL); + TRI_ASSERT_MAINTAINER(header->_prev != header); + TRI_ASSERT_MAINTAINER(header->_next != header); } //////////////////////////////////////////////////////////////////////////////// @@ -257,6 +305,9 @@ static void RelinkHeader (TRI_headers_t* h, MoveHeader(h, header, old); headers->_nrLinked++; + + TRI_ASSERT_MAINTAINER(header->_prev != header); + TRI_ASSERT_MAINTAINER(header->_next != header); } //////////////////////////////////////////////////////////////////////////////// @@ -402,13 +453,13 @@ static void DumpHeaders (TRI_headers_t const* h) { TRI_doc_mptr_t* next = headers->_begin; size_t i = 0; - LOG_TRACE("number of allocated headers: %lu\n", (unsigned long) headers->_nrAllocated); - LOG_TRACE("number of linked headers: %lu\n", (unsigned long) headers->_nrLinked); - LOG_TRACE("begin ptr: %p\n", headers->_begin); - LOG_TRACE("end ptr: %p\n", headers->_end); + printf("number of allocated headers: %lu\n", (unsigned long) headers->_nrAllocated); + printf("number of linked headers: %lu\n", (unsigned long) headers->_nrLinked); + printf("begin ptr: %p\n", headers->_begin); + printf("end ptr: %p\n", headers->_end); while (next != NULL) { - LOG_TRACE("- header #%lu: ptr: %p, prev: %p, next: %p, key: %s\n", + printf("- header #%lu: ptr: %p, prev: %p, next: %p, key: %s\n", (unsigned long) i, next, next->_prev, @@ -424,7 +475,6 @@ static void DumpHeaders (TRI_headers_t const* h) { } TRI_ASSERT_MAINTAINER(i == headers->_nrLinked); - TRI_ASSERT_MAINTAINER(i == headers->_nrLinked); } #endif diff --git a/js/server/modules/org/arangodb/graph.js b/js/server/modules/org/arangodb/graph.js index 7642fad2c8..fa7b26face 100644 --- a/js/server/modules/org/arangodb/graph.js +++ b/js/server/modules/org/arangodb/graph.js @@ -963,6 +963,7 @@ Graph.prototype.removeEdge = function (edge, waitForSync) { throw "cannot delete edge"; } + this._edgesCache[edge._properties._id] = undefined; edge._properties = undefined; } }; diff --git a/js/server/tests/transactions.js b/js/server/tests/transactions.js index b3e3a9b3fa..b8f259473f 100644 --- a/js/server/tests/transactions.js +++ b/js/server/tests/transactions.js @@ -1439,6 +1439,314 @@ function transactionOperationsSuite () { /// @brief test suite //////////////////////////////////////////////////////////////////////////////// +function transactionBarriersSuite () { + var cn1 = "UnitTestsTransaction1"; + var cn2 = "UnitTestsTransaction2"; + + var c1 = null; + var c2 = null; + + return { + +//////////////////////////////////////////////////////////////////////////////// +/// @brief set up +//////////////////////////////////////////////////////////////////////////////// + + setUp : function () { + db._drop(cn1); + db._drop(cn2); + }, + +//////////////////////////////////////////////////////////////////////////////// +/// @brief tear down +//////////////////////////////////////////////////////////////////////////////// + + tearDown : function () { + if (c1 !== null) { + c1.drop(); + } + + c1 = null; + + if (c2 !== null) { + c2.drop(); + } + + c2 = null; + }, + +//////////////////////////////////////////////////////////////////////////////// +/// @brief test: usage of barriers outside of transaction +//////////////////////////////////////////////////////////////////////////////// + + testBarriersOutsideCommit : function () { + c1 = db._create(cn1); + + var docs = [ ]; + var i; + + var obj = { + collections : { + write: [ cn1 ] + }, + action : function () { + for (i = 0; i < 100; ++i) { + c1.save({ _key: "foo" + i, value1: i, value2: "foo" + i + "x" }); + } + + for (i = 0; i < 100; ++i) { + docs.push(c1.document("foo" + i)); + } + + return c1.document("foo0"); + } + }; + + var result = TRANSACTION(obj); + + assertEqual(100, docs.length); + assertEqual(100, c1.count()); + + assertEqual("foo0", result._key); + assertEqual(0, result.value1); + assertEqual("foo0x", result.value2); + + for (i = 0; i < 100; ++i) { + assertEqual("foo" + i, docs[i]._key); + assertEqual(i, docs[i].value1); + assertEqual("foo" + i + "x", docs[i].value2); + } + }, + +//////////////////////////////////////////////////////////////////////////////// +/// @brief test: usage of barriers outside of transaction +//////////////////////////////////////////////////////////////////////////////// + + testBarriersOutsideRollback : function () { + c1 = db._create(cn1); + + var docs = [ ]; + var i; + + var obj = { + collections : { + write: [ cn1 ] + }, + action : function () { + for (i = 0; i < 100; ++i) { + c1.save({ _key: "foo" + i, value1: i, value2: "foo" + i + "x" }); + } + + for (i = 0; i < 100; ++i) { + docs.push(c1.document("foo" + i)); + } + + throw "doh!"; + } + }; + + try { + TRANSACTION(obj); + fail(); + } + catch (err) { + } + + assertEqual(100, docs.length); + + for (i = 0; i < 100; ++i) { + assertEqual("foo" + i, docs[i]._key); + assertEqual(i, docs[i].value1); + assertEqual("foo" + i + "x", docs[i].value2); + } + } + + }; +} + +// ----------------------------------------------------------------------------- +// --SECTION-- test suite +// ----------------------------------------------------------------------------- + +//////////////////////////////////////////////////////////////////////////////// +/// @brief test suite +//////////////////////////////////////////////////////////////////////////////// + +function transactionGraphSuite () { + var cn1 = "UnitTestsVertices"; + var cn2 = "UnitTestsEdges"; + + var g = require('org/arangodb/graph').Graph; + + var c1 = null; + var c2 = null; + + return { + +//////////////////////////////////////////////////////////////////////////////// +/// @brief set up +//////////////////////////////////////////////////////////////////////////////// + + setUp : function () { + db._drop(cn1); + db._drop(cn2); + + db._create(cn1); + db._createEdgeCollection(cn2); + }, + +//////////////////////////////////////////////////////////////////////////////// +/// @brief tear down +//////////////////////////////////////////////////////////////////////////////// + + tearDown : function () { + if (c1 !== null) { + c1.drop(); + } + + c1 = null; + + if (c2 !== null) { + c2.drop(); + } + + c2 = null; + }, + +//////////////////////////////////////////////////////////////////////////////// +/// @brief test: rollback updates in a graph transaction +//////////////////////////////////////////////////////////////////////////////// + + testRollbackGraphUpdates : function () { + var graph; + + try { + graph = new g('UnitTestsGraph'); + graph.drop(); + } + catch (err) { + } + + graph = new g('UnitTestsGraph', cn1, cn2); + var gotHere = 0; + + var obj = { + collections: { + write: [ cn1, cn2 ] + }, + action : function () { + var result = { }; + result.enxirvp = graph.addVertex(null, { _rev : null })._properties; + result.biitqtk = graph.addVertex(null, { _rev : null })._properties; + result.oboyuhh = graph.addEdge(graph.getVertex(result.enxirvp._id), graph.getVertex(result.biitqtk._id), null, { name: "john smith" })._properties; + result.cvwmkym = db[cn1].replace(result.enxirvp._id, { _rev : null }); + result.gsalfxu = db[cn1].replace(result.biitqtk._id, { _rev : null }); + result.xsjzbst = function (){ + graph.removeEdge(graph.getEdge(result.oboyuhh._id)); + return true; + }(); + + result.thizhdd = graph.addEdge(graph.getVertex(result.cvwmkym._id), graph.getVertex(result.gsalfxu._id), result.oboyuhh._key, { name: "david smith" }); + gotHere = 1; + + result.rldfnre = graph.addEdge(graph.getVertex(result.cvwmkym._id), graph.getVertex(result.gsalfxu._id), result.oboyuhh._key, { name : "david smith" })._properties; + gotHere = 2; + + return result; + } + }; + + try { + TRANSACTION(obj); + fail(); + } + catch (err) { + } + + assertEqual(0, db[cn1].count()); + assertEqual(0, db[cn2].count()); + assertEqual(1, gotHere); + }, + +//////////////////////////////////////////////////////////////////////////////// +/// @brief test: usage of barriers outside of graph transaction +//////////////////////////////////////////////////////////////////////////////// + + testUseBarriersOutsideGraphTransaction : function () { + var graph; + + try { + graph = new g('UnitTestsGraph'); + graph.drop(); + } + catch (err) { + } + + graph = new g('UnitTestsGraph', cn1, cn2); + + var obj = { + collections: { + write: [ cn1, cn2 ] + }, + action : function () { + var result = { }; + + result.enxirvp = graph.addVertex(null, { _rev : null })._properties; + result.biitqtk = graph.addVertex(null, { _rev : null })._properties; + result.oboyuhh = graph.addEdge(graph.getVertex(result.enxirvp._id), graph.getVertex(result.biitqtk._id), null, { name : "john smith" })._properties; + result.cvwmkym = db[cn1].replace(result.enxirvp._id, { _rev : null }); + result.gsalfxu = db[cn1].replace(result.biitqtk._id, { _rev : null }); + result.xsjzbst = function (){ + graph.removeEdge(graph.getEdge(result.oboyuhh._id)); + return true; + }(); + + result.rldfnre = graph.addEdge(graph.getVertex(result.cvwmkym._id), graph.getVertex(result.gsalfxu._id), result.oboyuhh._key, { name : "david smith" })._properties; + + return result; + } + }; + + var result = TRANSACTION(obj); + assertTrue(result.enxirvp._key.length > 0); + assertEqual(undefined, result.enxirvp.name); + + assertTrue(result.biitqtk._key.length > 0); + assertEqual(undefined, result.biitqtk.name); + + assertTrue(result.oboyuhh._key.length > 0); + assertEqual("john smith", result.oboyuhh.name); + assertEqual(null, result.oboyuhh.$label); + assertTrue(result.oboyuhh._from.length > 0); + assertTrue(result.oboyuhh._to.length > 0); + + assertTrue(result.cvwmkym._key.length > 0); + assertEqual(undefined, result.cvwmkym.name); + assertEqual(result.enxirvp._rev, result.cvwmkym._oldRev); + + assertTrue(result.gsalfxu._key.length > 0); + assertEqual(undefined, result.gsalfxu.name); + assertEqual(result.biitqtk._rev, result.gsalfxu._oldRev); + + assertEqual(true, result.xsjzbst); + + assertTrue(result.rldfnre._key.length > 0); + assertEqual(result.oboyuhh._key, result.rldfnre._key); + assertEqual("david smith", result.rldfnre.name); + assertEqual(null, result.rldfnre.$label); + } + + }; +} + +// ----------------------------------------------------------------------------- +// --SECTION-- test suite +// ----------------------------------------------------------------------------- + +//////////////////////////////////////////////////////////////////////////////// +/// @brief test suite +//////////////////////////////////////////////////////////////////////////////// + function transactionRollbackSuite () { var cn1 = "UnitTestsTransaction1"; @@ -2130,7 +2438,7 @@ function transactionRollbackSuite () { c1.save({ _key: "test" }); assertEqual(3, c1.count()); assertEqual([ "bar", "baz", "test" ], sortedKeys(c1)); - }, + } }; } @@ -2601,6 +2909,8 @@ function transactionCrossCollectionSuite () { jsunity.run(transactionInvocationSuite); jsunity.run(transactionCollectionsSuite); jsunity.run(transactionOperationsSuite); +jsunity.run(transactionBarriersSuite); +jsunity.run(transactionGraphSuite); jsunity.run(transactionRollbackSuite); jsunity.run(transactionCountSuite); jsunity.run(transactionCrossCollectionSuite); diff --git a/js/server/version-check.js b/js/server/version-check.js index d3298643cf..3317f85254 100644 --- a/js/server/version-check.js +++ b/js/server/version-check.js @@ -240,7 +240,8 @@ // create the _routing collection addTask("createRouting", "setup _routing collection", function () { - return createSystemCollection("_routing"); + // needs to be big enough for assets + return createSystemCollection("_routing", { journalSize: 32 * 1024 * 1024 }); }); // create the default route in the _routing collection