diff --git a/arangod/CMakeLists.txt b/arangod/CMakeLists.txt index 65588298c3..8088a040f0 100644 --- a/arangod/CMakeLists.txt +++ b/arangod/CMakeLists.txt @@ -54,7 +54,6 @@ if (USE_IRESEARCH) IResearch/Containers.cpp IResearch/Containers.h IResearch/IResearchAnalyzerFeature.cpp IResearch/IResearchAnalyzerFeature.h IResearch/IResearchCommon.cpp IResearch/IResearchCommon.h - IResearch/IResearchViewDBServer.cpp IResearch/IResearchViewDBServer.h IResearch/IResearchKludge.cpp IResearch/IResearchKludge.h IResearch/IResearchLink.cpp IResearch/IResearchLink.h IResearch/IResearchLinkCoordinator.cpp IResearch/IResearchLinkCoordinator.h diff --git a/arangod/ClusterEngine/ClusterEngine.cpp b/arangod/ClusterEngine/ClusterEngine.cpp index eaaccb73ff..d0b7785efc 100644 --- a/arangod/ClusterEngine/ClusterEngine.cpp +++ b/arangod/ClusterEngine/ClusterEngine.cpp @@ -351,15 +351,15 @@ Result ClusterEngine::createView( } arangodb::Result ClusterEngine::dropView( - TRI_vocbase_t& vocbase, - LogicalView& view + TRI_vocbase_t const& vocbase, + LogicalView const& view ) { return TRI_ERROR_NOT_IMPLEMENTED; } void ClusterEngine::destroyView( - TRI_vocbase_t& /*vocbase*/, - LogicalView& /*view*/ + TRI_vocbase_t const& /*vocbase*/, + LogicalView const& /*view*/ ) noexcept { // nothing to do here } @@ -424,4 +424,4 @@ std::unique_ptr ClusterEngine::openExistingDatabase( // ----------------------------------------------------------------------------- // --SECTION-- END-OF-FILE -// ----------------------------------------------------------------------------- +// ----------------------------------------------------------------------------- \ No newline at end of file diff --git a/arangod/ClusterEngine/ClusterEngine.h b/arangod/ClusterEngine/ClusterEngine.h index 8c2e380772..926e44f9db 100644 --- a/arangod/ClusterEngine/ClusterEngine.h +++ b/arangod/ClusterEngine/ClusterEngine.h @@ -297,13 +297,13 @@ class ClusterEngine final : public StorageEngine { } arangodb::Result dropView( - TRI_vocbase_t& vocbase, - LogicalView& view + TRI_vocbase_t const& vocbase, + LogicalView const& view ) override; void destroyView( - TRI_vocbase_t& vocbase, - LogicalView& view + TRI_vocbase_t const& vocbase, + LogicalView const& view ) noexcept override; void signalCleanup(TRI_vocbase_t& vocbase) override; @@ -358,4 +358,4 @@ class ClusterEngine final : public StorageEngine { } // namespace arangodb -#endif +#endif \ No newline at end of file diff --git a/arangod/IResearch/IResearchFeature.cpp b/arangod/IResearch/IResearchFeature.cpp index a8653706b9..0af2b02fe9 100644 --- a/arangod/IResearch/IResearchFeature.cpp +++ b/arangod/IResearch/IResearchFeature.cpp @@ -41,7 +41,6 @@ #include "IResearchRocksDBRecoveryHelper.h" #include "IResearchView.h" #include "IResearchViewCoordinator.h" -#include "IResearchViewDBServer.h" #include "Aql/AqlValue.h" #include "Aql/AqlFunctionFeature.h" #include "Aql/Function.h" @@ -289,7 +288,7 @@ void registerViewFactory() { if (arangodb::ServerState::instance()->isCoordinator()) { res = viewTypes->emplace(viewType, arangodb::iresearch::IResearchViewCoordinator::factory()); } else if (arangodb::ServerState::instance()->isDBServer()) { - res = viewTypes->emplace(viewType, arangodb::iresearch::IResearchViewDBServer::factory()); + res = viewTypes->emplace(viewType, arangodb::iresearch::IResearchView::factory()); } else if (arangodb::ServerState::instance()->isSingleServer()) { res = viewTypes->emplace(viewType, arangodb::iresearch::IResearchView::factory()); } else { @@ -799,4 +798,4 @@ NS_END // arangodb // ----------------------------------------------------------------------------- // --SECTION-- END-OF-FILE -// ----------------------------------------------------------------------------- +// ----------------------------------------------------------------------------- \ No newline at end of file diff --git a/arangod/IResearch/IResearchLink.cpp b/arangod/IResearch/IResearchLink.cpp index 36818fdfee..aa2ebbc719 100644 --- a/arangod/IResearch/IResearchLink.cpp +++ b/arangod/IResearch/IResearchLink.cpp @@ -27,7 +27,7 @@ #include "IResearchLinkHelper.h" #include "IResearchPrimaryKeyFilter.h" #include "IResearchView.h" -#include "IResearchViewDBServer.h" +#include "IResearchViewCoordinator.h" #include "Basics/LocalTaskQueue.h" #include "Basics/StaticStrings.h" #include "Cluster/ClusterInfo.h" @@ -626,55 +626,12 @@ arangodb::Result IResearchLink::init( auto idSlice = definition.get(StaticStrings::ViewIdField); auto viewId = idSlice.copyString(); auto& vocbase = _collection.vocbase(); - auto logicalView = arangodb::ServerState::instance()->isCoordinator() + auto logicalView = arangodb::ServerState::instance()->isClusterRole() && arangodb::ClusterInfo::instance() ? arangodb::ClusterInfo::instance()->getView(vocbase.name(), viewId) - : vocbase.lookupView(viewId) // will only contain IResearchView (even for a DBServer) + : vocbase.lookupView(viewId) ; - // creation of link on a DBServer - if (!logicalView && arangodb::ServerState::instance()->isDBServer()) { - auto* ci = ClusterInfo::instance(); - - if (!ci) { - return arangodb::Result( - TRI_ERROR_INTERNAL, - std::string("failure to find 'ClusterInfo' instance for lookup of link '") + std::to_string(_id) + "'" - ); - } - - auto logicalWiew = ci->getView(vocbase.name(), viewId); - auto* wiew = LogicalView::cast(logicalWiew.get()); - - if (wiew) { - // FIXME figure out elegant way of testing for cluster wide LogicalCollection - if (_collection.id() == _collection.planId() && _collection.isAStub()) { - // this is a cluster-wide collection/index/link (per-cid view links have their corresponding collections in vocbase) - auto clusterCol = ci->getCollectionCurrent( - vocbase.name(), std::to_string(_collection.id()) - ); - - if (clusterCol) { - for (auto& entry: clusterCol->errorNum()) { - auto collection = vocbase.lookupCollection(entry.first); // find shard collection - - if (collection) { - // ensure the shard collection is registered with the cluster-wide view - // required from creating snapshots for per-cid views loaded from WAL - // only register existing per-cid view instances, do not create new per-cid view - // instances since they will be created/registered by their per-cid links just below - wiew->ensure(collection->id(), false); - } - } - } - - return arangodb::Result(); // leave '_view' uninitialized to mark the index as unloaded/unusable - } - - logicalView = wiew->ensure(_collection.id()); // repoint LogicalView at the per-cid instance - } - } - if (!logicalView || arangodb::iresearch::DATA_SOURCE_TYPE != logicalView->type()) { return arangodb::Result( @@ -683,15 +640,8 @@ arangodb::Result IResearchLink::init( ); } - if (!arangodb::ServerState::instance()->isCoordinator()) { // link on coordinator does not have a data-store - auto* dbServerView = dynamic_cast(logicalView.get()); - - // dbserver has both IResearchViewDBServer and IResearchView instances - auto* view = LogicalView::cast( - dbServerView - ? dbServerView->ensure(_collection.id()).get() - : logicalView.get() - ); + if (arangodb::ServerState::instance()->isCoordinator()) { + auto* view = LogicalView::cast(logicalView.get()); if (!view) { return arangodb::Result( @@ -700,19 +650,64 @@ arangodb::Result IResearchLink::init( ); } - auto res = initDataStore(*view); - - if (!res.ok()) { - return res; - } - - if (!view->link(_asyncSelf)) { - unload(); // unlock the directory + if (!view->emplace(_collection.id(), _collection.name(), definition)) { return arangodb::Result( TRI_ERROR_INTERNAL, std::string("failed to link with view '") + view->name() + "' while initializing link '" + std::to_string(_id) + "'" ); } + } else { + auto* view = LogicalView::cast(logicalView.get()); + + if (!view) { + return arangodb::Result( + TRI_ERROR_ARANGO_DATA_SOURCE_NOT_FOUND, + std::string("error finding view: '") + viewId + "' for link '" + std::to_string(_id) + "'" + ); + } + + if (arangodb::ServerState::instance()->isDBServer() + && _collection.id() == _collection.planId() + && _collection.isAStub()) { // cluster cluster-wide link + auto shardIds = _collection.shardIds(); + + // go through all shard IDs of the collection and try to link any links + // missing links will be populated when they are created in the per-shard collection + if (shardIds) { + for (auto& entry: *shardIds) { + auto collection = vocbase.lookupCollection(entry.first); // per-shard collections are always in 'vocbase' + + if (!collection) { + continue; // missing collection should be created after Plan becomes Current + } + + auto link = IResearchLinkHelper::find(*collection, *view); + + if (link && !view->link(link->self())) { + unload(); // unlock the directory + return arangodb::Result( + TRI_ERROR_INTERNAL, + std::string("failed to link with view '") + view->name() + "' while initializing link '" + std::to_string(_id) + "', collection '" + collection->name() + "'" + ); + } + } + } + } else if (arangodb::ServerState::instance()->isSingleServer() // single-server link + || arangodb::ServerState::instance()->isDBServer()) { // cluster per-shard link + auto res = initDataStore(*view); + + if (!res.ok()) { + return res; + } + + if (!view->link(_asyncSelf)) { + unload(); // unlock the directory + return arangodb::Result( + TRI_ERROR_INTERNAL, + std::string("failed to link with view '") + view->name() + "' while initializing link '" + std::to_string(_id) + "'" + ); + } + } } const_cast(_viewGuid) = logicalView->guid(); // ensue that this is a GUID (required by operator==(IResearchView)) @@ -1173,12 +1168,12 @@ arangodb::Result IResearchLink::unload() { } std::shared_ptr IResearchLink::view() const { - // FIXME TODO change to lookup in CollectionNameResolver once per-shard views are removed + // IResearchView instances are in ClusterInfo for coordinator and db-server return std::dynamic_pointer_cast( - arangodb::ServerState::instance()->isCoordinator() + arangodb::ServerState::instance()->isClusterRole() && arangodb::ClusterInfo::instance() ? arangodb::ClusterInfo::instance()->getView(_collection.vocbase().name(), _viewGuid) - : _collection.vocbase().lookupView(_viewGuid) // always look up in vocbase (single server or cluster per-shard view) + : _collection.vocbase().lookupView(_viewGuid) ); } diff --git a/arangod/IResearch/IResearchLinkCoordinator.cpp b/arangod/IResearch/IResearchLinkCoordinator.cpp index 2ed13f121f..1415c76069 100644 --- a/arangod/IResearch/IResearchLinkCoordinator.cpp +++ b/arangod/IResearch/IResearchLinkCoordinator.cpp @@ -181,18 +181,9 @@ arangodb::Result IResearchLinkCoordinator::init( ); } - if (!view->emplace(Index::collection().id(), Index::collection().name(), definition)) { - return arangodb::Result( - TRI_ERROR_INTERNAL, - std::string("error emplacing link to collection '") + Index::collection().name() + "' into arangosearch view '" + viewId + "' link '" + std::to_string(Index::id()) + "'" - ); - } - _view = view; - IResearchLink::init(definition); - - return arangodb::Result(); + return IResearchLink::init(definition); } bool IResearchLinkCoordinator::matchesDefinition(VPackSlice const& slice) const { diff --git a/arangod/IResearch/IResearchLinkHelper.cpp b/arangod/IResearch/IResearchLinkHelper.cpp index 5bc1082793..9b9e0868de 100644 --- a/arangod/IResearch/IResearchLinkHelper.cpp +++ b/arangod/IResearch/IResearchLinkHelper.cpp @@ -29,7 +29,6 @@ #include "IResearchLinkMeta.h" #include "IResearchView.h" #include "IResearchViewCoordinator.h" -#include "IResearchViewDBServer.h" #include "VelocyPackHelper.h" #include "Basics/StaticStrings.h" #include "Logger/Logger.h" @@ -681,19 +680,6 @@ namespace iresearch { ); } - auto* dbServerView = dynamic_cast(&view); - - // dbserver has both IResearchViewDBServer and IResearchView instances - if (dbServerView) { - return modifyLinks( - modified, - vocbase, - *dbServerView, - links, - stale - ); - } - return modifyLinks( modified, vocbase, diff --git a/arangod/IResearch/IResearchRocksDBLink.cpp b/arangod/IResearch/IResearchRocksDBLink.cpp index f5f2cf8ed7..7469624521 100644 --- a/arangod/IResearch/IResearchRocksDBLink.cpp +++ b/arangod/IResearch/IResearchRocksDBLink.cpp @@ -149,6 +149,11 @@ void IResearchRocksDBLink::toVelocyPack( )); } + if (arangodb::Index::hasFlag(flags, arangodb::Index::Serialize::ObjectId)) { + TRI_ASSERT(_objectId != 0); // If we store it, it cannot be 0 + builder.add("objectId", VPackValue(std::to_string(_objectId))); + } + if (arangodb::Index::hasFlag(flags, arangodb::Index::Serialize::Figures)) { VPackBuilder figuresBuilder; diff --git a/arangod/IResearch/IResearchView.cpp b/arangod/IResearch/IResearchView.cpp index 39b1702cb3..b4ee88c4e7 100644 --- a/arangod/IResearch/IResearchView.cpp +++ b/arangod/IResearch/IResearchView.cpp @@ -28,6 +28,7 @@ #include "VelocyPackHelper.h" #include "Aql/AstNode.h" +#include "Aql/PlanCache.h" #include "Aql/QueryCache.h" #include "Basics/StaticStrings.h" #include "StorageEngine/EngineSelectorFeature.h" @@ -70,24 +71,50 @@ class ViewTrxState final return *(_subReaders[subReaderId]); } - void add(arangodb::iresearch::IResearchLink::Snapshot&& snapshot); - void clear() noexcept { _subReaders.clear(); _snapshots.clear(); } + void add( + TRI_voc_cid_t cid, + arangodb::iresearch::IResearchLink::Snapshot&& snapshot + ); + + void clear() noexcept { + _collections.clear(); + _subReaders.clear(); + _snapshots.clear(); + } + + template + bool equalCollections(Itr begin, Itr end) { + size_t count = 0; + + for (; begin != end; ++count, ++begin) { + if (_collections.find(*begin) == _collections.end() + || count > _collections.size()) { + return false; + } + } + + return _collections.size() == count; + } + virtual uint64_t docs_count() const override; virtual uint64_t live_docs_count() const override; virtual size_t size() const noexcept override { return _subReaders.size(); } private: + std::unordered_set _collections; std::vector _snapshots; // prevent data-store deallocation (lock @ AsyncSelf) std::vector _subReaders; }; void ViewTrxState::add( + TRI_voc_cid_t cid, arangodb::iresearch::IResearchLink::Snapshot&& snapshot ) { for(auto& entry: static_cast(snapshot)) { _subReaders.emplace_back(&entry); } + _collections.emplace(cid); _snapshots.emplace_back(std::move(snapshot)); } @@ -132,123 +159,6 @@ inline arangodb::FlushFeature* getFlushFeature() noexcept { >("Flush"); } -//////////////////////////////////////////////////////////////////////////////// -/// @brief persist view definition to the storage engine -/// if in-recovery then register a post-recovery lambda for persistence -/// @return success -//////////////////////////////////////////////////////////////////////////////// -arangodb::Result persistProperties( - arangodb::LogicalView const& view, - arangodb::iresearch::IResearchView::AsyncViewPtr const& asyncSelf -) { - auto* engine = arangodb::EngineSelectorFeature::ENGINE; - - if (!engine) { - return arangodb::Result( - TRI_ERROR_INTERNAL, - std::string("failure to get storage engine while persisting definition for LogicalView '") + view.name() + "'" - ); - } - - if (!engine->inRecovery()) { - // change view throws exception on error - try { - engine->changeView(view.vocbase(), view, true); - } catch (arangodb::basics::Exception& e) { - IR_LOG_EXCEPTION(); - - return arangodb::Result( - e.code(), - std::string("caught exception during persistance of properties for arangosearch View '") + view.name() + "': " + e.what() - ); - } catch (std::exception const& e) { - IR_LOG_EXCEPTION(); - - return arangodb::Result( - TRI_ERROR_INTERNAL, - std::string("caught exception during persistance of properties for arangosearch View '") + view.name() + "': " + e.what() - ); - } catch (...) { - IR_LOG_EXCEPTION(); - - return arangodb::Result( - TRI_ERROR_INTERNAL, - std::string("caught exception during persistance of properties for arangosearch View '") + view.name() + "'" - ); - } - - return arangodb::Result(); - } - - auto* feature = arangodb::application_features::ApplicationServer::lookupFeature< - arangodb::DatabaseFeature - >("Database"); - - if (!feature) { - return arangodb::Result( - TRI_ERROR_INTERNAL, - std::string("failure to get 'Database' feature while persisting definition for LogicalView '") + view.name() + "'" - ); - } - - return feature->registerPostRecoveryCallback( - [&view, asyncSelf]()->arangodb::Result { - auto* engine = arangodb::EngineSelectorFeature::ENGINE; - - if (!engine) { - return arangodb::Result( - TRI_ERROR_INTERNAL, - std::string("failure to get storage engine while persisting definition for LogicalView") - ); - } - - if (!asyncSelf) { - return arangodb::Result( - TRI_ERROR_INTERNAL, - std::string("invalid view instance passed while persisting definition for LogicalView") - ); - } - - SCOPED_LOCK(asyncSelf->mutex()); - - if (!asyncSelf->get()) { - LOG_TOPIC(INFO, arangodb::iresearch::TOPIC) - << "no view instance available while persisting definition for LogicalView"; - - return arangodb::Result(); // nothing to persist, view allready deallocated - } - - // change view throws exception on error - try { - engine->changeView(view.vocbase(), view, true); - } catch (arangodb::basics::Exception& e) { - IR_LOG_EXCEPTION(); - - return arangodb::Result( - e.code(), - std::string("caught exception during persistance of properties for arangosearch View '") + view.name() + "': " + e.what() - ); - } catch (std::exception const& e) { - IR_LOG_EXCEPTION(); - - return arangodb::Result( - TRI_ERROR_INTERNAL, - std::string("caught exception during persistance of properties for arangosearch View '") + view.name() + "': " + e.what() - ); - } catch (...) { - IR_LOG_EXCEPTION(); - - return arangodb::Result( - TRI_ERROR_INTERNAL, - std::string("caught exception during persistance of properties for arangosearch View '") + view.name() + "'" - ); - } - - return arangodb::Result(); - } - ); -} - } namespace arangodb { @@ -278,12 +188,20 @@ struct IResearchView::ViewFactory: public arangodb::ViewFactory { return res; } - TRI_set_errno(TRI_ERROR_NO_ERROR); // reset before calling createView(...) - auto impl = vocbase.createView(definition); + arangodb::LogicalView::ptr impl; + + res = arangodb::ServerState::instance()->isSingleServer() + ? arangodb::LogicalViewHelperStorageEngine::construct(impl, vocbase, definition) + : arangodb::LogicalViewHelperClusterInfo::construct(impl, vocbase, definition) + ; + + if (!res.ok()) { + return res; + } if (!impl) { return arangodb::Result( - TRI_ERROR_NO_ERROR == TRI_errno() ? TRI_ERROR_INTERNAL : TRI_errno(), + TRI_ERROR_INTERNAL, std::string("failure during instantiation while creating arangosearch View in database '") + vocbase.name() + "'" ); } @@ -375,7 +293,7 @@ IResearchView::IResearchView( TRI_vocbase_t& vocbase, arangodb::velocypack::Slice const& info, uint64_t planVersion -): LogicalViewStorageEngine(vocbase, info, planVersion), +): LogicalView(vocbase, info, planVersion), FlushTransaction(toString(*this)), _asyncFeature(nullptr), _asyncSelf(irs::memory::make_unique(this)), @@ -487,7 +405,8 @@ IResearchView::IResearchView( SCOPED_LOCK(self->mutex()); auto* view = self->get(); - if (view) { + // populate snapshot when view is registred with a transaction on single-server + if (view && arangodb::ServerState::instance()->isSingleServer()) { view->snapshot(trx, IResearchView::Snapshot::FindOrCreate); } }; @@ -498,12 +417,31 @@ IResearchView::~IResearchView() { updateProperties(_meta); // trigger reload of settings for async jobs _asyncSelf->reset(); // the view is being deallocated, its use is no longer valid (wait for all the view users to finish) _flushCallback.reset(); // unregister flush callback from flush thread + + if (arangodb::ServerState::instance()->isSingleServer()) { + arangodb::LogicalViewHelperStorageEngine::destruct(*this); // cleanup of the storage engine + } } -arangodb::Result IResearchView::appendVelocyPackDetailed( +arangodb::Result IResearchView::appendVelocyPackImpl( arangodb::velocypack::Builder& builder, + bool detailed, bool forPersistence ) const { + if (forPersistence && arangodb::ServerState::instance()->isSingleServer()) { + auto res = arangodb::LogicalViewHelperStorageEngine::properties( + builder, *this + ); + + if (!res.ok()) { + return res; + } + } + + if (!detailed) { + return arangodb::Result(); // nothing more to output + } + if (!builder.isOpenObject()) { return arangodb::Result(TRI_ERROR_BAD_PARAMETER); } @@ -773,7 +711,10 @@ arangodb::Result IResearchView::dropImpl() { ); } - return arangodb::Result(); + return arangodb::ServerState::instance()->isSingleServer() + ? arangodb::LogicalViewHelperStorageEngine::drop(*this) // single-server additionaly requires removal from the StorageEngine + : arangodb::Result() + ; } /*static*/ arangodb::ViewFactory const& IResearchView::factory() { @@ -794,7 +735,8 @@ bool IResearchView::link(AsyncLinkPtr const& link) { if (itr == _links.end()) { _links.emplace(cid, link); - } else if (ServerState::instance()->isSingleServer() && !itr->second) { + } else if (arangodb::ServerState::instance()->isSingleServer() + && !itr->second) { _links[cid] = link; return true; // single-server persisted cid placeholder substituted with actual link @@ -806,38 +748,18 @@ bool IResearchView::link(AsyncLinkPtr const& link) { return false; // link already present } - try { - auto res = persistProperties(*this, _asyncSelf); + auto res = arangodb::ServerState::instance()->isSingleServer() + ? arangodb::LogicalViewHelperStorageEngine::properties(*this) + : arangodb::Result() + ; - if (!res.ok()) { - _links.erase(cid); // undo meta modification - LOG_TOPIC(WARN, arangodb::iresearch::TOPIC) - << "failed to persist logical view while emplacing collection '" << cid - << "' into arangosearch View '" << name() << "': " << res.errorMessage(); + if (!res.ok()) { + _links.erase(cid); // undo meta modification + LOG_TOPIC(WARN, arangodb::iresearch::TOPIC) + << "failed to persist logical view while emplacing collection '" << cid + << "' into arangosearch View '" << name() << "': " << res.errorMessage(); - return false; - } - } catch (arangodb::basics::Exception& e) { - _links.erase(cid); // undo meta modification - LOG_TOPIC(WARN, arangodb::iresearch::TOPIC) - << "caught exception during persisting of logical view while emplacing collection ' " << cid - << "' into arangosearch View '" << name() << "': " << e.code() << " " << e.what(); - IR_LOG_EXCEPTION(); - throw; - } catch (std::exception const& e) { - _links.erase(cid); // undo meta modification - LOG_TOPIC(WARN, arangodb::iresearch::TOPIC) - << "caught exception during persisting of logical view while emplacing collection ' " << cid - << "' into arangosearch View '" << name() << "': " << e.what(); - IR_LOG_EXCEPTION(); - throw; - } catch (...) { - _links.erase(cid); // undo meta modification - LOG_TOPIC(WARN, arangodb::iresearch::TOPIC) - << "caught exception during persisting of logical view while emplacing collection ' " << cid - << "' into arangosearch View '" << name() << "'"; - IR_LOG_EXCEPTION(); - throw; + return false; } return true; @@ -936,9 +858,36 @@ void IResearchView::open() { } } +arangodb::Result IResearchView::properties( + arangodb::velocypack::Slice const& properties, + bool partialUpdate +) { + auto res = updateProperties(properties, partialUpdate); + + if (!res.ok()) { + return res; + } + + arangodb::aql::PlanCache::instance()->invalidate(&vocbase()); + arangodb::aql::QueryCache::instance()->invalidate(&vocbase()); + + return arangodb::ServerState::instance()->isSingleServer() + ? arangodb::LogicalViewHelperStorageEngine::properties(*this) + : arangodb::LogicalViewHelperClusterInfo::properties(*this) + ; +} + +arangodb::Result IResearchView::renameImpl(std::string const& oldName) { + return arangodb::ServerState::instance()->isSingleServer() + ? arangodb::LogicalViewHelperStorageEngine::rename(*this, oldName) + : arangodb::LogicalViewHelperClusterInfo::rename(*this, oldName) + ; +} + irs::index_reader const* IResearchView::snapshot( transaction::Methods& trx, - IResearchView::Snapshot mode /*= IResearchView::Snapshot::Find*/ + IResearchView::Snapshot mode /*= IResearchView::Snapshot::Find*/, + std::unordered_set const* shards /*= nullptr*/ ) const { if (!trx.state()) { LOG_TOPIC(WARN, arangodb::iresearch::TOPIC) @@ -947,6 +896,16 @@ irs::index_reader const* IResearchView::snapshot( return nullptr; } + std::unordered_set collections; // use set to avoid duplicate iteration of same link + + if (shards) { // add requested shards + collections = *shards; + } else { // add all known shards + for (auto& entry: _links) { + collections.emplace(entry.first); + } + } + auto& state = *(trx.state()); auto* key = this; @@ -959,10 +918,11 @@ irs::index_reader const* IResearchView::snapshot( switch (mode) { case Snapshot::Find: - return ctx; + return ctx && ctx->equalCollections(collections.begin(), collections.end()) + ? ctx : nullptr; // ensure same collections case Snapshot::FindOrCreate: - if (ctx) { - return ctx; + if (ctx && ctx->equalCollections(collections.begin(), collections.end())) { + return ctx; // ensure same collections } break; case Snapshot::SyncAndReplace: { @@ -1001,10 +961,11 @@ irs::index_reader const* IResearchView::snapshot( SCOPED_LOCK(mutex); try { - // collect snapshots from all known links - for (auto& entry: _links) { - auto cid = entry.first; - auto* link = entry.second ? entry.second->get() : nullptr; // do not need to lock link since collection is part of the transaction + // collect snapshots from all requested links + for (auto& cid: collections) { + auto itr = _links.find(cid); + auto* link = + itr != _links.end() && itr->second ? itr->second->get() : nullptr; // do not need to lock link since collection is part of the transaction if (!link) { LOG_TOPIC(ERR, arangodb::iresearch::TOPIC) @@ -1024,7 +985,7 @@ irs::index_reader const* IResearchView::snapshot( return nullptr; // skip failed readers } - ctx->add(std::move(snapshot)); + ctx->add(cid, std::move(snapshot)); } } catch (arangodb::basics::Exception& e) { LOG_TOPIC(WARN, arangodb::iresearch::TOPIC) @@ -1063,17 +1024,18 @@ arangodb::Result IResearchView::unlink(TRI_voc_cid_t cid) noexcept { _links.erase(itr); - try { - auto res = persistProperties(*this, _asyncSelf); + auto res = arangodb::ServerState::instance()->isSingleServer() + ? arangodb::LogicalViewHelperStorageEngine::properties(*this) + : arangodb::Result() + ; - if (!res.ok()) { - _links.swap(links); // restore original collections - - return res; - } - } catch (...) { + if (!res.ok()) { _links.swap(links); // restore original collections - throw; + LOG_TOPIC(WARN, arangodb::iresearch::TOPIC) + << "failed to persist logical view while unlinking collection '" << cid + << "' from arangosearch view '" << name() << "': " << res.errorMessage(); + + return res; } } catch (arangodb::basics::Exception const& e) { return arangodb::Result( @@ -1350,8 +1312,8 @@ void IResearchView::verifyKnownCollections() { ++itr; } - if (modified) { - persistProperties(*this, _asyncSelf); + if (modified && arangodb::ServerState::instance()->isSingleServer()) { + arangodb::LogicalViewHelperStorageEngine::properties(*this); } } diff --git a/arangod/IResearch/IResearchView.h b/arangod/IResearch/IResearchView.h index 0ea4479d2f..9868a4da0d 100644 --- a/arangod/IResearch/IResearchView.h +++ b/arangod/IResearch/IResearchView.h @@ -101,7 +101,7 @@ class AsyncMeta: public IResearchViewMeta { /// the IResearchLink or IResearchViewBlock /////////////////////////////////////////////////////////////////////////////// class IResearchView final - : public arangodb::LogicalViewStorageEngine, + : public arangodb::LogicalView, public arangodb::FlushTransaction { typedef std::shared_ptr> AsyncLinkPtr; public: @@ -168,7 +168,20 @@ class IResearchView final /////////////////////////////////////////////////////////////////////////////// void open() override; + ////////////////////////////////////////////////////////////////////////////// + /// @brief updates properties of an existing view + ////////////////////////////////////////////////////////////////////////////// + using LogicalDataSource::properties; + virtual arangodb::Result properties( + arangodb::velocypack::Slice const& properties, + bool partialUpdate + ) override final; + //////////////////////////////////////////////////////////////////////////////// + /// @param shards the list of shard to restrict the snaphost to + /// nullptr == use all registered links + /// !nullptr && shard not registred then return nullptr + /// if mode == Find && list found doesn't match then return nullptr /// @return pointer to an index reader containing the datastore record snapshot /// associated with 'state' /// (nullptr == no view snapshot associated with the specified state) @@ -176,7 +189,8 @@ class IResearchView final //////////////////////////////////////////////////////////////////////////////// irs::index_reader const* snapshot( transaction::Methods& trx, - Snapshot mode = Snapshot::Find + Snapshot mode = Snapshot::Find, + std::unordered_set const* shards = nullptr ) const; ////////////////////////////////////////////////////////////////////////////// @@ -186,11 +200,6 @@ class IResearchView final ////////////////////////////////////////////////////////////////////////////// arangodb::Result unlink(TRI_voc_cid_t cid) noexcept; - ////////////////////////////////////////////////////////////////////////////// - /// @brief updates properties of an existing view - ////////////////////////////////////////////////////////////////////////////// - arangodb::Result updateProperties(std::shared_ptr const& meta); // nullptr == TRI_ERROR_BAD_PARAMETER - /////////////////////////////////////////////////////////////////////////////// /// @brief visit all collection IDs that were added to the view /// @return 'visitor' success @@ -203,8 +212,9 @@ class IResearchView final /// @brief fill and return a JSON description of a IResearchView object /// only fields describing the view itself, not 'link' descriptions ////////////////////////////////////////////////////////////////////////////// - virtual arangodb::Result appendVelocyPackDetailed( + virtual arangodb::Result appendVelocyPackImpl( arangodb::velocypack::Builder& builder, + bool detailed, bool forPersistence ) const override; @@ -214,12 +224,10 @@ class IResearchView final arangodb::Result dropImpl() override; ////////////////////////////////////////////////////////////////////////////// - /// @brief called when a view's properties are updated (i.e. delta-modified) + /// @brief renames implementation-specific parts of an existing view + /// including persistance of properties ////////////////////////////////////////////////////////////////////////////// - arangodb::Result updateProperties( - arangodb::velocypack::Slice const& slice, - bool partialUpdate - ) override; + arangodb::Result renameImpl(std::string const& oldName) override; private: struct ViewFactory; // forward declaration @@ -259,6 +267,20 @@ class IResearchView final std::function _trxCallback; // for snapshot(...) std::atomic _asyncTerminate; // trigger termination of long-running async jobs std::atomic _inRecovery; + + ////////////////////////////////////////////////////////////////////////////// + /// @brief updates properties of an existing view + ////////////////////////////////////////////////////////////////////////////// + // FIXME TODO does this need to be public? + arangodb::Result updateProperties(std::shared_ptr const& meta); // nullptr == TRI_ERROR_BAD_PARAMETER + + ////////////////////////////////////////////////////////////////////////////// + /// @brief called when a view's properties are updated (i.e. delta-modified) + ////////////////////////////////////////////////////////////////////////////// + arangodb::Result updateProperties( + arangodb::velocypack::Slice const& slice, + bool partialUpdate + ); }; } // iresearch diff --git a/arangod/IResearch/IResearchViewCoordinator.cpp b/arangod/IResearch/IResearchViewCoordinator.cpp index e8d62a40df..c041b0600d 100644 --- a/arangod/IResearch/IResearchViewCoordinator.cpp +++ b/arangod/IResearch/IResearchViewCoordinator.cpp @@ -86,44 +86,12 @@ struct IResearchViewCoordinator::ViewFactory: public arangodb::ViewFactory { arangodb::LogicalView::ptr impl; - res = instantiate(impl, vocbase, definition, 0); - - if (!res.ok()) { - return res; - } - - if (!impl) { - return arangodb::Result( - TRI_ERROR_INTERNAL, - std::string("failure during instantiation while creating arangosearch View in database '") + vocbase.name() + "'" - ); - } - - arangodb::velocypack::Builder builder; - - builder.openObject(); - res = impl->properties(builder, true, true); // include links so that Agency will always have a full definition - - if (!res.ok()) { - return res; - } - - builder.close(); - - std::string error; - auto resNum = ci->createViewCoordinator( - vocbase.name(), std::to_string(impl->id()), builder.slice(), error + res = arangodb::LogicalViewHelperClusterInfo::construct( + impl, vocbase, definition ); - if (TRI_ERROR_NO_ERROR != resNum) { - if (error.empty()) { - error = TRI_errno_string(resNum); - } - - return arangodb::Result( - resNum, - std::string("failure during ClusterInfo persistance of created view while creating arangosearch View in database '") + vocbase.name() + "', error: " + error - ); + if (!res.ok()) { + return res; } // create links on a best-effor basis @@ -188,10 +156,29 @@ struct IResearchViewCoordinator::ViewFactory: public arangodb::ViewFactory { } }; -arangodb::Result IResearchViewCoordinator::appendVelocyPackDetailed( - arangodb::velocypack::Builder& builder, - bool forPersistence +IResearchViewCoordinator::~IResearchViewCoordinator() { + arangodb::LogicalViewHelperClusterInfo::destruct(*this); // cleanup of the storage engine +} + +arangodb::Result IResearchViewCoordinator::appendVelocyPackImpl( + arangodb::velocypack::Builder& builder, + bool detailed, + bool forPersistence ) const { + if (forPersistence) { + auto res = arangodb::LogicalViewHelperClusterInfo::properties( + builder, *this + ); + + if (!res.ok()) { + return res; + } + } + + if (!detailed) { + return arangodb::Result(); // nothing more to output + } + if (!builder.isOpenObject()) { return arangodb::Result( TRI_ERROR_BAD_PARAMETER, @@ -328,6 +315,12 @@ bool IResearchViewCoordinator::emplace( return factory; } +arangodb::Result IResearchViewCoordinator::renameImpl( + std::string const& oldName +) { + return arangodb::LogicalViewHelperClusterInfo::rename(*this, oldName); +} + arangodb::Result IResearchViewCoordinator::unlink(TRI_voc_cid_t cid) noexcept { return arangodb::Result(); // NOOP since no internal store } @@ -336,7 +329,7 @@ IResearchViewCoordinator::IResearchViewCoordinator( TRI_vocbase_t& vocbase, velocypack::Slice info, uint64_t planVersion -) : LogicalViewClusterInfo(vocbase, info, planVersion) { +) : LogicalView(vocbase, info, planVersion) { TRI_ASSERT(ServerState::instance()->isCoordinator()); } @@ -416,21 +409,13 @@ arangodb::Result IResearchViewCoordinator::properties( // only trigger persisting of properties if they have changed if (_meta != meta) { - arangodb::velocypack::Builder builder; + auto oldMeta = std::move(_meta); - builder.openObject(); - meta.json(builder); + _meta = std::move(meta); // update meta for persistence - auto result = properties(builder, false, true); + auto result = arangodb::LogicalViewHelperClusterInfo::properties(*this); - if (!result.ok()) { - return result; - } - - builder.close(); - result = engine->setViewPropertiesCoordinator( - vocbase().name(), std::to_string(id()), builder.slice() - ); + _meta = std::move(oldMeta); // restore meta if (!result.ok()) { return result; @@ -551,7 +536,7 @@ Result IResearchViewCoordinator::dropImpl() { } } - return {}; + return arangodb::LogicalViewHelperClusterInfo::drop(*this); } } // iresearch diff --git a/arangod/IResearch/IResearchViewCoordinator.h b/arangod/IResearch/IResearchViewCoordinator.h index addbde0d4a..32e07ac4c7 100644 --- a/arangod/IResearch/IResearchViewCoordinator.h +++ b/arangod/IResearch/IResearchViewCoordinator.h @@ -44,8 +44,9 @@ namespace iresearch { /// @brief an abstraction over the distributed IResearch index implementing the /// LogicalView interface /////////////////////////////////////////////////////////////////////////////// -class IResearchViewCoordinator final : public arangodb::LogicalViewClusterInfo { +class IResearchViewCoordinator final: public arangodb::LogicalView { public: + virtual ~IResearchViewCoordinator(); //////////////////////////////////////////////////////////////////////////////// /// @brief acquire locks on the specified 'cid' during read-transactions @@ -89,13 +90,16 @@ class IResearchViewCoordinator final : public arangodb::LogicalViewClusterInfo { protected: - virtual Result appendVelocyPackDetailed( - arangodb::velocypack::Builder& builder, - bool forPersistence + virtual Result appendVelocyPackImpl( + arangodb::velocypack::Builder& builder, + bool detailed, + bool forPersistence ) const override; virtual arangodb::Result dropImpl() override; + arangodb::Result renameImpl(std::string const& oldName) override; + private: struct ViewFactory; // forward declaration diff --git a/arangod/IResearch/IResearchViewDBServer.cpp b/arangod/IResearch/IResearchViewDBServer.cpp deleted file mode 100644 index a4cf42f46f..0000000000 --- a/arangod/IResearch/IResearchViewDBServer.cpp +++ /dev/null @@ -1,774 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// -/// DISCLAIMER -/// -/// Copyright 2018 ArangoDB GmbH, Cologne, Germany -/// -/// Licensed under the Apache License, Version 2.0 (the "License"); -/// you may not use this file except in compliance with the License. -/// You may obtain a copy of the License at -/// -/// http://www.apache.org/licenses/LICENSE-2.0 -/// -/// Unless required by applicable law or agreed to in writing, software -/// distributed under the License is distributed on an "AS IS" BASIS, -/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -/// See the License for the specific language governing permissions and -/// limitations under the License. -/// -/// Copyright holder is ArangoDB GmbH, Cologne, Germany -/// -/// @author Andrey Abramov -/// @author Vasiliy Nabatchikov -//////////////////////////////////////////////////////////////////////////////// - -#include "IResearchCommon.h" -#include "IResearchDocument.h" -#include "IResearchFeature.h" -#include "IResearchLinkHelper.h" -#include "IResearchView.h" -#include "IResearchViewDBServer.h" -#include "VelocyPackHelper.h" -#include "Basics/StaticStrings.h" -#include "Cluster/ClusterInfo.h" -#include "Logger/LogMacros.h" -#include "RestServer/DatabasePathFeature.h" -#include "RestServer/ViewTypesFeature.h" -#include "StorageEngine/TransactionState.h" -#include "Transaction/Methods.h" -#include "VocBase/LogicalCollection.h" -#include "VocBase/vocbase.h" - -namespace { - -typedef irs::async_utils::read_write_mutex::read_mutex ReadMutex; -typedef irs::async_utils::read_write_mutex::write_mutex WriteMutex; - -//////////////////////////////////////////////////////////////////////////////// -/// @brief the view name prefix of per-cid view instances -//////////////////////////////////////////////////////////////////////////////// -std::string const VIEW_NAME_PREFIX("_iresearch_"); - -//////////////////////////////////////////////////////////////////////////////// -/// @brief index reader implementation over multiple irs::index_reader -//////////////////////////////////////////////////////////////////////////////// -class CompoundReader final: public irs::index_reader { - public: - irs::sub_reader const& operator[]( - size_t subReaderId - ) const noexcept override { - TRI_ASSERT(subReaderId < _subReaders.size()); - return *(_subReaders[subReaderId]); - } - - void add(irs::index_reader const& reader) { - for(auto& entry: reader) { - _subReaders.emplace_back(&entry); - } - } - - void clear() noexcept { _subReaders.clear(); } - virtual uint64_t docs_count() const override; - virtual uint64_t live_docs_count() const override; - virtual size_t size() const noexcept override { return _subReaders.size(); } - - private: - std::vector _subReaders; -}; - -uint64_t CompoundReader::docs_count() const { - uint64_t count = 0; - - for (auto& entry: _subReaders) { - count += entry->docs_count(); - } - - return count; -} - -uint64_t CompoundReader::live_docs_count() const { - uint64_t count = 0; - - for (auto& entry: _subReaders) { - count += entry->live_docs_count(); - } - - return count; -} - -//////////////////////////////////////////////////////////////////////////////// -/// @brief the container storing the view state for a given TransactionState -/// @note it is assumed that DBServer ViewState resides in the same -/// TransactionState as the IResearchView ViewState, therefore a separate -/// lock is not required to be held by the DBServer CompoundReader -//////////////////////////////////////////////////////////////////////////////// -struct ViewState: public arangodb::TransactionState::Cookie { - CompoundReader _snapshot; -}; - -//////////////////////////////////////////////////////////////////////////////// -/// @brief generate the name used for the per-cid views -/// must be unique to avoid view collisions in vocbase -//////////////////////////////////////////////////////////////////////////////// -std::string generateName(TRI_voc_cid_t viewId, TRI_voc_cid_t collectionId) { - return VIEW_NAME_PREFIX - + std::to_string(collectionId) - + "_" + std::to_string(viewId) - ; -} - -} - -namespace arangodb { -namespace iresearch { - -//////////////////////////////////////////////////////////////////////////////// -/// @brief IResearchView-specific implementation of a ViewFactory -//////////////////////////////////////////////////////////////////////////////// -struct IResearchViewDBServer::ViewFactory: public arangodb::ViewFactory { - virtual arangodb::Result create( - arangodb::LogicalView::ptr& view, - TRI_vocbase_t& vocbase, - arangodb::velocypack::Slice const& definition - ) const override { - auto* ci = ClusterInfo::instance(); - - if (!ci) { - return arangodb::Result( - TRI_ERROR_INTERNAL, - std::string("failure to find 'ClusterInfo' instance while creating arangosearch View in database '") + vocbase.name() + "'" - ); - } - - arangodb::LogicalView::ptr impl; - auto res = instantiate(impl, vocbase, definition, 0); - - if (!res.ok()) { - return res; - } - - if (!impl) { - return arangodb::Result( - TRI_ERROR_INTERNAL, - std::string("failure during instantiation while creating arangosearch View in database '") + vocbase.name() + "'" - ); - } - - arangodb::velocypack::Builder builder; - - builder.openObject(); - res = impl->properties(builder, true, true); // include links so that Agency will always have a full definition - - if (!res.ok()) { - return res; - } - - builder.close(); - - std::string error; - auto resNum = ci->createViewCoordinator( - vocbase.name(), std::to_string(impl->id()), builder.slice(), error - ); - - if (TRI_ERROR_NO_ERROR != resNum) { - if (error.empty()) { - error = TRI_errno_string(resNum); - } - - return arangodb::Result( - resNum, - std::string("failure during ClusterInfo persistance of created view while creating arangosearch View in database '") + vocbase.name() + "', error: " + error - ); - } - - // NOTE: link creation is ignored since on the db-server links are created - // by their LogicalCollections themselves - - view = ci->getView(vocbase.name(), std::to_string(impl->id())); // refresh view from Agency - - if (view) { - view->open(); // open view to match the behaviour in StorageEngine::openExistingDatabase(...) and original behaviour of TRI_vocbase_t::createView(...) - } - - return arangodb::Result(); - } - - virtual arangodb::Result instantiate( - arangodb::LogicalView::ptr& view, - TRI_vocbase_t& vocbase, - arangodb::velocypack::Slice const& definition, - uint64_t planVersion - ) const override { - irs::string_ref name; - bool seen; - - if (!getString(name, definition, arangodb::StaticStrings::DataSourceName, seen, irs::string_ref::EMPTY) - || !seen) { - return arangodb::Result( - TRI_ERROR_BAD_PARAMETER, - std::string("definition supplied without a 'name' while instantiating arangosearch View in database '") + vocbase.name() + "'" - ); - } - - // not a per-cid view instance (get here from ClusterInfo) - if (!irs::starts_with(name, VIEW_NAME_PREFIX)) { - auto* feature = arangodb::application_features::ApplicationServer::lookupFeature< - arangodb::DatabasePathFeature - >("DatabasePath"); - - if (!feature) { - return arangodb::Result( - TRI_ERROR_INTERNAL, - std::string("failure to find feature 'DatabasePath' while constructing arangosearch View in database '") + vocbase.name() + "'" - ); - } - - auto* ci = ClusterInfo::instance(); - - if (!ci) { - return arangodb::Result( - TRI_ERROR_INTERNAL, - std::string("failure to find 'ClusterInfo' instance while constructing arangosearch View in database '") + vocbase.name() + "'" - ); - } - - std::string error; - auto meta = std::make_shared(); - - if (!meta->init(definition, error)) { - return arangodb::Result( - TRI_ERROR_BAD_PARAMETER, - error.empty() - ? (std::string("failed to initialize arangosearch View '") + static_cast(name) + "' from definition: " + definition.toString()) - : (std::string("failed to initialize arangosearch View '") + static_cast(name) + "' from definition, error in attribute '" + error + "': " + definition.toString()) - ); - } - - view = std::shared_ptr( - new IResearchViewDBServer(vocbase, definition, *feature, planVersion, std::move(meta)) - ); - - return arangodb::Result(); - } - - // ......................................................................... - // a per-cid view instance - // get here only from StorageEngine startup or WAL recovery - // ......................................................................... - - view = vocbase.lookupView(name); - - if (view) { - return arangodb::Result(); // resuse view from vocbase - } - - // no view for shard - arangodb::LogicalView::ptr impl; - auto res = IResearchView::factory().instantiate( - impl, vocbase, definition, planVersion - ); - - if (!res.ok()) { - return res; - } - - if (!impl) { - return arangodb::Result( - TRI_ERROR_INTERNAL, - std::string("failure during instantiation while creating an arangosearch View '") + std::string(name) + "' in database '" + vocbase.name() + "'" - ); - } - - // a wrapper to remove the view from vocbase if it no longer has any links - // hold a reference to the original view in the deleter so that the view is - // still valid for the duration of the pointer wrapper - view = std::shared_ptr( - impl.get(), - [impl] (arangodb::LogicalView*) noexcept -> void { - auto& vocbase = impl->vocbase(); - - // suppress any errors in destructor - - try { - // same view in vocbase and with no collections - if (impl == vocbase.lookupView(impl->id()) // avoid double dropView(...) - && impl->visitCollections([](TRI_voc_cid_t){ return false; }) - && !impl->drop().ok()) { // per-cid collections always system - LOG_TOPIC(WARN, arangodb::iresearch::TOPIC) - << "failure to drop stale arangosearch View '" << impl->name() << "' while from database '" << vocbase.name() << "'"; - } - } catch (basics::Exception const& e) { - LOG_TOPIC(ERR, arangodb::iresearch::TOPIC) - << "caught exception while dropping stale arangosearch View '" << impl->name() - << "' while from database '" << vocbase.name() - << "', errorCode: '" << e.code() - << "', error: '" << e.message(); - } catch (std::exception const& e) { - LOG_TOPIC(ERR, arangodb::iresearch::TOPIC) - << "caught exception while dropping stale arangosearch View '" << impl->name() - << "' while from database '" << vocbase.name() - << "', error: '" << e.what() << "'"; - } catch (...) { - LOG_TOPIC(ERR, arangodb::iresearch::TOPIC) - << "caught an unspecified exception while dropping stale arangosearch View '" << impl->name() - << "' while from database '" << vocbase.name() << "'"; - } - } - ); - - return arangodb::Result(); - } -}; - -IResearchViewDBServer::IResearchViewDBServer( - TRI_vocbase_t& vocbase, - arangodb::velocypack::Slice const& info, - arangodb::DatabasePathFeature const& /*dbPathFeature*/, - uint64_t planVersion, - std::shared_ptr meta /*=nullptr*/ -) : LogicalViewClusterInfo(vocbase, info, planVersion), - _meta(std::move(meta)) { -} - -IResearchViewDBServer::~IResearchViewDBServer() noexcept { - _collections.clear(); // ensure view distructors called before mutex is deallocated -} - -arangodb::Result IResearchViewDBServer::appendVelocyPackDetailed( - arangodb::velocypack::Builder& builder, - bool forPersistence -) const { - if (!builder.isOpenObject()) { - return arangodb::Result( - TRI_ERROR_BAD_PARAMETER, - std::string("invalid builder provided for IResearchViewDBServer definition") - ); - } - - { - SCOPED_LOCK(_meta->read()); // '_meta' can be asynchronously updated - - static const std::function acceptor = []( - irs::string_ref const& key - )->bool { - return key != StaticStrings::VersionField; // ignored fields - }; - static const std::function persistenceAcceptor = []( - irs::string_ref const& - )->bool { - return true; - }; - arangodb::velocypack::Builder sanitizedBuilder; - - sanitizedBuilder.openObject(); - - if (!_meta->json(sanitizedBuilder) - || !mergeSliceSkipKeys(builder, sanitizedBuilder.close().slice(), forPersistence ? persistenceAcceptor : acceptor)) { - return arangodb::Result( - TRI_ERROR_INTERNAL, - std::string("failure to generate definition while generating properties jSON for arangosearch view in database '") + vocbase().name() + "'" - ); - } - } - - return arangodb::Result(); -} - - -arangodb::Result IResearchViewDBServer::dropImpl() { - WriteMutex mutex(_mutex); - SCOPED_LOCK(mutex); // 'collections_' can be asynchronously read - - for (auto itr = _collections.begin(); itr != _collections.end();) { - auto res = itr->second->drop(); - - if (!res.ok()) { - return res; // fail on first failure - } - - itr = _collections.erase(itr); - } - - return arangodb::Result(); -} - -std::shared_ptr IResearchViewDBServer::ensure( - TRI_voc_cid_t cid, - bool create /*= true*/ -) { - WriteMutex mutex(_mutex); - SCOPED_LOCK(mutex); // 'collections_' can be asynchronously read - auto itr = _collections.find(cid); - - if (itr != _collections.end()) { - return itr->second; - } - - auto viewName = generateName(id(), cid); - auto view = vocbase().lookupView(viewName); // on startup a IResearchView might only be in vocbase but not in a brand new IResearchViewDBServer - auto* impl = LogicalView::cast(view.get()); - - if (impl) { - _collections.emplace(cid, view); // track the IResearchView instance from vocbase - impl->updateProperties(_meta); - - return view; // do not wrap in deleter since view already present in vocbase (as if already present in '_collections') - } - - if (!create) { - return nullptr; - } - - arangodb::velocypack::Builder builder; - - builder.openObject(); - builder.add( - arangodb::StaticStrings::DataSourceSystem, - arangodb::velocypack::Value(true) - ); // required to for use of VIEW_NAME_PREFIX - builder.add( - arangodb::StaticStrings::DataSourceName, - toValuePair(viewName) - ); // mark the view definition as an internal per-cid instance - builder.add( - arangodb::StaticStrings::DataSourcePlanId, - arangodb::velocypack::Value(id()) - ); // planId required for cluster-wide view lookup from per-cid view - builder.add( - arangodb::StaticStrings::DataSourceType, - toValuePair(DATA_SOURCE_TYPE.name()) - ); // type required for proper factory selection - - { - SCOPED_LOCK(_meta->read()); // '_meta' can be asynchronously updated - - if (!_meta->json(builder)) { - builder.close(); // close StaticStrings::PropertiesField - LOG_TOPIC(WARN, arangodb::iresearch::TOPIC) - << "failure to generate properties definition while constructing arangosearch view in database '" << vocbase().name() << "'"; - - return nullptr; - } - } - - builder.close(); - view = vocbase().createView(builder.slice()); - impl = LogicalView::cast(view.get()); - - if (!impl) { - LOG_TOPIC(WARN, arangodb::iresearch::TOPIC) - << "failure while creating an arangosearch view for collection '" << cid << "' in database '" << vocbase().name() << "'"; - - return nullptr; - } - - // FIXME should we register? - _collections.emplace(cid, view); - impl->updateProperties(_meta); - - // hold a reference to the original view in the deleter so that the view is still valid for the duration of the pointer wrapper - // this shared_ptr should not be stored in TRI_vocbase_t since the deleter depends on 'this' - return std::shared_ptr( - view.get(), - [this, view, cid](arangodb::LogicalView*)->void { - // FIXME destructor has to be noexcept - static const auto visitor = [](TRI_voc_cid_t)->bool { return false; }; - auto& vocbase = view->vocbase(); - - // same view in vocbase and with no collections - if (view.get() == vocbase.lookupView(view->id()).get() // avoid double dropView(...) - && view->visitCollections(visitor)) { - // FIXME TODO ensure somehow that 'this' is still valid - unlink(cid); - } - } - ); -} - -/*static*/ arangodb::ViewFactory const& IResearchViewDBServer::factory() { - static const ViewFactory factory; - - return factory; -} - -void IResearchViewDBServer::open() { - ReadMutex mutex(_mutex); - SCOPED_LOCK(mutex); // 'collections_' can be asynchronously modified - - for (auto& entry: _collections) { - entry.second->open(); - } -} - -irs::index_reader const* IResearchViewDBServer::snapshot( - transaction::Methods& trx, - std::vector const& shards, - IResearchView::Snapshot mode /*= IResearchView::Snapshot::Find*/ -) const { - auto* state = trx.state(); - - if (!state) { - LOG_TOPIC(WARN, arangodb::iresearch::TOPIC) - << "failed to get transaction state while creating arangosearch view snapshot"; - - return nullptr; - } - - // TODO FIXME find a better way to look up a ViewState - #ifdef ARANGODB_ENABLE_MAINTAINER_MODE - auto* cookie = dynamic_cast(state->cookie(this)); - #else - auto* cookie = static_cast(state->cookie(this)); - #endif - - switch (mode) { - case IResearchView::Snapshot::Find: - return cookie ? &cookie->_snapshot : nullptr; - case IResearchView::Snapshot::FindOrCreate: - if (cookie) { - return &cookie->_snapshot; - } - break; - default: - break; - } - - auto* resolver = trx.resolver(); - - if (!resolver) { - LOG_TOPIC(WARN, arangodb::iresearch::TOPIC) - << "failed to retrieve CollectionNameResolver from the transaction"; - - return nullptr; - } - - std::unique_ptr cookiePtr; - CompoundReader* reader = nullptr; - - if (!cookie) { - cookiePtr = irs::memory::make_unique(); - reader = &cookiePtr->_snapshot; - } else { - reader = &cookie->_snapshot; - reader->clear(); - } - - TRI_ASSERT(reader); - - ReadMutex mutex(_mutex); - SCOPED_LOCK(mutex); // 'collections_' can be asynchronously modified - - try { - for (auto& shardId : shards) { - auto shard = resolver->getCollection(shardId); - - if (!shard) { - LOG_TOPIC(ERR, arangodb::iresearch::TOPIC) - << "failed to find shard by id '" << shardId << "', skipping it"; - continue; - } - - auto cid = shard->id(); - auto const shardView = _collections.find(cid); - - if (shardView == _collections.end()) { - LOG_TOPIC(ERR, arangodb::iresearch::TOPIC) - << "failed to find shard view for shard id '" << cid << "', skipping it"; - continue; - } - - auto* rdr = LogicalView::cast(*shardView->second).snapshot(trx, mode); - - if (rdr) { - reader->add(*rdr); - } - } - } catch (arangodb::basics::Exception& e) { - LOG_TOPIC(WARN, arangodb::iresearch::TOPIC) - << "caught exception while collecting readers for snapshot of DBServer arangosearch view '" << id() - << "': " << e.code() << " " << e.what(); - IR_LOG_EXCEPTION(); - - return nullptr; - } catch (std::exception const& e) { - LOG_TOPIC(WARN, arangodb::iresearch::TOPIC) - << "caught exception while collecting readers for snapshot of DBServer arangosearch view '" << id() - << "': " << e.what(); - IR_LOG_EXCEPTION(); - - return nullptr; - } catch (...) { - LOG_TOPIC(WARN, arangodb::iresearch::TOPIC) - << "caught exception while collecting readers for snapshot of DBServer arangosearch view '" << id() << "'"; - IR_LOG_EXCEPTION(); - - return nullptr; - } - - if (cookiePtr) { - state->cookie(this, std::move(cookiePtr)); - } - - return reader; -} - -arangodb::Result IResearchViewDBServer::unlink(TRI_voc_cid_t cid) noexcept { - try { - WriteMutex mutex(_mutex); - SCOPED_LOCK(mutex); // 'collections_' can be asynchronously read - auto itr = _collections.find(cid); - - if (itr == _collections.end()) { - return arangodb::Result(TRI_ERROR_ARANGO_DATA_SOURCE_NOT_FOUND); - } - - auto res = itr->second->drop(); - - if (!res.ok()) { - return res; - } - - _collections.erase(itr); - } catch (arangodb::basics::Exception const& e) { - return arangodb::Result( - e.code(), - std::string("caught exception while unlinking collection '") + std::to_string(cid) + "' from arangosearch view '" + name() + "': " + e.what() - ); - } catch (std::exception const& e) { - return arangodb::Result( - TRI_ERROR_INTERNAL, - std::string("caught exception while unlinking collection '") + std::to_string(cid) + "' from arangosearch view '" + name() + "': " + e.what() - ); - } catch (...) { - return arangodb::Result( - TRI_ERROR_INTERNAL, - std::string("caught exception while unlinking collection '") + std::to_string(cid) + "' from arangosearch view '" + name() + "'" - ); - } - - return arangodb::Result(); -} - -arangodb::Result IResearchViewDBServer::properties( - arangodb::velocypack::Slice const& slice, - bool partialUpdate -) { - if (!slice.isObject()) { - return arangodb::Result( - TRI_ERROR_BAD_PARAMETER, - std::string("invalid properties supplied while updating arangosearch view in database '") + vocbase().name() + "'" - ); - } - - // ........................................................................... - // sanitize update slice - // ........................................................................... - - static const std::function propsAcceptor = []( - irs::string_ref const& key - )->bool { - return key != StaticStrings::LinksField; // ignored fields - }; - arangodb::velocypack::Builder props; - - props.openObject(); - - if (!mergeSliceSkipKeys(props, slice, propsAcceptor)) { - return arangodb::Result( - TRI_ERROR_INTERNAL, - std::string("failure to generate definition while updating arangosearch view in database '") + vocbase().name() + "'" - ); - } - - props.close(); - - IResearchViewMeta meta; - std::string error; - - if (partialUpdate) { - SCOPED_LOCK(_meta->read()); - - if (!meta.init(props.slice(), error, *_meta)) { - return arangodb::Result( - TRI_ERROR_BAD_PARAMETER, - std::string("failure parsing properties while updating arangosearch view in database '") + vocbase().name() + "'" - ); - } - } else if (!meta.init(props.slice(), error)) { - return arangodb::Result( - TRI_ERROR_BAD_PARAMETER, - std::string("failure parsing properties while updating arangosearch view in database '") + vocbase().name() + "'" - ); - } - - WriteMutex mutex(_mutex); - SCOPED_LOCK(mutex); // '_collections' can be asynchronously read - - { - SCOPED_LOCK(_meta->write()); - - // reset non-updatable values to match current meta - meta._locale = _meta->_locale; - - static_cast(*_meta) = std::move(meta); - } - - auto* feature = arangodb::application_features::ApplicationServer::lookupFeature< - arangodb::iresearch::IResearchFeature - >(); - - if (feature) { - feature->asyncNotify(); - } - - if (!slice.hasKey(StaticStrings::LinksField) && partialUpdate) { - return arangodb::Result(); - } - - // ........................................................................... - // update links if requested (on a best-effort basis) - // ........................................................................... - - std::unordered_set collections; - auto links = slice.hasKey(StaticStrings::LinksField) - ? slice.get(StaticStrings::LinksField) - : arangodb::velocypack::Slice::emptyObjectSlice(); // used for !partialUpdate - - - if (partialUpdate) { - return IResearchLinkHelper::updateLinks( - collections, vocbase(), *this, links - ); - } - - std::unordered_set stale; - - for (auto& entry: _collections) { - stale.emplace(entry.first); - } - - return IResearchLinkHelper::updateLinks( - collections, vocbase(), *this, links, stale - ); -} - -bool IResearchViewDBServer::visitCollections( - CollectionVisitor const& visitor -) const { - ReadMutex mutex(_mutex); - SCOPED_LOCK(mutex); // 'collections_' can be asynchronously modified - - for (auto& entry: _collections) { - if (!visitor(entry.first)) { - return false; - } - } - - return true; -} - -} // iresearch -} // arangodb - -// ----------------------------------------------------------------------------- -// --SECTION-- END-OF-FILE -// ----------------------------------------------------------------------------- \ No newline at end of file diff --git a/arangod/IResearch/IResearchViewDBServer.h b/arangod/IResearch/IResearchViewDBServer.h deleted file mode 100644 index 26d6d5dc62..0000000000 --- a/arangod/IResearch/IResearchViewDBServer.h +++ /dev/null @@ -1,132 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// -/// DISCLAIMER -/// -/// Copyright 2018 ArangoDB GmbH, Cologne, Germany -/// -/// Licensed under the Apache License, Version 2.0 (the "License"); -/// you may not use this file except in compliance with the License. -/// You may obtain a copy of the License at -/// -/// http://www.apache.org/licenses/LICENSE-2.0 -/// -/// Unless required by applicable law or agreed to in writing, software -/// distributed under the License is distributed on an "AS IS" BASIS, -/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -/// See the License for the specific language governing permissions and -/// limitations under the License. -/// -/// Copyright holder is ArangoDB GmbH, Cologne, Germany -/// -/// @author Andrey Abramov -/// @author Vasiliy Nabatchikov -//////////////////////////////////////////////////////////////////////////////// - -#ifndef ARANGOD_IRESEARCH__IRESEARCH_VIEW_DBSERVER_H -#define ARANGOD_IRESEARCH__IRESEARCH_VIEW_DBSERVER_H 1 - -#include "utils/async_utils.hpp" - -#include "IResearchView.h" -#include "Transaction/Status.h" -#include "velocypack/Builder.h" -#include "VocBase/LogicalView.h" - -namespace arangodb { - -class DatabasePathFeature; -class TransactionState; -struct ViewFactory; // forward declaration -class CollectionNameResolver; - -namespace transaction { - -class Methods; // forward declaration - -} // transaction - -} // arangodb - -namespace arangodb { -namespace iresearch { - -class AsyncMeta; - -class IResearchViewDBServer final: public arangodb::LogicalViewClusterInfo { - public: - virtual ~IResearchViewDBServer() noexcept; - - ////////////////////////////////////////////////////////////////////////////// - /// @brief ensure there is a view instance for the specified 'cid' - /// @param force creation of a new instance if none is available in vocbase - /// @return an existing instance or create a new instance if none is registred - /// on ptr reset the view will be dropped if it has no collections - /// @note view created in vocbase() to match callflow during regular startup - ////////////////////////////////////////////////////////////////////////////// - std::shared_ptr ensure( - TRI_voc_cid_t cid, - bool create = true - ); - - ////////////////////////////////////////////////////////////////////////////// - /// @brief the factory for this type of view - ////////////////////////////////////////////////////////////////////////////// - static arangodb::ViewFactory const& factory(); - - virtual void open() override; - - virtual arangodb::Result properties( - arangodb::velocypack::Slice const& properties, - bool partialUpdate - ) override; - - //////////////////////////////////////////////////////////////////////////////// - /// @return pointer to an index reader containing the datastore record snapshot - /// associated with 'state' - /// (nullptr == no view snapshot associated with the specified state) - /// if force == true && no snapshot -> associate current snapshot - //////////////////////////////////////////////////////////////////////////////// - irs::index_reader const* snapshot( - transaction::Methods& trx, - std::vector const& shards, - IResearchView::Snapshot mode = IResearchView::Snapshot::Find - ) const; - - ////////////////////////////////////////////////////////////////////////////// - /// @brief unlink remove 'cid' from the persisted list of tracked collection - /// IDs - /// @return success == view does not track collection - ////////////////////////////////////////////////////////////////////////////// - arangodb::Result unlink(TRI_voc_cid_t cid) noexcept; - - virtual bool visitCollections( - CollectionVisitor const& visitor - ) const override; - - protected: - virtual arangodb::Result appendVelocyPackDetailed( - arangodb::velocypack::Builder& builder, - bool forPersistence - ) const override; - - virtual arangodb::Result dropImpl() override; - - private: - struct ViewFactory; // forward declaration - - std::map> _collections; - std::shared_ptr _meta; // the shared view configuration (never null!!!) - mutable irs::async_utils::read_write_mutex _mutex; // for use with members - - IResearchViewDBServer( - TRI_vocbase_t& vocbase, - arangodb::velocypack::Slice const& info, - arangodb::DatabasePathFeature const& dbPathFeature, - uint64_t planVersion, - std::shared_ptr meta = nullptr - ); -}; - -} // iresearch -} // arangodb - -#endif \ No newline at end of file diff --git a/arangod/IResearch/IResearchViewNode.cpp b/arangod/IResearch/IResearchViewNode.cpp index aa55aa7d86..b8dedf43be 100644 --- a/arangod/IResearch/IResearchViewNode.cpp +++ b/arangod/IResearch/IResearchViewNode.cpp @@ -23,7 +23,6 @@ #include "IResearchCommon.h" #include "IResearchViewCoordinator.h" -#include "IResearchViewDBServer.h" #include "IResearchViewNode.h" #include "IResearchViewBlock.h" #include "IResearchOrderFactory.h" @@ -39,8 +38,8 @@ #include "Basics/StringUtils.h" #include "Cluster/ClusterInfo.h" #include "StorageEngine/TransactionState.h" - #include "velocypack/Iterator.h" +#include "VocBase/LogicalCollection.h" namespace { @@ -676,9 +675,24 @@ std::unique_ptr IResearchViewNode::createBlock( IResearchView::Snapshot::FindOrCreate, IResearchView::Snapshot::SyncAndReplace }; + std::unordered_set collections; + auto& resolver = engine.getQuery()->resolver(); - reader = LogicalView::cast(view).snapshot( - *trx, _shards, SNAPSHOT[size_t(_options.forceSync)] + for (auto& shard: _shards) { + auto collection = resolver.getCollection(shard); + + if (!collection) { + THROW_ARANGO_EXCEPTION(arangodb::Result( + TRI_ERROR_ARANGO_DATA_SOURCE_NOT_FOUND, + std::string("failed to find shard by id '") + shard + "'" + )); + } + + collections.emplace(collection->id()); + } + + reader = LogicalView::cast(view).snapshot( + *trx, SNAPSHOT[size_t(_options.forceSync)], &collections ); } else { static IResearchView::Snapshot const SNAPSHOT[] { @@ -693,7 +707,7 @@ std::unique_ptr IResearchViewNode::createBlock( if (!reader) { LOG_TOPIC(WARN, arangodb::iresearch::TOPIC) - << "failed to get snapshot while creating arangosearch view ExecutionBlock for view '" << view.name() << "' tid '"; + << "failed to get snapshot while creating arangosearch view ExecutionBlock for view '" << view.name() << "'"; THROW_ARANGO_EXCEPTION_MESSAGE( TRI_ERROR_INTERNAL, diff --git a/arangod/MMFiles/MMFilesEngine.cpp b/arangod/MMFiles/MMFilesEngine.cpp index 8686a57af6..e51bf5e5b5 100644 --- a/arangod/MMFiles/MMFilesEngine.cpp +++ b/arangod/MMFiles/MMFilesEngine.cpp @@ -1400,8 +1400,7 @@ Result MMFilesEngine::createView( "Database") ->forceSyncProperties(); - saveViewInfo(&vocbase, &view, doSync); - + saveViewInfo(vocbase, view, doSync); if (inRecovery()) { // Nothing more do. In recovery we do not write markers. @@ -1452,13 +1451,13 @@ void MMFilesEngine::getViewProperties( } arangodb::Result MMFilesEngine::dropView( - TRI_vocbase_t& vocbase, - LogicalView& view + TRI_vocbase_t const& vocbase, + LogicalView const& view ) { auto* db = application_features::ApplicationServer::getFeature("Database"); TRI_ASSERT(db); - saveViewInfo(&vocbase, &view, db->forceSyncProperties()); + saveViewInfo(vocbase, view, db->forceSyncProperties()); if (inRecovery()) { // nothing to do here @@ -1498,8 +1497,8 @@ arangodb::Result MMFilesEngine::dropView( } void MMFilesEngine::destroyView( - TRI_vocbase_t& vocbase, - LogicalView& view + TRI_vocbase_t const& vocbase, + LogicalView const& view ) noexcept { try { auto directory = viewDirectory(vocbase.id(), view.id()); @@ -1514,14 +1513,16 @@ void MMFilesEngine::destroyView( } } -void MMFilesEngine::saveViewInfo(TRI_vocbase_t* vocbase, - arangodb::LogicalView const* view, - bool forceSync) const { - std::string const filename = viewParametersFilename(vocbase->id(), view->id()); +void MMFilesEngine::saveViewInfo( + TRI_vocbase_t const& vocbase, + LogicalView const& view, + bool forceSync +) const { + auto filename = viewParametersFilename(vocbase.id(), view.id()); VPackBuilder builder; builder.openObject(); - view->properties(builder, true, true); + view.properties(builder, true, true); builder.close(); LOG_TOPIC(TRACE, Logger::VIEWS) @@ -1572,7 +1573,8 @@ Result MMFilesEngine::changeView( } } - saveViewInfo(&vocbase, &view, doSync); + saveViewInfo(vocbase, view, doSync); + return {}; } diff --git a/arangod/MMFiles/MMFilesEngine.h b/arangod/MMFiles/MMFilesEngine.h index fbf91675d2..0d63c2b860 100644 --- a/arangod/MMFiles/MMFilesEngine.h +++ b/arangod/MMFiles/MMFilesEngine.h @@ -374,19 +374,23 @@ class MMFilesEngine final : public StorageEngine { ) override; arangodb::Result dropView( - TRI_vocbase_t& vocbase, - LogicalView& view + TRI_vocbase_t const& vocbase, + LogicalView const& view ) override; void destroyView( - TRI_vocbase_t& vocbase, - LogicalView& view + TRI_vocbase_t const& vocbase, + LogicalView const& view ) noexcept override; std::string createViewDirectoryName(std::string const& basePath, TRI_voc_cid_t id); - void saveViewInfo(TRI_vocbase_t* vocbase, arangodb::LogicalView const*, bool sync) const; + void saveViewInfo( + TRI_vocbase_t const& vocbase, + LogicalView const& view, + bool sync + ) const; void signalCleanup(TRI_vocbase_t& vocbase) override; @@ -627,4 +631,4 @@ class MMFilesEngine final : public StorageEngine { } -#endif +#endif \ No newline at end of file diff --git a/arangod/RocksDBEngine/RocksDBEngine.cpp b/arangod/RocksDBEngine/RocksDBEngine.cpp index 122cc24c43..134ed6e75e 100644 --- a/arangod/RocksDBEngine/RocksDBEngine.cpp +++ b/arangod/RocksDBEngine/RocksDBEngine.cpp @@ -1439,8 +1439,8 @@ Result RocksDBEngine::createView( } arangodb::Result RocksDBEngine::dropView( - TRI_vocbase_t& vocbase, - LogicalView& view + TRI_vocbase_t const& vocbase, + LogicalView const& view ) { #ifdef ARANGODB_ENABLE_MAINTAINER_MODE LOG_TOPIC(DEBUG, Logger::ENGINES) << "RocksDBEngine::dropView"; @@ -1469,8 +1469,8 @@ arangodb::Result RocksDBEngine::dropView( } void RocksDBEngine::destroyView( - TRI_vocbase_t& /*vocbase*/, - LogicalView& /*view*/ + TRI_vocbase_t const& /*vocbase*/, + LogicalView const& /*view*/ ) noexcept { // nothing to do here #ifdef ARANGODB_ENABLE_MAINTAINER_MODE @@ -2316,4 +2316,4 @@ bool RocksDBEngine::canUseRangeDeleteInWal() const { // ----------------------------------------------------------------------------- // --SECTION-- END-OF-FILE -// ----------------------------------------------------------------------------- +// ----------------------------------------------------------------------------- \ No newline at end of file diff --git a/arangod/RocksDBEngine/RocksDBEngine.h b/arangod/RocksDBEngine/RocksDBEngine.h index 9d7d98af5b..3ef38e6e61 100644 --- a/arangod/RocksDBEngine/RocksDBEngine.h +++ b/arangod/RocksDBEngine/RocksDBEngine.h @@ -293,13 +293,13 @@ class RocksDBEngine final : public StorageEngine { } arangodb::Result dropView( - TRI_vocbase_t& vocbase, - LogicalView& view + TRI_vocbase_t const& vocbase, + LogicalView const& view ) override; void destroyView( - TRI_vocbase_t& vocbase, - LogicalView& view + TRI_vocbase_t const& vocbase, + LogicalView const& view ) noexcept override; void signalCleanup(TRI_vocbase_t& vocbase) override; @@ -479,4 +479,4 @@ class RocksDBEngine final : public StorageEngine { } // namespace arangodb -#endif +#endif \ No newline at end of file diff --git a/arangod/StorageEngine/StorageEngine.h b/arangod/StorageEngine/StorageEngine.h index 0468b0aadf..498848e7af 100644 --- a/arangod/StorageEngine/StorageEngine.h +++ b/arangod/StorageEngine/StorageEngine.h @@ -360,8 +360,8 @@ class StorageEngine : public application_features::ApplicationFeature { // the WAL entry for view deletion will be written *after* the call // to "dropView" returns virtual arangodb::Result dropView( - TRI_vocbase_t& vocbase, - LogicalView& view + TRI_vocbase_t const& vocbase, + LogicalView const& view ) = 0; // perform a physical deletion of the view @@ -369,8 +369,8 @@ class StorageEngine : public application_features::ApplicationFeature { // assured that no one is using the view anymore // 'noexcept' becuase it may be used in destructor virtual void destroyView( - TRI_vocbase_t& vocbase, - LogicalView& view + TRI_vocbase_t const& vocbase, + LogicalView const& view ) noexcept = 0; // Returns the StorageEngine-specific implementation @@ -490,4 +490,4 @@ class StorageEngine : public application_features::ApplicationFeature { } -#endif +#endif \ No newline at end of file diff --git a/arangod/VocBase/LogicalView.cpp b/arangod/VocBase/LogicalView.cpp index 71c0bba6e0..e42f0608ae 100644 --- a/arangod/VocBase/LogicalView.cpp +++ b/arangod/VocBase/LogicalView.cpp @@ -23,8 +23,6 @@ #include "LogicalView.h" -#include "Aql/PlanCache.h" -#include "Aql/QueryCache.h" #include "Basics/StaticStrings.h" #include "Basics/VelocyPackHelper.h" #include "Cluster/ClusterInfo.h" @@ -85,6 +83,26 @@ LogicalView::LogicalView( TRI_UpdateTickServer(static_cast(id())); } +Result LogicalView::appendVelocyPack( + velocypack::Builder& builder, + bool detailed, + bool forPersistence +) const { + if (!builder.isOpenObject()) { + return Result( + TRI_ERROR_BAD_PARAMETER, + std::string("invalid builder provided for LogicalView definition") + ); + } + + builder.add( + StaticStrings::DataSourceType, + arangodb::velocypack::Value(type().name()) + ); + + return appendVelocyPackImpl(builder, detailed, forPersistence); +} + /*static*/ LogicalDataSource::Category const& LogicalView::category() noexcept { static const Category category; @@ -114,6 +132,30 @@ LogicalView::LogicalView( return factory.create(view, vocbase, definition); } +Result LogicalView::drop() { + if (deleted()) { + return Result(); // view already dropped + } + + try { + deleted(true); // mark as deleted to avoid double-delete (including recursive calls) + + auto res = dropImpl(); + + if (!res.ok()) { + deleted(false); // not fully deleted + + return res; + } + } catch (...) { + deleted(false); // not fully deleted + + throw; + } + + return Result(); +} + /*static*/ bool LogicalView::enumerate( TRI_vocbase_t& vocbase, std::function const&)> const& callback @@ -172,327 +214,361 @@ LogicalView::LogicalView( return factory.instantiate(view, vocbase, definition, planVersion); } -// ----------------------------------------------------------------------------- -// --SECTION-- LogicalViewClusterInfo -// ----------------------------------------------------------------------------- - -LogicalViewClusterInfo::LogicalViewClusterInfo( - TRI_vocbase_t& vocbase, - VPackSlice const& definition, - uint64_t planVersion -): LogicalView(vocbase, definition, planVersion) { - TRI_ASSERT( - ServerState::instance()->isCoordinator() - || ServerState::instance()->isDBServer() - ); -} - -arangodb::Result LogicalViewClusterInfo::appendVelocyPack( - arangodb::velocypack::Builder& builder, - bool detailed, - bool forPersistence -) const { - if (!builder.isOpenObject()) { - return arangodb::Result( - TRI_ERROR_BAD_PARAMETER, - std::string("invalid builder provided for LogicalView definition") - ); - } - - builder.add( - StaticStrings::DataSourceType, - arangodb::velocypack::Value(type().name()) - ); - - // implementation Information - if (detailed) { - auto res = appendVelocyPackDetailed(builder, forPersistence); - - if (!res.ok()) { - return res; - } - } - - // ensure that the object is still open - if (!builder.isOpenObject()) { - return arangodb::Result(TRI_ERROR_INTERNAL); - } - - return arangodb::Result(); -} - -arangodb::Result LogicalViewClusterInfo::drop() { - if (deleted()) { - return Result(); // view already dropped - } - - auto* engine = arangodb::ClusterInfo::instance(); - - if (!engine) { - return arangodb::Result( - TRI_ERROR_INTERNAL, - std::string("failure to get storage engine while dropping View '") + name() + "'" - ); - } +Result LogicalView::rename(std::string&& newName) { + auto oldName = name(); try { - deleted(true); // mark as deleted to avoid double-delete (including recursive calls) + name(std::move(newName)); - auto res = dropImpl(); + auto res = renameImpl(oldName); if (!res.ok()) { - deleted(false); // not fully deleted + name(std::move(oldName)); // restore name return res; } + } catch (...) { + name(std::move(oldName)); + + throw; + } + + return Result(); +} + +// ----------------------------------------------------------------------------- +// --SECTION-- LogicalViewHelperClusterInfo +// ----------------------------------------------------------------------------- + +/*static*/ Result LogicalViewHelperClusterInfo::construct( + LogicalView::ptr& view, + TRI_vocbase_t& vocbase, + velocypack::Slice const& definition +) noexcept { + try { + auto* engine = ClusterInfo::instance(); + + if (!engine) { + return Result( + TRI_ERROR_INTERNAL, + std::string("failure to find storage engine while creating arangosearch View in database '") + vocbase.name() + "'" + ); + } + + LogicalView::ptr impl; + auto res = LogicalView::instantiate(impl, vocbase, definition); + + if (!res.ok()) { + return res; + } + + if (!impl) { + return arangodb::Result( + TRI_ERROR_INTERNAL, + std::string("failure during instantiation while creating arangosearch View in database '") + vocbase.name() + "'" + ); + } + + velocypack::Builder builder; + + builder.openObject(); + res = impl->properties(builder, true, true); // include links so that Agency will always have a full definition + + if (!res.ok()) { + return res; + } + + builder.close(); std::string error; - auto resNum = engine->dropViewCoordinator( - vocbase().name(), std::to_string(id()), error + auto resNum = engine->createViewCoordinator( + vocbase.name(), std::to_string(impl->id()), builder.slice(), error ); if (TRI_ERROR_NO_ERROR != resNum) { - deleted(false); // not fully deleted - if (error.empty()) { error = TRI_errno_string(resNum); } return arangodb::Result( resNum, - std::string("failure during ClusterInfo removal of View in database '") + vocbase().name() + "', error: " + error + std::string("failure during ClusterInfo persistance of created view while creating arangosearch View in database '") + vocbase.name() + "', error: " + error ); } + + view = engine->getView(vocbase.name(), std::to_string(impl->id())); // refresh view from Agency + + if (view) { + view->open(); // open view to match the behaviour in StorageEngine::openExistingDatabase(...) and original behaviour of TRI_vocbase_t::createView(...) + } + + return Result(); + } catch (basics::Exception const& e) { + return Result(e.code()); // noexcept constructor } catch (...) { - deleted(false); // not fully deleted - - throw; + // NOOP } - return arangodb::Result(); + return Result(TRI_ERROR_INTERNAL); // noexcept constructor } -arangodb::Result LogicalViewClusterInfo::rename(std::string&&) { - // renaming a view in a cluster is unsupported - return TRI_ERROR_CLUSTER_UNSUPPORTED; +/*static*/ Result LogicalViewHelperClusterInfo::destruct( + LogicalView const& view +) noexcept { + return Result(); // nothing to clean up since the Plan is managed by the Agency } -// ----------------------------------------------------------------------------- -// --SECTION-- LogicalViewStorageEngine -// ----------------------------------------------------------------------------- +/*static*/ Result LogicalViewHelperClusterInfo::drop( + LogicalView const& view +) noexcept { + try { + auto* engine = ClusterInfo::instance(); -LogicalViewStorageEngine::LogicalViewStorageEngine( - TRI_vocbase_t& vocbase, - VPackSlice const& definition, - uint64_t planVersion -): LogicalView(vocbase, definition, planVersion) { - TRI_ASSERT( - ServerState::instance()->isDBServer() - || ServerState::instance()->isSingleServer() - ); -} + if (!engine) { + return Result( + TRI_ERROR_INTERNAL, + std::string("failure to find storage engine while dropping view '") + view.name() + "' from database '" + view.vocbase().name() + "'" + ); + } -LogicalViewStorageEngine::~LogicalViewStorageEngine() { - if (deleted()) { - StorageEngine* engine = EngineSelectorFeature::ENGINE; - TRI_ASSERT(engine); - // FIXME TODO is this required? - engine->destroyView(vocbase(), *this); - } -} - -arangodb::Result LogicalViewStorageEngine::appendVelocyPack( - arangodb::velocypack::Builder& builder, - bool detailed, - bool forPersistence -) const { - if (!builder.isOpenObject()) { - return arangodb::Result( - TRI_ERROR_BAD_PARAMETER, - std::string("invalid builder provided for LogicalView definition") + std::string error; + auto res = engine->dropViewCoordinator( + view.vocbase().name(), std::to_string(view.id()), error ); + + if (TRI_ERROR_NO_ERROR == res) { + return Result(); + } + + if (error.empty()) { + error = TRI_errno_string(res); + } + + return Result( + res, + std::string("failure during ClusterInfo removal of view '") + view.name() + "' from database '" + view.vocbase().name() + "': " + error + ); + } catch (basics::Exception const& e) { + return Result(e.code()); // noexcept constructor + } catch (...) { + // NOOP } - builder.add( - StaticStrings::DataSourceType, - arangodb::velocypack::Value(type().name()) - ); + return Result(TRI_ERROR_INTERNAL); // noexcept constructor +} - // note: includeSystem and forPersistence are not 100% synonymous, - // however, for our purposes this is an okay mapping; we only set - // includeSystem if we are persisting the properties - if (forPersistence) { - // storage engine related properties +/*static*/ Result LogicalViewHelperClusterInfo::properties( + velocypack::Builder& builder, + LogicalView const& view +) noexcept { + return Result(); // NOOP +} + +/*static*/ Result LogicalViewHelperClusterInfo::properties( + LogicalView const& view +) noexcept { + try { + auto* engine = ClusterInfo::instance(); + + if (!engine) { + return Result( + TRI_ERROR_INTERNAL, + std::string("failure to find storage engine while updating definition of view '") + view.name() + "' from database '" + view.vocbase().name() + "'" + ); + } + + velocypack::Builder builder; + + builder.openObject(); + + auto res = view.properties(builder, true, true); + + if (!res.ok()) { + return res; + } + + builder.close(); + + return engine->setViewPropertiesCoordinator( + view.vocbase().name(), std::to_string(view.id()), builder.slice() + ); + } catch (basics::Exception const& e) { + return Result(e.code()); // noexcept constructor + } catch (...) { + // NOOP + } + + return Result(TRI_ERROR_INTERNAL); // noexcept constructor +} + +/*static*/ Result LogicalViewHelperClusterInfo::rename( + LogicalView const& view, + std::string const& oldName +) noexcept { + return Result(TRI_ERROR_CLUSTER_UNSUPPORTED); // renaming a view in a cluster is not supported +} + +// ----------------------------------------------------------------------------- +// --SECTION-- LogicalViewHelperStorageEngine +// ----------------------------------------------------------------------------- + +/*static*/ Result LogicalViewHelperStorageEngine::construct( + LogicalView::ptr& view, + TRI_vocbase_t& vocbase, + velocypack::Slice const& definition +) noexcept { + try { + TRI_set_errno(TRI_ERROR_NO_ERROR); // reset before calling createView(...) + auto impl = vocbase.createView(definition); + + if (!impl) { + return Result( + TRI_ERROR_NO_ERROR == TRI_errno() ? TRI_ERROR_INTERNAL : TRI_errno(), + std::string("failure during instantiation while creating arangosearch View in database '") + vocbase.name() + "'" + ); + } + + view = impl; + + return Result(); + } catch (basics::Exception const& e) { + return Result(e.code()); // noexcept constructor + } catch (...) { + // NOOP + } + + return Result(TRI_ERROR_INTERNAL); // noexcept constructor +} + +/*static*/ Result LogicalViewHelperStorageEngine::destruct( + LogicalView const& view +) noexcept { + if (!view.deleted()) { + return Result(); // NOOP + } + + try { auto* engine = EngineSelectorFeature::ENGINE; if (!engine) { - return TRI_ERROR_INTERNAL; + return Result( + TRI_ERROR_INTERNAL, + std::string("failed to find a storage engine while destructing view '") + view.name() + "' in database '" + view.vocbase().name() + "'" + ); } - engine->getViewProperties(vocbase(), *this, builder); + engine->destroyView(view.vocbase(), view); + + return Result(); + } catch (basics::Exception const& e) { + return Result(e.code()); // noexcept constructor + } catch (...) { + // NOOP } - // implementation Information - if (detailed) { - auto res = appendVelocyPackDetailed(builder, forPersistence); - - if (!res.ok()) { - return res; - } - } - - // ensure that the object is still open - if (!builder.isOpenObject()) { - return arangodb::Result(TRI_ERROR_INTERNAL); - } - - return arangodb::Result(); + return Result(TRI_ERROR_INTERNAL); // noexcept constructor } -arangodb::Result LogicalViewStorageEngine::drop() { - if (deleted()) { - return Result(); // view already dropped - } - +/*static*/ Result LogicalViewHelperStorageEngine::drop( + LogicalView const& view +) noexcept { try { - deleted(true); // mark as deleted to avoid double-delete (including recursive calls) - - auto res = dropImpl(); - - if (!res.ok()) { - deleted(false); // not fully deleted - - return res; - } - - res = vocbase().dropView(id(), true); // true since caller should have checked for 'system' - - if (!res.ok()) { - deleted(false); // not fully deleted - - return res; - } + return view.vocbase().dropView(view.id(), true); // true since caller should have checked for 'system' + } catch (basics::Exception const& e) { + return Result(e.code()); // noexcept constructor } catch (...) { - deleted(false); // not fully deleted - - throw; + // NOOP } - return arangodb::Result(); + return Result(TRI_ERROR_INTERNAL); // noexcept constructor } -Result LogicalViewStorageEngine::rename(std::string&& newName) { - TRI_ASSERT(!ServerState::instance()->isCoordinator()); - auto* databaseFeature = application_features::ApplicationServer::lookupFeature< - DatabaseFeature - >("Database"); - - if (!databaseFeature) { - return Result( - TRI_ERROR_INTERNAL, - "failed to find feature 'Database' while renaming view" - ); - } - - StorageEngine* engine = EngineSelectorFeature::ENGINE; - - if (!engine) { - return Result( - TRI_ERROR_INTERNAL, - "failed to find a storage engine while renaming view" - ); - } - - auto doSync = databaseFeature->forceSyncProperties(); - auto oldName = name(); - auto res = vocbase().renameView(id(), newName); - - if (!res.ok()) { - return res; - } - +/*static*/ Result LogicalViewHelperStorageEngine::properties( + velocypack::Builder& builder, + LogicalView const& view +) noexcept { try { - name(std::move(newName)); - - auto res = engine->inRecovery() - ? Result() : engine->changeView(vocbase(), *this, doSync); - - if (!res.ok()) { - name(std::move(oldName)); // restore name - vocbase().renameView(id(), oldName); - - return res; + if (!builder.isOpenObject()) { + return Result( + TRI_ERROR_BAD_PARAMETER, + std::string("invalid builder provided for LogicalView definition") + ); } - } catch (basics::Exception const& ex) { - name(std::move(oldName)); - vocbase().renameView(id(), oldName); - return Result(ex.code(), ex.message()); + auto* engine = EngineSelectorFeature::ENGINE; + + if (!engine) { + return Result( + TRI_ERROR_INTERNAL, + std::string("failed to find a storage engine while querying definition of view '") + view.name() + "' in database '" + view.vocbase().name() + "'" + ); + } + + engine->getViewProperties(view.vocbase(), view, builder); + + return Result(); + } catch (basics::Exception const& e) { + return Result(e.code()); // noexcept constructor } catch (...) { - name(std::move(oldName)); - vocbase().renameView(id(), oldName); - - return Result(TRI_ERROR_INTERNAL, "caught exception while renaming view");; + // NOOP } - return TRI_ERROR_NO_ERROR; + return Result(TRI_ERROR_INTERNAL); // noexcept constructor } -arangodb::Result LogicalViewStorageEngine::properties( - VPackSlice const& slice, - bool partialUpdate -) { - TRI_ASSERT(!ServerState::instance()->isCoordinator()); - - auto* databaseFeature = application_features::ApplicationServer::lookupFeature< - DatabaseFeature - >("Database"); - - if (!databaseFeature) { - return Result( - TRI_ERROR_INTERNAL, - "failed to find feature 'Database' while updating collection" - ); - } - - auto* engine = EngineSelectorFeature::ENGINE; - - if (!engine) { - return Result( - TRI_ERROR_INTERNAL, - "failed to find a storage engine while updating collection" - ); - } - - auto res = updateProperties(slice, partialUpdate); - - if (!res.ok()) { - LOG_TOPIC(ERR, Logger::VIEWS) << "failed to update view with properties '" - << slice.toJson() << "'"; - return res; - } - LOG_TOPIC(DEBUG, Logger::VIEWS) << "updated view with properties '" - << slice.toJson() << "'"; - - auto doSync = !engine->inRecovery() && databaseFeature->forceSyncProperties(); - - // after this call the properties are stored - if (engine->inRecovery()) { - return arangodb::Result(); // do not modify engine while in recovery - } - +/*static*/ Result LogicalViewHelperStorageEngine::properties( + LogicalView const& view +) noexcept { try { - engine->changeView(vocbase(), *this, doSync); - } catch (arangodb::basics::Exception const& e) { - return Result(e.code(), e.message()); + auto* databaseFeature = application_features::ApplicationServer::lookupFeature< + DatabaseFeature + >("Database"); + + if (!databaseFeature) { + return Result( + TRI_ERROR_INTERNAL, + std::string("failed to find feature 'Database' while updating definition of view '") + view.name() + "' in database '" + view.vocbase().name() + "'" + ); + } + + auto* engine = EngineSelectorFeature::ENGINE; + + if (!engine) { + return Result( + TRI_ERROR_INTERNAL, + std::string("failed to find a storage engine while updating definition of view '") + view.name() + "' in database '" + view.vocbase().name() + "'" + ); + } + + auto doSync = databaseFeature->forceSyncProperties(); + + if (engine->inRecovery()) { + return Result(); // do not modify engine while in recovery + } + + return engine->changeView(view.vocbase(), view, doSync); + } catch (basics::Exception const& e) { + return Result(e.code()); // noexcept constructor } catch (...) { - return Result(TRI_ERROR_INTERNAL, "caught exception while updating view"); + // NOOP } - arangodb::aql::PlanCache::instance()->invalidate(&vocbase()); - arangodb::aql::QueryCache::instance()->invalidate(&vocbase()); + return Result(TRI_ERROR_INTERNAL); // noexcept constructor +} - return {}; +/*static*/ Result LogicalViewHelperStorageEngine::rename( + LogicalView const& view, + std::string const& oldName +) noexcept { + try { + return view.vocbase().renameView(view.id(), oldName); + } catch (basics::Exception const& e) { + return Result(e.code()); // noexcept constructor + } catch (...) { + // NOOP + } + + return Result(TRI_ERROR_INTERNAL); // noexcept constructor } } // arangodb diff --git a/arangod/VocBase/LogicalView.h b/arangod/VocBase/LogicalView.h index 2b0fbd0595..850e95f6b3 100644 --- a/arangod/VocBase/LogicalView.h +++ b/arangod/VocBase/LogicalView.h @@ -108,6 +108,15 @@ class LogicalView : public LogicalDataSource { #endif } + ////////////////////////////////////////////////////////////////////////////// + /// @brief queries properties of an existing view + ////////////////////////////////////////////////////////////////////////////// + virtual Result appendVelocyPack( + velocypack::Builder& builder, + bool detailed, + bool forPersistence + ) const override final; + ////////////////////////////////////////////////////////////////////////////// /// @brief the category representing a logical view ////////////////////////////////////////////////////////////////////////////// @@ -127,6 +136,11 @@ class LogicalView : public LogicalDataSource { velocypack::Slice definition ); + ////////////////////////////////////////////////////////////////////////////// + /// @brief drop an existing view + ////////////////////////////////////////////////////////////////////////////// + virtual Result drop() override final; + ////////////////////////////////////////////////////////////////////////////// /// @brief calls the callback on every view found for the specified vocbase /// @param callback if false is returned then enumiration stops @@ -150,29 +164,15 @@ class LogicalView : public LogicalDataSource { uint64_t planVersion = 0 // '0' by default for non-cluster ); - ////////////////////////////////////////////////////////////////////////////// - /// @brief updates properties of an existing view - ////////////////////////////////////////////////////////////////////////////// - using LogicalDataSource::properties; - virtual arangodb::Result properties( - velocypack::Slice const& properties, - bool partialUpdate - ) override = 0; - ////////////////////////////////////////////////////////////////////////////// /// @brief opens an existing view when the server is restarted ////////////////////////////////////////////////////////////////////////////// virtual void open() = 0; - ////////////////////////////////////////////////////////////////////////////// - /// @brief drop an existing view - ////////////////////////////////////////////////////////////////////////////// - virtual arangodb::Result drop() override = 0; - ////////////////////////////////////////////////////////////////////////////// /// @brief renames an existing view ////////////////////////////////////////////////////////////////////////////// - virtual Result rename(std::string&& newName) override = 0; + virtual Result rename(std::string&& newName) override final; ////////////////////////////////////////////////////////////////////////////// /// @brief invoke visitor on all collections that a view will return @@ -187,6 +187,27 @@ class LogicalView : public LogicalDataSource { uint64_t planVersion ); + ////////////////////////////////////////////////////////////////////////////// + /// @brief queries properties of an existing view + ////////////////////////////////////////////////////////////////////////////// + virtual Result appendVelocyPackImpl( + velocypack::Builder& builder, + bool detailed, + bool forPersistence + ) const = 0; + + ////////////////////////////////////////////////////////////////////////////// + /// @brief drop implementation-specific parts of an existing view + /// including persisted properties + ////////////////////////////////////////////////////////////////////////////// + virtual Result dropImpl() = 0; + + ////////////////////////////////////////////////////////////////////////////// + /// @brief renames implementation-specific parts of an existing view + /// including persistance of properties + ////////////////////////////////////////////////////////////////////////////// + virtual Result renameImpl(std::string const& oldName) = 0; + private: // FIXME seems to be ugly friend struct ::TRI_vocbase_t; @@ -196,91 +217,57 @@ class LogicalView : public LogicalDataSource { }; // LogicalView //////////////////////////////////////////////////////////////////////////////// -/// @brief a LogicalView base class for ClusterInfo view implementations +/// @brief a helper for ClusterInfo View operations //////////////////////////////////////////////////////////////////////////////// -class LogicalViewClusterInfo: public LogicalView { - public: - virtual Result drop() override final; - virtual Result rename(std::string&& newName) override final; - - protected: - LogicalViewClusterInfo( +struct LogicalViewHelperClusterInfo { + static Result construct( + LogicalView::ptr& view, TRI_vocbase_t& vocbase, - velocypack::Slice const& definition, - uint64_t planVersion - ); + velocypack::Slice const& definition + ) noexcept; - virtual Result appendVelocyPack( - arangodb::velocypack::Builder& builder, - bool detailed, - bool forPersistence - ) const override final; + static Result destruct(LogicalView const& view) noexcept; + static Result drop(LogicalView const& view) noexcept; - ////////////////////////////////////////////////////////////////////////////// - /// @brief fill and return a jSON description of a View object implementation - ////////////////////////////////////////////////////////////////////////////// - virtual arangodb::Result appendVelocyPackDetailed( + static Result properties( velocypack::Builder& builder, - bool forPersistence - ) const = 0; + LogicalView const& view + ) noexcept; - protected: - ////////////////////////////////////////////////////////////////////////////// - /// @brief drop implementation-specific parts of an existing view - ////////////////////////////////////////////////////////////////////////////// - virtual arangodb::Result dropImpl() = 0; + static Result properties( + LogicalView const& view + ) noexcept; + + static Result rename( + LogicalView const& view, + std::string const& oldName + ) noexcept; }; //////////////////////////////////////////////////////////////////////////////// -/// @brief a LogicalView base class for StorageEngine view implementations +/// @brief a helper for StorageEngine View operations //////////////////////////////////////////////////////////////////////////////// -class LogicalViewStorageEngine: public LogicalView { - public: - ~LogicalViewStorageEngine() override; - - virtual Result drop() override final; - - using LogicalDataSource::properties; - virtual arangodb::Result properties( - velocypack::Slice const& properties, - bool partialUpdate - ) override final; - - virtual Result rename(std::string&& newName) override final; - - protected: - LogicalViewStorageEngine( +struct LogicalViewHelperStorageEngine { + static Result construct( + LogicalView::ptr& view, TRI_vocbase_t& vocbase, - velocypack::Slice const& definition, - uint64_t planVersion - ); + velocypack::Slice const& definition + ) noexcept; - virtual Result appendVelocyPack( - arangodb::velocypack::Builder& builder, - bool detailed, - bool forPersistence - ) const override final; + static Result destruct(LogicalView const& view) noexcept; + static Result drop(LogicalView const& view) noexcept; - ////////////////////////////////////////////////////////////////////////////// - /// @brief fill and return a jSON description of a View object implementation - ////////////////////////////////////////////////////////////////////////////// - virtual arangodb::Result appendVelocyPackDetailed( + static Result properties( velocypack::Builder& builder, - bool forPersistence - ) const = 0; + LogicalView const& view + ) noexcept; - ////////////////////////////////////////////////////////////////////////////// - /// @brief drop implementation-specific parts of an existing view - ////////////////////////////////////////////////////////////////////////////// - virtual arangodb::Result dropImpl() = 0; + static Result properties(LogicalView const& view) noexcept; - /////////////////////////////////////////////////////////////////////////////// - /// @brief called when a view's properties are updated (i.e. delta-modified) - /////////////////////////////////////////////////////////////////////////////// - virtual arangodb::Result updateProperties( - velocypack::Slice const& slice, - bool partialUpdate - ) = 0; + static Result rename( + LogicalView const& view, + std::string const& oldName + ) noexcept; }; } // namespace arangodb diff --git a/arangod/VocBase/vocbase.cpp b/arangod/VocBase/vocbase.cpp index b109015bc8..647c9f646f 100644 --- a/arangod/VocBase/vocbase.cpp +++ b/arangod/VocBase/vocbase.cpp @@ -1353,7 +1353,7 @@ arangodb::Result TRI_vocbase_t::dropCollection( /// @brief renames a view arangodb::Result TRI_vocbase_t::renameView( TRI_voc_cid_t cid, - std::string const& newName + std::string const& oldName ) { TRI_ASSERT(!ServerState::instance()->isCoordinator()); auto const view = lookupView(cid); @@ -1362,8 +1362,28 @@ arangodb::Result TRI_vocbase_t::renameView( return TRI_ERROR_ARANGO_DATA_SOURCE_NOT_FOUND; } + auto* databaseFeature = application_features::ApplicationServer::lookupFeature< + DatabaseFeature + >("Database"); + + if (!databaseFeature) { + return Result( + TRI_ERROR_INTERNAL, + std::string("failed to find feature 'Database' while renaming view '") + view->name() + "' in database '" + name() + "'" + ); + } + + auto* engine = EngineSelectorFeature::ENGINE; + + if (!engine) { + return arangodb::Result( + TRI_ERROR_INTERNAL, + std::string("failed to find StorageEngine while renaming view '") + view->name() + "' in database '" + name() + "'" + ); + } + // lock collection because we are going to copy its current name - std::string oldName = view->name(); + auto newName = view->name(); // old name should be different @@ -1398,6 +1418,15 @@ arangodb::Result TRI_vocbase_t::renameView( TRI_ASSERT(std::dynamic_pointer_cast(itr1->second)); + auto doSync = databaseFeature->forceSyncProperties(); + auto res = engine->inRecovery() + ? arangodb::Result() // skip persistence while in recovery since definition already from engine + : engine->changeView(*this, *view, doSync); + + if (!res.ok()) { + return res; + } + // stores the parameters on disk auto itr2 = _dataSourceByName.emplace(newName, itr1->second); @@ -1717,15 +1746,16 @@ arangodb::Result TRI_vocbase_t::dropView( TRI_ASSERT(writeLocker.isLocked()); TRI_ASSERT(locker.isLocked()); - arangodb::aql::PlanCache::instance()->invalidate(this); - arangodb::aql::QueryCache::instance()->invalidate(this); - auto res = engine->dropView(*this, *view); if (!res.ok()) { return res; } + // invalidate all entries in the plan and query cache now + arangodb::aql::PlanCache::instance()->invalidate(this); + arangodb::aql::QueryCache::instance()->invalidate(this); + unregisterView(*view); locker.unlock(); @@ -2205,4 +2235,4 @@ TRI_voc_rid_t TRI_StringToRid(char const* p, size_t len, bool& isOld, // ----------------------------------------------------------------------------- // --SECTION-- END-OF-FILE -// ----------------------------------------------------------------------------- +// ----------------------------------------------------------------------------- \ No newline at end of file diff --git a/arangod/VocBase/vocbase.h b/arangod/VocBase/vocbase.h index 6867100108..07701f39e4 100644 --- a/arangod/VocBase/vocbase.h +++ b/arangod/VocBase/vocbase.h @@ -333,7 +333,7 @@ struct TRI_vocbase_t { /// @brief renames a view arangodb::Result renameView( TRI_voc_cid_t cid, - std::string const& newName + std::string const& oldName ); /// @brief creates a new collection from parameter set @@ -454,4 +454,4 @@ void TRI_SanitizeObject(arangodb::velocypack::Slice const slice, void TRI_SanitizeObjectWithEdges(arangodb::velocypack::Slice const slice, arangodb::velocypack::Builder& builder); -#endif +#endif \ No newline at end of file diff --git a/tests/Cluster/ClusterInfo-test.cpp b/tests/Cluster/ClusterInfo-test.cpp index 3884af5639..92ca6c6c0d 100644 --- a/tests/Cluster/ClusterInfo-test.cpp +++ b/tests/Cluster/ClusterInfo-test.cpp @@ -54,26 +54,13 @@ struct TestView: public arangodb::LogicalView { TestView(TRI_vocbase_t& vocbase, arangodb::velocypack::Slice const& definition, uint64_t planVersion) : arangodb::LogicalView(vocbase, definition, planVersion), _definition(definition) { } - virtual arangodb::Result appendVelocyPack(arangodb::velocypack::Builder& builder, bool , bool) const override { + virtual arangodb::Result appendVelocyPackImpl(arangodb::velocypack::Builder& builder, bool , bool) const override { return arangodb::iresearch::mergeSlice(builder, _definition.slice()) ? TRI_ERROR_NO_ERROR : TRI_ERROR_INTERNAL; } - virtual arangodb::Result drop() override { - auto* ci = arangodb::ClusterInfo::instance(); - - if (!ci) { - return TRI_ERROR_INTERNAL; - } - - deleted(true); - - std::string error; - auto res = ci->dropViewCoordinator(vocbase().name(), std::to_string(id()), error); - - return arangodb::Result(res, error); - } + virtual arangodb::Result dropImpl() override { return arangodb::LogicalViewHelperClusterInfo::drop(*this); } virtual void open() override {} virtual arangodb::Result properties(arangodb::velocypack::Slice const&, bool) override { return arangodb::Result(); } - virtual arangodb::Result rename(std::string&& newName) override { name(std::move(newName)); return arangodb::Result(); } + virtual arangodb::Result renameImpl(std::string const& oldName) override { return arangodb::LogicalViewHelperStorageEngine::rename(*this, oldName); } virtual bool visitCollections(CollectionVisitor const& visitor) const override { return true; } }; @@ -277,4 +264,4 @@ SECTION("test_drop_databse") { // ----------------------------------------------------------------------------- // --SECTION-- END-OF-FILE -// ----------------------------------------------------------------------------- +// ----------------------------------------------------------------------------- \ No newline at end of file diff --git a/tests/IResearch/IResearchQueryJoin-test.cpp b/tests/IResearch/IResearchQueryJoin-test.cpp index 5d5263494e..5251ea2008 100644 --- a/tests/IResearch/IResearchQueryJoin-test.cpp +++ b/tests/IResearch/IResearchQueryJoin-test.cpp @@ -1638,8 +1638,7 @@ TEST_CASE("IResearchQueryTestJoin", "[iresearch][iresearch-query]") { )); auto queryResult = arangodb::tests::executeQuery(vocbase, query); - //REQUIRE(TRI_ERROR_NOT_IMPLEMENTED == queryResult.code); - REQUIRE(TRI_ERROR_NO_ERROR == queryResult.code); // FIXME TODO check, has this case been implmemented? + REQUIRE(TRI_ERROR_NOT_IMPLEMENTED == queryResult.code); } // multiple sorts (not supported now) diff --git a/tests/IResearch/IResearchView-test.cpp b/tests/IResearch/IResearchView-test.cpp index 72db08a208..54f191010e 100644 --- a/tests/IResearch/IResearchView-test.cpp +++ b/tests/IResearch/IResearchView-test.cpp @@ -1066,7 +1066,7 @@ SECTION("test_drop_cid") { arangodb::DatabaseFeature >("Database"); - CHECK_THROWS((feature->recoveryDone())); + CHECK_NOTHROW((feature->recoveryDone())); } } } @@ -1573,7 +1573,7 @@ SECTION("test_emplace_cid") { arangodb::DatabaseFeature >("Database"); - CHECK_THROWS((feature->recoveryDone())); + CHECK_NOTHROW((feature->recoveryDone())); } } } diff --git a/tests/IResearch/IResearchViewDBServer-test.cpp b/tests/IResearch/IResearchViewDBServer-test.cpp index 3b86af517d..a19b62870d 100644 --- a/tests/IResearch/IResearchViewDBServer-test.cpp +++ b/tests/IResearch/IResearchViewDBServer-test.cpp @@ -44,10 +44,10 @@ #include "GeneralServer/AuthenticationFeature.h" #include "IResearch/IResearchCommon.h" #include "IResearch/IResearchFeature.h" +#include "IResearch/IResearchLinkHelper.h" #include "IResearch/IResearchLinkMeta.h" #include "IResearch/IResearchMMFilesLink.h" #include "IResearch/IResearchView.h" -#include "IResearch/IResearchViewDBServer.h" #include "RestServer/DatabaseFeature.h" #include "RestServer/DatabasePathFeature.h" #include "RestServer/FlushFeature.h" @@ -228,9 +228,9 @@ SECTION("test_drop") { { auto json = arangodb::velocypack::Parser::fromJson("{ \"name\": \"testView\", \"type\": \"arangosearch\" }"); arangodb::LogicalView::ptr wiew; - REQUIRE((arangodb::iresearch::IResearchViewDBServer::factory().create(wiew, *vocbase, json->slice()).ok())); + REQUIRE((arangodb::iresearch::IResearchView::factory().create(wiew, *vocbase, json->slice()).ok())); CHECK((false == !wiew)); - auto* impl = dynamic_cast(wiew.get()); + auto* impl = dynamic_cast(wiew.get()); CHECK((nullptr != impl)); CHECK((true == impl->drop().ok())); @@ -238,99 +238,117 @@ SECTION("test_drop") { // drop non-empty { + auto collectionJson = arangodb::velocypack::Parser::fromJson("{ \"name\": \"testCollection0\" }"); + auto linkJson = arangodb::velocypack::Parser::fromJson("{ \"view\": \"testView0\", \"type\": \"arangosearch\", \"includeAllFields\": true }"); + auto viewJson = arangodb::velocypack::Parser::fromJson("{ \"name\": \"testView0\", \"type\": \"arangosearch\" }"); auto const wiewId = std::to_string(ci->uniqid() + 1); // +1 because LogicalView creation will generate a new ID - auto json = arangodb::velocypack::Parser::fromJson("{ \"name\": \"testView\", \"type\": \"arangosearch\" }"); + auto logicalCollection = vocbase->createCollection(collectionJson->slice()); + REQUIRE((false == !logicalCollection)); arangodb::LogicalView::ptr wiew; - REQUIRE((arangodb::iresearch::IResearchViewDBServer::factory().create(wiew, *vocbase, json->slice()).ok())); + REQUIRE((arangodb::iresearch::IResearchView::factory().create(wiew, *vocbase, viewJson->slice()).ok())); CHECK((false == !wiew)); - auto* impl = dynamic_cast(wiew.get()); + auto* impl = dynamic_cast(wiew.get()); CHECK((nullptr != impl)); // ensure we have shard view in vocbase - auto const shardViewName = "_iresearch_123_" + wiewId; - auto jsonShard = arangodb::velocypack::Parser::fromJson( - "{ \"id\": 100, \"name\": \"" + shardViewName + "\", \"type\": \"arangosearch\", \"isSystem\": true }" - ); - CHECK((true == !vocbase->lookupView(shardViewName))); - auto shardView = vocbase->createView(jsonShard->slice()); - CHECK(shardView); + bool created; + auto index = logicalCollection->createIndex(linkJson->slice(), created); + REQUIRE((false == !index)); + auto link = std::dynamic_pointer_cast(index); + REQUIRE((false == !link)); - auto view = impl->ensure(123); - auto const viewId = view->id(); - CHECK((false == !view)); - CHECK(view == shardView); static auto visitor = [](TRI_voc_cid_t)->bool { return false; }; CHECK((false == impl->visitCollections(visitor))); - CHECK((false == !vocbase->lookupView(view->id()))); + CHECK((false == !arangodb::iresearch::IResearchLinkHelper::find(*logicalCollection, *wiew))); CHECK((true == impl->drop().ok())); - CHECK((true == !vocbase->lookupView(viewId))); + CHECK((true == !arangodb::iresearch::IResearchLinkHelper::find(*logicalCollection, *wiew))); CHECK((true == impl->visitCollections(visitor))); } // drop non-empty (drop failure) { + auto collectionJson = arangodb::velocypack::Parser::fromJson("{ \"name\": \"testCollection1\" }"); + auto linkJson = arangodb::velocypack::Parser::fromJson("{ \"view\": \"testView1\", \"type\": \"arangosearch\", \"includeAllFields\": true }"); + auto viewJson = arangodb::velocypack::Parser::fromJson("{ \"name\": \"testView1\", \"type\": \"arangosearch\" }"); auto const wiewId = std::to_string(ci->uniqid() + 1); // +1 because LogicalView creation will generate a new ID - auto json = arangodb::velocypack::Parser::fromJson("{ \"name\": \"testView\", \"type\": \"arangosearch\" }"); + auto logicalCollection = vocbase->createCollection(collectionJson->slice()); + REQUIRE((false == !logicalCollection)); arangodb::LogicalView::ptr wiew; - REQUIRE((arangodb::iresearch::IResearchViewDBServer::factory().create(wiew, *vocbase, json->slice()).ok())); + REQUIRE((arangodb::iresearch::IResearchView::factory().create(wiew, *vocbase, viewJson->slice()).ok())); CHECK((false == !wiew)); - auto* impl = dynamic_cast(wiew.get()); + auto* impl = dynamic_cast(wiew.get()); CHECK((nullptr != impl)); // ensure we have shard view in vocbase - auto const shardViewName = "_iresearch_123_" + wiewId; - auto jsonShard = arangodb::velocypack::Parser::fromJson( - "{ \"id\": 100, \"name\": \"" + shardViewName + "\", \"type\": \"arangosearch\", \"isSystem\": true }" - ); - CHECK((true == !vocbase->lookupView(shardViewName))); - auto shardView = vocbase->createView(jsonShard->slice()); - CHECK(shardView); + bool created; + auto index = logicalCollection->createIndex(linkJson->slice(), created); + REQUIRE((false == !index)); + auto link = std::dynamic_pointer_cast(index); + REQUIRE((false == !link)); - auto view = impl->ensure(123); - CHECK((false == !view)); - CHECK(view == shardView); static auto visitor = [](TRI_voc_cid_t)->bool { return false; }; CHECK((false == impl->visitCollections(visitor))); - CHECK((false == !vocbase->lookupView(view->id()))); + CHECK((false == !arangodb::iresearch::IResearchLinkHelper::find(*logicalCollection, *wiew))); - auto before = StorageEngineMock::before; - auto restore = irs::make_finally([&before]()->void { StorageEngineMock::before = before; }); - StorageEngineMock::before = []()->void { throw std::exception(); }; + auto before = PhysicalCollectionMock::before; + auto restore = irs::make_finally([&before]()->void { PhysicalCollectionMock::before = before; }); + PhysicalCollectionMock::before = []()->void { throw std::exception(); }; - CHECK_THROWS((impl->drop())); - CHECK((false == !vocbase->lookupView(view->id()))); + CHECK((!impl->drop().ok())); + CHECK((false == !arangodb::iresearch::IResearchLinkHelper::find(*logicalCollection, *wiew))); CHECK((false == impl->visitCollections(visitor))); } } SECTION("test_drop_cid") { - auto json = arangodb::velocypack::Parser::fromJson("{ \"name\": \"testView\", \"type\": \"arangosearch\" }"); - TRI_vocbase_t vocbase(TRI_vocbase_type_e::TRI_VOCBASE_TYPE_NORMAL, 1, "testVocbase"); + auto* database = arangodb::DatabaseFeature::DATABASE; + REQUIRE((nullptr != database)); + auto* ci = arangodb::ClusterInfo::instance(); + REQUIRE((nullptr != ci)); + std::string error; + TRI_vocbase_t* vocbase; // will be owned by DatabaseFeature + + // create database + { + // simulate heartbeat thread + REQUIRE(TRI_ERROR_NO_ERROR == database->createDatabase(1, "testDatabase", vocbase)); + + REQUIRE(nullptr != vocbase); + CHECK("testDatabase" == vocbase->name()); + CHECK(TRI_vocbase_type_e::TRI_VOCBASE_TYPE_NORMAL == vocbase->type()); + CHECK(1 == vocbase->id()); + + CHECK(TRI_ERROR_NO_ERROR == ci->createDatabaseCoordinator( + vocbase->name(), VPackSlice::emptyObjectSlice(), error, 0.0 + )); + CHECK("no error" == error); + } + + auto collectionJson = arangodb::velocypack::Parser::fromJson("{ \"name\": \"testCollection\" }"); + auto linkJson = arangodb::velocypack::Parser::fromJson("{ \"view\": \"testView\", \"type\": \"arangosearch\", \"includeAllFields\": true }"); + auto viewJson = arangodb::velocypack::Parser::fromJson("{ \"name\": \"testView\", \"type\": \"arangosearch\" }"); + auto logicalCollection = vocbase->createCollection(collectionJson->slice()); + REQUIRE((false == !logicalCollection)); arangodb::LogicalView::ptr wiew; - REQUIRE((arangodb::iresearch::IResearchViewDBServer::factory().instantiate(wiew, vocbase, json->slice(), 42).ok())); + REQUIRE((arangodb::iresearch::IResearchView::factory().create(wiew, *vocbase, viewJson->slice()).ok())); CHECK((false == !wiew)); - auto* impl = dynamic_cast(wiew.get()); + auto* impl = dynamic_cast(wiew.get()); CHECK((nullptr != impl)); // ensure we have shard view in vocbase - auto jsonShard = arangodb::velocypack::Parser::fromJson( - "{ \"id\": 100, \"name\": \"_iresearch_123_1\", \"type\": \"arangosearch\", \"isSystem\": true }" - ); - CHECK((true == !vocbase.lookupView("_iresearch_123_1"))); - auto shardView = vocbase.createView(jsonShard->slice()); - CHECK(shardView); + bool created; + auto index = logicalCollection->createIndex(linkJson->slice(), created); + REQUIRE((false == !index)); + auto link = std::dynamic_pointer_cast(index); + REQUIRE((false == !link)); - auto view = impl->ensure(123); - auto const viewId = view->id(); - CHECK((false == !view)); - CHECK(shardView == view); static auto visitor = [](TRI_voc_cid_t)->bool { return false; }; CHECK((false == impl->visitCollections(visitor))); - CHECK((false == !vocbase.lookupView(view->id()))); - CHECK((TRI_ERROR_NO_ERROR == impl->unlink(123).errorNumber())); - CHECK((true == !vocbase.lookupView(viewId))); + CHECK((false == !arangodb::iresearch::IResearchLinkHelper::find(*logicalCollection, *wiew))); + CHECK((impl->unlink(logicalCollection->id()).ok())); + CHECK((false == !arangodb::iresearch::IResearchLinkHelper::find(*logicalCollection, *wiew))); CHECK((true == impl->visitCollections(visitor))); - CHECK((TRI_ERROR_ARANGO_DATA_SOURCE_NOT_FOUND == impl->unlink(123).errorNumber())); // no longer present + CHECK((impl->unlink(logicalCollection->id()).ok())); } SECTION("test_drop_database") { @@ -345,11 +363,9 @@ SECTION("test_drop_database") { auto viewUpdateJson = arangodb::velocypack::Parser::fromJson("{ \"links\": { \"testCollection\": { \"includeAllFields\": true } } }"); size_t beforeCount = 0; - auto before = StorageEngineMock::before; - auto restore = irs::make_finally([&before]()->void { StorageEngineMock::before = before; }); - StorageEngineMock::before = [&beforeCount]()->void { - ++beforeCount; - }; + auto before = PhysicalCollectionMock::before; + auto restore = irs::make_finally([&before]()->void { PhysicalCollectionMock::before = before; }); + PhysicalCollectionMock::before = [&beforeCount]()->void { ++beforeCount; }; TRI_vocbase_t* vocbase; // will be owned by DatabaseFeature REQUIRE((TRI_ERROR_NO_ERROR == databaseFeature->createDatabase(0, "testDatabase" TOSTRING(__LINE__), vocbase))); @@ -360,40 +376,63 @@ SECTION("test_drop_database") { CHECK((TRI_ERROR_NO_ERROR == ci->createViewCoordinator(vocbase->name(), "42", viewCreateJson->slice(), error))); auto logicalWiew = ci->getView(vocbase->name(), "42"); // link creation requires cluster-view to be in ClusterInfo instead of TRI_vocbase_t REQUIRE((false == !logicalWiew)); - auto* wiewImpl = dynamic_cast(logicalWiew.get()); + auto* wiewImpl = dynamic_cast(logicalWiew.get()); REQUIRE((false == !wiewImpl)); beforeCount = 0; // reset before call to StorageEngine::createView(...) auto res = logicalWiew->properties(viewUpdateJson->slice(), true); REQUIRE(true == res.ok()); - CHECK((1 + 2 + 1 == beforeCount)); // +1 for StorageEngineMock::createView(...), +2 for StorageEngineMock::getViewProperties(...), +1 for StorageEngineMock::changeView(...) + CHECK((1 == beforeCount)); // +1 for StorageEngineMock::createIndex(...) beforeCount = 0; // reset before call to StorageEngine::dropView(...) CHECK((TRI_ERROR_NO_ERROR == databaseFeature->dropDatabase(vocbase->id(), true, true))); - CHECK((3 == beforeCount)); // +1 for StorageEngineMock::changeView(...), +1 for StorageEngineMock::getViewProperties(...), +1 for StorageEngineMock::dropView(...) + CHECK((0 == beforeCount)); } SECTION("test_ensure") { - auto json = arangodb::velocypack::Parser::fromJson("{ \"name\": \"testView\", \"type\": \"arangosearch\", \"collections\": [ 3, 4, 5 ] }"); - TRI_vocbase_t vocbase(TRI_vocbase_type_e::TRI_VOCBASE_TYPE_NORMAL, 1, "testVocbase"); + auto* database = arangodb::DatabaseFeature::DATABASE; + REQUIRE((nullptr != database)); + auto* ci = arangodb::ClusterInfo::instance(); + REQUIRE((nullptr != ci)); + std::string error; + TRI_vocbase_t* vocbase; // will be owned by DatabaseFeature + + // create database + { + // simulate heartbeat thread + REQUIRE(TRI_ERROR_NO_ERROR == database->createDatabase(1, "testDatabase", vocbase)); + + REQUIRE(nullptr != vocbase); + CHECK("testDatabase" == vocbase->name()); + CHECK(TRI_vocbase_type_e::TRI_VOCBASE_TYPE_NORMAL == vocbase->type()); + CHECK(1 == vocbase->id()); + + CHECK(TRI_ERROR_NO_ERROR == ci->createDatabaseCoordinator( + vocbase->name(), VPackSlice::emptyObjectSlice(), error, 0.0 + )); + CHECK("no error" == error); + } + + auto collectionJson = arangodb::velocypack::Parser::fromJson("{ \"name\": \"testCollection\" }"); + auto linkJson = arangodb::velocypack::Parser::fromJson("{ \"view\": \"testView\", \"type\": \"arangosearch\", \"includeAllFields\": true }"); + auto viewJson = arangodb::velocypack::Parser::fromJson("{ \"name\": \"testView\", \"type\": \"arangosearch\", \"collections\": [ 3, 4, 5 ] }"); + auto logicalCollection = vocbase->createCollection(collectionJson->slice()); + REQUIRE((false == !logicalCollection)); arangodb::LogicalView::ptr wiew; - REQUIRE((arangodb::iresearch::IResearchViewDBServer::factory().instantiate(wiew, vocbase, json->slice(), 42).ok())); + REQUIRE((arangodb::iresearch::IResearchView::factory().create(wiew, *vocbase, viewJson->slice()).ok())); CHECK((false == !wiew)); - auto* impl = dynamic_cast(wiew.get()); + auto* impl = dynamic_cast(wiew.get()); CHECK((nullptr != impl)); - auto view = impl->ensure(123); - CHECK((false == !view)); - CHECK((std::string("_iresearch_123_1") == view->name())); - CHECK((false == view->deleted())); - CHECK((wiew->id() != view->id())); // must have unique ID - CHECK((wiew->id() == view->planId())); // same as view ID - CHECK((0 == view->planVersion())); - CHECK((arangodb::iresearch::DATA_SOURCE_TYPE == view->type())); - CHECK((&vocbase == &(view->vocbase()))); + bool created; + auto index = logicalCollection->createIndex(linkJson->slice(), created); + REQUIRE((false == !index)); + auto link = std::dynamic_pointer_cast(index); + REQUIRE((false == !link)); + static auto visitor = [](TRI_voc_cid_t)->bool { return false; }; - CHECK((true == view->visitCollections(visitor))); // no collections in view - CHECK((false == !vocbase.lookupView("_iresearch_123_1"))); + CHECK((false == wiew->visitCollections(visitor))); // no collections in view + CHECK((false == !arangodb::iresearch::IResearchLinkHelper::find(*logicalCollection, *wiew))); } SECTION("test_make") { @@ -406,9 +445,9 @@ SECTION("test_make") { auto json = arangodb::velocypack::Parser::fromJson("{ \"name\": \"testView\", \"type\": \"arangosearch\" }"); TRI_vocbase_t vocbase(TRI_vocbase_type_e::TRI_VOCBASE_TYPE_NORMAL, 1, "testVocbase"); arangodb::LogicalView::ptr wiew; - REQUIRE((arangodb::iresearch::IResearchViewDBServer::factory().instantiate(wiew, vocbase, json->slice(), 42).ok())); + REQUIRE((arangodb::iresearch::IResearchView::factory().instantiate(wiew, vocbase, json->slice(), 42).ok())); CHECK((false == !wiew)); - auto* impl = dynamic_cast(wiew.get()); + auto* impl = dynamic_cast(wiew.get()); CHECK((nullptr != impl)); CHECK((std::string("testView") == wiew->name())); @@ -419,40 +458,21 @@ SECTION("test_make") { CHECK((arangodb::iresearch::DATA_SOURCE_TYPE == wiew->type())); CHECK((&vocbase == &(wiew->vocbase()))); } - - // make IResearchView (DBServer view also created) - { - auto json = arangodb::velocypack::Parser::fromJson("{ \"id\": 100, \"name\": \"_iresearch_123_456\", \"type\": \"arangosearch\", \"isSystem\": true }"); - TRI_vocbase_t vocbase(TRI_vocbase_type_e::TRI_VOCBASE_TYPE_NORMAL, 1, "testVocbase"); - CHECK((true == !vocbase.lookupView("testView"))); - arangodb::LogicalView::ptr view; - REQUIRE((arangodb::iresearch::IResearchViewDBServer::factory().instantiate(view, vocbase, json->slice(), 42).ok())); - CHECK((false == !view)); - auto* impl = dynamic_cast(view.get()); - CHECK((nullptr != impl)); - - CHECK((std::string("_iresearch_123_456") == view->name())); - CHECK((false == view->deleted())); - CHECK((100 == view->id())); - CHECK((view->id() == view->planId())); // same as view ID - CHECK((42 == view->planVersion())); - CHECK((arangodb::iresearch::DATA_SOURCE_TYPE == view->type())); - CHECK((&vocbase == &(view->vocbase()))); - } } SECTION("test_open") { auto* ci = arangodb::ClusterInfo::instance(); REQUIRE(nullptr != ci); + std::string error; // open empty { auto json = arangodb::velocypack::Parser::fromJson("{ \"name\": \"testView\", \"type\": \"arangosearch\" }"); TRI_vocbase_t vocbase(TRI_vocbase_type_e::TRI_VOCBASE_TYPE_NORMAL, 1, "testVocbase"); arangodb::LogicalView::ptr wiew; - REQUIRE((arangodb::iresearch::IResearchViewDBServer::factory().instantiate(wiew, vocbase, json->slice(), 42).ok())); + REQUIRE((arangodb::iresearch::IResearchView::factory().instantiate(wiew, vocbase, json->slice(), 42).ok())); CHECK((false == !wiew)); - auto* impl = dynamic_cast(wiew.get()); + auto* impl = dynamic_cast(wiew.get()); CHECK((nullptr != impl)); static auto visitor = [](TRI_voc_cid_t)->bool { return false; }; @@ -464,34 +484,34 @@ SECTION("test_open") { { auto const wiewId = std::to_string(ci->uniqid() + 1); // +1 because LogicalView creation will generate a new ID std::string dataPath = (((irs::utf8_path()/=s.testFilesystemPath)/=std::string("databases"))/=std::string("arangosearch-123")).utf8(); + auto collectionJson = arangodb::velocypack::Parser::fromJson("{ \"name\": \"testCollection\" }"); auto json = arangodb::velocypack::Parser::fromJson("{ \"name\": \"testView\", \"type\": \"arangosearch\" }"); TRI_vocbase_t vocbase(TRI_vocbase_type_e::TRI_VOCBASE_TYPE_NORMAL, 1, "testVocbase"); + auto logicalCollection = vocbase.createCollection(collectionJson->slice()); + REQUIRE((false == !logicalCollection)); arangodb::LogicalView::ptr wiew; - REQUIRE((arangodb::iresearch::IResearchViewDBServer::factory().instantiate(wiew, vocbase, json->slice(), 42).ok())); + REQUIRE((arangodb::iresearch::IResearchView::factory().instantiate(wiew, vocbase, json->slice(), 42).ok())); CHECK((false == !wiew)); - auto* impl = dynamic_cast(wiew.get()); + auto* impl = dynamic_cast(wiew.get()); CHECK((nullptr != impl)); // ensure we have shard view in vocbase - auto const shardViewName = "_iresearch_123_" + wiewId; - auto jsonShard = arangodb::velocypack::Parser::fromJson( - "{ \"id\": 100, \"name\": \"" + shardViewName + "\", \"type\": \"arangosearch\", \"isSystem\": true }" - ); - CHECK((true == !vocbase.lookupView(shardViewName))); - auto shardView = vocbase.createView(jsonShard->slice()); - CHECK(shardView); + struct Link: public arangodb::iresearch::IResearchLink { + Link(TRI_idx_iid_t id, arangodb::LogicalCollection& col): IResearchLink(id, col) {} + } link(42, *logicalCollection); + auto asyncLinkPtr = std::make_shared(&link); static auto visitor = [](TRI_voc_cid_t)->bool { return false; }; CHECK((true == impl->visitCollections(visitor))); - auto view = impl->ensure(123); - CHECK((false == !view)); - CHECK(view == shardView); + CHECK((true == impl->link(asyncLinkPtr))); CHECK((false == impl->visitCollections(visitor))); wiew->open(); } } SECTION("test_query") { + auto* database = arangodb::DatabaseFeature::DATABASE; + REQUIRE((nullptr != database)); auto* ci = arangodb::ClusterInfo::instance(); REQUIRE((nullptr != ci)); auto* databaseFeature = arangodb::application_features::ApplicationServer::getFeature("Database"); @@ -511,49 +531,57 @@ SECTION("test_query") { // no filter/order provided, means "RETURN *" { - TRI_vocbase_t vocbase(TRI_vocbase_type_e::TRI_VOCBASE_TYPE_NORMAL, 1, "testVocbase"); auto collectionJson = arangodb::velocypack::Parser::fromJson("{ \"name\": \"testCollection\" }"); - auto logicalCollection = vocbase.createCollection(collectionJson->slice()); + auto linkJson = arangodb::velocypack::Parser::fromJson("{ \"view\": \"testView\", \"type\": \"arangosearch\", \"includeAllFields\": true }"); + TRI_vocbase_t* vocbase; // will be owned by DatabaseFeature + REQUIRE((TRI_ERROR_NO_ERROR == database->createDatabase(1, "testDatabase0", vocbase))); + CHECK((TRI_ERROR_NO_ERROR == ci->createDatabaseCoordinator(vocbase->name(), VPackSlice::emptyObjectSlice(), error, 0.0))); + CHECK("no error" == error); + auto logicalCollection = vocbase->createCollection(collectionJson->slice()); REQUIRE(nullptr != logicalCollection); arangodb::LogicalView::ptr logicalWiew; - REQUIRE((arangodb::iresearch::IResearchViewDBServer::factory().instantiate(logicalWiew, vocbase, createJson->slice(), 42).ok())); + REQUIRE((arangodb::iresearch::IResearchView::factory().create(logicalWiew, *vocbase, createJson->slice()).ok())); REQUIRE((false == !logicalWiew)); - auto* wiewImpl = dynamic_cast(logicalWiew.get()); + auto* wiewImpl = dynamic_cast(logicalWiew.get()); REQUIRE((false == !wiewImpl)); - auto logicalView = wiewImpl->ensure(logicalCollection->id()); - REQUIRE((false == !logicalView)); - auto* viewImpl = dynamic_cast(logicalView.get()); - REQUIRE((false == !viewImpl)); + + bool created; + auto index = logicalCollection->createIndex(linkJson->slice(), created); + REQUIRE((false == !index)); + auto link = std::dynamic_pointer_cast(index); + REQUIRE((false == !link)); arangodb::transaction::Methods trx( - arangodb::transaction::StandaloneContext::Create(vocbase), + arangodb::transaction::StandaloneContext::Create(*vocbase), std::vector{logicalCollection->name()}, EMPTY, EMPTY, arangodb::transaction::Options() ); CHECK((trx.begin().ok())); - auto* snapshot = wiewImpl->snapshot(trx, { logicalCollection->name() }, arangodb::iresearch::IResearchView::Snapshot::FindOrCreate); + std::unordered_set collections = { logicalCollection->id() }; + auto* snapshot = wiewImpl->snapshot(trx, arangodb::iresearch::IResearchView::Snapshot::FindOrCreate, &collections); CHECK(0 == snapshot->docs_count()); CHECK((trx.commit().ok())); } // ordered iterator { - TRI_vocbase_t vocbase(TRI_vocbase_type_e::TRI_VOCBASE_TYPE_NORMAL, 1, "testVocbase"); auto collectionJson = arangodb::velocypack::Parser::fromJson("{ \"name\": \"testCollection\" }"); - auto linkJson = arangodb::velocypack::Parser::fromJson("{ \"view\": \"testView\", \"includeAllFields\": true }"); - auto logicalCollection = vocbase.createCollection(collectionJson->slice()); + auto linkJson = arangodb::velocypack::Parser::fromJson("{ \"view\": \"testView\", \"type\": \"arangosearch\", \"includeAllFields\": true }"); + TRI_vocbase_t* vocbase; // will be owned by DatabaseFeature + REQUIRE((TRI_ERROR_NO_ERROR == database->createDatabase(1, "testDatabase1", vocbase))); + CHECK((TRI_ERROR_NO_ERROR == ci->createDatabaseCoordinator(vocbase->name(), VPackSlice::emptyObjectSlice(), error, 0.0))); + auto logicalCollection = vocbase->createCollection(collectionJson->slice()); REQUIRE(nullptr != logicalCollection); - auto logicalWiew = vocbase.createView(createJson->slice()); + arangodb::LogicalView::ptr logicalWiew; + REQUIRE((arangodb::iresearch::IResearchView::factory().create(logicalWiew, *vocbase, createJson->slice()).ok())); REQUIRE((false == !logicalWiew)); - auto* wiewImpl = dynamic_cast(logicalWiew.get()); + auto* wiewImpl = dynamic_cast(logicalWiew.get()); REQUIRE((false == !wiewImpl)); - auto logicalView = wiewImpl->ensure(logicalCollection->id()); - REQUIRE((false == !logicalView)); - auto* viewImpl = dynamic_cast(logicalView.get()); - std::shared_ptr index; - REQUIRE((arangodb::iresearch::IResearchMMFilesLink::factory().instantiate(index, *logicalCollection, linkJson->slice(), 42, false).ok())); + + bool created; + auto index = logicalCollection->createIndex(linkJson->slice(), created); REQUIRE((false == !index)); auto link = std::dynamic_pointer_cast(index); REQUIRE((false == !link)); @@ -564,7 +592,7 @@ SECTION("test_query") { arangodb::iresearch::IResearchLinkMeta meta; meta._includeAllFields = true; arangodb::transaction::Methods trx( - arangodb::transaction::StandaloneContext::Create(vocbase), + arangodb::transaction::StandaloneContext::Create(*vocbase), EMPTY, std::vector{logicalCollection->name()}, EMPTY, @@ -577,18 +605,19 @@ SECTION("test_query") { } CHECK((trx.commit().ok())); - CHECK(viewImpl->commit().ok()); + CHECK(wiewImpl->commit().ok()); } arangodb::transaction::Methods trx( - arangodb::transaction::StandaloneContext::Create(vocbase), + arangodb::transaction::StandaloneContext::Create(*vocbase), std::vector{logicalCollection->name()}, EMPTY, EMPTY, arangodb::transaction::Options() ); CHECK((trx.begin().ok())); - auto* snapshot = wiewImpl->snapshot(trx, { logicalCollection->name() }, arangodb::iresearch::IResearchView::Snapshot::FindOrCreate); + std::unordered_set collections = { logicalCollection->id() }; + auto* snapshot = wiewImpl->snapshot(trx, arangodb::iresearch::IResearchView::Snapshot::FindOrCreate, &collections); CHECK(12 == snapshot->docs_count()); CHECK((trx.commit().ok())); } @@ -609,7 +638,7 @@ SECTION("test_query") { CHECK((TRI_ERROR_NO_ERROR == ci->createViewCoordinator(vocbase->name(), "42", createJson->slice(), error))); auto logicalWiew = ci->getView(vocbase->name(), "42"); // link creation requires cluster-view to be in ClusterInfo instead of TRI_vocbase_t CHECK((false == !logicalWiew)); - auto* wiewImpl = dynamic_cast(logicalWiew.get()); + auto* wiewImpl = dynamic_cast(logicalWiew.get()); CHECK((false == !wiewImpl)); arangodb::Result res = logicalWiew->properties(links->slice(), true); CHECK(true == res.ok()); @@ -647,9 +676,10 @@ SECTION("test_query") { trxOptions ); CHECK((trx0.begin().ok())); - CHECK(nullptr == wiewImpl->snapshot(trx0, { logicalCollection->name() }, arangodb::iresearch::IResearchView::Snapshot::Find)); - auto* snapshot0 = wiewImpl->snapshot(trx0, { logicalCollection->name() }, arangodb::iresearch::IResearchView::Snapshot::SyncAndReplace); - CHECK(snapshot0 == wiewImpl->snapshot(trx0, { logicalCollection->name() }, arangodb::iresearch::IResearchView::Snapshot::Find)); + std::unordered_set collectionIds = { logicalCollection->id() }; + CHECK((nullptr == wiewImpl->snapshot(trx0, arangodb::iresearch::IResearchView::Snapshot::Find, &collectionIds))); + auto* snapshot0 = wiewImpl->snapshot(trx0, arangodb::iresearch::IResearchView::Snapshot::SyncAndReplace, &collectionIds); + CHECK((snapshot0 == wiewImpl->snapshot(trx0, arangodb::iresearch::IResearchView::Snapshot::Find, &collectionIds))); CHECK(12 == snapshot0->docs_count()); CHECK((trx0.commit().ok())); @@ -687,7 +717,7 @@ SECTION("test_query") { trxOptions ); CHECK((trx1.begin().ok())); - auto* snapshot1 = wiewImpl->snapshot(trx1, { logicalCollection->name() }, arangodb::iresearch::IResearchView::Snapshot::SyncAndReplace); + auto* snapshot1 = wiewImpl->snapshot(trx1, arangodb::iresearch::IResearchView::Snapshot::SyncAndReplace, &collectionIds); CHECK(24 == snapshot1->docs_count()); CHECK((trx1.commit().ok())); } @@ -709,7 +739,7 @@ SECTION("test_query") { CHECK((TRI_ERROR_NO_ERROR == ci->createViewCoordinator(vocbase->name(), "42", createJson->slice(), error))); auto logicalWiew = ci->getView(vocbase->name(), "42"); // link creation requires cluster-view to be in ClusterInfo instead of TRI_vocbase_t REQUIRE((false == !logicalWiew)); - auto* wiewImpl = dynamic_cast(logicalWiew.get()); + auto* wiewImpl = dynamic_cast(logicalWiew.get()); REQUIRE((false == !wiewImpl)); arangodb::Result res = logicalWiew->properties(viewUpdateJson->slice(), true); REQUIRE(true == res.ok()); @@ -759,7 +789,8 @@ SECTION("test_query") { arangodb::transaction::Options{} ); CHECK((trx.begin().ok())); - auto* snapshot = wiewImpl->snapshot(trx, { logicalCollection->name() }, arangodb::iresearch::IResearchView::Snapshot::SyncAndReplace); + std::unordered_set collections = { logicalCollection->id() }; + auto* snapshot = wiewImpl->snapshot(trx, arangodb::iresearch::IResearchView::Snapshot::SyncAndReplace, &collections); CHECK(i == snapshot->docs_count()); CHECK((trx.commit().ok())); } @@ -773,12 +804,15 @@ SECTION("test_rename") { // rename empty { + auto collectionJson = arangodb::velocypack::Parser::fromJson("{ \"name\": \"testCollection\" }"); auto json = arangodb::velocypack::Parser::fromJson("{ \"name\": \"testView\", \"type\": \"arangosearch\" }"); TRI_vocbase_t vocbase(TRI_vocbase_type_e::TRI_VOCBASE_TYPE_NORMAL, 1, "testVocbase"); + auto logicalCollection = vocbase.createCollection(collectionJson->slice()); + REQUIRE((false == !logicalCollection)); arangodb::LogicalView::ptr wiew; - REQUIRE((arangodb::iresearch::IResearchViewDBServer::factory().instantiate(wiew, vocbase, json->slice(), 42).ok())); + REQUIRE((arangodb::iresearch::IResearchView::factory().instantiate(wiew, vocbase, json->slice(), 42).ok())); CHECK((false == !wiew)); - auto* impl = dynamic_cast(wiew.get()); + auto* impl = dynamic_cast(wiew.get()); CHECK((nullptr != impl)); CHECK((std::string("testView") == wiew->name())); @@ -805,35 +839,33 @@ SECTION("test_rename") { CHECK((std::string("testView") == builder.slice().get("name").copyString())); } - auto view = impl->ensure(123); - CHECK((false == !view)); - CHECK((std::string("_iresearch_123_1") == view->name())); + struct Link: public arangodb::iresearch::IResearchLink { + Link(TRI_idx_iid_t id, arangodb::LogicalCollection& col): IResearchLink(id, col) {} + } link(42, *logicalCollection); + auto asyncLinkPtr = std::make_shared(&link); + CHECK((true == impl->link(asyncLinkPtr))); } // rename non-empty { + auto collectionJson = arangodb::velocypack::Parser::fromJson("{ \"name\": \"testCollection\" }"); auto const wiewId = std::to_string(ci->uniqid() + 1); // +1 because LogicalView creation will generate a new ID auto json = arangodb::velocypack::Parser::fromJson("{ \"name\": \"testView\", \"type\": \"arangosearch\" }"); TRI_vocbase_t vocbase(TRI_vocbase_type_e::TRI_VOCBASE_TYPE_NORMAL, 1, "testVocbase"); + auto logicalCollection = vocbase.createCollection(collectionJson->slice()); + REQUIRE((false == !logicalCollection)); arangodb::LogicalView::ptr wiew; - REQUIRE((arangodb::iresearch::IResearchViewDBServer::factory().instantiate(wiew, vocbase, json->slice(), 42).ok())); + REQUIRE((arangodb::iresearch::IResearchView::factory().instantiate(wiew, vocbase, json->slice(), 42).ok())); CHECK((false == !wiew)); - auto* impl = dynamic_cast(wiew.get()); + auto* impl = dynamic_cast(wiew.get()); CHECK((nullptr != impl)); // ensure we have shard view in vocbase - auto const shardViewName = "_iresearch_123_" + wiewId; - auto jsonShard = arangodb::velocypack::Parser::fromJson( - "{ \"id\": 100, \"name\": \"" + shardViewName + "\", \"type\": \"arangosearch\", \"isSystem\": true }" - ); - CHECK((true == !vocbase.lookupView(shardViewName))); - auto shardView = vocbase.createView(jsonShard->slice()); - CHECK(shardView); - - CHECK((std::string("testView") == wiew->name())); - auto view = impl->ensure(123); - CHECK((false == !view)); - CHECK((shardViewName == view->name())); + struct Link: public arangodb::iresearch::IResearchLink { + Link(TRI_idx_iid_t id, arangodb::LogicalCollection& col): IResearchLink(id, col) {} + } link(42, *logicalCollection); + auto asyncLinkPtr = std::make_shared(&link); + CHECK((true == impl->link(asyncLinkPtr))); { arangodb::velocypack::Builder builder; @@ -857,7 +889,6 @@ SECTION("test_rename") { CHECK((std::string("testView") == builder.slice().get("name").copyString())); } - CHECK((("_iresearch_123_" + wiewId) == view->name())); wiew->rename("testView"); // rename back or vocbase will be out of sync } } @@ -868,9 +899,9 @@ SECTION("test_toVelocyPack") { auto json = arangodb::velocypack::Parser::fromJson("{ \"name\": \"testView\", \"type\": \"arangosearch\", \"unusedKey\": \"unusedValue\" }"); TRI_vocbase_t vocbase(TRI_vocbase_type_e::TRI_VOCBASE_TYPE_NORMAL, 1, "testVocbase"); arangodb::LogicalView::ptr wiew; - REQUIRE((arangodb::iresearch::IResearchViewDBServer::factory().instantiate(wiew, vocbase, json->slice(), 42).ok())); + REQUIRE((arangodb::iresearch::IResearchView::factory().instantiate(wiew, vocbase, json->slice(), 42).ok())); CHECK((false == !wiew)); - auto* impl = dynamic_cast(wiew.get()); + auto* impl = dynamic_cast(wiew.get()); CHECK((nullptr != impl)); arangodb::velocypack::Builder builder; @@ -891,9 +922,9 @@ SECTION("test_toVelocyPack") { auto json = arangodb::velocypack::Parser::fromJson("{ \"name\": \"testView\", \"type\": \"arangosearch\", \"unusedKey\": \"unusedValue\" }"); TRI_vocbase_t vocbase(TRI_vocbase_type_e::TRI_VOCBASE_TYPE_NORMAL, 1, "testVocbase"); arangodb::LogicalView::ptr wiew; - REQUIRE((arangodb::iresearch::IResearchViewDBServer::factory().instantiate(wiew, vocbase, json->slice(), 42).ok())); + REQUIRE((arangodb::iresearch::IResearchView::factory().instantiate(wiew, vocbase, json->slice(), 42).ok())); CHECK((false == !wiew)); - auto* impl = dynamic_cast(wiew.get()); + auto* impl = dynamic_cast(wiew.get()); CHECK((nullptr != impl)); arangodb::velocypack::Builder builder; @@ -902,7 +933,7 @@ SECTION("test_toVelocyPack") { wiew->properties(builder, true, false); builder.close(); auto slice = builder.slice(); - CHECK((10U == slice.length())); + CHECK((11U == slice.length())); CHECK((slice.hasKey("globallyUniqueId") && slice.get("globallyUniqueId").isString() && false == slice.get("globallyUniqueId").copyString().empty())); CHECK((slice.hasKey("id") && slice.get("id").isString() && std::string("2") == slice.get("id").copyString())); CHECK((slice.hasKey("name") && slice.get("name").isString() && std::string("testView") == slice.get("name").copyString())); @@ -914,9 +945,9 @@ SECTION("test_toVelocyPack") { auto json = arangodb::velocypack::Parser::fromJson("{ \"name\": \"testView\", \"type\": \"arangosearch\", \"unusedKey\": \"unusedValue\" }"); TRI_vocbase_t vocbase(TRI_vocbase_type_e::TRI_VOCBASE_TYPE_NORMAL, 1, "testVocbase"); arangodb::LogicalView::ptr wiew; - REQUIRE((arangodb::iresearch::IResearchViewDBServer::factory().instantiate(wiew, vocbase, json->slice(), 42).ok())); + REQUIRE((arangodb::iresearch::IResearchView::factory().instantiate(wiew, vocbase, json->slice(), 42).ok())); CHECK((false == !wiew)); - auto* impl = dynamic_cast(wiew.get()); + auto* impl = dynamic_cast(wiew.get()); CHECK((nullptr != impl)); arangodb::velocypack::Builder builder; @@ -937,23 +968,43 @@ SECTION("test_toVelocyPack") { } SECTION("test_transaction_snapshot") { + auto* database = arangodb::DatabaseFeature::DATABASE; + REQUIRE((nullptr != database)); + auto* ci = arangodb::ClusterInfo::instance(); + REQUIRE((nullptr != ci)); + std::string error; + TRI_vocbase_t* vocbase; // will be owned by DatabaseFeature + + // create database + { + // simulate heartbeat thread + REQUIRE(TRI_ERROR_NO_ERROR == database->createDatabase(1, "testDatabase", vocbase)); + + REQUIRE(nullptr != vocbase); + CHECK("testDatabase" == vocbase->name()); + CHECK(TRI_vocbase_type_e::TRI_VOCBASE_TYPE_NORMAL == vocbase->type()); + CHECK(1 == vocbase->id()); + + CHECK(TRI_ERROR_NO_ERROR == ci->createDatabaseCoordinator( + vocbase->name(), VPackSlice::emptyObjectSlice(), error, 0.0 + )); + CHECK("no error" == error); + } + static std::vector const EMPTY; auto viewJson = arangodb::velocypack::Parser::fromJson("{ \"name\": \"testView\", \"type\": \"arangosearch\", \"consolidationIntervalMsec\": 0 }"); auto collectionJson = arangodb::velocypack::Parser::fromJson("{ \"name\": \"testCollection\" }"); - auto linkJson = arangodb::velocypack::Parser::fromJson("{ \"view\": \"testView\", \"includeAllFields\": true }"); - TRI_vocbase_t vocbase(TRI_vocbase_type_e::TRI_VOCBASE_TYPE_NORMAL, 1, "testVocbase"); - auto logicalCollection = vocbase.createCollection(collectionJson->slice()); + auto linkJson = arangodb::velocypack::Parser::fromJson("{ \"view\": \"testView\", \"type\": \"arangosearch\", \"includeAllFields\": true }"); + auto logicalCollection = vocbase->createCollection(collectionJson->slice()); REQUIRE(nullptr != logicalCollection); - auto logicalWiew = vocbase.createView(viewJson->slice()); + arangodb::LogicalView::ptr logicalWiew; + REQUIRE((arangodb::iresearch::IResearchView::factory().create(logicalWiew, *vocbase, viewJson->slice()).ok())); REQUIRE((false == !logicalWiew)); - auto* wiewImpl = dynamic_cast(logicalWiew.get()); + auto* wiewImpl = dynamic_cast(logicalWiew.get()); REQUIRE((nullptr != wiewImpl)); - auto logicalView = wiewImpl->ensure(logicalCollection->id()); - REQUIRE((false == !logicalView)); - auto* viewImpl = dynamic_cast(logicalView.get()); - REQUIRE((nullptr != viewImpl)); - std::shared_ptr index; - REQUIRE((arangodb::iresearch::IResearchMMFilesLink::factory().instantiate(index, *logicalCollection, linkJson->slice(), 42, false).ok())); + + bool created; + auto index = logicalCollection->createIndex(linkJson->slice(), created); REQUIRE((false == !index)); auto link = std::dynamic_pointer_cast(index); REQUIRE((false == !link)); @@ -964,7 +1015,7 @@ SECTION("test_transaction_snapshot") { arangodb::iresearch::IResearchLinkMeta meta; meta._includeAllFields = true; arangodb::transaction::Methods trx( - arangodb::transaction::StandaloneContext::Create(vocbase), + arangodb::transaction::StandaloneContext::Create(*vocbase), EMPTY, std::vector{logicalCollection->name()}, EMPTY, @@ -978,14 +1029,15 @@ SECTION("test_transaction_snapshot") { // no snapshot in TransactionState (force == false, waitForSync = false) { arangodb::transaction::Methods trx( - arangodb::transaction::StandaloneContext::Create(vocbase), + arangodb::transaction::StandaloneContext::Create(*vocbase), std::vector{logicalCollection->name()}, EMPTY, EMPTY, arangodb::transaction::Options() ); CHECK((trx.begin().ok())); - auto* snapshot = wiewImpl->snapshot(trx, { logicalCollection->name() }); + std::unordered_set collections = { logicalCollection->id() }; + auto* snapshot = wiewImpl->snapshot(trx, arangodb::iresearch::IResearchView::Snapshot::Find, &collections); CHECK((nullptr == snapshot)); CHECK((trx.commit().ok())); } @@ -993,16 +1045,17 @@ SECTION("test_transaction_snapshot") { // no snapshot in TransactionState (force == true, waitForSync = false) { arangodb::transaction::Methods trx( - arangodb::transaction::StandaloneContext::Create(vocbase), + arangodb::transaction::StandaloneContext::Create(*vocbase), std::vector{logicalCollection->name()}, EMPTY, EMPTY, arangodb::transaction::Options() ); CHECK((trx.begin().ok())); - CHECK(nullptr == wiewImpl->snapshot(trx, { logicalCollection->name() }, arangodb::iresearch::IResearchView::Snapshot::Find)); - auto* snapshot = wiewImpl->snapshot(trx, { logicalCollection->name() }, arangodb::iresearch::IResearchView::Snapshot::FindOrCreate); - CHECK(snapshot == wiewImpl->snapshot(trx, { logicalCollection->name() }, arangodb::iresearch::IResearchView::Snapshot::FindOrCreate)); + std::unordered_set collections = { logicalCollection->id() }; + CHECK((nullptr == wiewImpl->snapshot(trx, arangodb::iresearch::IResearchView::Snapshot::Find, &collections))); + auto* snapshot = wiewImpl->snapshot(trx, arangodb::iresearch::IResearchView::Snapshot::FindOrCreate, &collections); + CHECK((snapshot == wiewImpl->snapshot(trx, arangodb::iresearch::IResearchView::Snapshot::FindOrCreate, &collections))); CHECK((0 == snapshot->live_docs_count())); CHECK((trx.commit().ok())); } @@ -1012,14 +1065,15 @@ SECTION("test_transaction_snapshot") { arangodb::transaction::Options opts; opts.waitForSync = true; arangodb::transaction::Methods trx( - arangodb::transaction::StandaloneContext::Create(vocbase), + arangodb::transaction::StandaloneContext::Create(*vocbase), std::vector{logicalCollection->name()}, EMPTY, EMPTY, opts ); CHECK((trx.begin().ok())); - auto* snapshot = wiewImpl->snapshot(trx, { logicalCollection->name() }); + std::unordered_set collections = { logicalCollection->id() }; + auto* snapshot = wiewImpl->snapshot(trx, arangodb::iresearch::IResearchView::Snapshot::Find, &collections); CHECK((nullptr == snapshot)); CHECK((trx.commit().ok())); } @@ -1028,17 +1082,18 @@ SECTION("test_transaction_snapshot") { { arangodb::transaction::Options opts; arangodb::transaction::Methods trx( - arangodb::transaction::StandaloneContext::Create(vocbase), + arangodb::transaction::StandaloneContext::Create(*vocbase), std::vector{logicalCollection->name()}, EMPTY, EMPTY, opts ); CHECK((trx.begin().ok())); - CHECK(nullptr == wiewImpl->snapshot(trx, { logicalCollection->name() }, arangodb::iresearch::IResearchView::Snapshot::Find)); - auto* snapshot = wiewImpl->snapshot(trx, { logicalCollection->name() }, arangodb::iresearch::IResearchView::Snapshot::SyncAndReplace); - CHECK(snapshot == wiewImpl->snapshot(trx, { logicalCollection->name() }, arangodb::iresearch::IResearchView::Snapshot::Find)); - CHECK(snapshot == wiewImpl->snapshot(trx, { logicalCollection->name() }, arangodb::iresearch::IResearchView::Snapshot::FindOrCreate)); + std::unordered_set collections = { logicalCollection->id() }; + CHECK((nullptr == wiewImpl->snapshot(trx, arangodb::iresearch::IResearchView::Snapshot::Find, &collections))); + auto* snapshot = wiewImpl->snapshot(trx, arangodb::iresearch::IResearchView::Snapshot::SyncAndReplace, &collections); + CHECK((snapshot == wiewImpl->snapshot(trx, arangodb::iresearch::IResearchView::Snapshot::Find, &collections))); + CHECK((snapshot == wiewImpl->snapshot(trx, arangodb::iresearch::IResearchView::Snapshot::FindOrCreate, &collections))); CHECK((nullptr != snapshot)); CHECK((1 == snapshot->live_docs_count())); CHECK((trx.commit().ok())); @@ -1065,7 +1120,7 @@ SECTION("test_updateProperties") { CHECK((TRI_ERROR_NO_ERROR == ci->createViewCoordinator(vocbase->name(), "42", viewJson->slice(), error))); auto wiew = ci->getView(vocbase->name(), "42"); // link creation requires cluster-view to be in ClusterInfo instead of TRI_vocbase_t CHECK((false == !wiew)); - auto* impl = dynamic_cast(wiew.get()); + auto* impl = dynamic_cast(wiew.get()); CHECK((nullptr != impl)); { @@ -1077,10 +1132,10 @@ SECTION("test_updateProperties") { auto slice = builder.slice(); CHECK((slice.isObject())); - CHECK((10U == slice.length())); + CHECK((11U == slice.length())); CHECK((slice.hasKey("cleanupIntervalStep") && slice.get("cleanupIntervalStep").isNumber() && 24 == slice.get("cleanupIntervalStep").getNumber())); CHECK((slice.hasKey("consolidationIntervalMsec") && slice.get("consolidationIntervalMsec").isNumber() && 42 == slice.get("consolidationIntervalMsec").getNumber())); - CHECK((!slice.hasKey("links"))); + CHECK((slice.hasKey("links") && slice.get("links").isObject() && 0 == slice.get("links").length())); } { @@ -1097,23 +1152,23 @@ SECTION("test_updateProperties") { auto slice = builder.slice(); CHECK((slice.isObject())); - CHECK((10U == slice.length())); + CHECK((11U == slice.length())); CHECK((slice.hasKey("cleanupIntervalStep") && slice.get("cleanupIntervalStep").isNumber() && 24 == slice.get("cleanupIntervalStep").getNumber())); CHECK((slice.hasKey("consolidationIntervalMsec") && slice.get("consolidationIntervalMsec").isNumber() && 52 == slice.get("consolidationIntervalMsec").getNumber())); - CHECK((!slice.hasKey("links"))); + CHECK((slice.hasKey("links") && slice.get("links").isObject() && 1 == slice.get("links").length())); } - auto view = impl->ensure(123); - CHECK((false == !view)); + + CHECK((false == !arangodb::iresearch::IResearchLinkHelper::find(*logicalCollection, *wiew))); static auto visitor = [](TRI_voc_cid_t)->bool { return false; }; - CHECK((true == view->visitCollections(visitor))); // no collections in view + CHECK((false == wiew->visitCollections(visitor))); // no collections in view // not for persistence { arangodb::velocypack::Builder builder; builder.openObject(); - view->properties(builder, true, false); + wiew->properties(builder, true, false); builder.close(); auto slice = builder.slice(); @@ -1121,7 +1176,7 @@ SECTION("test_updateProperties") { CHECK((11U == slice.length())); CHECK((slice.hasKey("cleanupIntervalStep") && slice.get("cleanupIntervalStep").isNumber() && 24 == slice.get("cleanupIntervalStep").getNumber())); CHECK((slice.hasKey("consolidationIntervalMsec") && slice.get("consolidationIntervalMsec").isNumber() && 52 == slice.get("consolidationIntervalMsec").getNumber())); - CHECK((slice.hasKey("links") && slice.get("links").isObject() && 0 == slice.get("links").length())); + CHECK((slice.hasKey("links") && slice.get("links").isObject() && 1 == slice.get("links").length())); } // for persistence @@ -1129,13 +1184,13 @@ SECTION("test_updateProperties") { arangodb::velocypack::Builder builder; builder.openObject(); - view->properties(builder, true, true); + wiew->properties(builder, true, true); builder.close(); auto slice = builder.slice(); CHECK((slice.isObject())); CHECK((15U == slice.length())); - CHECK((slice.hasKey("collections") && slice.get("collections").isArray() && 0 == slice.get("collections").length())); + CHECK((slice.hasKey("collections") && slice.get("collections").isArray() && 1 == slice.get("collections").length())); CHECK((slice.hasKey("cleanupIntervalStep") && slice.get("cleanupIntervalStep").isNumber() && 24 == slice.get("cleanupIntervalStep").getNumber())); CHECK((slice.hasKey("consolidationIntervalMsec") && slice.get("consolidationIntervalMsec").isNumber() && 52 == slice.get("consolidationIntervalMsec").getNumber())); CHECK((false == slice.hasKey("links"))); @@ -1155,7 +1210,7 @@ SECTION("test_updateProperties") { CHECK((TRI_ERROR_NO_ERROR == ci->createViewCoordinator(vocbase->name(), "42", viewJson->slice(), error))); auto wiew = ci->getView(vocbase->name(), "42"); // link creation requires cluster-view to be in ClusterInfo instead of TRI_vocbase_t CHECK((false == !wiew)); - auto* impl = dynamic_cast(wiew.get()); + auto* impl = dynamic_cast(wiew.get()); CHECK((nullptr != impl)); { @@ -1167,10 +1222,10 @@ SECTION("test_updateProperties") { auto slice = builder.slice(); CHECK((slice.isObject())); - CHECK((10U == slice.length())); + CHECK((11U == slice.length())); CHECK((slice.hasKey("cleanupIntervalStep") && slice.get("cleanupIntervalStep").isNumber() && 24 == slice.get("cleanupIntervalStep").getNumber())); CHECK((slice.hasKey("consolidationIntervalMsec") && slice.get("consolidationIntervalMsec").isNumber() && 42 == slice.get("consolidationIntervalMsec").getNumber())); - CHECK((!slice.hasKey("links"))); + CHECK((slice.hasKey("links") && slice.get("links").isObject() && 0 == slice.get("links").length())); } { @@ -1187,23 +1242,22 @@ SECTION("test_updateProperties") { auto slice = builder.slice(); CHECK((slice.isObject())); - CHECK((10U == slice.length())); + CHECK((11U == slice.length())); CHECK((slice.hasKey("cleanupIntervalStep") && slice.get("cleanupIntervalStep").isNumber() && 10 == slice.get("cleanupIntervalStep").getNumber())); CHECK((slice.hasKey("consolidationIntervalMsec") && slice.get("consolidationIntervalMsec").isNumber() && 52 == slice.get("consolidationIntervalMsec").getNumber())); - CHECK((!slice.hasKey("links"))); + CHECK((slice.hasKey("links") && slice.get("links").isObject() && 1 == slice.get("links").length())); } - auto view = impl->ensure(123); - CHECK((false == !view)); + CHECK((false == !arangodb::iresearch::IResearchLinkHelper::find(*logicalCollection, *wiew))); static auto visitor = [](TRI_voc_cid_t)->bool { return false; }; - CHECK((true == view->visitCollections(visitor))); // no collections in view + CHECK((false == wiew->visitCollections(visitor))); // no collections in view // not for persistence { arangodb::velocypack::Builder builder; builder.openObject(); - view->properties(builder, true, false); + wiew->properties(builder, true, false); builder.close(); auto slice = builder.slice(); @@ -1211,7 +1265,7 @@ SECTION("test_updateProperties") { CHECK((11U == slice.length())); CHECK((slice.hasKey("cleanupIntervalStep") && slice.get("cleanupIntervalStep").isNumber() && 10 == slice.get("cleanupIntervalStep").getNumber())); CHECK((slice.hasKey("consolidationIntervalMsec") && slice.get("consolidationIntervalMsec").isNumber() && 52 == slice.get("consolidationIntervalMsec").getNumber())); - CHECK((slice.hasKey("links") && slice.get("links").isObject() && 0 == slice.get("links").length())); + CHECK((slice.hasKey("links") && slice.get("links").isObject() && 1 == slice.get("links").length())); } // for persistence @@ -1219,13 +1273,13 @@ SECTION("test_updateProperties") { arangodb::velocypack::Builder builder; builder.openObject(); - view->properties(builder, true, true); + wiew->properties(builder, true, true); builder.close(); auto slice = builder.slice(); CHECK((slice.isObject())); CHECK((15U == slice.length())); - CHECK((slice.hasKey("collections") && slice.get("collections").isArray() && 0 == slice.get("collections").length())); + CHECK((slice.hasKey("collections") && slice.get("collections").isArray() && 1 == slice.get("collections").length())); CHECK((slice.hasKey("cleanupIntervalStep") && slice.get("cleanupIntervalStep").isNumber() && 10 == slice.get("cleanupIntervalStep").getNumber())); CHECK((slice.hasKey("consolidationIntervalMsec") && slice.get("consolidationIntervalMsec").isNumber() && 52 == slice.get("consolidationIntervalMsec").getNumber())); CHECK((false == slice.hasKey("links"))); @@ -1235,6 +1289,7 @@ SECTION("test_updateProperties") { // update non-empty (partial) { auto collectionJson = arangodb::velocypack::Parser::fromJson("{ \"name\": \"testCollection\" }"); + auto linkJson = arangodb::velocypack::Parser::fromJson("{ \"view\": \"testView\", \"type\": \"arangosearch\", \"includeAllFields\": true }"); auto viewJson = arangodb::velocypack::Parser::fromJson("{ \"id\": \"42\", \"name\": \"testView\", \"type\": \"arangosearch\", \"collections\": [ 3, 4, 5 ], \"cleanupIntervalStep\": 24, \"consolidationIntervalMsec\": 42 }"); TRI_vocbase_t* vocbase; // will be owned by DatabaseFeature REQUIRE((TRI_ERROR_NO_ERROR == databaseFeature->createDatabase(0, "testDatabase" TOSTRING(__LINE__), vocbase))); @@ -1245,13 +1300,16 @@ SECTION("test_updateProperties") { CHECK((TRI_ERROR_NO_ERROR == ci->createViewCoordinator(vocbase->name(), "42", viewJson->slice(), error))); auto wiew = ci->getView(vocbase->name(), "42"); // link creation requires cluster-view to be in ClusterInfo instead of TRI_vocbase_t CHECK((false == !wiew)); - auto* impl = dynamic_cast(wiew.get()); + auto* impl = dynamic_cast(wiew.get()); CHECK((nullptr != impl)); - auto view = impl->ensure(123); - CHECK((false == !view)); + bool created; + auto index = logicalCollection->createIndex(linkJson->slice(), created); + REQUIRE((false == !index)); + auto link = std::dynamic_pointer_cast(index); + REQUIRE((false == !link)); static auto visitor = [](TRI_voc_cid_t)->bool { return false; }; - CHECK((true == view->visitCollections(visitor))); // no collections in view + CHECK((false == wiew->visitCollections(visitor))); // 1 collection in view { arangodb::velocypack::Builder builder; @@ -1262,10 +1320,10 @@ SECTION("test_updateProperties") { auto slice = builder.slice(); CHECK((slice.isObject())); - CHECK((10U == slice.length())); + CHECK((11U == slice.length())); CHECK((slice.hasKey("cleanupIntervalStep") && slice.get("cleanupIntervalStep").isNumber() && 24 == slice.get("cleanupIntervalStep").getNumber())); CHECK((slice.hasKey("consolidationIntervalMsec") && slice.get("consolidationIntervalMsec").isNumber() && 42 == slice.get("consolidationIntervalMsec").getNumber())); - CHECK((!slice.hasKey("links"))); + CHECK((slice.hasKey("links") && slice.get("links").isObject() && 1 == slice.get("links").length())); } { @@ -1282,20 +1340,18 @@ SECTION("test_updateProperties") { auto slice = builder.slice(); CHECK((slice.isObject())); - CHECK((10U == slice.length())); + CHECK((11U == slice.length())); CHECK((slice.hasKey("cleanupIntervalStep") && slice.get("cleanupIntervalStep").isNumber() && 24 == slice.get("cleanupIntervalStep").getNumber())); CHECK((slice.hasKey("consolidationIntervalMsec") && slice.get("consolidationIntervalMsec").isNumber() && 52 == slice.get("consolidationIntervalMsec").getNumber())); - CHECK((!slice.hasKey("links"))); + CHECK((slice.hasKey("links") && slice.get("links").isObject() && 1 == slice.get("links").length())); } - CHECK((true == view->visitCollections(visitor))); // no collections in view - // not for persistence { arangodb::velocypack::Builder builder; builder.openObject(); - view->properties(builder, true, false); + wiew->properties(builder, true, false); builder.close(); auto slice = builder.slice(); @@ -1303,7 +1359,7 @@ SECTION("test_updateProperties") { CHECK((11U == slice.length())); CHECK((slice.hasKey("cleanupIntervalStep") && slice.get("cleanupIntervalStep").isNumber() && 24 == slice.get("cleanupIntervalStep").getNumber())); CHECK((slice.hasKey("consolidationIntervalMsec") && slice.get("consolidationIntervalMsec").isNumber() && 52 == slice.get("consolidationIntervalMsec").getNumber())); - CHECK((slice.hasKey("links") && slice.get("links").isObject() && 0 == slice.get("links").length())); + CHECK((slice.hasKey("links") && slice.get("links").isObject() && 1 == slice.get("links").length())); } // for persistence @@ -1311,13 +1367,13 @@ SECTION("test_updateProperties") { arangodb::velocypack::Builder builder; builder.openObject(); - view->properties(builder, true, true); + wiew->properties(builder, true, true); builder.close(); auto slice = builder.slice(); CHECK((slice.isObject())); CHECK((15U == slice.length())); - CHECK((slice.hasKey("collections") && slice.get("collections").isArray() && 0 == slice.get("collections").length())); + CHECK((slice.hasKey("collections") && slice.get("collections").isArray() && 1 == slice.get("collections").length())); CHECK((slice.hasKey("cleanupIntervalStep") && slice.get("cleanupIntervalStep").isNumber() && 24 == slice.get("cleanupIntervalStep").getNumber())); CHECK((slice.hasKey("consolidationIntervalMsec") && slice.get("consolidationIntervalMsec").isNumber() && 52 == slice.get("consolidationIntervalMsec").getNumber())); CHECK((false == slice.hasKey("links"))); @@ -1328,6 +1384,7 @@ SECTION("test_updateProperties") { { auto collection0Json = arangodb::velocypack::Parser::fromJson("{ \"name\": \"testCollection\" }"); auto collection1Json = arangodb::velocypack::Parser::fromJson("{ \"name\": \"testCollection1\", \"id\": \"123\" }"); + auto linkJson = arangodb::velocypack::Parser::fromJson("{ \"view\": \"testView\", \"type\": \"arangosearch\", \"includeAllFields\": true }"); auto viewJson = arangodb::velocypack::Parser::fromJson("{ \"id\": \"42\", \"name\": \"testView\", \"type\": \"arangosearch\", \"collections\": [ 3, 4, 5 ], \"cleanupIntervalStep\": 24, \"consolidationIntervalMsec\": 42 }"); TRI_vocbase_t* vocbase; // will be owned by DatabaseFeature REQUIRE((TRI_ERROR_NO_ERROR == databaseFeature->createDatabase(0, "testDatabase" TOSTRING(__LINE__), vocbase))); @@ -1340,13 +1397,16 @@ SECTION("test_updateProperties") { CHECK((TRI_ERROR_NO_ERROR == ci->createViewCoordinator(vocbase->name(), "42", viewJson->slice(), error))); auto wiew = ci->getView(vocbase->name(), "42"); // link creation requires cluster-view to be in ClusterInfo instead of TRI_vocbase_t CHECK((false == !wiew)); - auto* impl = dynamic_cast(wiew.get()); + auto* impl = dynamic_cast(wiew.get()); CHECK((nullptr != impl)); - auto view = impl->ensure(123); - CHECK((false == !view)); + bool created; + auto index = logicalCollection1->createIndex(linkJson->slice(), created); + REQUIRE((false == !index)); + auto link = std::dynamic_pointer_cast(index); + REQUIRE((false == !link)); static auto visitor = [](TRI_voc_cid_t)->bool { return false; }; - CHECK((true == view->visitCollections(visitor))); // no collections in view + CHECK((false == wiew->visitCollections(visitor))); // 1 collection in view { arangodb::velocypack::Builder builder; @@ -1357,10 +1417,10 @@ SECTION("test_updateProperties") { auto slice = builder.slice(); CHECK((slice.isObject())); - CHECK((10U == slice.length())); + CHECK((11U == slice.length())); CHECK((slice.hasKey("cleanupIntervalStep") && slice.get("cleanupIntervalStep").isNumber() && 24 == slice.get("cleanupIntervalStep").getNumber())); CHECK((slice.hasKey("consolidationIntervalMsec") && slice.get("consolidationIntervalMsec").isNumber() && 42 == slice.get("consolidationIntervalMsec").getNumber())); - CHECK((!slice.hasKey("links"))); + CHECK((slice.hasKey("links") && slice.get("links").isObject() && 1 == slice.get("links").length())); } { @@ -1377,20 +1437,18 @@ SECTION("test_updateProperties") { auto slice = builder.slice(); CHECK((slice.isObject())); - CHECK((10U == slice.length())); + CHECK((11U == slice.length())); CHECK((slice.hasKey("cleanupIntervalStep") && slice.get("cleanupIntervalStep").isNumber() && 10 == slice.get("cleanupIntervalStep").getNumber())); CHECK((slice.hasKey("consolidationIntervalMsec") && slice.get("consolidationIntervalMsec").isNumber() && 52 == slice.get("consolidationIntervalMsec").getNumber())); - CHECK((!slice.hasKey("links"))); + CHECK((slice.hasKey("links") && slice.get("links").isObject() && 1 == slice.get("links").length())); } - CHECK((true == view->visitCollections(visitor))); // no collections in view - // not for persistence { arangodb::velocypack::Builder builder; builder.openObject(); - view->properties(builder, true, false); + wiew->properties(builder, true, false); builder.close(); auto slice = builder.slice(); @@ -1398,7 +1456,7 @@ SECTION("test_updateProperties") { CHECK((11U == slice.length())); CHECK((slice.hasKey("cleanupIntervalStep") && slice.get("cleanupIntervalStep").isNumber() && 10 == slice.get("cleanupIntervalStep").getNumber())); CHECK((slice.hasKey("consolidationIntervalMsec") && slice.get("consolidationIntervalMsec").isNumber() && 52 == slice.get("consolidationIntervalMsec").getNumber())); - CHECK((slice.hasKey("links") && slice.get("links").isObject() && 0 == slice.get("links").length())); + CHECK((slice.hasKey("links") && slice.get("links").isObject() && 1 == slice.get("links").length())); } // for persistence @@ -1406,13 +1464,13 @@ SECTION("test_updateProperties") { arangodb::velocypack::Builder builder; builder.openObject(); - view->properties(builder, true, true); + wiew->properties(builder, true, true); builder.close(); auto slice = builder.slice(); CHECK((slice.isObject())); CHECK((15U == slice.length())); - CHECK((slice.hasKey("collections") && slice.get("collections").isArray() && 0 == slice.get("collections").length())); + CHECK((slice.hasKey("collections") && slice.get("collections").isArray() && 1 == slice.get("collections").length())); CHECK((slice.hasKey("cleanupIntervalStep") && slice.get("cleanupIntervalStep").isNumber() && 10 == slice.get("cleanupIntervalStep").getNumber())); CHECK((slice.hasKey("consolidationIntervalMsec") && slice.get("consolidationIntervalMsec").isNumber() && 52 == slice.get("consolidationIntervalMsec").getNumber())); CHECK((false == slice.hasKey("links"))); @@ -1429,9 +1487,9 @@ SECTION("test_visitCollections") { auto json = arangodb::velocypack::Parser::fromJson("{ \"name\": \"testView\", \"type\": \"arangosearch\" }"); TRI_vocbase_t vocbase(TRI_vocbase_type_e::TRI_VOCBASE_TYPE_NORMAL, 1, "testVocbase"); arangodb::LogicalView::ptr wiew; - REQUIRE((arangodb::iresearch::IResearchViewDBServer::factory().instantiate(wiew, vocbase, json->slice(), 42).ok())); + REQUIRE((arangodb::iresearch::IResearchView::factory().instantiate(wiew, vocbase, json->slice(), 42).ok())); CHECK((false == !wiew)); - auto* impl = dynamic_cast(wiew.get()); + auto* impl = dynamic_cast(wiew.get()); CHECK((nullptr != impl)); static auto visitor = [](TRI_voc_cid_t)->bool { return false; }; @@ -1440,32 +1498,31 @@ SECTION("test_visitCollections") { // visit non-empty { + auto collectionJson = arangodb::velocypack::Parser::fromJson("{ \"name\": \"testCollection\" }"); + auto linkJson = arangodb::velocypack::Parser::fromJson("{ \"view\": \"testView\", \"includeAllFields\": true }"); auto const wiewId = std::to_string(ci->uniqid() + 1); // +1 because LogicalView creation will generate a new ID auto json = arangodb::velocypack::Parser::fromJson("{ \"name\": \"testView\", \"type\": \"arangosearch\" }"); TRI_vocbase_t vocbase(TRI_vocbase_type_e::TRI_VOCBASE_TYPE_NORMAL, 1, "testVocbase"); + auto logicalCollection = vocbase.createCollection(collectionJson->slice()); + REQUIRE((false == !logicalCollection)); arangodb::LogicalView::ptr wiew; - REQUIRE((arangodb::iresearch::IResearchViewDBServer::factory().instantiate(wiew, vocbase, json->slice(), 42).ok())); + REQUIRE((arangodb::iresearch::IResearchView::factory().instantiate(wiew, vocbase, json->slice(), 42).ok())); CHECK((false == !wiew)); - auto* impl = dynamic_cast(wiew.get()); + auto* impl = dynamic_cast(wiew.get()); CHECK((nullptr != impl)); // ensure we have shard view in vocbase - auto const shardViewName = "_iresearch_123_" + wiewId; - auto jsonShard = arangodb::velocypack::Parser::fromJson( - "{ \"id\": 100, \"name\": \"" + shardViewName + "\", \"type\": \"arangosearch\", \"isSystem\": true }" - ); - CHECK((true == !vocbase.lookupView(shardViewName))); - auto shardView = vocbase.createView(jsonShard->slice()); - CHECK(shardView); + struct Link: public arangodb::iresearch::IResearchLink { + Link(TRI_idx_iid_t id, arangodb::LogicalCollection& col): IResearchLink(id, col) {} + } link(42, *logicalCollection); + auto asyncLinkPtr = std::make_shared(&link); + CHECK((true == impl->link(asyncLinkPtr))); - auto view = impl->ensure(123); - CHECK((false == !view)); - CHECK(shardView == view); - std::set cids = { 123 }; + std::set cids = { logicalCollection->id() }; static auto visitor = [&cids](TRI_voc_cid_t cid)->bool { return 1 == cids.erase(cid); }; CHECK((true == wiew->visitCollections(visitor))); // all collections expected CHECK((true == cids.empty())); - CHECK((true == impl->unlink(123).ok())); + CHECK((true == impl->unlink(logicalCollection->id()).ok())); CHECK((true == wiew->visitCollections(visitor))); // no collections in view } } diff --git a/tests/IResearch/StorageEngineMock.cpp b/tests/IResearch/StorageEngineMock.cpp index 8feb46db4d..c1a7d36ec6 100644 --- a/tests/IResearch/StorageEngineMock.cpp +++ b/tests/IResearch/StorageEngineMock.cpp @@ -1194,8 +1194,8 @@ void StorageEngineMock::destroyCollection( } void StorageEngineMock::destroyView( - TRI_vocbase_t& vocbase, - arangodb::LogicalView& view + TRI_vocbase_t const& vocbase, + arangodb::LogicalView const& view ) noexcept { before(); // NOOP, assume physical view destroyed OK @@ -1214,8 +1214,8 @@ arangodb::Result StorageEngineMock::dropDatabase(TRI_vocbase_t& vocbase) { } arangodb::Result StorageEngineMock::dropView( - TRI_vocbase_t& vocbase, - arangodb::LogicalView& view + TRI_vocbase_t const& vocbase, + arangodb::LogicalView const& view ) { before(); TRI_ASSERT(views.find(std::make_pair(vocbase.id(), view.id())) != views.end()); diff --git a/tests/IResearch/StorageEngineMock.h b/tests/IResearch/StorageEngineMock.h index 3371bd99a8..8a97407da6 100644 --- a/tests/IResearch/StorageEngineMock.h +++ b/tests/IResearch/StorageEngineMock.h @@ -188,10 +188,10 @@ class StorageEngineMock: public arangodb::StorageEngine { virtual TRI_voc_tick_t currentTick() const override; virtual std::string databasePath(TRI_vocbase_t const* vocbase) const override; virtual void destroyCollection(TRI_vocbase_t& vocbase, arangodb::LogicalCollection& collection) override; - virtual void destroyView(TRI_vocbase_t& vocbase, arangodb::LogicalView& view) noexcept override; + virtual void destroyView(TRI_vocbase_t const& vocbase, arangodb::LogicalView const& view) noexcept override; virtual arangodb::Result dropCollection(TRI_vocbase_t& vocbase, arangodb::LogicalCollection& collection) override; virtual arangodb::Result dropDatabase(TRI_vocbase_t& vocbase) override; - virtual arangodb::Result dropView(TRI_vocbase_t& vocbase, arangodb::LogicalView& view) override; + virtual arangodb::Result dropView(TRI_vocbase_t const& vocbase, arangodb::LogicalView const& view) override; virtual arangodb::Result firstTick(uint64_t&) override; virtual std::vector currentWalFiles() const override; virtual arangodb::Result flushWal(bool waitForSync, bool waitForCollector, bool writeShutdownFile) override; diff --git a/tests/RestHandler/RestUsersHandler-test.cpp b/tests/RestHandler/RestUsersHandler-test.cpp index 05af7087dc..cb1da7fd6d 100644 --- a/tests/RestHandler/RestUsersHandler-test.cpp +++ b/tests/RestHandler/RestUsersHandler-test.cpp @@ -56,13 +56,13 @@ struct TestView: public arangodb::LogicalView { TestView(TRI_vocbase_t& vocbase, arangodb::velocypack::Slice const& definition, uint64_t planVersion) : arangodb::LogicalView(vocbase, definition, planVersion) { } - virtual arangodb::Result appendVelocyPack(arangodb::velocypack::Builder& builder, bool /*detailed*/, bool /*forPersistence*/) const override { + virtual arangodb::Result appendVelocyPackImpl(arangodb::velocypack::Builder& builder, bool, bool) const override { builder.add("properties", _properties.slice()); return _appendVelocyPackResult; } - virtual arangodb::Result drop() override { return arangodb::Result(); } + virtual arangodb::Result dropImpl() override { return arangodb::Result(); } virtual void open() override {} - virtual arangodb::Result rename(std::string&& newName) override { name(std::move(newName)); return arangodb::Result(); } + virtual arangodb::Result renameImpl(std::string const&) override { return arangodb::Result(); } virtual arangodb::Result properties(arangodb::velocypack::Slice const& properties, bool partialUpdate) override { _properties = arangodb::velocypack::Builder(properties); return arangodb::Result(); } virtual bool visitCollections(CollectionVisitor const& visitor) const override { return true; } }; diff --git a/tests/RestHandler/RestViewHandler-test.cpp b/tests/RestHandler/RestViewHandler-test.cpp index cb52acc370..15bbe0199d 100644 --- a/tests/RestHandler/RestViewHandler-test.cpp +++ b/tests/RestHandler/RestViewHandler-test.cpp @@ -46,13 +46,13 @@ struct TestView: public arangodb::LogicalView { TestView(TRI_vocbase_t& vocbase, arangodb::velocypack::Slice const& definition, uint64_t planVersion) : arangodb::LogicalView(vocbase, definition, planVersion) { } - virtual arangodb::Result appendVelocyPack(arangodb::velocypack::Builder& builder, bool /*detailed*/, bool /*forPersistence*/) const override { + virtual arangodb::Result appendVelocyPackImpl(arangodb::velocypack::Builder& builder, bool, bool) const override { builder.add("properties", _properties.slice()); return _appendVelocyPackResult; } - virtual arangodb::Result drop() override { return vocbase().dropView(id(), true); } + virtual arangodb::Result dropImpl() override { return arangodb::LogicalViewHelperStorageEngine::drop(*this); } virtual void open() override {} - virtual arangodb::Result rename(std::string&& newName) override { auto res = vocbase().renameView(id(), newName); name(std::move(newName)); return res; } + virtual arangodb::Result renameImpl(std::string const& oldName) override { return arangodb::LogicalViewHelperStorageEngine::rename(*this, oldName); } virtual arangodb::Result properties(arangodb::velocypack::Slice const& properties, bool partialUpdate) override { _properties = arangodb::velocypack::Builder(properties); return arangodb::Result(); } virtual bool visitCollections(CollectionVisitor const& visitor) const override { return true; } }; diff --git a/tests/Utils/CollectionNameResolver-test.cpp b/tests/Utils/CollectionNameResolver-test.cpp index d9d6eafa75..d340fa29db 100644 --- a/tests/Utils/CollectionNameResolver-test.cpp +++ b/tests/Utils/CollectionNameResolver-test.cpp @@ -40,10 +40,10 @@ struct TestView: public arangodb::LogicalView { TestView(TRI_vocbase_t& vocbase, arangodb::velocypack::Slice const& definition, uint64_t planVersion) : arangodb::LogicalView(vocbase, definition, planVersion) { } - virtual arangodb::Result appendVelocyPack(arangodb::velocypack::Builder&, bool , bool) const override { return arangodb::Result(); } - virtual arangodb::Result drop() override { deleted(true); return vocbase().dropView(id(), true); } + virtual arangodb::Result appendVelocyPackImpl(arangodb::velocypack::Builder&, bool , bool) const override { return arangodb::Result(); } + virtual arangodb::Result dropImpl() override { return arangodb::LogicalViewHelperStorageEngine::drop(*this); } virtual void open() override {} - virtual arangodb::Result rename(std::string&& newName) override { name(std::move(newName)); return arangodb::Result(); } + virtual arangodb::Result renameImpl(std::string const& oldName) override { return arangodb::LogicalViewHelperStorageEngine::rename(* this, oldName); } virtual arangodb::Result properties(arangodb::velocypack::Slice const&, bool) override { return arangodb::Result(); } virtual bool visitCollections(CollectionVisitor const& visitor) const override { return true; } }; diff --git a/tests/V8Server/v8-users-test.cpp b/tests/V8Server/v8-users-test.cpp index 7f11e400c6..562595179f 100644 --- a/tests/V8Server/v8-users-test.cpp +++ b/tests/V8Server/v8-users-test.cpp @@ -75,13 +75,13 @@ struct TestView: public arangodb::LogicalView { TestView(TRI_vocbase_t& vocbase, arangodb::velocypack::Slice const& definition, uint64_t planVersion) : arangodb::LogicalView(vocbase, definition, planVersion) { } - virtual arangodb::Result appendVelocyPack(arangodb::velocypack::Builder& builder, bool /*detailed*/, bool /*forPersistence*/) const override { + virtual arangodb::Result appendVelocyPackImpl(arangodb::velocypack::Builder& builder, bool, bool) const override { builder.add("properties", _properties.slice()); return _appendVelocyPackResult; } - virtual arangodb::Result drop() override { return arangodb::Result(); } + virtual arangodb::Result dropImpl() override { return arangodb::Result(); } virtual void open() override {} - virtual arangodb::Result rename(std::string&& newName) override { name(std::move(newName)); return arangodb::Result(); } + virtual arangodb::Result renameImpl(std::string const&) override { return arangodb::Result(); } virtual arangodb::Result properties(arangodb::velocypack::Slice const& properties, bool partialUpdate) override { _properties = arangodb::velocypack::Builder(properties); return arangodb::Result(); } virtual bool visitCollections(CollectionVisitor const& visitor) const override { return true; } }; diff --git a/tests/V8Server/v8-views-test.cpp b/tests/V8Server/v8-views-test.cpp index ecd2c900dd..43c87cc809 100644 --- a/tests/V8Server/v8-views-test.cpp +++ b/tests/V8Server/v8-views-test.cpp @@ -73,13 +73,13 @@ struct TestView: public arangodb::LogicalView { TestView(TRI_vocbase_t& vocbase, arangodb::velocypack::Slice const& definition, uint64_t planVersion) : arangodb::LogicalView(vocbase, definition, planVersion) { } - virtual arangodb::Result appendVelocyPack(arangodb::velocypack::Builder& builder, bool /*detailed*/, bool /*forPersistence*/) const override { + virtual arangodb::Result appendVelocyPackImpl(arangodb::velocypack::Builder& builder, bool, bool) const override { builder.add("properties", _properties.slice()); return _appendVelocyPackResult; } - virtual arangodb::Result drop() override { return vocbase().dropView(id(), true); } + virtual arangodb::Result dropImpl() override { return arangodb::LogicalViewHelperStorageEngine::drop(*this); } virtual void open() override {} - virtual arangodb::Result rename(std::string&& newName) override { auto res = vocbase().renameView(id(), newName); name(std::move(newName)); return res; } + virtual arangodb::Result renameImpl(std::string const& oldName) override { return arangodb::LogicalViewHelperStorageEngine::rename(*this, oldName); } virtual arangodb::Result properties(arangodb::velocypack::Slice const& properties, bool partialUpdate) override { _properties = arangodb::velocypack::Builder(properties); return arangodb::Result(); } virtual bool visitCollections(CollectionVisitor const& visitor) const override { return true; } }; diff --git a/tests/VocBase/LogicalDataSource-test.cpp b/tests/VocBase/LogicalDataSource-test.cpp index 4e4cef61f0..4a3a4abfb9 100644 --- a/tests/VocBase/LogicalDataSource-test.cpp +++ b/tests/VocBase/LogicalDataSource-test.cpp @@ -107,9 +107,10 @@ SECTION("test_category") { LogicalViewImpl(TRI_vocbase_t& vocbase, arangodb::velocypack::Slice const& definition) : LogicalView(vocbase, definition, 0) { } - virtual arangodb::Result drop() override { return arangodb::Result(); } + virtual arangodb::Result appendVelocyPackImpl(arangodb::velocypack::Builder&, bool, bool) const override { return arangodb::Result(); } + virtual arangodb::Result dropImpl() override { return arangodb::Result(); } virtual void open() override {} - virtual arangodb::Result rename(std::string&& newName) override { return arangodb::Result(); } + virtual arangodb::Result renameImpl(std::string const&) override { return arangodb::Result(); } virtual arangodb::Result properties(arangodb::velocypack::Slice const& properties, bool partialUpdate) override { return arangodb::Result(); } virtual bool visitCollections(CollectionVisitor const& visitor) const override { return true; } }; @@ -141,9 +142,10 @@ SECTION("test_construct") { LogicalViewImpl(TRI_vocbase_t& vocbase, arangodb::velocypack::Slice const& definition) : LogicalView(vocbase, definition, 0) { } - virtual arangodb::Result drop() override { return arangodb::Result(); } + virtual arangodb::Result appendVelocyPackImpl(arangodb::velocypack::Builder&, bool, bool) const override { return arangodb::Result(); } + virtual arangodb::Result dropImpl() override { return arangodb::Result(); } virtual void open() override {} - virtual arangodb::Result rename(std::string&& newName) override { return arangodb::Result(); } + virtual arangodb::Result renameImpl(std::string const&) override { return arangodb::Result(); } virtual arangodb::Result properties(arangodb::velocypack::Slice const& properties, bool partialUpdate) override { return arangodb::Result(); } virtual bool visitCollections(CollectionVisitor const& visitor) const override { return true; } }; @@ -177,9 +179,10 @@ SECTION("test_defaults") { LogicalViewImpl(TRI_vocbase_t& vocbase, arangodb::velocypack::Slice const& definition) : LogicalView(vocbase, definition, 0) { } - virtual arangodb::Result drop() override { return arangodb::Result(); } + virtual arangodb::Result appendVelocyPackImpl(arangodb::velocypack::Builder&, bool, bool) const override { return arangodb::Result(); } + virtual arangodb::Result dropImpl() override { return arangodb::Result(); } virtual void open() override {} - virtual arangodb::Result rename(std::string&& newName) override { return arangodb::Result(); } + virtual arangodb::Result renameImpl(std::string const&) override { return arangodb::Result(); } virtual arangodb::Result properties(arangodb::velocypack::Slice const& properties, bool partialUpdate) override { return arangodb::Result(); } virtual bool visitCollections(CollectionVisitor const& visitor) const override { return true; } }; @@ -203,4 +206,4 @@ SECTION("test_defaults") { // ----------------------------------------------------------------------------- // --SECTION-- END-OF-FILE -// ----------------------------------------------------------------------------- +// ----------------------------------------------------------------------------- \ No newline at end of file diff --git a/tests/VocBase/vocbase-test.cpp b/tests/VocBase/vocbase-test.cpp index e2913f6751..2a7fa47518 100644 --- a/tests/VocBase/vocbase-test.cpp +++ b/tests/VocBase/vocbase-test.cpp @@ -41,10 +41,10 @@ struct TestView: public arangodb::LogicalView { TestView(TRI_vocbase_t& vocbase, arangodb::velocypack::Slice const& definition, uint64_t planVersion) : arangodb::LogicalView(vocbase, definition, planVersion) { } - virtual arangodb::Result appendVelocyPack(arangodb::velocypack::Builder&, bool , bool) const override { return arangodb::Result(); } - virtual arangodb::Result drop() override { deleted(true); return vocbase().dropView(id(), true); } + virtual arangodb::Result appendVelocyPackImpl(arangodb::velocypack::Builder&, bool , bool) const override { return arangodb::Result(); } + virtual arangodb::Result dropImpl() override { return arangodb::LogicalViewHelperStorageEngine::drop(*this); } virtual void open() override {} - virtual arangodb::Result rename(std::string&& newName) override { name(std::move(newName)); return arangodb::Result(); } + virtual arangodb::Result renameImpl(std::string const& oldName) override { return arangodb::LogicalViewHelperStorageEngine::rename(*this, oldName); } virtual arangodb::Result properties(arangodb::velocypack::Slice const&, bool) override { return arangodb::Result(); } virtual bool visitCollections(CollectionVisitor const& visitor) const override { return true; } };