From 57d2517bd6cbed012de4b0293e393c7aae9dd21f Mon Sep 17 00:00:00 2001 From: Vasiliy Date: Wed, 11 Jul 2018 21:21:51 +0300 Subject: [PATCH] issue 399.1: store only transient link information by IResearchViewCoordinator (#5834) * issue 399.1: store only transient link information by IResearchViewCoordinator * some fixes --- arangod/Aql/ExecutionBlock.cpp | 40 ++- arangod/Aql/ExecutionBlock.h | 3 +- .../IResearch/IResearchLinkCoordinator.cpp | 50 +--- arangod/IResearch/IResearchLinkCoordinator.h | 4 +- arangod/IResearch/IResearchView.cpp | 8 +- .../IResearch/IResearchViewCoordinator.cpp | 273 ++++++++++-------- arangod/IResearch/IResearchViewCoordinator.h | 24 +- arangod/RestServer/AqlFeature.cpp | 1 - arangod/V8Server/v8-views.cpp | 11 +- .../aql/aql-view-arangosearch-ddl-cluster.js | 33 +-- .../IResearchViewCoordinator-test.cpp | 48 +-- 11 files changed, 272 insertions(+), 223 deletions(-) diff --git a/arangod/Aql/ExecutionBlock.cpp b/arangod/Aql/ExecutionBlock.cpp index 969a4a0919..2dbfd03977 100644 --- a/arangod/Aql/ExecutionBlock.cpp +++ b/arangod/Aql/ExecutionBlock.cpp @@ -33,7 +33,7 @@ using namespace arangodb; using namespace arangodb::aql; - + namespace { std::string const doneString = "DONE"; @@ -54,6 +54,13 @@ static std::string const& stateToString(ExecutionState state) { return unknownString; } +struct ExecutionBlockTypeHash { + size_t operator()(ExecutionBlock::Type value) const noexcept { + typedef std::underlying_type::type UnderlyingType; + return std::hash()(UnderlyingType(value)); + } +}; + std::unordered_map const NamesToBlockTypeMap = { { "-undefined-", arangodb::aql::ExecutionBlock::Type::_UNDEFINED}, { "CalculationBlock", arangodb::aql::ExecutionBlock::Type::CALCULATION}, @@ -83,23 +90,34 @@ std::unordered_map const Names { "UpsertBlock", arangodb::aql::ExecutionBlock::Type::UPSERT}, { "ScatterBlock", arangodb::aql::ExecutionBlock::Type::SCATTER}, { "DistributeBlock", arangodb::aql::ExecutionBlock::Type::DISTRIBUTE}, +#ifdef USE_IRESEARCH { "IResearchViewBlock", arangodb::aql::ExecutionBlock::Type::IRESEARCH_VIEW}, { "IResearchViewOrderedBlock", arangodb::aql::ExecutionBlock::Type::IRESEARCH_VIEW_ORDERED}, { "IResearchViewUnorderedBlock", arangodb::aql::ExecutionBlock::Type::IRESEARCH_VIEW_UNORDERED} +#endif }; -std::unordered_map blockTypeToNamesMap; + +std::unordered_map< + arangodb::aql::ExecutionBlock::Type, + std::reference_wrapper, + ExecutionBlockTypeHash +> blockTypeToNamesMap; + +struct BlockTypeToNameMapInitializer { + BlockTypeToNameMapInitializer() { + blockTypeToNamesMap.reserve(NamesToBlockTypeMap.size()); + std::for_each(NamesToBlockTypeMap.begin(), + NamesToBlockTypeMap.end(), + [](std::pair const& p) { + blockTypeToNamesMap.emplace(p.second, p.first); + }); + } + + +} initializeBlockTypeToNameMap; } // namespace -void ExecutionBlock::init() { - blockTypeToNamesMap.reserve(NamesToBlockTypeMap.size()); - std::for_each(NamesToBlockTypeMap.begin(), - NamesToBlockTypeMap.end(), - [](std::pair const& p) { - blockTypeToNamesMap.insert(std::make_pair(p.second, p.first)); - }); -} - ExecutionBlock::ExecutionBlock(ExecutionEngine* engine, ExecutionNode const* ep) : _engine(engine), _trx(engine->getQuery()->trx()), diff --git a/arangod/Aql/ExecutionBlock.h b/arangod/Aql/ExecutionBlock.h index d9ded50dc8..80449b8507 100644 --- a/arangod/Aql/ExecutionBlock.h +++ b/arangod/Aql/ExecutionBlock.h @@ -111,9 +111,11 @@ class ExecutionBlock { UPSERT, SCATTER, DISTRIBUTE, +#ifdef USE_IRESEARCH IRESEARCH_VIEW, IRESEARCH_VIEW_ORDERED, IRESEARCH_VIEW_UNORDERED, +#endif }; // omitted in this list are (because): // WaitingExecutionBlockMock (mock) @@ -124,7 +126,6 @@ class ExecutionBlock { static std::string typeToString(Type type); static Type typeFromString(std::string const& type); - static void init(); public: /// @brief batch size value diff --git a/arangod/IResearch/IResearchLinkCoordinator.cpp b/arangod/IResearch/IResearchLinkCoordinator.cpp index f23a5f4985..5bdf193ec2 100644 --- a/arangod/IResearch/IResearchLinkCoordinator.cpp +++ b/arangod/IResearch/IResearchLinkCoordinator.cpp @@ -69,47 +69,6 @@ IResearchLinkCoordinator::IResearchLinkCoordinator( _sparse = true; // always sparse } -IResearchLinkCoordinator::~IResearchLinkCoordinator() { - if (!_collection || !_view) { - return; // nothing else can be done - } - - if (_collection->deleted() - || TRI_vocbase_col_status_e::TRI_VOC_COL_STATUS_DELETED == _collection->status()) { - _view->drop(_collection->id()); - } -} - -int IResearchLinkCoordinator::drop() { - if (!_collection) { - return TRI_ERROR_ARANGO_COLLECTION_NOT_LOADED; // '_collection' required - } - - if (!_view) { - return TRI_ERROR_ARANGO_COLLECTION_NOT_LOADED; // IResearchView required - } - - // if the collection is in the process of being removed then drop it from the view - if (_collection->deleted() - || TRI_vocbase_col_status_e::TRI_VOC_COL_STATUS_DELETED == _collection->status()) { - // revalidate all links - auto const result = _view->updateProperties( - emptyObjectSlice(), true, false - ); - - if (!result.ok()) { - LOG_TOPIC(WARN, arangodb::iresearch::TOPIC) - << "failed to force view link revalidation while unloading dropped IResearch link '" << id() - << "' for IResearch view '" << _view->id() << "'"; - - return result.errorNumber(); - } - } - - // drop it from view - return _view->drop(_collection->id()).errorNumber(); -} - bool IResearchLinkCoordinator::operator==(LogicalView const& view) const noexcept { return _view && _view->id() == view.id(); } @@ -159,7 +118,14 @@ bool IResearchLinkCoordinator::init(VPackSlice definition) { if (!view) { LOG_TOPIC(WARN, arangodb::iresearch::TOPIC) - << "error finding view: '" << viewId << "' for link '" << id() << "'"; + << "error finding view '" << viewId << "' for link '" << id() << "'"; + + return false; + } + + if (!view->emplace(_collection->id(), _collection->name(), definition)) { + LOG_TOPIC(WARN, arangodb::iresearch::TOPIC) + << "error emplacing link to collection '" << _collection->name() << "' into IResearch view '" << viewId << "' link '" << id() << "'"; return false; } diff --git a/arangod/IResearch/IResearchLinkCoordinator.h b/arangod/IResearch/IResearchLinkCoordinator.h index 9261bc697a..a49fd6825b 100644 --- a/arangod/IResearch/IResearchLinkCoordinator.h +++ b/arangod/IResearch/IResearchLinkCoordinator.h @@ -43,7 +43,7 @@ class IResearchLinkCoordinator final: public arangodb::Index { //////////////////////////////////////////////////////////////////////////////// /// @brief destructor //////////////////////////////////////////////////////////////////////////////// - virtual ~IResearchLinkCoordinator(); + virtual ~IResearchLinkCoordinator() = default; //////////////////////////////////////////////////////////////////////////////// /// @brief does this IResearch Link reference the supplied view @@ -74,7 +74,7 @@ class IResearchLinkCoordinator final: public arangodb::Index { virtual bool canBeDropped() const override { return true; } - virtual int drop() override; + virtual int drop() override { return TRI_ERROR_NO_ERROR; } //////////////////////////////////////////////////////////////////////////////// /// @brief finds first link between specified collection and view diff --git a/arangod/IResearch/IResearchView.cpp b/arangod/IResearch/IResearchView.cpp index 69d0a8c8ee..955d11ac1e 100644 --- a/arangod/IResearch/IResearchView.cpp +++ b/arangod/IResearch/IResearchView.cpp @@ -733,12 +733,13 @@ IResearchView::IResearchView( it->_next = _memoryNodes; auto* viewPtr = this; - auto* iresearchFeature = arangodb::application_features::ApplicationServer::lookupFeature< + + _asyncFeature = arangodb::application_features::ApplicationServer::lookupFeature< arangodb::iresearch::IResearchFeature >(); // add asynchronous commit tasks - if (iresearchFeature) { + if (_asyncFeature) { struct State: public IResearchViewMeta::CommitMeta { size_t _cleanupIntervalCount; std::chrono::system_clock::time_point _last; @@ -817,8 +818,7 @@ IResearchView::IResearchView( state ); - iresearchFeature->async(self(), 0, std::move(task)); - _asyncFeature = iresearchFeature; + _asyncFeature->async(self(), 0, std::move(task)); } } diff --git a/arangod/IResearch/IResearchViewCoordinator.cpp b/arangod/IResearch/IResearchViewCoordinator.cpp index f2bc2e00e6..f0f0cc6745 100644 --- a/arangod/IResearch/IResearchViewCoordinator.cpp +++ b/arangod/IResearch/IResearchViewCoordinator.cpp @@ -40,7 +40,9 @@ namespace { -using namespace arangodb::basics; +typedef irs::async_utils::read_write_mutex::read_mutex ReadMutex; +typedef irs::async_utils::read_write_mutex::write_mutex WriteMutex; + using namespace arangodb::iresearch; arangodb::Result dropLink( @@ -127,9 +129,10 @@ arangodb::Result updateLinks( return { TRI_ERROR_BAD_PARAMETER, std::string("error parsing link parameters from json for IResearch view '") - + StringUtils::itoa(view.id()) + + view.name() + "' offset '" - + StringUtils::itoa(linksItr.index()) + '"' + + std::to_string(linksItr.index()) + + '"' }; } @@ -208,8 +211,9 @@ arangodb::Result updateLinks( return { TRI_ERROR_ARANGO_INDEX_NOT_FOUND, std::string("no link between view '") - + StringUtils::itoa(view.id()) - + "' and collection '" + StringUtils::itoa(cid) + + view.name() + + "' and collection '" + + std::to_string(cid) + "' found" }; } @@ -221,7 +225,7 @@ arangodb::Result updateLinks( auto link = currentLinks.get(collection->name()); if (!link.isObject()) { - auto const collectiondId = StringUtils::itoa(collection->id()); + auto const collectiondId = std::to_string(collection->id()); link = currentLinks.get(collectiondId); if (!link.isObject()) { @@ -256,7 +260,7 @@ namespace iresearch { arangodb::Result IResearchViewCoordinator::appendVelocyPack( arangodb::velocypack::Builder& builder, bool detailed, - bool //forPersistence + bool forPersistence ) const { if (!builder.isOpenObject()) { return arangodb::Result( @@ -279,12 +283,29 @@ arangodb::Result IResearchViewCoordinator::appendVelocyPack( arangodb::velocypack::Value(arangodb::velocypack::ValueType::Object) ); _meta.json(builder); // regular properites - _metaState.json(builder); // regular properites - auto links = _links.slice(); + arangodb::velocypack::Builder links; + IResearchViewMetaState metaState; - if (links.isObject() && links.length()) { - builder.add(StaticStrings::LinksField, links); + { + ReadMutex mutex(_mutex); + SCOPED_LOCK(mutex); // '_collections' can be asynchronously modified + + links.openObject(); + + for (auto& entry: _collections) { + links.add(entry.second.first, entry.second.second.slice()); + metaState._collections.emplace(entry.first); + } + + links.close(); + } + + metaState.json(builder); // FIXME TODO remove and fix JavaScript tests (no longer required) + + // links are not persisted, their definitions are part of the corresponding collections + if (!forPersistence) { + builder.add(StaticStrings::LinksField, links.slice()); } builder.close(); // close PROPERTIES_FIELD @@ -292,6 +313,42 @@ arangodb::Result IResearchViewCoordinator::appendVelocyPack( return arangodb::Result(); } +bool IResearchViewCoordinator::emplace( + TRI_voc_cid_t cid, + std::string const& key, + arangodb::velocypack::Slice const& value +) { + static const std::function acceptor = []( + irs::string_ref const& key + )->bool { + return key != arangodb::StaticStrings::IndexType + && key != StaticStrings::ViewIdField; // ignored fields + }; + arangodb::velocypack::Builder builder; + + builder.openObject(); + + // strip internal keys (added in createLink(...)) from externally visible link definition + if (!mergeSliceSkipKeys(builder, value, acceptor)) { + LOG_TOPIC(WARN, iresearch::TOPIC) + << "failed to generate externally visible link definition while emplacing link definition into IResearch view '" + << name() << "' collection '" << cid << "'"; + + return false; + } + + builder.close(); + + WriteMutex mutex(_mutex); + SCOPED_LOCK(mutex); // '_collections' can be asynchronously read + + return _collections.emplace( + std::piecewise_construct, + std::forward_as_tuple(cid), + std::forward_as_tuple(key, std::move(builder)) + ).second; +} + /*static*/ std::shared_ptr IResearchViewCoordinator::make( TRI_vocbase_t& vocbase, velocypack::Slice const& info, @@ -307,53 +364,13 @@ arangodb::Result IResearchViewCoordinator::appendVelocyPack( auto& properties = props.isObject() ? props : emptyObjectSlice(); // if no 'props' then assume defaults std::string error; - if (!view->_meta.init(properties, error) - || !view->_metaState.init(properties, error)) { + if (!view->_meta.init(properties, error)) { LOG_TOPIC(WARN, iresearch::TOPIC) << "failed to initialize IResearch view from definition, error: " << error; return nullptr; } - auto const links = properties.get(StaticStrings::LinksField); - IResearchLinkMeta linkMeta; - - if (links.isObject()) { - auto& builder = view->_links; - VPackObjectBuilder guard(&builder); - - size_t idx = 0; - for (auto link : velocypack::ObjectIterator(links)) { - auto const nameSlice = link.key; - - if (!nameSlice.isString()) { - LOG_TOPIC(WARN, iresearch::TOPIC) - << "failed to initialize IResearch view link from definition at index " << idx - << ", link name is not string"; - - return nullptr; - } - - if (!linkMeta.init(link.value, error)) { - LOG_TOPIC(WARN, iresearch::TOPIC) - << "failed to initialize IResearch view link from definition at index " << idx - << ", error: " << error; - - return nullptr; - } - - // append normalized link definition - { - VPackValueLength length; - char const* name = nameSlice.getString(length); - - builder.add(name, length, VPackValue(VPackValueType::Object)); - linkMeta.json(builder); - builder.close(); - } - } - } - if (preCommit && !preCommit(view)) { LOG_TOPIC(ERR, arangodb::iresearch::TOPIC) << "Failure during pre-commit while constructing IResearch View in database '" << vocbase.id() << "'"; @@ -411,8 +428,11 @@ IResearchViewCoordinator::IResearchViewCoordinator( bool IResearchViewCoordinator::visitCollections( CollectionVisitor const& visitor ) const { - for (auto& cid: _metaState._collections) { - if (!visitor(cid)) { + ReadMutex mutex(_mutex); + SCOPED_LOCK(mutex); // '_collections' can be asynchronously modified + + for (auto& entry: _collections) { + if (!visitor(entry.first)) { return false; } } @@ -427,7 +447,6 @@ arangodb::Result IResearchViewCoordinator::updateProperties( ) { try { IResearchViewMeta meta; - IResearchViewMetaState metaState; std::string error; auto const& defaults = partialUpdate @@ -438,53 +457,85 @@ arangodb::Result IResearchViewCoordinator::updateProperties( return { TRI_ERROR_BAD_PARAMETER, error }; } - VPackBuilder builder; - { - VPackObjectBuilder const guard(&builder); + // only trigger persisting of properties if they have changed + if (_meta != meta) { + auto* engine = arangodb::ClusterInfo::instance(); - // system properties - toVelocyPack(builder, false, true); + if (!engine) { + return arangodb::Result( + TRI_ERROR_INTERNAL, + std::string("failure to get storage engine while updating IResearch View '") + name() + "'" + ); + } - // properties - { - VPackObjectBuilder const guard(&builder, StaticStrings::PropertiesField); + arangodb::velocypack::Builder builder; - // links - { - VPackObjectBuilder const guard(&builder, StaticStrings::LinksField); + builder.openObject(); + builder.add( + StaticStrings::PropertiesField, + arangodb::velocypack::Value(arangodb::velocypack::ValueType::Object) + ); + meta.json(builder); + builder.close(); // close PROPERTIES_FIELD - bool modified = false; - auto const res = updateLinks( - properties.get(StaticStrings::LinksField), - _links.slice(), - *this, - partialUpdate, - _metaState._collections, - modified, - builder, - metaState._collections - ); + auto result = toVelocyPack(builder, false, true); - if (!res.ok()) { - return res; - } - - if (!modified && _meta == meta && _metaState == metaState) { - // nothing to do - return {}; - } + if (!result.ok()) { + return result; } - // meta - meta.json(builder); - metaState.json(builder); + builder.close(); + result = engine->setViewPropertiesCoordinator( + vocbase().name(), std::to_string(id()), builder.slice() + ); + + if (!result.ok()) { + return result; } } - return ClusterInfo::instance()->setViewPropertiesCoordinator( - vocbase().name(), // database name, - StringUtils::itoa(id()), // cluster-wide view id - builder.slice() + if (!properties.hasKey(StaticStrings::LinksField)) { + return arangodb::Result(); // nothing more to do + } + + // ........................................................................... + // update links if requested (on a best-effort basis) + // indexing of collections is done in different threads so no locks can be held and rollback is not possible + // as a result it's also possible for links to be simultaneously modified via a different callflow (e.g. from collections) + // ........................................................................... + + arangodb::velocypack::Builder currentLinks; + std::unordered_set currentCids; + + { + ReadMutex mutex(_mutex); + SCOPED_LOCK(mutex); // '_collections' can be asynchronously modified + + currentLinks.openObject(); + + for (auto& entry: _collections) { + currentCids.emplace(entry.first); + currentLinks.add(entry.second.first, entry.second.second.slice()); + } + + currentLinks.close(); + } + + arangodb::velocypack::Builder viewNewProperties; + bool modified = false; + std::unordered_set newCids; + + viewNewProperties.openObject(); + + return updateLinks( + properties.get(StaticStrings::LinksField), + currentLinks.slice(), + *this, + partialUpdate, + currentCids, + modified, + viewNewProperties, + newCids ); } catch (std::exception const& e) { LOG_TOPIC(WARN, iresearch::TOPIC) @@ -510,8 +561,22 @@ arangodb::Result IResearchViewCoordinator::updateProperties( Result IResearchViewCoordinator::drop() { // drop links first { - auto const res = updateProperties( - VPackSlice::emptyObjectSlice(), false, true + std::unordered_set currentCids; + + visitCollections([¤tCids](TRI_voc_cid_t cid)->bool { currentCids.emplace(cid); return true; }); + + arangodb::velocypack::Builder builder; + bool modified; + std::unordered_set newCids; + auto res = updateLinks( + arangodb::velocypack::Slice::emptyObjectSlice(), + arangodb::velocypack::Slice::emptyObjectSlice(), + *this, + false, + currentCids, + modified, + builder, + newCids ); if (!res.ok()) { @@ -539,26 +604,6 @@ Result IResearchViewCoordinator::drop() { return {}; } -Result IResearchViewCoordinator::drop(TRI_voc_cid_t cid) { - auto cid_itr = _metaState._collections.find(cid); - - if (cid_itr == _metaState._collections.end()) { - // no such cid - return { TRI_ERROR_BAD_PARAMETER }; - } - - VPackBuilder builder; - { - VPackObjectBuilder const guard(&builder); - { - VPackObjectBuilder const guard(&builder, StaticStrings::LinksField); - builder.add(StringUtils::itoa(cid), VPackSlice::nullSlice()); - } - } - - return updateProperties(builder.slice(), true, true); -} - } // iresearch } // arangodb diff --git a/arangod/IResearch/IResearchViewCoordinator.h b/arangod/IResearch/IResearchViewCoordinator.h index 9d2c310297..72bf8c6fe8 100644 --- a/arangod/IResearch/IResearchViewCoordinator.h +++ b/arangod/IResearch/IResearchViewCoordinator.h @@ -40,6 +40,22 @@ namespace iresearch { /////////////////////////////////////////////////////////////////////////////// class IResearchViewCoordinator final : public arangodb::LogicalView { public: + //////////////////////////////////////////////////////////////////////////////// + /// @brief acquire locks on the specified 'cid' during read-transactions + /// allowing retrieval of documents contained in the aforementioned + /// collection + /// @note definitions are not persisted + /// @param cid the collection ID to track + /// @param key the key of the link definition for use in appendVelocyPack(...) + /// @param value the link definition to use in appendVelocyPack(...) + /// @return the 'cid' was newly added to the IResearch View + //////////////////////////////////////////////////////////////////////////////// + bool emplace( + TRI_voc_cid_t cid, + std::string const& key, + arangodb::velocypack::Slice const& value + ); + /////////////////////////////////////////////////////////////////////////////// /// @brief view factory /// @returns initialized view object @@ -60,9 +76,6 @@ class IResearchViewCoordinator final : public arangodb::LogicalView { Result drop() override; - // drops collection from a view - Result drop(TRI_voc_cid_t cid); - virtual Result rename( std::string&& /*newName*/, bool /*doSync*/ @@ -89,10 +102,9 @@ class IResearchViewCoordinator final : public arangodb::LogicalView { TRI_vocbase_t& vocbase, velocypack::Slice info, uint64_t planVersion ); + std::unordered_map> _collections; // transient member, not persisted + mutable irs::async_utils::read_write_mutex _mutex; // for use with '_collections' IResearchViewMeta _meta; - IResearchViewMetaState _metaState; - velocypack::Builder _links; - }; // IResearchViewCoordinator } // iresearch diff --git a/arangod/RestServer/AqlFeature.cpp b/arangod/RestServer/AqlFeature.cpp index 6ea10b34f8..515ef8449b 100644 --- a/arangod/RestServer/AqlFeature.cpp +++ b/arangod/RestServer/AqlFeature.cpp @@ -73,7 +73,6 @@ void AqlFeature::start() { MUTEX_LOCKER(locker, AqlFeature::_aqlFeatureMutex); TRI_ASSERT(_AQL == nullptr); _AQL = this; - aql::ExecutionBlock::init(); LOG_TOPIC(DEBUG, Logger::QUERIES) << "AQL feature started"; } diff --git a/arangod/V8Server/v8-views.cpp b/arangod/V8Server/v8-views.cpp index 91f66e9bbb..16790c1af3 100644 --- a/arangod/V8Server/v8-views.cpp +++ b/arangod/V8Server/v8-views.cpp @@ -239,9 +239,9 @@ static void JS_DropViewVocbase( auto view = vocbase.lookupView(name); if (view) { - auto res = vocbase.dropView(view->id(), allowDropSystem).errorNumber(); + auto res = vocbase.dropView(view->id(), allowDropSystem); - if (res != TRI_ERROR_NO_ERROR) { + if (!res.ok()) { TRI_V8_THROW_EXCEPTION(res); } } @@ -285,11 +285,10 @@ static void JS_DropViewVocbaseObj( } } - auto res = - view->vocbase().dropView(view->id(), allowDropSystem).errorNumber(); + auto res = view->vocbase().dropView(view->id(), allowDropSystem); - if (res != TRI_ERROR_NO_ERROR) { - TRI_V8_THROW_EXCEPTION_MESSAGE(res, "cannot drop view"); + if (!res.ok()) { + TRI_V8_THROW_EXCEPTION(res); } TRI_V8_RETURN_UNDEFINED(); diff --git a/js/common/tests/aql/aql-view-arangosearch-ddl-cluster.js b/js/common/tests/aql/aql-view-arangosearch-ddl-cluster.js index c2ba55a465..9a0bebfeb5 100644 --- a/js/common/tests/aql/aql-view-arangosearch-ddl-cluster.js +++ b/js/common/tests/aql/aql-view-arangosearch-ddl-cluster.js @@ -110,23 +110,22 @@ function IResearchFeatureDDLTestSuite () { } }, -//FIXME -// testRemoveLinkViaCollection : function() { -// db._drop("TestCollection0"); -// db._dropView("TestView"); -// -// var view = db._createView("TestView", "arangosearch", {}); -// db._create("TestCollection0"); -// var addLink = { links: { "TestCollection0": {} } }; -// view.properties(addLink, true); // partial update -// properties = view.properties(); -// assertTrue(Array === properties.collections.constructor); -// assertEqual(1, properties.collections.length); -// db._drop("TestCollection0"); -// properties = view.properties(); -// assertTrue(Array === properties.collections.constructor); -// assertEqual(0, properties.collections.length); -// }, + testRemoveLinkViaCollection : function() { + db._drop("TestCollection0"); + db._dropView("TestView"); + + var view = db._createView("TestView", "arangosearch", {}); + db._create("TestCollection0"); + var addLink = { links: { "TestCollection0": {} } }; + view.properties(addLink, true); // partial update + properties = view.properties(); + assertTrue(Array === properties.collections.constructor); + assertEqual(1, properties.collections.length); + db._drop("TestCollection0"); + properties = view.properties(); + assertTrue(Array === properties.collections.constructor); + assertEqual(0, properties.collections.length); + }, testViewDDL: function() { // collections diff --git a/tests/IResearch/IResearchViewCoordinator-test.cpp b/tests/IResearch/IResearchViewCoordinator-test.cpp index 879680a321..5bc26109b4 100644 --- a/tests/IResearch/IResearchViewCoordinator-test.cpp +++ b/tests/IResearch/IResearchViewCoordinator-test.cpp @@ -40,6 +40,7 @@ #include "Agency/Store.h" #include "Basics/ArangoGlobalContext.h" #include "Basics/files.h" +#include "VocBase/Methods/Indexes.h" #if USE_ENTERPRISE #include "Enterprise/Ldap/LdapFeature.h" @@ -256,15 +257,12 @@ SECTION("test_rename") { } SECTION("visit_collections") { - auto json = arangodb::velocypack::Parser::fromJson( - "{ \"name\": \"testView\", \"type\": \"arangosearch\", \"id\": \"1\", \"properties\": { \"collections\": [1,2,3] } }"); - + auto json = arangodb::velocypack::Parser::fromJson("{ \"name\": \"testView\", \"type\": \"arangosearch\", \"id\": \"1\" }"); Vocbase vocbase(TRI_vocbase_type_e::TRI_VOCBASE_TYPE_COORDINATOR, 1, "testVocbase"); - - auto view = arangodb::LogicalView::create(vocbase, json->slice(), false); // false == do not persist + auto logicalView = arangodb::LogicalView::create(vocbase, json->slice(), false); // false == do not persist + auto* view = dynamic_cast(logicalView.get()); CHECK(nullptr != view); - CHECK(nullptr != std::dynamic_pointer_cast(view)); CHECK(0 == view->planVersion()); CHECK("testView" == view->name()); CHECK(false == view->deleted()); @@ -273,6 +271,10 @@ SECTION("visit_collections") { CHECK(arangodb::LogicalView::category() == view->category()); CHECK(&vocbase == &view->vocbase()); + CHECK((true == view->emplace(1, "1", arangodb::velocypack::Slice::emptyObjectSlice()))); + CHECK((true == view->emplace(2, "2", arangodb::velocypack::Slice::emptyObjectSlice()))); + CHECK((true == view->emplace(3, "3", arangodb::velocypack::Slice::emptyObjectSlice()))); + // visit view TRI_voc_cid_t expectedCollections[] = {1,2,3}; auto* begin = expectedCollections; @@ -353,8 +355,8 @@ SECTION("test_defaults") { CHECK(!slice.hasKey("deleted")); slice = slice.get("properties"); CHECK(slice.isObject()); - CHECK((3 == slice.length())); - CHECK((!slice.hasKey("links"))); + CHECK((4 == slice.length())); + CHECK((slice.hasKey("links") && slice.get("links").isObject() && 0 == slice.get("links").length())); CHECK((meta.init(slice, error) && expectedMeta == meta)); CHECK((true == metaState.init(slice, error) && expectedMetaState == metaState)); } @@ -899,7 +901,7 @@ SECTION("test_update_links_partial_remove") { // testCollection2 { - auto const value = linksSlice.get(std::to_string(logicalCollection2->id())); + auto const value = linksSlice.get(logicalCollection2->name()); CHECK(value.isObject()); arangodb::iresearch::IResearchLinkMeta expectedMeta; @@ -1631,7 +1633,7 @@ SECTION("test_update_links_partial_add") { // testCollection2 { - auto const value = linksSlice.get(std::to_string(logicalCollection2->id())); + auto const value = linksSlice.get(logicalCollection2->name()); CHECK(value.isObject()); arangodb::iresearch::IResearchLinkMeta expectedMeta; @@ -2192,7 +2194,7 @@ SECTION("test_update_links_replace") { // testCollection2 { - auto const value = linksSlice.get(std::to_string(logicalCollection2->id())); + auto const value = linksSlice.get(logicalCollection2->name()); CHECK(value.isObject()); arangodb::iresearch::IResearchLinkMeta expectedMeta; @@ -2521,7 +2523,7 @@ SECTION("test_update_links_clear") { // explicitly specify id for the sake of tests auto linksJson = arangodb::velocypack::Parser::fromJson( - "{ \"links\" : {" + "{ \"locale\": \"en\", \"links\" : {" " \"testCollection1\" : { \"id\": \"1\", \"includeAllFields\" : true }, " " \"2\" : { \"id\": \"2\", \"trackListPositions\" : true }, " " \"testCollection3\" : { \"id\": \"3\" } " @@ -2588,7 +2590,7 @@ SECTION("test_update_links_clear") { // testCollection2 { - auto const value = linksSlice.get(std::to_string(logicalCollection2->id())); + auto const value = linksSlice.get(logicalCollection2->name()); CHECK(value.isObject()); arangodb::iresearch::IResearchLinkMeta expectedMeta; @@ -2771,7 +2773,7 @@ SECTION("test_update_links_clear") { CHECK(arangodb::AgencyComm().setValue(currentCollection3Path, value->slice(), 0.0).successful()); } - auto const updateJson = arangodb::velocypack::Parser::fromJson("{}"); + auto const updateJson = arangodb::velocypack::Parser::fromJson("{ \"links\": {}}"); CHECK(view->updateProperties(updateJson->slice(), false, true).ok()); CHECK(planVersion < arangodb::tests::getCurrentPlanVersion()); // plan version changed planVersion = arangodb::tests::getCurrentPlanVersion(); @@ -2803,7 +2805,10 @@ SECTION("test_update_links_clear") { info.close(); auto const properties = info.slice().get("properties"); - CHECK(!properties.hasKey(arangodb::iresearch::StaticStrings::LinksField)); + CHECK((properties.hasKey(arangodb::iresearch::StaticStrings::LinksField))); + auto links = properties.get(arangodb::iresearch::StaticStrings::LinksField); + CHECK((links.isObject())); + CHECK((0 == links.length())); } // test index in testCollection1 @@ -2899,7 +2904,6 @@ SECTION("test_drop_link") { REQUIRE(view); auto const viewId = std::to_string(view->planId()); CHECK("42" == viewId); - CHECK(TRI_ERROR_BAD_PARAMETER == view->drop(logicalCollection->id()).errorNumber()); // unable to drop nonexistent link // simulate heartbeat thread (create index in current) { @@ -2965,6 +2969,8 @@ SECTION("test_drop_link") { CHECK(expectedMeta == actualMeta); } + TRI_idx_iid_t linkId; + // check indexes { // get new version from plan @@ -2972,6 +2978,7 @@ SECTION("test_drop_link") { REQUIRE(updatedCollection); auto link = arangodb::iresearch::IResearchLinkCoordinator::find(*updatedCollection, *view); CHECK(link); + linkId = link->id(); auto index = std::dynamic_pointer_cast(link); REQUIRE((false == !index)); @@ -3021,7 +3028,7 @@ SECTION("test_drop_link") { } // drop link - CHECK(view->drop(logicalCollection->id()).ok()); + CHECK((arangodb::methods::Indexes::drop(logicalCollection.get(), arangodb::velocypack::Parser::fromJson(std::to_string(linkId))->slice()).ok())); CHECK(planVersion < arangodb::tests::getCurrentPlanVersion()); // plan version changed planVersion = arangodb::tests::getCurrentPlanVersion(); oldView = view; @@ -3050,7 +3057,10 @@ SECTION("test_drop_link") { info.close(); auto const properties = info.slice().get("properties"); - CHECK(!properties.hasKey(arangodb::iresearch::StaticStrings::LinksField)); + CHECK((properties.hasKey(arangodb::iresearch::StaticStrings::LinksField))); + auto links = properties.get(arangodb::iresearch::StaticStrings::LinksField); + CHECK((links.isObject())); + CHECK((0 == links.length())); } // check indexes @@ -3165,4 +3175,4 @@ SECTION("IResearchViewNode::createBlock") { // ----------------------------------------------------------------------------- // --SECTION-- END-OF-FILE -// ----------------------------------------------------------------------------- +// ----------------------------------------------------------------------------- \ No newline at end of file