diff --git a/CMakeLists.txt b/CMakeLists.txt index d29934c0c62..c75f200bd8a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -83,6 +83,7 @@ macro(nebula_add_library name type) graph_thrift_generator storage_thrift_generator meta_thrift_generator + drainer_thrift_generator raftex_thrift_generator # hbase_thrift_generator parser_target diff --git a/src/clients/meta/MetaClient.cpp b/src/clients/meta/MetaClient.cpp index 1d56abdce57..f5b07ef3a65 100644 --- a/src/clients/meta/MetaClient.cpp +++ b/src/clients/meta/MetaClient.cpp @@ -190,8 +190,8 @@ bool MetaClient::loadData() { return false; } - if (!loadFulltextClients()) { - LOG(ERROR) << "Load fulltext services Failed"; + if (!loadGlobalServiceClients()) { + LOG(ERROR) << "Load global services Failed"; return false; } @@ -263,6 +263,16 @@ bool MetaClient::loadData() { return false; } + if (!loadListenerDrainers(spaceId, spaceCache)) { + LOG(ERROR) << "Load Listener Drainers Failed"; + return false; + } + + if (!loadDrainers(spaceId, spaceCache)) { + LOG(ERROR) << "Load Drainers Failed"; + return false; + } + // get space properties auto resp = getSpace(spaceName).get(); if (!resp.ok()) { @@ -483,7 +493,7 @@ bool MetaClient::loadIndexes(GraphSpaceID spaceId, std::shared_ptr cache) { - auto listenerRet = listListener(spaceId).get(); + auto listenerRet = listListeners(spaceId, cpp2::ListenerType::ALL).get(); if (!listenerRet.ok()) { LOG(ERROR) << "Get listeners failed for spaceId " << spaceId << ", " << listenerRet.status(); return false; @@ -497,15 +507,38 @@ bool MetaClient::loadListeners(GraphSpaceID spaceId, std::shared_ptr cache) { + auto listenerDrainerRet = listListenerDrainers(spaceId).get(); + if (!listenerDrainerRet.ok()) { + LOG(ERROR) << "Get sync listener drainers failed for spaceId " << spaceId << ", " + << listenerDrainerRet.status(); + return false; + } + + cache->drainerclients_ = std::move(listenerDrainerRet.value()); + return true; +} + +bool MetaClient::loadDrainers(GraphSpaceID spaceId, std::shared_ptr cache) { + auto drainerRet = listDrainers(spaceId).get(); + if (!drainerRet.ok()) { + LOG(ERROR) << "Get drainers failed for spaceId " << spaceId << ", " << drainerRet.status(); + return false; + } + + cache->drainerServer_ = std::move(drainerRet.value()); + return true; +} + +bool MetaClient::loadGlobalServiceClients() { + auto ret = listServiceClients(cpp2::ExternalServiceType::ALL).get(); + if (!ret.ok()) { + LOG(ERROR) << "List services failed, status:" << ret.status(); return false; } { folly::RWSpinLock::WriteHolder holder(localCacheLock_); - fulltextClientList_ = std::move(ftRet).value(); + serviceClientList_ = std::move(ret).value(); } return true; } @@ -824,6 +857,14 @@ Status MetaClient::handleResponse(const RESP& resp) { return Status::Error("Backup table failure!"); case nebula::cpp2::ErrorCode::E_SESSION_NOT_FOUND: return Status::Error("Session not existed!"); + case nebula::cpp2::ErrorCode::E_SERVICE_NOT_FOUND: + return Status::Error("Service not existed!"); + case nebula::cpp2::ErrorCode::E_DRAINER_NOT_FOUND: + return Status::Error("Drainer not existed!"); + case nebula::cpp2::ErrorCode::E_NO_VALID_DRAINER: + return Status::Error("Invalid drainer!"); + case nebula::cpp2::ErrorCode::E_LISTENER_CONFLICT: + return Status::Error("Listener host conflict!"); default: return Status::Error("Unknown error!"); } @@ -2737,11 +2778,15 @@ folly::Future>> MetaClient::listSnapshots() folly::Future> MetaClient::addListener(GraphSpaceID spaceId, cpp2::ListenerType type, - std::vector hosts) { + std::vector hosts, + const std::string* spaceName) { cpp2::AddListenerReq req; req.set_space_id(spaceId); req.set_type(type); req.set_hosts(std::move(hosts)); + if (spaceName) { + req.set_space_name(*spaceName); + } folly::Promise> promise; auto future = promise.getFuture(); getResponse( @@ -2771,22 +2816,39 @@ folly::Future> MetaClient::removeListener(GraphSpaceID spaceId, return future; } -folly::Future>> MetaClient::listListener( - GraphSpaceID spaceId) { - cpp2::ListListenerReq req; +folly::Future>> MetaClient::listListeners( + GraphSpaceID spaceId, const cpp2::ListenerType& type) { + cpp2::ListListenersReq req; req.set_space_id(spaceId); + req.set_type(type); folly::Promise>> promise; auto future = promise.getFuture(); getResponse( std::move(req), - [](auto client, auto request) { return client->future_listListener(request); }, - [](cpp2::ListListenerResp&& resp) -> decltype(auto) { + [](auto client, auto request) { return client->future_listListeners(request); }, + [](cpp2::ListListenersResp&& resp) -> decltype(auto) { return std::move(resp).get_listeners(); }, std::move(promise)); return future; } +folly::Future>> MetaClient::listListenerDrainers( + GraphSpaceID spaceId) { + cpp2::ListListenerDrainersReq req; + req.set_space_id(spaceId); + folly::Promise>> promise; + auto future = promise.getFuture(); + getResponse( + std::move(req), + [](auto client, auto request) { return client->future_listListenerDrainers(request); }, + [](cpp2::ListListenerDrainersResp&& resp) -> decltype(auto) { + return std::move(resp).get_drainerClients(); + }, + std::move(promise)); + return future; +} + bool MetaClient::registerCfg() { auto ret = regConfig(gflagsDeclared_).get(); if (ret.ok()) { @@ -2898,6 +2960,54 @@ StatusOr> MetaClient::getListenerHostTypeBySpace return items; } +folly::Future> MetaClient::addDrainer(GraphSpaceID spaceId, + std::vector hosts) { + cpp2::AddDrainerReq req; + req.set_space_id(spaceId); + req.set_hosts(std::move(hosts)); + folly::Promise> promise; + auto future = promise.getFuture(); + getResponse( + std::move(req), + [](auto client, auto request) { return client->future_addDrainer(request); }, + [](cpp2::ExecResp&& resp) -> bool { + return resp.get_code() == nebula::cpp2::ErrorCode::SUCCEEDED; + }, + std::move(promise)); + return future; +} + +folly::Future> MetaClient::removeDrainer(GraphSpaceID spaceId) { + cpp2::RemoveDrainerReq req; + req.set_space_id(spaceId); + folly::Promise> promise; + auto future = promise.getFuture(); + getResponse( + std::move(req), + [](auto client, auto request) { return client->future_removeDrainer(request); }, + [](cpp2::ExecResp&& resp) -> bool { + return resp.get_code() == nebula::cpp2::ErrorCode::SUCCEEDED; + }, + std::move(promise)); + return future; +} + +folly::Future>> MetaClient::listDrainers( + GraphSpaceID spaceId) { + cpp2::ListDrainersReq req; + req.set_space_id(spaceId); + folly::Promise>> promise; + auto future = promise.getFuture(); + getResponse( + std::move(req), + [](auto client, auto request) { return client->future_listDrainers(request); }, + [](cpp2::ListDrainersResp&& resp) -> decltype(auto) { + return std::move(resp).get_drainers(); + }, + std::move(promise)); + return future; +} + bool MetaClient::loadCfg() { if (!configReady_ && !registerCfg()) { return false; @@ -3243,16 +3353,16 @@ folly::Future> MetaClient::reportTaskFinish( return fut; } -folly::Future> MetaClient::signInFTService( - cpp2::FTServiceType type, const std::vector& clients) { - cpp2::SignInFTServiceReq req; +folly::Future> MetaClient::signInService( + const cpp2::ExternalServiceType& type, const std::vector& clients) { + cpp2::SignInServiceReq req; req.set_type(type); req.set_clients(clients); folly::Promise> promise; auto future = promise.getFuture(); getResponse( std::move(req), - [](auto client, auto request) { return client->future_signInFTService(request); }, + [](auto client, auto request) { return client->future_signInService(request); }, [](cpp2::ExecResp&& resp) -> bool { return resp.get_code() == nebula::cpp2::ErrorCode::SUCCEEDED; }, @@ -3261,13 +3371,14 @@ folly::Future> MetaClient::signInFTService( return future; } -folly::Future> MetaClient::signOutFTService() { - cpp2::SignOutFTServiceReq req; +folly::Future> MetaClient::signOutService(const cpp2::ExternalServiceType& type) { + cpp2::SignOutServiceReq req; + req.set_type(type); folly::Promise> promise; auto future = promise.getFuture(); getResponse( std::move(req), - [](auto client, auto request) { return client->future_signOutFTService(request); }, + [](auto client, auto request) { return client->future_signOutService(request); }, [](cpp2::ExecResp&& resp) -> bool { return resp.get_code() == nebula::cpp2::ErrorCode::SUCCEEDED; }, @@ -3276,25 +3387,77 @@ folly::Future> MetaClient::signOutFTService() { return future; } -folly::Future>> MetaClient::listFTClients() { - cpp2::ListFTClientsReq req; - folly::Promise>> promise; +folly::Future> MetaClient::listServiceClients( + const cpp2::ExternalServiceType& type) { + cpp2::ListServiceClientsReq req; + req.set_type(type); + folly::Promise> promise; auto future = promise.getFuture(); getResponse( std::move(req), - [](auto client, auto request) { return client->future_listFTClients(request); }, - [](cpp2::ListFTClientsResp&& resp) -> decltype(auto) { + [](auto client, auto request) { return client->future_listServiceClients(request); }, + [](cpp2::ListServiceClientsResp&& resp) -> decltype(auto) { return std::move(resp).get_clients(); }, std::move(promise)); return future; } -StatusOr> MetaClient::getFTClientsFromCache() { +StatusOr> MetaClient::getServiceClientsFromCache( + const cpp2::ExternalServiceType& type) { if (!ready_) { return Status::Error("Not ready!"); } - return fulltextClientList_; + + folly::RWSpinLock::ReadHolder holder(localCacheLock_); + if (type == cpp2::ExternalServiceType::ELASTICSEARCH || + type == cpp2::ExternalServiceType::DRAINER) { + auto sIter = serviceClientList_.find(type); + if (sIter != serviceClientList_.end()) { + return sIter->second; + } + } + return Status::Error("Service not found!"); +} + +StatusOr MetaClient::getDrainerClientFromCache(GraphSpaceID spaceId, PartitionID partId) { + if (!ready_) { + return Status::Error("Not ready!"); + } + + folly::RWSpinLock::ReadHolder holder(localCacheLock_); + auto spaceIt = localCache_.find(spaceId); + if (spaceIt == localCache_.end()) { + VLOG(3) << "Space " << spaceId << " not found!"; + return Status::SpaceNotFound(); + } + + auto iter = spaceIt->second->drainerclients_.find(partId); + if (iter == spaceIt->second->drainerclients_.end()) { + VLOG(3) << "Space " << spaceId << " part " << partId << " listener drainer client not found!"; + return Status::DrainerClientNotFound(); + } + return iter->second; +} + +StatusOr> MetaClient::getDrainerFromCache(GraphSpaceID spaceId) { + if (!ready_) { + return Status::Error("Not ready!"); + } + + folly::RWSpinLock::ReadHolder holder(localCacheLock_); + auto spaceIt = localCache_.find(spaceId); + if (spaceIt == localCache_.end()) { + VLOG(3) << "Space " << spaceId << " not found!"; + return Status::SpaceNotFound(); + } + + auto drainers = spaceIt->second->drainerServer_; + if (drainers.empty()) { + VLOG(3) << "Space " << spaceId << " drainer not found!"; + return Status::DrainerNotFound(); + } + return drainers; } folly::Future> MetaClient::createFTIndex(const std::string& name, diff --git a/src/clients/meta/MetaClient.h b/src/clients/meta/MetaClient.h index 90ffecee886..324b5a40a85 100644 --- a/src/clients/meta/MetaClient.h +++ b/src/clients/meta/MetaClient.h @@ -71,6 +71,10 @@ using Indexes = std::unordered_map>; using Listeners = std::unordered_map>>; +// Get services +using ServiceClientsList = + std::unordered_map>; + struct SpaceInfoCache { cpp2::SpaceDesc spaceDesc_; PartsAlloc partsAlloc_; @@ -83,6 +87,11 @@ struct SpaceInfoCache { // objPool used to decode when adding field ObjectPool pool_; std::unordered_map termOfPartition_; + + // sync listener drainer client for master cluster + std::unordered_map drainerclients_; + // drainer server for slave cluster + std::vector drainerServer_; }; using LocalCache = std::unordered_map>; @@ -121,9 +130,6 @@ using UserPasswordMap = std::unordered_map; using MetaConfigMap = std::unordered_map, cpp2::ConfigItem>; -// get fulltext services -using FulltextClientsList = std::vector; - using FTIndexMap = std::unordered_map; using SessionMap = std::unordered_map; @@ -172,7 +178,7 @@ struct MetaClientOptions { std::string serviceName_ = ""; // Whether to skip the config manager bool skipConfig_ = false; - // host role(graph/meta/storage) using this client + // host role(graph/meta/storage/drainer) using this client cpp2::HostRole role_ = cpp2::HostRole::UNKNOWN; // gitInfoSHA of Host using this client std::string gitInfoSHA_{""}; @@ -398,14 +404,18 @@ class MetaClient { folly::Future>> listSnapshots(); // Opeartions for listener. - folly::Future> addListener(GraphSpaceID spaceId, cpp2::ListenerType type, - std::vector hosts); + std::vector hosts, + const std::string* spaceName = nullptr); folly::Future> removeListener(GraphSpaceID spaceId, cpp2::ListenerType type); - folly::Future>> listListener(GraphSpaceID spaceId); + folly::Future>> listListeners( + GraphSpaceID spaceId, const cpp2::ListenerType& type); + + folly::Future>> listListenerDrainers( + GraphSpaceID spaceId); StatusOr>> getListenersBySpaceHostFromCache(GraphSpaceID spaceId, const HostAddr& host); @@ -419,18 +429,30 @@ class MetaClient { StatusOr> getListenerHostTypeBySpacePartType(GraphSpaceID spaceId, PartitionID partId); - // Operations for fulltext services - folly::Future> signInFTService(cpp2::FTServiceType type, - const std::vector& clients); + // Opeartions for drainer. + folly::Future> addDrainer(GraphSpaceID spaceId, std::vector hosts); - folly::Future> signOutFTService(); + folly::Future> removeDrainer(GraphSpaceID spaceId); - folly::Future>> listFTClients(); + folly::Future>> listDrainers(GraphSpaceID spaceId); - StatusOr> getFTClientsFromCache(); + // Operations for services + folly::Future> signInService(const cpp2::ExternalServiceType& type, + const std::vector& clients); - // Opeartions for fulltext index. + folly::Future> signOutService(const cpp2::ExternalServiceType& type); + + folly::Future> listServiceClients( + const cpp2::ExternalServiceType& type); + StatusOr> getServiceClientsFromCache( + const cpp2::ExternalServiceType& type); + + StatusOr getDrainerClientFromCache(GraphSpaceID spaceId, PartitionID partId); + + StatusOr> getDrainerFromCache(GraphSpaceID spaceId); + + // Opeartions for fulltext index. folly::Future> createFTIndex(const std::string& name, const cpp2::FTIndex& index); folly::Future> dropFTIndex(GraphSpaceID spaceId, const std::string& name); @@ -653,7 +675,13 @@ class MetaClient { bool loadListeners(GraphSpaceID spaceId, std::shared_ptr cache); - bool loadFulltextClients(); + // For the master cluster, the drainer used by the sync listener of each part under the space + bool loadListenerDrainers(GraphSpaceID spaceId, std::shared_ptr cache); + + // For slave cluster, the space uses drainers + bool loadDrainers(GraphSpaceID spaceId, std::shared_ptr cache); + + bool loadGlobalServiceClients(); bool loadFulltextIndexes(); @@ -752,7 +780,9 @@ class MetaClient { NameIndexMap tagNameIndexMap_; NameIndexMap edgeNameIndexMap_; - FulltextClientsList fulltextClientList_; + + // Global service client + ServiceClientsList serviceClientList_; FTIndexMap fulltextIndexMap_; mutable folly::RWSpinLock localCacheLock_; @@ -779,4 +809,5 @@ class MetaClient { } // namespace meta } // namespace nebula + #endif // CLIENTS_META_METACLIENT_H_ diff --git a/src/codec/test/CMakeLists.txt b/src/codec/test/CMakeLists.txt index 1d761a12d88..fc8fe1e3bc3 100644 --- a/src/codec/test/CMakeLists.txt +++ b/src/codec/test/CMakeLists.txt @@ -12,6 +12,7 @@ set(CODEC_TEST_LIBS $ $ $ + $ $ $ $ diff --git a/src/common/base/Status.cpp b/src/common/base/Status.cpp index 961c26c2d9d..38122d323ca 100644 --- a/src/common/base/Status.cpp +++ b/src/common/base/Status.cpp @@ -100,6 +100,10 @@ const char *Status::toString(Code code) { return "ListenerNotFound"; case kSessionNotFound: return "SessionNotFound"; + case kDrainerNotFound: + return "DrainerNotFound"; + case kDrainerClientNotFound: + return "DrainerClientNotFound"; } DLOG(FATAL) << "Invalid status code: " << static_cast(code); return ""; diff --git a/src/common/base/Status.h b/src/common/base/Status.h index 87f2f995996..72f9382fdc2 100644 --- a/src/common/base/Status.h +++ b/src/common/base/Status.h @@ -126,6 +126,9 @@ class Status final { STATUS_GENERATOR(PartNotFound); STATUS_GENERATOR(ListenerNotFound); STATUS_GENERATOR(SessionNotFound); + STATUS_GENERATOR(DrainerNotFound); + STATUS_GENERATOR(DrainerClientNotFound); + // User or permission errors STATUS_GENERATOR(PermissionError); @@ -168,6 +171,8 @@ class Status final { kZoneNotFound = 414, kListenerNotFound = 415, kSessionNotFound = 416, + kDrainerNotFound = 417, + kDrainerClientNotFound = 418, // 5xx for user or permission error kPermissionError = 501, }; diff --git a/src/common/meta/SchemaManager.h b/src/common/meta/SchemaManager.h index 15f89d69347..a062622cb5b 100644 --- a/src/common/meta/SchemaManager.h +++ b/src/common/meta/SchemaManager.h @@ -81,8 +81,6 @@ class SchemaManager { // get all latest version of all edge schema virtual StatusOr getAllLatestVerEdgeSchema(GraphSpaceID space) = 0; - virtual StatusOr> getFTClients() = 0; - // Get the TagID or EdgeType by the name. // The first one is a bool which is used to distinguish the type. // When the result is an edge, it's true, otherwise it's false. @@ -92,6 +90,17 @@ class SchemaManager { virtual StatusOr> getFTIndex( GraphSpaceID spaceId, int32_t schemaId) = 0; + virtual StatusOr> getServiceClients( + cpp2::ExternalServiceType type) = 0; + + // TODO(pandasheep) move to new man file + // sync listener drainer client for master cluster + virtual StatusOr getDrainerClient(GraphSpaceID space, PartitionID partId) = 0; + + // TODO(pandasheep) move to new man file + // drainer server for slave cluster + virtual StatusOr> getDrainerServer(GraphSpaceID space) = 0; + protected: SchemaManager() = default; }; diff --git a/src/common/meta/ServerBasedSchemaManager.cpp b/src/common/meta/ServerBasedSchemaManager.cpp index 4efbbca9f94..e7230fd83cb 100644 --- a/src/common/meta/ServerBasedSchemaManager.cpp +++ b/src/common/meta/ServerBasedSchemaManager.cpp @@ -148,13 +148,32 @@ StatusOr ServerBasedSchemaManager::getAllLatestVerEdgeSchema(GraphSp return metaClient_->getAllLatestVerEdgeSchemaFromCache(space); } -StatusOr> ServerBasedSchemaManager::getFTClients() { - auto ret = metaClient_->getFTClientsFromCache(); +StatusOr> +ServerBasedSchemaManager::getServiceClients(meta::cpp2::ExternalServiceType type) { + auto ret = metaClient_->getServiceClientsFromCache(type); if (!ret.ok()) { return ret.status(); } if (ret.value().empty()) { - return Status::Error("fulltext client list is empty"); + return Status::Error("Service list is empty"); + } + return std::move(ret).value(); +} + +StatusOr ServerBasedSchemaManager::getDrainerClient(GraphSpaceID space, + PartitionID partId) { + auto ret = metaClient_->getDrainerClientFromCache(space, partId); + if (!ret.ok()) { + return ret.status(); + } + return std::move(ret).value(); +} + +StatusOr> ServerBasedSchemaManager::getDrainerServer( + GraphSpaceID space) { + auto ret = metaClient_->getDrainerFromCache(space); + if (!ret.ok()) { + return ret.status(); } return std::move(ret).value(); } diff --git a/src/common/meta/ServerBasedSchemaManager.h b/src/common/meta/ServerBasedSchemaManager.h index fd5a36450ec..934cb2f1796 100644 --- a/src/common/meta/ServerBasedSchemaManager.h +++ b/src/common/meta/ServerBasedSchemaManager.h @@ -69,7 +69,12 @@ class ServerBasedSchemaManager : public SchemaManager { // get all latest version of all edges StatusOr getAllLatestVerEdgeSchema(GraphSpaceID space) override; - StatusOr> getFTClients() override; + StatusOr> getServiceClients( + cpp2::ExternalServiceType type) override; + + StatusOr getDrainerClient(GraphSpaceID space, PartitionID partId) override; + + StatusOr> getDrainerServer(GraphSpaceID spaceId) override; StatusOr> getFTIndex( GraphSpaceID spaceId, int32_t schemaId) override; diff --git a/src/common/utils/MetaKeyUtils.cpp b/src/common/utils/MetaKeyUtils.cpp index 07445e07146..b0b253f9561 100644 --- a/src/common/utils/MetaKeyUtils.cpp +++ b/src/common/utils/MetaKeyUtils.cpp @@ -25,7 +25,7 @@ static const std::unordered_map> syste {"configs", {"__configs__", true}}, {"groups", {"__groups__", true}}, {"zones", {"__zones__", true}}, - {"ft_service", {"__ft_service__", false}}, + {"services", {"__services__", false}}, {"sessions", {"__sessions__", true}}}; // SystemInfo will always be backuped @@ -48,6 +48,8 @@ static const std::unordered_map< {"leaders", {"__leaders__", nullptr}}, {"leader_terms", {"__leader_terms__", nullptr}}, {"listener", {"__listener__", nullptr}}, + {"listener_drainer", {"__listener_drainer__", nullptr}}, + {"drainer", {"__drainer__", nullptr}}, {"stats", {"__stats__", MetaKeyUtils::parseStatsSpace}}, {"balance_task", {"__balance_task__", nullptr}}, {"balance_plan", {"__balance_plan__", nullptr}}, @@ -67,11 +69,13 @@ static const std::string kUsersTable = systemTableMaps.at("users").firs static const std::string kRolesTable = tableMaps.at("roles").first; // NOLINT static const std::string kConfigsTable = systemTableMaps.at("configs").first; // NOLINT static const std::string kSnapshotsTable = systemTableMaps.at("snapshots").first; // NOLINT -static const std::string kLeadersTable = tableMaps.at("leaders").first; // NOLINT -static const std::string kLeaderTermsTable = tableMaps.at("leader_terms").first; // NOLINT -static const std::string kGroupsTable = systemTableMaps.at("groups").first; // NOLINT -static const std::string kZonesTable = systemTableMaps.at("zones").first; // NOLINT -static const std::string kListenerTable = tableMaps.at("listener").first; // NOLINT +static const std::string kLeadersTable = tableMaps.at("leaders").first; // NOLINT +static const std::string kLeaderTermsTable = tableMaps.at("leader_terms").first; // NOLINT +static const std::string kGroupsTable = systemTableMaps.at("groups").first; // NOLINT +static const std::string kZonesTable = systemTableMaps.at("zones").first; // NOLINT +static const std::string kListenerTable = tableMaps.at("listener").first; // NOLINT +static const std::string kListenerDrainerTable = tableMaps.at("listener_drainer").first; // NOLINT +static const std::string kDrainerTable = tableMaps.at("drainer").first; // NOLINT // Used to record the number of vertices and edges in the space // The number of vertices of each tag in the space @@ -82,7 +86,7 @@ static const std::string kBalancePlanTable = tableMaps.at("balance_plan").fir static const std::string kLocalIdTable = tableMaps.at("local_id").first; // NOLINT const std::string kFTIndexTable = tableMaps.at("ft_index").first; // NOLINT -const std::string kFTServiceTable = systemTableMaps.at("ft_service").first; // NOLINT +const std::string kServicesTable = systemTableMaps.at("services").first; // NOLINT const std::string kSessionsTable = systemTableMaps.at("sessions").first; // NOLINT const std::string kIdKey = systemInfoMaps.at("autoIncrementId").first; // NOLINT @@ -1024,7 +1028,7 @@ std::vector MetaKeyUtils::parseZoneHosts(folly::StringPiece rawData) { std::string MetaKeyUtils::listenerKey(GraphSpaceID spaceId, PartitionID partId, - meta::cpp2::ListenerType type) { + const meta::cpp2::ListenerType& type) { std::string key; key.reserve(kListenerTable.size() + sizeof(GraphSpaceID) + sizeof(meta::cpp2::ListenerType) + sizeof(PartitionID)); @@ -1035,6 +1039,82 @@ std::string MetaKeyUtils::listenerKey(GraphSpaceID spaceId, return key; } +std::string MetaKeyUtils::listenerVal(const HostAddr& host, const std::string& spaceName) { + std::string val; + val.reserve(64); + auto hoststr = serializeHostAddr(host); + size_t hostLen = hoststr.size(); + size_t len = spaceName.size(); + val.append(reinterpret_cast(&hostLen), sizeof(size_t)) + .append(hoststr) + .append(reinterpret_cast(&len), sizeof(size_t)) + .append(spaceName); + return val; +} + +HostAddr MetaKeyUtils::parseListenerHost(const folly::StringPiece& rawVal) { + auto hostLen = *reinterpret_cast(rawVal.begin()); + auto hoststr = rawVal.subpiece(sizeof(size_t), hostLen); + return MetaKeyUtils::deserializeHostAddr(hoststr); +} + +std::string MetaKeyUtils::parseListenerSpacename(const folly::StringPiece& rawVal) { + auto hostLen = *reinterpret_cast(rawVal.begin()); + auto offset = sizeof(size_t) + hostLen; + auto spaceNameLen = *reinterpret_cast(rawVal.begin() + offset); + offset += sizeof(size_t); + return rawVal.subpiece(offset, spaceNameLen).str(); +} + +std::string MetaKeyUtils::listenerDrainerKey(GraphSpaceID spaceId, PartitionID partId) { + std::string key; + key.reserve(kListenerDrainerTable.size() + sizeof(GraphSpaceID) + sizeof(PartitionID)); + key.append(kListenerDrainerTable.data(), kListenerDrainerTable.size()) + .append(reinterpret_cast(&spaceId), sizeof(GraphSpaceID)) + .append(reinterpret_cast(&partId), sizeof(PartitionID)); + return key; +} + +std::string MetaKeyUtils::listenerDrainerVal(const HostAddr& host, const std::string& spaceName) { + std::string val; + val.reserve(64); + auto hoststr = serializeHostAddr(host); + size_t hostLen = hoststr.size(); + size_t len = spaceName.size(); + val.append(reinterpret_cast(&hostLen), sizeof(size_t)) + .append(hoststr) + .append(reinterpret_cast(&len), sizeof(size_t)) + .append(spaceName); + return val; +} + +HostAddr MetaKeyUtils::parseListenerDrainerHost(const folly::StringPiece& rawVal) { + auto hostLen = *reinterpret_cast(rawVal.begin()); + auto hoststr = rawVal.subpiece(sizeof(size_t), hostLen); + return MetaKeyUtils::deserializeHostAddr(hoststr); +} + +std::string MetaKeyUtils::parseListenerDrainerSpacename(const folly::StringPiece& rawVal) { + auto hostLen = *reinterpret_cast(rawVal.begin()); + auto offset = sizeof(size_t) + hostLen; + auto spaceNameLen = *reinterpret_cast(rawVal.begin() + offset); + offset += sizeof(size_t); + return rawVal.subpiece(offset, spaceNameLen).str(); +} + +std::string MetaKeyUtils::listenerDrainerPrefix(GraphSpaceID spaceId) { + std::string key; + key.reserve(kListenerDrainerTable.size() + sizeof(GraphSpaceID)); + key.append(kListenerDrainerTable.data(), kListenerDrainerTable.size()) + .append(reinterpret_cast(&spaceId), sizeof(GraphSpaceID)); + return key; +} + +PartitionID MetaKeyUtils::parseListenerDrainerPart(folly::StringPiece rawData) { + auto offset = kListenerDrainerTable.size() + sizeof(GraphSpaceID); + return *reinterpret_cast(rawData.data() + offset); +} + std::string MetaKeyUtils::listenerPrefix(GraphSpaceID spaceId) { std::string key; key.reserve(kListenerTable.size() + sizeof(GraphSpaceID)); @@ -1043,7 +1123,8 @@ std::string MetaKeyUtils::listenerPrefix(GraphSpaceID spaceId) { return key; } -std::string MetaKeyUtils::listenerPrefix(GraphSpaceID spaceId, meta::cpp2::ListenerType type) { +std::string MetaKeyUtils::listenerPrefix(GraphSpaceID spaceId, + const meta::cpp2::ListenerType& type) { std::string key; key.reserve(kListenerTable.size() + sizeof(GraphSpaceID) + sizeof(meta::cpp2::ListenerType)); key.append(kListenerTable.data(), kListenerTable.size()) @@ -1067,6 +1148,31 @@ PartitionID MetaKeyUtils::parseListenerPart(folly::StringPiece rawData) { return *reinterpret_cast(rawData.data() + offset); } +std::string MetaKeyUtils::drainerKey(GraphSpaceID spaceId) { + std::string key; + key.reserve(kDrainerTable.size() + sizeof(GraphSpaceID)); + key.append(kDrainerTable.data(), kDrainerTable.size()) + .append(reinterpret_cast(&spaceId), sizeof(GraphSpaceID)); + return key; +} + +std::string MetaKeyUtils::drainerVal(const std::vector& hosts) { + std::string val; + val.append(network::NetworkUtils::toHostsStr(hosts)); + return val; +} + +std::vector MetaKeyUtils::parseDrainerHosts(folly::StringPiece rawData) { + std::vector addresses; + auto hostsOrErr = network::NetworkUtils::toHosts(rawData.str()); + if (hostsOrErr.ok()) { + addresses = std::move(hostsOrErr.value()); + } else { + LOG(ERROR) << "invalid input for parseDrainerHosts()"; + } + return addresses; +} + std::string MetaKeyUtils::statsKey(GraphSpaceID spaceId) { std::string key; key.reserve(kStatsTable.size() + sizeof(GraphSpaceID)); @@ -1094,27 +1200,31 @@ GraphSpaceID MetaKeyUtils::parseStatsSpace(folly::StringPiece rawData) { const std::string& MetaKeyUtils::statsKeyPrefix() { return kStatsTable; } -std::string MetaKeyUtils::fulltextServiceKey() { +std::string MetaKeyUtils::serviceKey(const meta::cpp2::ExternalServiceType& type) { std::string key; - key.reserve(kFTServiceTable.size()); - key.append(kFTServiceTable.data(), kFTServiceTable.size()); + key.reserve(kServicesTable.size() + sizeof(meta::cpp2::ExternalServiceType)); + key.append(kServicesTable.data(), kServicesTable.size()) + .append(reinterpret_cast(&type), sizeof(meta::cpp2::ExternalServiceType)); return key; } -std::string MetaKeyUtils::fulltextServiceVal(meta::cpp2::FTServiceType type, - const std::vector& clients) { - std::string val, cval; - apache::thrift::CompactSerializer::serialize(clients, &cval); - val.reserve(sizeof(meta::cpp2::FTServiceType) + cval.size()); - val.append(reinterpret_cast(&type), sizeof(meta::cpp2::FTServiceType)).append(cval); +const std::string& MetaKeyUtils::servicePrefix() { return kServicesTable; } + +meta::cpp2::ExternalServiceType MetaKeyUtils::parseServiceType(folly::StringPiece rawData) { + auto offset = kServicesTable.size(); + return *reinterpret_cast(rawData.data() + offset); +} + +std::string MetaKeyUtils::serviceVal(const std::vector& clients) { + std::string val; + apache::thrift::CompactSerializer::serialize(clients, &val); return val; } -std::vector MetaKeyUtils::parseFTClients(folly::StringPiece rawData) { - std::vector clients; - int32_t offset = sizeof(meta::cpp2::FTServiceType); - auto clientsRaw = rawData.subpiece(offset, rawData.size() - offset); - apache::thrift::CompactSerializer::deserialize(clientsRaw, clients); +std::vector MetaKeyUtils::parseServiceClients( + folly::StringPiece rawData) { + std::vector clients; + apache::thrift::CompactSerializer::deserialize(rawData, clients); return clients; } diff --git a/src/common/utils/MetaKeyUtils.h b/src/common/utils/MetaKeyUtils.h index 47cc1f1f69e..390b087c9c5 100644 --- a/src/common/utils/MetaKeyUtils.h +++ b/src/common/utils/MetaKeyUtils.h @@ -313,11 +313,29 @@ class MetaKeyUtils final { static std::string listenerKey(GraphSpaceID spaceId, PartitionID partId, - meta::cpp2::ListenerType type); + const meta::cpp2::ListenerType& type); + + static std::string listenerVal(const HostAddr& host, const std::string& spaceName); static std::string listenerPrefix(GraphSpaceID spaceId); - static std::string listenerPrefix(GraphSpaceID spaceId, meta::cpp2::ListenerType type); + static std::string listenerPrefix(GraphSpaceID spaceId, const meta::cpp2::ListenerType& type); + + static HostAddr parseListenerHost(const folly::StringPiece& rawVal); + + static std::string parseListenerSpacename(const folly::StringPiece& rawVal); + + static std::string listenerDrainerKey(GraphSpaceID spaceId, PartitionID partId); + + static std::string listenerDrainerVal(const HostAddr& host, const std::string& spaceName); + + static HostAddr parseListenerDrainerHost(const folly::StringPiece& rawVal); + + static std::string parseListenerDrainerSpacename(const folly::StringPiece& rawVal); + + static std::string listenerDrainerPrefix(GraphSpaceID spaceId); + + static PartitionID parseListenerDrainerPart(folly::StringPiece rawData); static meta::cpp2::ListenerType parseListenerType(folly::StringPiece rawData); @@ -325,6 +343,13 @@ class MetaKeyUtils final { static PartitionID parseListenerPart(folly::StringPiece rawData); + // drainer + static std::string drainerKey(GraphSpaceID spaceId); + + static std::string drainerVal(const std::vector& hosts); + + static std::vector parseDrainerHosts(folly::StringPiece rawData); + static std::string statsKey(GraphSpaceID spaceId); static std::string statsVal(const meta::cpp2::StatsItem& statsItem); @@ -335,12 +360,15 @@ class MetaKeyUtils final { static GraphSpaceID parseStatsSpace(folly::StringPiece rawData); - static std::string fulltextServiceKey(); + static std::string serviceKey(const meta::cpp2::ExternalServiceType& type); + + static std::string serviceVal(const std::vector& client); + + static const std::string& servicePrefix(); - static std::string fulltextServiceVal(meta::cpp2::FTServiceType type, - const std::vector& clients); + static meta::cpp2::ExternalServiceType parseServiceType(folly::StringPiece rawData); - static std::vector parseFTClients(folly::StringPiece rawData); + static std::vector parseServiceClients(folly::StringPiece rawData); static const std::string& sessionPrefix(); diff --git a/src/daemons/CMakeLists.txt b/src/daemons/CMakeLists.txt index e58fecb2c0d..fde3c54aee0 100644 --- a/src/daemons/CMakeLists.txt +++ b/src/daemons/CMakeLists.txt @@ -11,6 +11,7 @@ set(common_deps $ $ $ + $ $ $ $ diff --git a/src/graph/context/test/CMakeLists.txt b/src/graph/context/test/CMakeLists.txt index 54f353abbca..d3d8ca55695 100644 --- a/src/graph/context/test/CMakeLists.txt +++ b/src/graph/context/test/CMakeLists.txt @@ -20,6 +20,7 @@ SET(CONTEXT_TEST_LIBS $ $ $ + $ $ $ $ diff --git a/src/graph/executor/CMakeLists.txt b/src/graph/executor/CMakeLists.txt index a78ecc4b304..a69d2e513d0 100644 --- a/src/graph/executor/CMakeLists.txt +++ b/src/graph/executor/CMakeLists.txt @@ -62,6 +62,7 @@ nebula_add_library( admin/SpaceExecutor.cpp admin/SnapshotExecutor.cpp admin/ListenerExecutor.cpp + admin/DrainerExecutor.cpp admin/PartExecutor.cpp admin/CharsetExecutor.cpp admin/ShowStatsExecutor.cpp @@ -70,9 +71,9 @@ nebula_add_library( admin/ConfigExecutor.cpp admin/GroupExecutor.cpp admin/ZoneExecutor.cpp - admin/ShowTSClientsExecutor.cpp - admin/SignInTSServiceExecutor.cpp - admin/SignOutTSServiceExecutor.cpp + admin/ShowServiceClientsExecutor.cpp + admin/SignInServiceExecutor.cpp + admin/SignOutServiceExecutor.cpp admin/SessionExecutor.cpp admin/ShowQueriesExecutor.cpp admin/KillQueryExecutor.cpp diff --git a/src/graph/executor/Executor.cpp b/src/graph/executor/Executor.cpp index ea4eac46e1a..51ce9766264 100644 --- a/src/graph/executor/Executor.cpp +++ b/src/graph/executor/Executor.cpp @@ -24,6 +24,7 @@ #include "graph/executor/admin/ConfigExecutor.h" #include "graph/executor/admin/CreateUserExecutor.h" #include "graph/executor/admin/DownloadExecutor.h" +#include "graph/executor/admin/DrainerExecutor.h" #include "graph/executor/admin/DropUserExecutor.h" #include "graph/executor/admin/GrantRoleExecutor.h" #include "graph/executor/admin/GroupExecutor.h" @@ -41,10 +42,10 @@ #include "graph/executor/admin/ShowHostsExecutor.h" #include "graph/executor/admin/ShowMetaLeaderExecutor.h" #include "graph/executor/admin/ShowQueriesExecutor.h" +#include "graph/executor/admin/ShowServiceClientsExecutor.h" #include "graph/executor/admin/ShowStatsExecutor.h" -#include "graph/executor/admin/ShowTSClientsExecutor.h" -#include "graph/executor/admin/SignInTSServiceExecutor.h" -#include "graph/executor/admin/SignOutTSServiceExecutor.h" +#include "graph/executor/admin/SignInServiceExecutor.h" +#include "graph/executor/admin/SignOutServiceExecutor.h" #include "graph/executor/admin/SnapshotExecutor.h" #include "graph/executor/admin/SpaceExecutor.h" #include "graph/executor/admin/StopBalanceExecutor.h" @@ -495,20 +496,29 @@ Executor *Executor::makeExecutor(QueryContext *qctx, const PlanNode *node) { case PlanNode::Kind::kShowListener: { return pool->add(new ShowListenerExecutor(node, qctx)); } + case PlanNode::Kind::kAddDrainer: { + return pool->add(new AddDrainerExecutor(node, qctx)); + } + case PlanNode::Kind::kRemoveDrainer: { + return pool->add(new RemoveDrainerExecutor(node, qctx)); + } + case PlanNode::Kind::kShowDrainers: { + return pool->add(new ListDrainersExecutor(node, qctx)); + } case PlanNode::Kind::kShowStats: { return pool->add(new ShowStatsExecutor(node, qctx)); } - case PlanNode::Kind::kShowTSClients: { - return pool->add(new ShowTSClientsExecutor(node, qctx)); + case PlanNode::Kind::kShowServiceClients: { + return pool->add(new ShowServiceClientsExecutor(node, qctx)); } case PlanNode::Kind::kShowFTIndexes: { return pool->add(new ShowFTIndexesExecutor(node, qctx)); } - case PlanNode::Kind::kSignInTSService: { - return pool->add(new SignInTSServiceExecutor(node, qctx)); + case PlanNode::Kind::kSignInService: { + return pool->add(new SignInServiceExecutor(node, qctx)); } - case PlanNode::Kind::kSignOutTSService: { - return pool->add(new SignOutTSServiceExecutor(node, qctx)); + case PlanNode::Kind::kSignOutService: { + return pool->add(new SignOutServiceExecutor(node, qctx)); } case PlanNode::Kind::kDownload: { return pool->add(new DownloadExecutor(node, qctx)); diff --git a/src/graph/executor/admin/DrainerExecutor.cpp b/src/graph/executor/admin/DrainerExecutor.cpp new file mode 100644 index 00000000000..11a0de9e3b3 --- /dev/null +++ b/src/graph/executor/admin/DrainerExecutor.cpp @@ -0,0 +1,77 @@ +/* Copyright (c) 2021 vesoft inc. All rights reserved. + * + * This source code is licensed under Apache 2.0 License, + * attached with Common Clause Condition 1.0, found in the LICENSES directory. + */ + +#include "graph/executor/admin/DrainerExecutor.h" + +#include + +#include "graph/planner/plan/Admin.h" + +namespace nebula { +namespace graph { + +folly::Future AddDrainerExecutor::execute() { + SCOPED_TIMER(&execTime_); + auto* alNode = asNode(node()); + auto spaceId = qctx()->rctx()->session()->space().id; + return qctx() + ->getMetaClient() + ->addDrainer(spaceId, alNode->hosts()) + .via(runner()) + .thenValue([this](StatusOr resp) { + SCOPED_TIMER(&execTime_); + if (!resp.ok()) { + LOG(ERROR) << resp.status(); + return resp.status(); + } + return Status::OK(); + }); +} + +folly::Future RemoveDrainerExecutor::execute() { + SCOPED_TIMER(&execTime_); + auto spaceId = qctx()->rctx()->session()->space().id; + return qctx()->getMetaClient()->removeDrainer(spaceId).via(runner()).thenValue( + [this](StatusOr resp) { + SCOPED_TIMER(&execTime_); + if (!resp.ok()) { + LOG(ERROR) << resp.status(); + return resp.status(); + } + return Status::OK(); + }); +} + +folly::Future ListDrainersExecutor::execute() { + SCOPED_TIMER(&execTime_); + auto spaceId = qctx()->rctx()->session()->space().id; + return qctx()->getMetaClient()->listDrainers(spaceId).via(runner()).thenValue( + [this](StatusOr> resp) { + SCOPED_TIMER(&execTime_); + if (!resp.ok()) { + LOG(ERROR) << resp.status(); + return resp.status(); + } + + auto drainerInfos = std::move(resp).value(); + std::sort(drainerInfos.begin(), drainerInfos.end(), [](const auto& a, const auto& b) { + return a.host_ref() < b.host_ref(); + }); + + DataSet result({"Host", "Status"}); + for (const auto& info : drainerInfos) { + Row row; + row.values.emplace_back((*info.host_ref()).toString()); + row.values.emplace_back(apache::thrift::util::enumNameSafe(info.get_status())); + result.emplace_back(std::move(row)); + } + + return finish(std::move(result)); + }); +} + +} // namespace graph +} // namespace nebula diff --git a/src/graph/executor/admin/DrainerExecutor.h b/src/graph/executor/admin/DrainerExecutor.h new file mode 100644 index 00000000000..bf42c71d8c5 --- /dev/null +++ b/src/graph/executor/admin/DrainerExecutor.h @@ -0,0 +1,42 @@ +/* Copyright (c) 2021 vesoft inc. All rights reserved. + * + * This source code is licensed under Apache 2.0 License, + * attached with Common Clause Condition 1.0, found in the LICENSES directory. + */ + +#ifndef GRAPH_EXECUTOR_ADMIN_DRAINEREXECUTOR_H_ +#define GRAPH_EXECUTOR_ADMIN_DRAINEREXECUTOR_H_ + +#include "graph/executor/Executor.h" + +namespace nebula { +namespace graph { + +class AddDrainerExecutor final : public Executor { + public: + AddDrainerExecutor(const PlanNode *node, QueryContext *qctx) + : Executor("AddDrainerExecutor", node, qctx) {} + + folly::Future execute() override; +}; + +class RemoveDrainerExecutor final : public Executor { + public: + RemoveDrainerExecutor(const PlanNode *node, QueryContext *qctx) + : Executor("RemoveDrainerExecutor", node, qctx) {} + + folly::Future execute() override; +}; + +class ListDrainersExecutor final : public Executor { + public: + ListDrainersExecutor(const PlanNode *node, QueryContext *qctx) + : Executor("ListDrainersExecutor", node, qctx) {} + + folly::Future execute() override; +}; + +} // namespace graph +} // namespace nebula + +#endif // GRAPH_EXECUTOR_ADMIN_DRAINEREXECUTOR_H_ diff --git a/src/graph/executor/admin/ListenerExecutor.cpp b/src/graph/executor/admin/ListenerExecutor.cpp index 13ee3b5b13b..8582724c22a 100644 --- a/src/graph/executor/admin/ListenerExecutor.cpp +++ b/src/graph/executor/admin/ListenerExecutor.cpp @@ -19,7 +19,7 @@ folly::Future AddListenerExecutor::execute() { auto spaceId = qctx()->rctx()->session()->space().id; return qctx() ->getMetaClient() - ->addListener(spaceId, alNode->type(), alNode->hosts()) + ->addListener(spaceId, alNode->type(), alNode->hosts(), alNode->spaceName()) .via(runner()) .thenValue([this](StatusOr resp) { SCOPED_TIMER(&execTime_); @@ -51,9 +51,14 @@ folly::Future RemoveListenerExecutor::execute() { folly::Future ShowListenerExecutor::execute() { SCOPED_TIMER(&execTime_); + auto* slNode = asNode(node()); auto spaceId = qctx()->rctx()->session()->space().id; - return qctx()->getMetaClient()->listListener(spaceId).via(runner()).thenValue( - [this](StatusOr> resp) { + auto type = slNode->type(); + return qctx() + ->getMetaClient() + ->listListeners(spaceId, type) + .via(runner()) + .thenValue([this, type](StatusOr> resp) { SCOPED_TIMER(&execTime_); if (!resp.ok()) { LOG(ERROR) << resp.status(); @@ -71,17 +76,34 @@ folly::Future ShowListenerExecutor::execute() { return a.host_ref() < b.host_ref(); }); - DataSet result({"PartId", "Type", "Host", "Status"}); - for (const auto& info : listenerInfos) { - Row row; - row.values.emplace_back(info.get_part_id()); - row.values.emplace_back(apache::thrift::util::enumNameSafe(info.get_type())); - row.values.emplace_back((*info.host_ref()).toString()); - row.values.emplace_back(apache::thrift::util::enumNameSafe(info.get_status())); - result.emplace_back(std::move(row)); + if (type == meta::cpp2::ListenerType::SYNC) { + DataSet result({"PartId", "Type", "Host", "SpaceName", "Status"}); + for (const auto& info : listenerInfos) { + Row row; + row.values.emplace_back(info.get_part_id()); + row.values.emplace_back(apache::thrift::util::enumNameSafe(info.get_type())); + row.values.emplace_back((*info.host_ref()).toString()); + if (info.space_name_ref().has_value()) { + row.values.emplace_back(*info.space_name_ref()); + } else { + row.values.emplace_back(""); + } + row.values.emplace_back(apache::thrift::util::enumNameSafe(info.get_status())); + result.emplace_back(std::move(row)); + } + return finish(std::move(result)); + } else { + DataSet result({"PartId", "Type", "Host", "Status"}); + for (const auto& info : listenerInfos) { + Row row; + row.values.emplace_back(info.get_part_id()); + row.values.emplace_back(apache::thrift::util::enumNameSafe(info.get_type())); + row.values.emplace_back((*info.host_ref()).toString()); + row.values.emplace_back(apache::thrift::util::enumNameSafe(info.get_status())); + result.emplace_back(std::move(row)); + } + return finish(std::move(result)); } - - return finish(std::move(result)); }); } diff --git a/src/graph/executor/admin/ShowServiceClientsExecutor.cpp b/src/graph/executor/admin/ShowServiceClientsExecutor.cpp new file mode 100644 index 00000000000..b9b23c788b0 --- /dev/null +++ b/src/graph/executor/admin/ShowServiceClientsExecutor.cpp @@ -0,0 +1,49 @@ +/* Copyright (c) 2020 vesoft inc. All rights reserved. + * + * This source code is licensed under Apache 2.0 License, + * attached with Common Clause Condition 1.0, found in the LICENSES directory. + */ + +#include "graph/executor/admin/ShowServiceClientsExecutor.h" + +#include + +#include "graph/context/QueryContext.h" +#include "graph/planner/plan/Admin.h" +#include "graph/service/PermissionManager.h" +#include "interface/gen-cpp2/meta_types.h" + +namespace nebula { +namespace graph { + +folly::Future ShowServiceClientsExecutor::execute() { + SCOPED_TIMER(&execTime_); + return showServiceClients(); +} + +folly::Future ShowServiceClientsExecutor::showServiceClients() { + auto *siNode = asNode(node()); + auto type = siNode->type(); + + return qctx()->getMetaClient()->listServiceClients(type).via(runner()).thenValue( + [this](auto &&resp) { + if (!resp.ok()) { + LOG(ERROR) << resp.status(); + return resp.status(); + } + auto values = std::move(resp).value(); + DataSet v({"Type", "Host", "Port"}); + for (const auto &value : values) { + for (const auto &client : value.second) { + nebula::Row r({apache::thrift::util::enumNameSafe(value.first), + client.host.host, + client.host.port}); + v.emplace_back(std::move(r)); + } + } + return finish(std::move(v)); + }); +} + +} // namespace graph +} // namespace nebula diff --git a/src/graph/executor/admin/ShowServiceClientsExecutor.h b/src/graph/executor/admin/ShowServiceClientsExecutor.h new file mode 100644 index 00000000000..ad046745132 --- /dev/null +++ b/src/graph/executor/admin/ShowServiceClientsExecutor.h @@ -0,0 +1,29 @@ +/* Copyright (c) 2020 vesoft inc. All rights reserved. + * + * This source code is licensed under Apache 2.0 License, + * attached with Common Clause Condition 1.0, found in the LICENSES directory. + */ + +#ifndef GRAPH_EXECUTOR_ADMIN_SHOW_SERVICE_CLIENTS_EXECUTOR_H_ +#define GRAPH_EXECUTOR_ADMIN_SHOW_SERVICE_CLIENTS_EXECUTOR_H_ + +#include "graph/executor/Executor.h" + +namespace nebula { +namespace graph { + +class ShowServiceClientsExecutor final : public Executor { + public: + ShowServiceClientsExecutor(const PlanNode *node, QueryContext *qctx) + : Executor("ShowServiceClientsExecutor", node, qctx) {} + + folly::Future execute() override; + + private: + folly::Future showServiceClients(); +}; + +} // namespace graph +} // namespace nebula + +#endif // GRAPH_EXECUTOR_ADMIN_SHOW_SERVICE_CLIENTS_EXECUTOR_H_ diff --git a/src/graph/executor/admin/ShowTSClientsExecutor.cpp b/src/graph/executor/admin/ShowTSClientsExecutor.cpp deleted file mode 100644 index 92b80c57966..00000000000 --- a/src/graph/executor/admin/ShowTSClientsExecutor.cpp +++ /dev/null @@ -1,39 +0,0 @@ -/* Copyright (c) 2020 vesoft inc. All rights reserved. - * - * This source code is licensed under Apache 2.0 License, - * attached with Common Clause Condition 1.0, found in the LICENSES directory. - */ - -#include "graph/executor/admin/ShowTSClientsExecutor.h" - -#include "graph/context/QueryContext.h" -#include "graph/planner/plan/Admin.h" -#include "graph/service/PermissionManager.h" -#include "interface/gen-cpp2/meta_types.h" - -namespace nebula { -namespace graph { - -folly::Future ShowTSClientsExecutor::execute() { - SCOPED_TIMER(&execTime_); - return showTSClients(); -} - -folly::Future ShowTSClientsExecutor::showTSClients() { - return qctx()->getMetaClient()->listFTClients().via(runner()).thenValue([this](auto &&resp) { - if (!resp.ok()) { - LOG(ERROR) << resp.status(); - return resp.status(); - } - auto value = std::move(resp).value(); - DataSet v({"Host", "Port"}); - for (const auto &client : value) { - nebula::Row r({client.host.host, client.host.port}); - v.emplace_back(std::move(r)); - } - return finish(std::move(v)); - }); -} - -} // namespace graph -} // namespace nebula diff --git a/src/graph/executor/admin/SignInTSServiceExecutor.cpp b/src/graph/executor/admin/SignInServiceExecutor.cpp similarity index 61% rename from src/graph/executor/admin/SignInTSServiceExecutor.cpp rename to src/graph/executor/admin/SignInServiceExecutor.cpp index 0754054f4e0..c7fe4e77491 100644 --- a/src/graph/executor/admin/SignInTSServiceExecutor.cpp +++ b/src/graph/executor/admin/SignInServiceExecutor.cpp @@ -4,29 +4,30 @@ * attached with Common Clause Condition 1.0, found in the LICENSES directory. */ -#include "graph/executor/admin/SignInTSServiceExecutor.h" +#include "graph/executor/admin/SignInServiceExecutor.h" #include "graph/planner/plan/Admin.h" namespace nebula { namespace graph { -folly::Future SignInTSServiceExecutor::execute() { +folly::Future SignInServiceExecutor::execute() { SCOPED_TIMER(&execTime_); - return signInTSService(); + return signInService(); } -folly::Future SignInTSServiceExecutor::signInTSService() { - auto *siNode = asNode(node()); +folly::Future SignInServiceExecutor::signInService() { + auto *siNode = asNode(node()); + auto type = siNode->type(); return qctx() ->getMetaClient() - ->signInFTService(siNode->type(), siNode->clients()) + ->signInService(type, siNode->clients()) .via(runner()) .thenValue([this](StatusOr resp) { SCOPED_TIMER(&execTime_); NG_RETURN_IF_ERROR(resp); if (!resp.value()) { - return Status::Error("Sign in text service failed!"); + return Status::Error("Sign in service failed!"); } return Status::OK(); }); diff --git a/src/graph/executor/admin/ShowTSClientsExecutor.h b/src/graph/executor/admin/SignInServiceExecutor.h similarity index 50% rename from src/graph/executor/admin/ShowTSClientsExecutor.h rename to src/graph/executor/admin/SignInServiceExecutor.h index 7e96569f550..0493f3d3a42 100644 --- a/src/graph/executor/admin/ShowTSClientsExecutor.h +++ b/src/graph/executor/admin/SignInServiceExecutor.h @@ -4,26 +4,26 @@ * attached with Common Clause Condition 1.0, found in the LICENSES directory. */ -#ifndef GRAPH_EXECUTOR_ADMIN_SHOW_TS_CLIENTS_EXECUTOR_H_ -#define GRAPH_EXECUTOR_ADMIN_SHOW_TS_CLIENTS_EXECUTOR_H_ +#ifndef GRAPH_EXECUTOR_ADMIN_SIGNINSERVICEEXECUTOR_H_ +#define GRAPH_EXECUTOR_ADMIN_SIGNINSERVICEEXECUTOR_H_ #include "graph/executor/Executor.h" namespace nebula { namespace graph { -class ShowTSClientsExecutor final : public Executor { +class SignInServiceExecutor final : public Executor { public: - ShowTSClientsExecutor(const PlanNode *node, QueryContext *qctx) - : Executor("ShowTSClientsExecutor", node, qctx) {} + SignInServiceExecutor(const PlanNode *node, QueryContext *qctx) + : Executor("SignInServiceExecutor", node, qctx) {} folly::Future execute() override; private: - folly::Future showTSClients(); + folly::Future signInService(); }; } // namespace graph } // namespace nebula -#endif // GRAPH_EXECUTOR_ADMIN_SHOW_TS_CLIENTS_EXECUTOR_H_ +#endif // GRAPH_EXECUTOR_ADMIN_SIGNINSERVICEEXECUTOR_H_ diff --git a/src/graph/executor/admin/SignInTSServiceExecutor.h b/src/graph/executor/admin/SignInTSServiceExecutor.h deleted file mode 100644 index b7941f2e00c..00000000000 --- a/src/graph/executor/admin/SignInTSServiceExecutor.h +++ /dev/null @@ -1,29 +0,0 @@ -/* Copyright (c) 2020 vesoft inc. All rights reserved. - * - * This source code is licensed under Apache 2.0 License, - * attached with Common Clause Condition 1.0, found in the LICENSES directory. - */ - -#ifndef GRAPH_EXECUTOR_ADMIN_SIGNINTSSERVICEEXECUTOR_H_ -#define GRAPH_EXECUTOR_ADMIN_SIGNINTSSERVICEEXECUTOR_H_ - -#include "graph/executor/Executor.h" - -namespace nebula { -namespace graph { - -class SignInTSServiceExecutor final : public Executor { - public: - SignInTSServiceExecutor(const PlanNode *node, QueryContext *qctx) - : Executor("SignInTSServiceExecutor", node, qctx) {} - - folly::Future execute() override; - - private: - folly::Future signInTSService(); -}; - -} // namespace graph -} // namespace nebula - -#endif // GRAPH_EXECUTOR_ADMIN_SIGNINTSSERVICEEXECUTOR_H_ diff --git a/src/graph/executor/admin/SignOutTSServiceExecutor.cpp b/src/graph/executor/admin/SignOutServiceExecutor.cpp similarity index 55% rename from src/graph/executor/admin/SignOutTSServiceExecutor.cpp rename to src/graph/executor/admin/SignOutServiceExecutor.cpp index bfd7ca38ece..58167e6ba96 100644 --- a/src/graph/executor/admin/SignOutTSServiceExecutor.cpp +++ b/src/graph/executor/admin/SignOutServiceExecutor.cpp @@ -4,25 +4,28 @@ * attached with Common Clause Condition 1.0, found in the LICENSES directory. */ -#include "graph/executor/admin/SignOutTSServiceExecutor.h" +#include "graph/executor/admin/SignOutServiceExecutor.h" #include "graph/planner/plan/Admin.h" namespace nebula { namespace graph { -folly::Future SignOutTSServiceExecutor::execute() { +folly::Future SignOutServiceExecutor::execute() { SCOPED_TIMER(&execTime_); - return signOutTSService(); + return signOutService(); } -folly::Future SignOutTSServiceExecutor::signOutTSService() { - return qctx()->getMetaClient()->signOutFTService().via(runner()).thenValue( +folly::Future SignOutServiceExecutor::signOutService() { + auto *siNode = asNode(node()); + auto type = siNode->type(); + + return qctx()->getMetaClient()->signOutService(type).via(runner()).thenValue( [this](StatusOr resp) { SCOPED_TIMER(&execTime_); NG_RETURN_IF_ERROR(resp); if (!resp.value()) { - return Status::Error("Sign out text service failed!"); + return Status::Error("Sign out service failed!"); } return Status::OK(); }); diff --git a/src/graph/executor/admin/SignOutTSServiceExecutor.h b/src/graph/executor/admin/SignOutServiceExecutor.h similarity index 71% rename from src/graph/executor/admin/SignOutTSServiceExecutor.h rename to src/graph/executor/admin/SignOutServiceExecutor.h index 9bd0b8b599f..cc156bc3a10 100644 --- a/src/graph/executor/admin/SignOutTSServiceExecutor.h +++ b/src/graph/executor/admin/SignOutServiceExecutor.h @@ -12,15 +12,15 @@ namespace nebula { namespace graph { -class SignOutTSServiceExecutor final : public Executor { +class SignOutServiceExecutor final : public Executor { public: - SignOutTSServiceExecutor(const PlanNode *node, QueryContext *qctx) - : Executor("SignInTSServiceExecutor", node, qctx) {} + SignOutServiceExecutor(const PlanNode *node, QueryContext *qctx) + : Executor("SignOutServiceExecutor", node, qctx) {} folly::Future execute() override; private: - folly::Future signOutTSService(); + folly::Future signOutService(); }; } // namespace graph diff --git a/src/graph/executor/test/CMakeLists.txt b/src/graph/executor/test/CMakeLists.txt index 6e48b1e4ed4..42d1fe644d7 100644 --- a/src/graph/executor/test/CMakeLists.txt +++ b/src/graph/executor/test/CMakeLists.txt @@ -15,6 +15,7 @@ SET(EXEC_QUERY_TEST_OBJS $ $ $ + $ $ $ $ diff --git a/src/graph/optimizer/test/CMakeLists.txt b/src/graph/optimizer/test/CMakeLists.txt index c9a3d8245b1..46e1917eaf0 100644 --- a/src/graph/optimizer/test/CMakeLists.txt +++ b/src/graph/optimizer/test/CMakeLists.txt @@ -14,6 +14,7 @@ set(OPTIMIZER_TEST_LIB $ $ $ + $ $ $ $ diff --git a/src/graph/planner/plan/Admin.h b/src/graph/planner/plan/Admin.h index 8d06377c010..5335e0f1e94 100644 --- a/src/graph/planner/plan/Admin.h +++ b/src/graph/planner/plan/Admin.h @@ -342,20 +342,25 @@ class AddListener final : public SingleDependencyNode { static AddListener* make(QueryContext* qctx, PlanNode* input, meta::cpp2::ListenerType type, - std::vector hosts) { - return qctx->objPool()->add(new AddListener(qctx, input, std::move(type), std::move(hosts))); + std::vector hosts, + const std::string* spaceName) { + return qctx->objPool()->add( + new AddListener(qctx, input, std::move(type), std::move(hosts), spaceName)); } const meta::cpp2::ListenerType& type() const { return type_; } const std::vector hosts() const { return hosts_; } + const std::string* spaceName() const { return spaceName_; } + private: explicit AddListener(QueryContext* qctx, PlanNode* input, meta::cpp2::ListenerType type, - std::vector hosts) - : SingleDependencyNode(qctx, Kind::kAddListener, input) { + std::vector hosts, + const std::string* spaceName) + : SingleDependencyNode(qctx, Kind::kAddListener, input), spaceName_(spaceName) { type_ = std::move(type); hosts_ = std::move(hosts); } @@ -363,6 +368,7 @@ class AddListener final : public SingleDependencyNode { private: meta::cpp2::ListenerType type_; std::vector hosts_; + const std::string* spaceName_; }; class RemoveListener final : public SingleDependencyNode { @@ -385,13 +391,60 @@ class RemoveListener final : public SingleDependencyNode { class ShowListener final : public SingleDependencyNode { public: - static ShowListener* make(QueryContext* qctx, PlanNode* input) { - return qctx->objPool()->add(new ShowListener(qctx, input)); + static ShowListener* make(QueryContext* qctx, PlanNode* input, meta::cpp2::ListenerType type) { + return qctx->objPool()->add(new ShowListener(qctx, input, std::move(type))); + } + + const meta::cpp2::ListenerType& type() const { return type_; } + + private: + explicit ShowListener(QueryContext* qctx, PlanNode* input, meta::cpp2::ListenerType type) + : SingleDependencyNode(qctx, Kind::kShowListener, input) { + type_ = std::move(type); + } + + private: + meta::cpp2::ListenerType type_; +}; + +class AddDrainer final : public SingleDependencyNode { + public: + static AddDrainer* make(QueryContext* qctx, PlanNode* input, std::vector hosts) { + return qctx->objPool()->add(new AddDrainer(qctx, input, std::move(hosts))); + } + + const std::vector hosts() const { return hosts_; } + + private: + explicit AddDrainer(QueryContext* qctx, PlanNode* input, std::vector hosts) + : SingleDependencyNode(qctx, Kind::kAddDrainer, input) { + hosts_ = std::move(hosts); + } + + private: + std::vector hosts_; +}; + +class RemoveDrainer final : public SingleDependencyNode { + public: + static RemoveDrainer* make(QueryContext* qctx, PlanNode* input) { + return qctx->objPool()->add(new RemoveDrainer(qctx, input)); + } + + private: + explicit RemoveDrainer(QueryContext* qctx, PlanNode* input) + : SingleDependencyNode(qctx, Kind::kRemoveDrainer, input) {} +}; + +class ShowDrainers final : public SingleDependencyNode { + public: + static ShowDrainers* make(QueryContext* qctx, PlanNode* input) { + return qctx->objPool()->add(new ShowDrainers(qctx, input)); } private: - explicit ShowListener(QueryContext* qctx, PlanNode* input) - : SingleDependencyNode(qctx, Kind::kShowListener, input) {} + explicit ShowDrainers(QueryContext* qctx, PlanNode* input) + : SingleDependencyNode(qctx, Kind::kShowDrainers, input) {} }; class Download final : public SingleDependencyNode { @@ -1137,45 +1190,67 @@ class ShowStats final : public SingleDependencyNode { : SingleDependencyNode(qctx, Kind::kShowStats, input) {} }; -class ShowTSClients final : public SingleDependencyNode { +class ShowServiceClients final : public SingleDependencyNode { public: - static ShowTSClients* make(QueryContext* qctx, PlanNode* input) { - return qctx->objPool()->add(new ShowTSClients(qctx, input)); + static ShowServiceClients* make(QueryContext* qctx, + PlanNode* input, + meta::cpp2::ExternalServiceType type) { + return qctx->objPool()->add(new ShowServiceClients(qctx, input, type)); } + meta::cpp2::ExternalServiceType type() const { return type_; } + + private: + ShowServiceClients(QueryContext* qctx, PlanNode* input, meta::cpp2::ExternalServiceType type) + : SingleDependencyNode(qctx, Kind::kShowServiceClients, input), type_(type) {} + private: - ShowTSClients(QueryContext* qctx, PlanNode* input) - : SingleDependencyNode(qctx, Kind::kShowTSClients, input) {} + meta::cpp2::ExternalServiceType type_; }; -class SignInTSService final : public SingleDependencyNode { +class SignInService final : public SingleDependencyNode { public: - static SignInTSService* make(QueryContext* qctx, - PlanNode* input, - std::vector clients) { - return qctx->objPool()->add(new SignInTSService(qctx, input, std::move(clients))); + static SignInService* make(QueryContext* qctx, + PlanNode* input, + std::vector clients, + meta::cpp2::ExternalServiceType type) { + return qctx->objPool()->add(new SignInService(qctx, input, std::move(clients), type)); } - const std::vector& clients() const { return clients_; } + const std::vector& clients() const { return clients_; } - meta::cpp2::FTServiceType type() const { return meta::cpp2::FTServiceType::ELASTICSEARCH; } + meta::cpp2::ExternalServiceType type() const { return type_; } private: - SignInTSService(QueryContext* qctx, PlanNode* input, std::vector clients) - : SingleDependencyNode(qctx, Kind::kSignInTSService, input), clients_(std::move(clients)) {} + SignInService(QueryContext* qctx, + PlanNode* input, + std::vector clients, + meta::cpp2::ExternalServiceType type) + : SingleDependencyNode(qctx, Kind::kSignInService, input), + clients_(std::move(clients)), + type_(type) {} - std::vector clients_; + private: + std::vector clients_; + meta::cpp2::ExternalServiceType type_; }; -class SignOutTSService final : public SingleDependencyNode { +class SignOutService final : public SingleDependencyNode { public: - static SignOutTSService* make(QueryContext* qctx, PlanNode* input) { - return qctx->objPool()->add(new SignOutTSService(qctx, input)); + static SignOutService* make(QueryContext* qctx, + PlanNode* input, + meta::cpp2::ExternalServiceType type) { + return qctx->objPool()->add(new SignOutService(qctx, input, type)); } + meta::cpp2::ExternalServiceType type() const { return type_; } + + private: + SignOutService(QueryContext* qctx, PlanNode* input, meta::cpp2::ExternalServiceType type) + : SingleDependencyNode(qctx, Kind::kSignOutService, input), type_(type) {} + private: - SignOutTSService(QueryContext* qctx, PlanNode* input) - : SingleDependencyNode(qctx, Kind::kSignOutTSService, input) {} + meta::cpp2::ExternalServiceType type_; }; class ShowSessions final : public SingleInputNode { diff --git a/src/graph/planner/plan/PlanNode.cpp b/src/graph/planner/plan/PlanNode.cpp index 9087bed240a..d2114c5adaa 100644 --- a/src/graph/planner/plan/PlanNode.cpp +++ b/src/graph/planner/plan/PlanNode.cpp @@ -272,17 +272,23 @@ const char* PlanNode::toString(PlanNode::Kind kind) { return "RemoveListener"; case Kind::kShowListener: return "ShowListener"; + case Kind::kAddDrainer: + return "AddDrainer"; + case Kind::kRemoveDrainer: + return "RemoveDrainer"; + case Kind::kShowDrainers: + return "ShowDrainers"; case Kind::kShowStats: return "ShowStats"; - // text search - case Kind::kShowTSClients: - return "ShowTSClients"; + // service search + case Kind::kShowServiceClients: + return "ShowServiceClients"; case Kind::kShowFTIndexes: return "ShowFTIndexes"; - case Kind::kSignInTSService: - return "SignInTSService"; - case Kind::kSignOutTSService: - return "SignOutTSService"; + case Kind::kSignInService: + return "SignInService"; + case Kind::kSignOutService: + return "SignOutService"; case Kind::kDownload: return "Download"; case Kind::kIngest: diff --git a/src/graph/planner/plan/PlanNode.h b/src/graph/planner/plan/PlanNode.h index b79d4181fcc..64bf7333a31 100644 --- a/src/graph/planner/plan/PlanNode.h +++ b/src/graph/planner/plan/PlanNode.h @@ -166,11 +166,16 @@ class PlanNode { kRemoveListener, kShowListener, - // text service related - kShowTSClients, + // drainer related + kAddDrainer, + kRemoveDrainer, + kShowDrainers, + + // service related + kShowServiceClients, kShowFTIndexes, - kSignInTSService, - kSignOutTSService, + kSignInService, + kSignOutService, kDownload, kIngest, kShowSessions, diff --git a/src/graph/planner/test/CMakeLists.txt b/src/graph/planner/test/CMakeLists.txt index 7fdd86e480d..2f7b6900e88 100644 --- a/src/graph/planner/test/CMakeLists.txt +++ b/src/graph/planner/test/CMakeLists.txt @@ -21,6 +21,7 @@ nebula_add_test( $ $ $ + $ $ $ $ diff --git a/src/graph/service/PermissionCheck.cpp b/src/graph/service/PermissionCheck.cpp index d9eccdf0932..0e4cba64aa5 100644 --- a/src/graph/service/PermissionCheck.cpp +++ b/src/graph/service/PermissionCheck.cpp @@ -74,8 +74,8 @@ Status PermissionCheck::permissionCheck(ClientSession *session, case Sentence::Kind::kGetConfig: case Sentence::Kind::kIngest: case Sentence::Kind::kDownload: - case Sentence::Kind::kSignOutTSService: - case Sentence::Kind::kSignInTSService: { + case Sentence::Kind::kSignOutService: + case Sentence::Kind::kSignInService: { return PermissionManager::canWriteSpace(session); } case Sentence::Kind::kCreateTag: @@ -91,7 +91,9 @@ Status PermissionCheck::permissionCheck(ClientSession *session, case Sentence::Kind::kDropEdgeIndex: case Sentence::Kind::kDropFTIndex: case Sentence::Kind::kAddListener: - case Sentence::Kind::kRemoveListener: { + case Sentence::Kind::kRemoveListener: + case Sentence::Kind::kAddDrainer: + case Sentence::Kind::kRemoveDrainer: { return PermissionManager::canWriteSchema(session, vctx); } case Sentence::Kind::kCreateUser: @@ -153,6 +155,7 @@ Status PermissionCheck::permissionCheck(ClientSession *session, case Sentence::Kind::kShowCreateTagIndex: case Sentence::Kind::kShowCreateEdgeIndex: case Sentence::Kind::kShowListener: + case Sentence::Kind::kShowDrainers: case Sentence::Kind::kShowFTIndexes: case Sentence::Kind::kAdminShowJobs: { /** @@ -187,7 +190,7 @@ Status PermissionCheck::permissionCheck(ClientSession *session, } case Sentence::Kind::kShowUsers: case Sentence::Kind::kShowSnapshots: - case Sentence::Kind::kShowTSClients: + case Sentence::Kind::kShowServiceClients: case Sentence::Kind::kShowSessions: { /** * Only GOD role can be show. @@ -195,7 +198,7 @@ Status PermissionCheck::permissionCheck(ClientSession *session, if (session->isGod()) { return Status::OK(); } else { - return Status::PermissionError("No permission to show users/snapshots/textClients"); + return Status::PermissionError("No permission to show users/snapshots/serviceClients"); } } case Sentence::Kind::kChangePassword: { diff --git a/src/graph/service/QueryInstance.cpp b/src/graph/service/QueryInstance.cpp index 0872f1021bf..7f46461c6b4 100644 --- a/src/graph/service/QueryInstance.cpp +++ b/src/graph/service/QueryInstance.cpp @@ -143,6 +143,8 @@ void QueryInstance::onError(Status status) { case Status::Code::kUserNotFound: case Status::Code::kListenerNotFound: case Status::Code::kSessionNotFound: + case Status::Code::kDrainerNotFound: + case Status::Code::kDrainerClientNotFound: rctx->resp().errorCode = ErrorCode::E_EXECUTION_ERROR; break; } diff --git a/src/graph/util/FTIndexUtils.cpp b/src/graph/util/FTIndexUtils.cpp index d85316cf0f9..000818b267a 100644 --- a/src/graph/util/FTIndexUtils.cpp +++ b/src/graph/util/FTIndexUtils.cpp @@ -28,7 +28,7 @@ bool FTIndexUtils::needTextSearch(const Expression* expr) { StatusOr> FTIndexUtils::getTSClients( meta::MetaClient* client) { - auto tcs = client->getFTClientsFromCache(); + auto tcs = client->getServiceClientsFromCache(meta::cpp2::ExternalServiceType::ELASTICSEARCH); if (!tcs.ok()) { return tcs.status(); } diff --git a/src/graph/util/test/CMakeLists.txt b/src/graph/util/test/CMakeLists.txt index afdbc51cc46..573d0cff5a5 100644 --- a/src/graph/util/test/CMakeLists.txt +++ b/src/graph/util/test/CMakeLists.txt @@ -15,6 +15,7 @@ nebula_add_test( $ $ $ + $ $ $ $ diff --git a/src/graph/validator/AdminValidator.cpp b/src/graph/validator/AdminValidator.cpp index 2d47e2b718e..9a668c0fd01 100644 --- a/src/graph/validator/AdminValidator.cpp +++ b/src/graph/validator/AdminValidator.cpp @@ -251,6 +251,13 @@ Status AddListenerValidator::validateImpl() { return Status::SemanticError("Listener hosts should not be empty"); } + if (sentence->type() == meta::cpp2::ListenerType::SYNC) { + auto spaceName = sentence->spaceName(); + if (!spaceName) { + return Status::SemanticError("Sync Listener toSpaceName should not be empty"); + } + } + // check the hosts, if the hosts the same with storage, return error auto status = qctx_->getMetaClient()->getStorageHosts(); if (!status.ok()) { @@ -270,8 +277,8 @@ Status AddListenerValidator::validateImpl() { Status AddListenerValidator::toPlan() { auto sentence = static_cast(sentence_); - auto *doNode = - AddListener::make(qctx_, nullptr, sentence->type(), sentence->listeners()->hosts()); + auto *doNode = AddListener::make( + qctx_, nullptr, sentence->type(), sentence->listeners()->hosts(), sentence->spaceName()); root_ = doNode; tail_ = root_; return Status::OK(); @@ -290,7 +297,58 @@ Status RemoveListenerValidator::toPlan() { Status ShowListenerValidator::validateImpl() { return Status::OK(); } Status ShowListenerValidator::toPlan() { - auto *doNode = ShowListener::make(qctx_, nullptr); + auto sentence = static_cast(sentence_); + auto *doNode = ShowListener::make(qctx_, nullptr, sentence->type()); + root_ = doNode; + tail_ = root_; + return Status::OK(); +} + +Status AddDrainerValidator::validateImpl() { + auto sentence = static_cast(sentence_); + auto hosts = sentence->drainers()->hosts(); + if (hosts.empty()) { + return Status::SemanticError("Drainer hosts should not be empty"); + } + + // check the hosts, if the hosts are the same with storage, return error + auto status = qctx_->getMetaClient()->getStorageHosts(); + if (!status.ok()) { + return status.status(); + } + + auto storageHosts = std::move(status).value(); + for (auto &host : hosts) { + auto iter = std::find(storageHosts.begin(), storageHosts.end(), host); + if (iter != storageHosts.end()) { + return Status::Error("The drainer host:%s couldn't on same with storage host info", + host.toString().c_str()); + } + } + return Status::OK(); +} + +Status AddDrainerValidator::toPlan() { + auto sentence = static_cast(sentence_); + auto *doNode = AddDrainer::make(qctx_, nullptr, sentence->drainers()->hosts()); + root_ = doNode; + tail_ = root_; + return Status::OK(); +} + +Status RemoveDrainerValidator::validateImpl() { return Status::OK(); } + +Status RemoveDrainerValidator::toPlan() { + auto *doNode = RemoveDrainer::make(qctx_, nullptr); + root_ = doNode; + tail_ = root_; + return Status::OK(); +} + +Status ListDrainersValidator::validateImpl() { return Status::OK(); } + +Status ListDrainersValidator::toPlan() { + auto *doNode = ShowDrainers::make(qctx_, nullptr); root_ = doNode; tail_ = root_; return Status::OK(); @@ -447,33 +505,38 @@ Status ShowStatusValidator::toPlan() { return Status::OK(); } -Status ShowTSClientsValidator::validateImpl() { return Status::OK(); } +Status ShowServiceClientsValidator::validateImpl() { return Status::OK(); } -Status ShowTSClientsValidator::toPlan() { - auto *doNode = ShowTSClients::make(qctx_, nullptr); +Status ShowServiceClientsValidator::toPlan() { + auto sentence = static_cast(sentence_); + auto type = sentence->getType(); + auto *doNode = ShowServiceClients::make(qctx_, nullptr, type); root_ = doNode; tail_ = root_; return Status::OK(); } -Status SignInTSServiceValidator::validateImpl() { return Status::OK(); } +Status SignInServiceValidator::validateImpl() { return Status::OK(); } -Status SignInTSServiceValidator::toPlan() { - auto sentence = static_cast(sentence_); - std::vector clients; +Status SignInServiceValidator::toPlan() { + auto sentence = static_cast(sentence_); + std::vector clients; if (sentence->clients() != nullptr) { clients = sentence->clients()->clients(); } - auto *node = SignInTSService::make(qctx_, nullptr, std::move(clients)); + auto type = sentence->getType(); + auto *node = SignInService::make(qctx_, nullptr, std::move(clients), type); root_ = node; tail_ = root_; return Status::OK(); } -Status SignOutTSServiceValidator::validateImpl() { return Status::OK(); } +Status SignOutServiceValidator::validateImpl() { return Status::OK(); } -Status SignOutTSServiceValidator::toPlan() { - auto *node = SignOutTSService::make(qctx_, nullptr); +Status SignOutServiceValidator::toPlan() { + auto sentence = static_cast(sentence_); + auto type = sentence->getType(); + auto *node = SignOutService::make(qctx_, nullptr, type); root_ = node; tail_ = root_; return Status::OK(); diff --git a/src/graph/validator/AdminValidator.h b/src/graph/validator/AdminValidator.h index dadae9492e9..94cb8ff3541 100644 --- a/src/graph/validator/AdminValidator.h +++ b/src/graph/validator/AdminValidator.h @@ -26,7 +26,8 @@ class CreateSpaceValidator final : public Validator { Status toPlan() override; - bool checkTSIndex(const std::vector& clients, const std::string& index); + bool checkTSIndex(const std::vector& clients, + const std::string& index); private: meta::cpp2::SpaceDesc spaceDesc_; @@ -168,6 +169,37 @@ class ShowListenerValidator final : public Validator { Status toPlan() override; }; +class AddDrainerValidator final : public Validator { + public: + AddDrainerValidator(Sentence* sentence, QueryContext* context) : Validator(sentence, context) {} + + private: + Status validateImpl() override; + + Status toPlan() override; +}; + +class RemoveDrainerValidator final : public Validator { + public: + RemoveDrainerValidator(Sentence* sentence, QueryContext* context) + : Validator(sentence, context) {} + + private: + Status validateImpl() override; + + Status toPlan() override; +}; + +class ListDrainersValidator final : public Validator { + public: + ListDrainersValidator(Sentence* sentence, QueryContext* context) : Validator(sentence, context) {} + + private: + Status validateImpl() override; + + Status toPlan() override; +}; + class ShowHostsValidator final : public Validator { public: ShowHostsValidator(Sentence* sentence, QueryContext* context) : Validator(sentence, context) { @@ -281,9 +313,10 @@ class ShowStatusValidator final : public Validator { Status toPlan() override; }; -class ShowTSClientsValidator final : public Validator { +class ShowServiceClientsValidator final : public Validator { public: - ShowTSClientsValidator(Sentence* sentence, QueryContext* context) : Validator(sentence, context) { + ShowServiceClientsValidator(Sentence* sentence, QueryContext* context) + : Validator(sentence, context) { setNoSpaceRequired(); } @@ -293,10 +326,9 @@ class ShowTSClientsValidator final : public Validator { Status toPlan() override; }; -class SignInTSServiceValidator final : public Validator { +class SignInServiceValidator final : public Validator { public: - SignInTSServiceValidator(Sentence* sentence, QueryContext* context) - : Validator(sentence, context) { + SignInServiceValidator(Sentence* sentence, QueryContext* context) : Validator(sentence, context) { setNoSpaceRequired(); } @@ -306,9 +338,9 @@ class SignInTSServiceValidator final : public Validator { Status toPlan() override; }; -class SignOutTSServiceValidator final : public Validator { +class SignOutServiceValidator final : public Validator { public: - SignOutTSServiceValidator(Sentence* sentence, QueryContext* context) + SignOutServiceValidator(Sentence* sentence, QueryContext* context) : Validator(sentence, context) { setNoSpaceRequired(); } diff --git a/src/graph/validator/Validator.cpp b/src/graph/validator/Validator.cpp index 45c72ae5034..0346f0fe93d 100644 --- a/src/graph/validator/Validator.cpp +++ b/src/graph/validator/Validator.cpp @@ -234,16 +234,22 @@ std::unique_ptr Validator::makeValidator(Sentence* sentence, QueryCon return std::make_unique(sentence, context); case Sentence::Kind::kShowListener: return std::make_unique(sentence, context); + case Sentence::Kind::kAddDrainer: + return std::make_unique(sentence, context); + case Sentence::Kind::kRemoveDrainer: + return std::make_unique(sentence, context); + case Sentence::Kind::kShowDrainers: + return std::make_unique(sentence, context); case Sentence::Kind::kShowStats: return std::make_unique(sentence, context); - case Sentence::Kind::kShowTSClients: - return std::make_unique(sentence, context); + case Sentence::Kind::kShowServiceClients: + return std::make_unique(sentence, context); case Sentence::Kind::kShowFTIndexes: return std::make_unique(sentence, context); - case Sentence::Kind::kSignInTSService: - return std::make_unique(sentence, context); - case Sentence::Kind::kSignOutTSService: - return std::make_unique(sentence, context); + case Sentence::Kind::kSignInService: + return std::make_unique(sentence, context); + case Sentence::Kind::kSignOutService: + return std::make_unique(sentence, context); case Sentence::Kind::kDownload: return std::make_unique(sentence, context); case Sentence::Kind::kIngest: diff --git a/src/graph/validator/test/CMakeLists.txt b/src/graph/validator/test/CMakeLists.txt index f6032fb78d8..0aab17a9904 100644 --- a/src/graph/validator/test/CMakeLists.txt +++ b/src/graph/validator/test/CMakeLists.txt @@ -37,6 +37,7 @@ set(VALIDATOR_TEST_LIBS $ $ $ + $ $ $ $ diff --git a/src/graph/validator/test/MockSchemaManager.cpp b/src/graph/validator/test/MockSchemaManager.cpp index 328ea53d64f..8829cbb9312 100644 --- a/src/graph/validator/test/MockSchemaManager.cpp +++ b/src/graph/validator/test/MockSchemaManager.cpp @@ -168,7 +168,15 @@ StatusOr> MockSchemaManager::getAllEdge(GraphSpaceID) { return edgeNames; } -StatusOr> MockSchemaManager::getFTClients() { +StatusOr> MockSchemaManager::getServiceClients( + meta::cpp2::ExternalServiceType type) { + UNUSED(type); + return Status::Error("Not implemented"); +} + +StatusOr MockSchemaManager::getDrainerClient(GraphSpaceID space, PartitionID partId) { + UNUSED(space); + UNUSED(partId); return Status::Error("Not implemented"); } diff --git a/src/graph/validator/test/MockSchemaManager.h b/src/graph/validator/test/MockSchemaManager.h index 6c8a4b7c204..25e4e4dfca6 100644 --- a/src/graph/validator/test/MockSchemaManager.h +++ b/src/graph/validator/test/MockSchemaManager.h @@ -109,7 +109,10 @@ class MockSchemaManager final : public nebula::meta::SchemaManager { return allLatestVerEdgeSchemas; } - StatusOr> getFTClients() override; + StatusOr> getServiceClients( + meta::cpp2::ExternalServiceType type) override; + + StatusOr getDrainerClient(GraphSpaceID space, PartitionID partId) override; StatusOr getPartsNum(GraphSpaceID) override { LOG(FATAL) << "Unimplemented."; } @@ -119,6 +122,11 @@ class MockSchemaManager final : public nebula::meta::SchemaManager { return Status::Error("Unimplemented"); } + StatusOr> getDrainerServer(GraphSpaceID) override { + LOG(FATAL) << "Unimplemented"; + return Status::Error("Unimplemented"); + } + private: std::unordered_map spaceNameIds_; std::unordered_map tagNameIds_; diff --git a/src/graph/visitor/test/CMakeLists.txt b/src/graph/visitor/test/CMakeLists.txt index cd27f31f110..9a4cb07e459 100644 --- a/src/graph/visitor/test/CMakeLists.txt +++ b/src/graph/visitor/test/CMakeLists.txt @@ -40,6 +40,7 @@ nebula_add_test( $ $ $ + $ $ $ $ diff --git a/src/interface/CMakeLists.txt b/src/interface/CMakeLists.txt index 02176703910..b465f990e02 100644 --- a/src/interface/CMakeLists.txt +++ b/src/interface/CMakeLists.txt @@ -32,6 +32,9 @@ thrift_generate( # Target object name : meta_thrift_obj thrift_generate("meta" "MetaService" ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR} "interface") +# Target object name : drainer_thrift_obj +thrift_generate("drainer" "DrainerService" ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR} "interface") + add_custom_target( clean-interface COMMAND "rm" "-fr" "gen-cpp2" "gen-java" "gen-go" "gen-py" "gen-rust" diff --git a/src/interface/common.thrift b/src/interface/common.thrift index c50fed85db6..8661350db22 100644 --- a/src/interface/common.thrift +++ b/src/interface/common.thrift @@ -36,6 +36,7 @@ cpp_include "common/datatypes/GeographyOps-inl.h" const binary (cpp.type = "char const *") version = "2.6.0" +typedef i64 (cpp.type = "nebula::ClusterID") ClusterID typedef i32 (cpp.type = "nebula::GraphSpaceID") GraphSpaceID typedef i32 (cpp.type = "nebula::PartitionID") PartitionID typedef i32 (cpp.type = "nebula::TagID") TagID @@ -284,6 +285,8 @@ enum ErrorCode { E_KEY_NOT_FOUND = -17, E_USER_NOT_FOUND = -18, E_STATS_NOT_FOUND = -19, + E_SERVICE_NOT_FOUND = -20, + E_DRAINER_NOT_FOUND = -21, // backup failed E_BACKUP_FAILED = -24, @@ -320,6 +323,7 @@ enum ErrorCode { E_CONFLICT = -2008, E_INVALID_PARM = -2009, E_WRONGCLUSTER = -2010, + E_LISTENER_CONFLICT = -2011, E_STORE_FAILURE = -2021, E_STORE_SEGMENT_ILLEGAL = -2022, @@ -329,7 +333,7 @@ enum ErrorCode { E_NO_VALID_HOST = -2026, E_CORRUPTTED_BALANCE_PLAN = -2027, E_NO_INVALID_BALANCE_PLAN = -2028, - + E_NO_VALID_DRAINER = -2029, // Authentication Failure E_IMPROPER_ROLE = -2030, @@ -432,5 +436,8 @@ enum ErrorCode { E_CLIENT_SERVER_INCOMPATIBLE = -3061, + // 4xxx for drainer + E_LOG_GAP = -4001, + E_UNKNOWN = -8000, } (cpp.enum_strict) diff --git a/src/interface/drainer.thrift b/src/interface/drainer.thrift new file mode 100644 index 00000000000..5e1a86f1619 --- /dev/null +++ b/src/interface/drainer.thrift @@ -0,0 +1,73 @@ +/* vim: ft=proto + * Copyright (c) 2021 vesoft inc. All rights reserved. + * + * This source code is licensed under Apache 2.0 License, + * attached with Common Clause Condition 1.0, found in the LICENSES directory. + */ + +namespace cpp nebula.drainer +namespace java com.vesoft.nebula.drainer +namespace go nebula.drainer +namespace csharp nebula.drainer +namespace js nebula.drainer +namespace py nebula2.drainer + +include "common.thrift" + +/* +enum ErrorCode { + // for common code + SUCCEEDED = 0, + E_DISCONNECTED = -1, // RPC Failure + E_FAIL_TO_CONNECT = -2, + E_RPC_FAILURE = -3, + E_LEADER_CHANGED = -4, + // only unify metad and storaged error code + E_SPACE_NOT_FOUND = -5, + E_WRONGCLUSTER = -6, +} +*/ + +struct LogEntry { + 1: common.ClusterID cluster; + 2: binary log_str; +} + + +/* + AppendLogRequest send a log message to from sync listener to drainer +*/ +struct AppendLogRequest { + // last_log_term_sent and last_log_id_sent are the term and log id + // for the last log being sent + // + 1: common.ClusterID clusterId; // source cluster ID + 2: common.GraphSpaceID space; // Graphspace ID + 3: common.PartitionID part; // Partition ID + 4: common.LogID last_log_id; // To be sent last log id this time + 5: common.TermID last_log_term_sent; // LastlogTerm sent successfully last time + 6: common.LogID last_log_id_sent; // LastlogId sent successfully last time + // + // In the case of AppendLogRequest, the id of the first log is greater than or equal to + // last_log_id_sent + 1 + // + // All logs in the log_str_list must belong to the same term, + // which specified by log_term + // + 7: common.TermID log_term; + // log count + 8: i64 count; + // log id is in the range [last_log_id_sent + 1, last_log_id] + 9: list log_str_list; + 10: bool sending_snapshot; +} + +struct AppendLogResponse { + 1: common.ErrorCode error_code; + 2: common.LogID last_log_id; + 3: common.TermID last_log_term; +} + +service DrainerService { + AppendLogResponse appendLog(1: AppendLogRequest req); +} diff --git a/src/interface/meta.thrift b/src/interface/meta.thrift index 17ed13578a9..c6dc9e5e856 100644 --- a/src/interface/meta.thrift +++ b/src/interface/meta.thrift @@ -466,6 +466,7 @@ enum ListHostType { GRAPH = 0x01, META = 0x02, STORAGE = 0x03, + DRAINER = 0x04, } (cpp.enum_strict) struct ListHostsReq { @@ -574,7 +575,8 @@ enum HostRole { META = 0x01, STORAGE = 0x02, LISTENER = 0x03, - UNKNOWN = 0x04 + DRAINER = 0x04, + UNKNOWN = 0x05 } (cpp.enum_strict) struct LeaderInfo { @@ -944,12 +946,16 @@ struct ListGroupsResp { enum ListenerType { UNKNOWN = 0x00, ELASTICSEARCH = 0x01, + SYNC = 0x02, + ALL = 0x03, } (cpp.enum_strict) struct AddListenerReq { 1: common.GraphSpaceID space_id, 2: ListenerType type, 3: list hosts, + // To space name + 4: optional binary space_name, } struct RemoveListenerReq { @@ -957,8 +963,9 @@ struct RemoveListenerReq { 2: ListenerType type, } -struct ListListenerReq { +struct ListListenersReq { 1: common.GraphSpaceID space_id, + 2: ListenerType type, } struct ListenerInfo { @@ -966,14 +973,54 @@ struct ListenerInfo { 2: common.HostAddr host, 3: common.PartitionID part_id, 4: HostStatus status, + // To space name + 5: optional binary space_name, } -struct ListListenerResp { +struct ListListenersResp { 1: common.ErrorCode code, 2: common.HostAddr leader, 3: list listeners, } +struct ListListenerDrainersReq { + 1: common.GraphSpaceID space_id, +} + +struct DrainerInfo { + 1: common.HostAddr host, + 2: HostStatus status, +} + +struct ListListenerDrainersResp { + 1: common.ErrorCode code, + // Valid if code equals E_LEADER_CHANGED. + 2: common.HostAddr leader, + 3: map + (cpp.template = "std::unordered_map") drainerClients; + 4: binary space_name; +} + +struct AddDrainerReq { + 1: common.GraphSpaceID space_id, + 2: list hosts, +} + +struct RemoveDrainerReq { + 1: common.GraphSpaceID space_id, +} + +struct ListDrainersReq { + 1: common.GraphSpaceID space_id, +} + +struct ListDrainersResp { + 1: common.ErrorCode code, + // Valid if code equals E_LEADER_CHANGED. + 2: common.HostAddr leader, + 3: list drainers, +} + struct GetStatsReq { 1: common.GraphSpaceID space_id, } @@ -1028,31 +1075,36 @@ struct RestoreMetaReq { 2: list hosts, } -enum FTServiceType { +enum ExternalServiceType { ELASTICSEARCH = 0x01, + DRAINER = 0x02, + ALL = 0x03, } (cpp.enum_strict) -struct FTClient { +struct ServiceClient { 1: required common.HostAddr host, 2: optional binary user, 3: optional binary pwd, } -struct SignInFTServiceReq { - 1: FTServiceType type, - 2: list clients, +struct SignInServiceReq { + 1: ExternalServiceType type, + 2: list clients, } -struct SignOutFTServiceReq { +struct SignOutServiceReq { + 1: ExternalServiceType type, } -struct ListFTClientsReq { +struct ListServiceClientsReq { + 1: ExternalServiceType type, } -struct ListFTClientsResp { +struct ListServiceClientsResp { 1: common.ErrorCode code, 2: common.HostAddr leader, - 3: list clients, + 3: map> + (cpp.template = "std::unordered_map") clients, } struct FTIndex { @@ -1283,12 +1335,17 @@ service MetaService { ExecResp restoreMeta(1: RestoreMetaReq req); ExecResp addListener(1: AddListenerReq req); ExecResp removeListener(1: RemoveListenerReq req); - ListListenerResp listListener(1: ListListenerReq req); + ListListenersResp listListeners(1: ListListenersReq req); + ListListenerDrainersResp listListenerDrainers(1: ListListenerDrainersReq req); + + ExecResp addDrainer(1: AddDrainerReq req); + ExecResp removeDrainer(1: RemoveDrainerReq req); + ListDrainersResp listDrainers(1: ListDrainersReq req); GetStatsResp getStats(1: GetStatsReq req); - ExecResp signInFTService(1: SignInFTServiceReq req); - ExecResp signOutFTService(1: SignOutFTServiceReq req); - ListFTClientsResp listFTClients(1: ListFTClientsReq req); + ExecResp signInService(1: SignInServiceReq req); + ExecResp signOutService(1: SignOutServiceReq req); + ListServiceClientsResp listServiceClients(1: ListServiceClientsReq req); ExecResp createFTIndex(1: CreateFTIndexReq req); ExecResp dropFTIndex(1: DropFTIndexReq req); diff --git a/src/kvstore/CMakeLists.txt b/src/kvstore/CMakeLists.txt index ffcc13401ab..12bd08d0662 100644 --- a/src/kvstore/CMakeLists.txt +++ b/src/kvstore/CMakeLists.txt @@ -10,6 +10,7 @@ nebula_add_library( NebulaSnapshotManager.cpp RateLimiter.cpp plugins/elasticsearch/ESListener.cpp + plugins/sync/SyncListener.cpp ) nebula_add_library( diff --git a/src/kvstore/Listener.cpp b/src/kvstore/Listener.cpp index 628758579fa..ba18bdf5424 100644 --- a/src/kvstore/Listener.cpp +++ b/src/kvstore/Listener.cpp @@ -297,5 +297,6 @@ bool Listener::pursueLeaderDone() { "pursue leader : leaderCommitId={}, lastApplyLogId_={}", leaderCommitId_, lastApplyLogId_); return (leaderCommitId_ - lastApplyLogId_) <= FLAGS_listener_pursue_leader_threshold; } + } // namespace kvstore } // namespace nebula diff --git a/src/kvstore/ListenerFactory.h b/src/kvstore/ListenerFactory.h index b9168372bbf..a4306c6fb94 100644 --- a/src/kvstore/ListenerFactory.h +++ b/src/kvstore/ListenerFactory.h @@ -9,6 +9,7 @@ #include "kvstore/Listener.h" #include "kvstore/plugins/elasticsearch/ESListener.h" +#include "kvstore/plugins/sync/SyncListener.h" namespace nebula { namespace kvstore { @@ -19,6 +20,8 @@ class ListenerFactory { static std::shared_ptr createListener(meta::cpp2::ListenerType type, Args&&... args) { if (type == meta::cpp2::ListenerType::ELASTICSEARCH) { return std::make_shared(std::forward(args)...); + } else if (type == meta::cpp2::ListenerType::SYNC) { + return std::make_shared(std::forward(args)...); } LOG(FATAL) << "Should not reach here"; return nullptr; @@ -27,4 +30,5 @@ class ListenerFactory { } // namespace kvstore } // namespace nebula + #endif // KVSTORE_LISTENER_FACTORY_H_ diff --git a/src/kvstore/NebulaStore.cpp b/src/kvstore/NebulaStore.cpp index 250e263cb22..bbfde7d2c45 100644 --- a/src/kvstore/NebulaStore.cpp +++ b/src/kvstore/NebulaStore.cpp @@ -484,7 +484,8 @@ std::shared_ptr NebulaStore::newListener(GraphSpaceID spaceId, nullptr, nullptr, nullptr, - options_.schemaMan_); + options_.schemaMan_, + drainerClientMan_); raftService_->addPartition(listener); // add raft group as learner std::vector raftPeers; diff --git a/src/kvstore/NebulaStore.h b/src/kvstore/NebulaStore.h index 0b1715015ca..ef8a77ee533 100644 --- a/src/kvstore/NebulaStore.h +++ b/src/kvstore/NebulaStore.h @@ -14,6 +14,7 @@ #include "common/base/Base.h" #include "common/ssl/SSLConfig.h" #include "common/utils/Utils.h" +#include "interface/gen-cpp2/DrainerServiceAsyncClient.h" #include "interface/gen-cpp2/RaftexServiceAsyncClient.h" #include "kvstore/DiskManager.h" #include "kvstore/KVEngine.h" @@ -69,6 +70,9 @@ class NebulaStore : public KVStore, public Handler { clientMan_ = std::make_shared>( FLAGS_enable_ssl); + // TODO(pandasheep) Use ssl for drainer + drainerClientMan_ = + std::make_shared>(); } ~NebulaStore(); @@ -339,6 +343,8 @@ class NebulaStore : public KVStore, public Handler { std::shared_ptr raftService_; std::shared_ptr snapshot_; std::shared_ptr> clientMan_; + std::shared_ptr> + drainerClientMan_; std::shared_ptr diskMan_; folly::ConcurrentHashMap&)>> onNewPartAdded_; diff --git a/src/kvstore/plugins/CMakeLists.txt b/src/kvstore/plugins/CMakeLists.txt index aef92163b2b..0e06abf1083 100644 --- a/src/kvstore/plugins/CMakeLists.txt +++ b/src/kvstore/plugins/CMakeLists.txt @@ -1,2 +1,3 @@ nebula_add_subdirectory(hbase) nebula_add_subdirectory(elasticsearch) +nebula_add_subdirectory(sync) diff --git a/src/kvstore/plugins/elasticsearch/ESListener.cpp b/src/kvstore/plugins/elasticsearch/ESListener.cpp index 9697450d430..154a277776a 100644 --- a/src/kvstore/plugins/elasticsearch/ESListener.cpp +++ b/src/kvstore/plugins/elasticsearch/ESListener.cpp @@ -14,6 +14,7 @@ DECLARE_int32(ft_bulk_batch_size); namespace nebula { namespace kvstore { + void ESListener::init() { auto vRet = schemaMan_->getSpaceVidLen(spaceId_); if (!vRet.ok()) { @@ -21,7 +22,7 @@ void ESListener::init() { } vIdLen_ = vRet.value(); - auto cRet = schemaMan_->getFTClients(); + auto cRet = schemaMan_->getServiceClients(meta::cpp2::ExternalServiceType::ELASTICSEARCH); if (!cRet.ok() || cRet.value().empty()) { LOG(FATAL) << "elasticsearch clients error"; } diff --git a/src/kvstore/plugins/elasticsearch/ESListener.h b/src/kvstore/plugins/elasticsearch/ESListener.h index 6ece1b0f570..bf5dcbedbc6 100644 --- a/src/kvstore/plugins/elasticsearch/ESListener.h +++ b/src/kvstore/plugins/elasticsearch/ESListener.h @@ -9,12 +9,14 @@ #include "codec/RowReaderWrapper.h" #include "common/plugin/fulltext/FTStorageAdapter.h" +#include "interface/gen-cpp2/DrainerServiceAsyncClient.h" #include "kvstore/Listener.h" namespace nebula { namespace kvstore { using nebula::plugin::DocItem; +using DrainerClient = thrift::ThriftClientManager; class ESListener : public Listener { public: @@ -28,7 +30,8 @@ class ESListener : public Listener { std::shared_ptr snapshotMan, std::shared_ptr clientMan, std::shared_ptr diskMan, - meta::SchemaManager* schemaMan) + meta::SchemaManager* schemaMan, + std::shared_ptr drainerClientMan) : Listener(spaceId, partId, std::move(localAddr), @@ -41,6 +44,7 @@ class ESListener : public Listener { diskMan, schemaMan) { CHECK(!!schemaMan); + UNUSED(drainerClientMan); lastApplyLogFile_ = std::make_unique( folly::stringPrintf("%s/last_apply_log_%d", walPath.c_str(), partId)); } diff --git a/src/kvstore/plugins/sync/SyncListener.cpp b/src/kvstore/plugins/sync/SyncListener.cpp new file mode 100644 index 00000000000..6a1a04b6f97 --- /dev/null +++ b/src/kvstore/plugins/sync/SyncListener.cpp @@ -0,0 +1,28 @@ +/* Copyright (c) 2021 vesoft inc. All rights reserved. + * + * This source code is licensed under Apache 2.0 License, + * attached with Common Clause Condition 1.0, found in the LICENSES directory. + */ + +#include "kvstore/plugins/sync/SyncListener.h" + +#include + +#include "common/fs/FileUtils.h" +#include "common/time/WallClock.h" +#include "common/utils/NebulaKeyUtils.h" +#include "kvstore/LogEncoder.h" + +namespace nebula { +namespace kvstore { + +void SyncListener::init() {} + +bool SyncListener::apply(const std::vector&) { return true; } + +std::pair SyncListener::lastCommittedLogId() { return {0, 0}; } + +LogID SyncListener::lastApplyLogId() { return 0; } + +} // namespace kvstore +} // namespace nebula diff --git a/src/kvstore/plugins/sync/SyncListener.h b/src/kvstore/plugins/sync/SyncListener.h new file mode 100644 index 00000000000..583ad1e73c7 --- /dev/null +++ b/src/kvstore/plugins/sync/SyncListener.h @@ -0,0 +1,81 @@ +/* Copyright (c) 2021 vesoft inc. All rights reserved. + * + * This source code is licensed under Apache 2.0 License, + * attached with Common Clause Condition 1.0, found in the LICENSES directory. + */ + +#ifndef KVSTORE_PLUGINS_SYNC_SYNCLISTENER_H_ +#define KVSTORE_PLUGINS_SYNC_SYNCLISTENER_H_ + +#include +#include +#include + +#include "common/base/Base.h" +#include "common/thrift/ThriftClientManager.h" +#include "interface/gen-cpp2/DrainerServiceAsyncClient.h" +#include "interface/gen-cpp2/drainer_types.h" +#include "kvstore/Listener.h" + +namespace nebula { +namespace kvstore { + +using DrainerClient = thrift::ThriftClientManager; + +class SyncListener : public Listener { + public: + SyncListener(GraphSpaceID spaceId, + PartitionID partId, + HostAddr localAddr, + const std::string& walPath, + std::shared_ptr ioPool, + std::shared_ptr workers, + std::shared_ptr handlers, + std::shared_ptr snapshotMan, // nullptr + std::shared_ptr clientMan, // nullptr + std::shared_ptr diskMan, // nullptr + meta::SchemaManager* schemaMan, + std::shared_ptr drainerClientMan) + : Listener(spaceId, + partId, + std::move(localAddr), + walPath, + ioPool, + workers, + handlers, + snapshotMan, + clientMan, + diskMan, + schemaMan), + drainerClientMan_(drainerClientMan) { + CHECK(!!schemaMan); + CHECK(!!drainerClientMan_); + lastApplyLogFile_ = std::make_unique( + folly::stringPrintf("%s/last_apply_log_%d", walPath.c_str(), partId)); + } + + protected: + void init() override; + + bool apply(const std::vector& data) override; + + std::pair lastCommittedLogId() override; + + LogID lastApplyLogId() override; + + bool persist(LogID, TermID, LogID) override { + LOG(FATAL) << "Should not reach here"; + return true; + } + + private: + // File name, store lastCommittedlogId + lastCommittedtermId + lastApplyLogId + lastApplyLogId + std::unique_ptr lastApplyLogFile_{nullptr}; + + std::shared_ptr drainerClientMan_; +}; + +} // namespace kvstore +} // namespace nebula + +#endif // KVSTORE_PLUGINS_SYNC_SYNCLISTENER_H_ diff --git a/src/kvstore/test/CMakeLists.txt b/src/kvstore/test/CMakeLists.txt index 75a807479cb..42b1bbfa851 100644 --- a/src/kvstore/test/CMakeLists.txt +++ b/src/kvstore/test/CMakeLists.txt @@ -13,6 +13,7 @@ set(KVSTORE_TEST_LIBS $ $ $ + $ $ $ $ diff --git a/src/meta/CMakeLists.txt b/src/meta/CMakeLists.txt index 1336615c590..04c0960c716 100644 --- a/src/meta/CMakeLists.txt +++ b/src/meta/CMakeLists.txt @@ -35,7 +35,7 @@ nebula_add_library( processors/index/DropEdgeIndexProcessor.cpp processors/index/GetEdgeIndexProcessor.cpp processors/index/ListEdgeIndexesProcessor.cpp - processors/index/FTServiceProcessor.cpp + processors/service/ServiceProcessor.cpp processors/index/FTIndexProcessor.cpp processors/kv/GetProcessor.cpp processors/kv/MultiGetProcessor.cpp @@ -96,6 +96,7 @@ nebula_add_library( processors/zone/UpdateGroupProcessor.cpp processors/listener/ListenerProcessor.cpp processors/session/SessionManagerProcessor.cpp + processors/drainer/DrainerProcessor.cpp ) add_dependencies( @@ -141,6 +142,7 @@ set(meta_test_deps $ $ $ + $ $ $ $ diff --git a/src/meta/MetaServiceHandler.cpp b/src/meta/MetaServiceHandler.cpp index 6ad92a8e29f..1bb6ed0b0cd 100644 --- a/src/meta/MetaServiceHandler.cpp +++ b/src/meta/MetaServiceHandler.cpp @@ -22,12 +22,12 @@ #include "meta/processors/config/ListConfigsProcessor.h" #include "meta/processors/config/RegConfigProcessor.h" #include "meta/processors/config/SetConfigProcessor.h" +#include "meta/processors/drainer/DrainerProcessor.h" #include "meta/processors/index/CreateEdgeIndexProcessor.h" #include "meta/processors/index/CreateTagIndexProcessor.h" #include "meta/processors/index/DropEdgeIndexProcessor.h" #include "meta/processors/index/DropTagIndexProcessor.h" #include "meta/processors/index/FTIndexProcessor.h" -#include "meta/processors/index/FTServiceProcessor.h" #include "meta/processors/index/GetEdgeIndexProcessor.h" #include "meta/processors/index/GetTagIndexProcessor.h" #include "meta/processors/index/ListEdgeIndexesProcessor.h" @@ -62,6 +62,7 @@ #include "meta/processors/schema/GetTagProcessor.h" #include "meta/processors/schema/ListEdgesProcessor.h" #include "meta/processors/schema/ListTagsProcessor.h" +#include "meta/processors/service/ServiceProcessor.h" #include "meta/processors/session/SessionManagerProcessor.h" #include "meta/processors/user/AuthenticationProcessor.h" #include "meta/processors/zone/AddGroupProcessor.h" @@ -287,21 +288,21 @@ folly::Future MetaServiceHandler::future_listEdgeInde RETURN_FUTURE(processor); } -folly::Future MetaServiceHandler::future_signInFTService( - const cpp2::SignInFTServiceReq& req) { - auto* processor = SignInFTServiceProcessor::instance(kvstore_); +folly::Future MetaServiceHandler::future_signInService( + const cpp2::SignInServiceReq& req) { + auto* processor = SignInServiceProcessor::instance(kvstore_); RETURN_FUTURE(processor); } -folly::Future MetaServiceHandler::future_signOutFTService( - const cpp2::SignOutFTServiceReq& req) { - auto* processor = SignOutFTServiceProcessor::instance(kvstore_); +folly::Future MetaServiceHandler::future_signOutService( + const cpp2::SignOutServiceReq& req) { + auto* processor = SignOutServiceProcessor::instance(kvstore_); RETURN_FUTURE(processor); } -folly::Future MetaServiceHandler::future_listFTClients( - const cpp2::ListFTClientsReq& req) { - auto* processor = ListFTClientsProcessor::instance(kvstore_); +folly::Future MetaServiceHandler::future_listServiceClients( + const cpp2::ListServiceClientsReq& req) { + auto* processor = ListServiceClientsProcessor::instance(kvstore_); RETURN_FUTURE(processor); } @@ -515,9 +516,33 @@ folly::Future MetaServiceHandler::future_removeListener( RETURN_FUTURE(processor); } -folly::Future MetaServiceHandler::future_listListener( - const cpp2::ListListenerReq& req) { - auto* processor = ListListenerProcessor::instance(kvstore_); +folly::Future MetaServiceHandler::future_listListeners( + const cpp2::ListListenersReq& req) { + auto* processor = ListListenersProcessor::instance(kvstore_); + RETURN_FUTURE(processor); +} + +folly::Future MetaServiceHandler::future_listListenerDrainers( + const cpp2::ListListenerDrainersReq& req) { + auto* processor = ListListenerDrainersProcessor::instance(kvstore_); + RETURN_FUTURE(processor); +} + +folly::Future MetaServiceHandler::future_addDrainer( + const cpp2::AddDrainerReq& req) { + auto* processor = AddDrainerProcessor::instance(kvstore_); + RETURN_FUTURE(processor); +} + +folly::Future MetaServiceHandler::future_removeDrainer( + const cpp2::RemoveDrainerReq& req) { + auto* processor = RemoveDrainerProcessor::instance(kvstore_); + RETURN_FUTURE(processor); +} + +folly::Future MetaServiceHandler::future_listDrainers( + const cpp2::ListDrainersReq& req) { + auto* processor = ListDrainersProcessor::instance(kvstore_); RETURN_FUTURE(processor); } diff --git a/src/meta/MetaServiceHandler.h b/src/meta/MetaServiceHandler.h index 0d8093846c6..672c91fd96d 100644 --- a/src/meta/MetaServiceHandler.h +++ b/src/meta/MetaServiceHandler.h @@ -113,14 +113,12 @@ class MetaServiceHandler final : public cpp2::MetaServiceSvIf { folly::Future future_listEdgeIndexStatus( const cpp2::ListIndexStatusReq& req) override; - folly::Future future_signInFTService( - const cpp2::SignInFTServiceReq& req) override; + folly::Future future_signInService(const cpp2::SignInServiceReq& req) override; - folly::Future future_signOutFTService( - const cpp2::SignOutFTServiceReq& req) override; + folly::Future future_signOutService(const cpp2::SignOutServiceReq& req) override; - folly::Future future_listFTClients( - const cpp2::ListFTClientsReq& req) override; + folly::Future future_listServiceClients( + const cpp2::ListServiceClientsReq& req) override; folly::Future future_createFTIndex(const cpp2::CreateFTIndexReq& req) override; @@ -214,8 +212,19 @@ class MetaServiceHandler final : public cpp2::MetaServiceSvIf { folly::Future future_removeListener(const cpp2::RemoveListenerReq& req) override; - folly::Future future_listListener( - const cpp2::ListListenerReq& req) override; + folly::Future future_listListeners( + const cpp2::ListListenersReq& req) override; + + folly::Future future_listListenerDrainers( + const cpp2::ListListenerDrainersReq& req) override; + + // drainer + folly::Future future_addDrainer(const cpp2::AddDrainerReq& req) override; + + folly::Future future_removeDrainer(const cpp2::RemoveDrainerReq& req) override; + + folly::Future future_listDrainers( + const cpp2::ListDrainersReq& req) override; folly::Future future_restoreMeta(const cpp2::RestoreMetaReq& req) override; diff --git a/src/meta/processors/BaseProcessor-inl.h b/src/meta/processors/BaseProcessor-inl.h index a947aa07aa5..ac34c0ea5a1 100644 --- a/src/meta/processors/BaseProcessor-inl.h +++ b/src/meta/processors/BaseProcessor-inl.h @@ -599,7 +599,8 @@ ErrorOr BaseProcessor::getZoneId( template nebula::cpp2::ErrorCode BaseProcessor::listenerExist(GraphSpaceID space, - cpp2::ListenerType type) { + cpp2::ListenerType& type, + std::vector hosts) { folly::SharedMutex::ReadHolder rHolder(LockUtils::listenerLock()); const auto& prefix = MetaKeyUtils::listenerPrefix(space, type); auto ret = doPrefix(prefix); @@ -608,10 +609,50 @@ nebula::cpp2::ErrorCode BaseProcessor::listenerExist(GraphSpaceID space, } auto iterRet = nebula::value(ret).get(); - if (!iterRet->valid()) { - return nebula::cpp2::ErrorCode::E_LISTENER_NOT_FOUND; + if (iterRet->valid()) { + return nebula::cpp2::ErrorCode::SUCCEEDED; } - return nebula::cpp2::ErrorCode::SUCCEEDED; + + if (!hosts.empty()) { + // Check the listener host, in the same space, a HostAddr can only be + // used as an es listener or a sync listener, not at the same time. + const auto& lPrefix = MetaKeyUtils::listenerPrefix(space); + ret = doPrefix(lPrefix); + if (!nebula::ok(ret)) { + return nebula::error(ret); + } + + auto iter = nebula::value(ret).get(); + std::unordered_set listenerHosts; + if (iter->valid()) { + listenerHosts.emplace(MetaKeyUtils::parseListenerHost(iter->val())); + iter->next(); + } + if (!listenerHosts.empty()) { + for (auto& host : hosts) { + if (std::find(listenerHosts.begin(), listenerHosts.end(), host) != listenerHosts.end()) { + return nebula::cpp2::ErrorCode::E_LISTENER_CONFLICT; + } + } + } + } + return nebula::cpp2::ErrorCode::E_LISTENER_NOT_FOUND; +} + +template +nebula::cpp2::ErrorCode BaseProcessor::drainerExist(GraphSpaceID space) { + folly::SharedMutex::ReadHolder rHolder(LockUtils::drainerLock()); + auto dKey = MetaKeyUtils::drainerKey(space); + auto ret = doGet(std::move(dKey)); + if (nebula::ok(ret)) { + return nebula::cpp2::ErrorCode::SUCCEEDED; + } + + auto retCode = nebula::error(ret); + if (retCode == nebula::cpp2::ErrorCode::E_KEY_NOT_FOUND) { + retCode = nebula::cpp2::ErrorCode::E_DRAINER_NOT_FOUND; + } + return retCode; } } // namespace meta diff --git a/src/meta/processors/BaseProcessor.h b/src/meta/processors/BaseProcessor.h index 40ba29fe438..c29d3c6a064 100644 --- a/src/meta/processors/BaseProcessor.h +++ b/src/meta/processors/BaseProcessor.h @@ -246,7 +246,12 @@ class BaseProcessor { ErrorOr getZoneId(const std::string& zoneName); - nebula::cpp2::ErrorCode listenerExist(GraphSpaceID space, cpp2::ListenerType type); + nebula::cpp2::ErrorCode listenerExist(GraphSpaceID space, + cpp2::ListenerType& type, + std::vector hosts = {}); + + // Used in the slave cluster + nebula::cpp2::ErrorCode drainerExist(GraphSpaceID space); // A direct value of true means that data will not be written to follow via // the raft protocol, but will be written directly to local disk diff --git a/src/meta/processors/Common.h b/src/meta/processors/Common.h index f897cbd48a9..5d11fa6d514 100644 --- a/src/meta/processors/Common.h +++ b/src/meta/processors/Common.h @@ -29,7 +29,7 @@ class LockUtils { GENERATE_LOCK(edge); GENERATE_LOCK(tagIndex); GENERATE_LOCK(edgeIndex); - GENERATE_LOCK(fulltextServices); + GENERATE_LOCK(service); GENERATE_LOCK(fulltextIndex); GENERATE_LOCK(user); GENERATE_LOCK(config); @@ -37,6 +37,7 @@ class LockUtils { GENERATE_LOCK(group); GENERATE_LOCK(zone); GENERATE_LOCK(listener); + GENERATE_LOCK(drainer); GENERATE_LOCK(session); #undef GENERATE_LOCK diff --git a/src/meta/processors/drainer/DrainerProcessor.cpp b/src/meta/processors/drainer/DrainerProcessor.cpp new file mode 100644 index 00000000000..77ca84ef4e4 --- /dev/null +++ b/src/meta/processors/drainer/DrainerProcessor.cpp @@ -0,0 +1,144 @@ +/* Copyright (c) 2021 vesoft inc. All rights reserved. + * + * This source code is licensed under Apache 2.0 License, + * attached with Common Clause Condition 1.0, found in the LICENSES directory. + */ + +#include "meta/processors/drainer/DrainerProcessor.h" + +#include "meta/ActiveHostsMan.h" + +DECLARE_int32(heartbeat_interval_secs); +DECLARE_uint32(expired_time_factor); + +namespace nebula { +namespace meta { + +// For Slave cluster +void AddDrainerProcessor::process(const cpp2::AddDrainerReq& req) { + auto space = req.get_space_id(); + CHECK_SPACE_ID_AND_RETURN(space); + const auto& hosts = req.get_hosts(); + auto ret = drainerExist(space); + if (ret != nebula::cpp2::ErrorCode::E_DRAINER_NOT_FOUND) { + if (ret == nebula::cpp2::ErrorCode::SUCCEEDED) { + LOG(ERROR) << "Add drainer failed, drainer already exists in space " << space; + ret = nebula::cpp2::ErrorCode::E_EXISTED; + } else { + LOG(ERROR) << "Add drainer failed, error: " << apache::thrift::util::enumNameSafe(ret); + } + handleErrorCode(ret); + onFinished(); + return; + } + + folly::SharedMutex::WriteHolder wHolder(LockUtils::drainerLock()); + folly::SharedMutex::ReadHolder rHolder(LockUtils::spaceLock()); + + // For the slave cluster, after the drainer server is started, + // it will send a heartbeat to the meta. Therefore, the drainer server added + // under this space must be in the global drainer server + auto activeHostsRet = ActiveHostsMan::getActiveHosts( + kvstore_, FLAGS_heartbeat_interval_secs * FLAGS_expired_time_factor, cpp2::HostRole::DRAINER); + if (!nebula::ok(activeHostsRet)) { + handleErrorCode(nebula::error(activeHostsRet)); + onFinished(); + return; + } + + auto activeDrainerHosts = std::move(nebula::value(activeHostsRet)); + for (auto& host : hosts) { + if (std::find(activeDrainerHosts.begin(), activeDrainerHosts.end(), host) == + activeDrainerHosts.end()) { + LOG(ERROR) << "Add drainer failed, host " << host << " is not active drainer"; + handleErrorCode(nebula::cpp2::ErrorCode::E_NO_VALID_DRAINER); + onFinished(); + return; + } + } + + std::vector data; + data.emplace_back(MetaKeyUtils::drainerKey(space), MetaKeyUtils::drainerVal(hosts)); + + LOG(INFO) << "Add drainer, spaceId " << space; + doSyncPutAndUpdate(std::move(data)); +} + +void RemoveDrainerProcessor::process(const cpp2::RemoveDrainerReq& req) { + auto space = req.get_space_id(); + CHECK_SPACE_ID_AND_RETURN(space); + + folly::SharedMutex::WriteHolder wHolder(LockUtils::drainerLock()); + const auto& drainerKey = MetaKeyUtils::drainerKey(space); + auto ret = doGet(drainerKey); + if (!nebula::ok(ret)) { + auto retCode = nebula::error(ret); + if (retCode == nebula::cpp2::ErrorCode::E_KEY_NOT_FOUND) { + retCode = nebula::cpp2::ErrorCode::E_DRAINER_NOT_FOUND; + LOG(ERROR) << "Remove drainer failed, drainer not exists."; + } else { + LOG(ERROR) << "Remove drainer failed, error: " << apache::thrift::util::enumNameSafe(retCode); + } + handleErrorCode(retCode); + onFinished(); + return; + } + + std::vector keys; + keys.emplace_back(drainerKey); + + LOG(INFO) << "Rmove drainer, spaceId " << space; + doSyncMultiRemoveAndUpdate(std::move(keys)); +} + +void ListDrainersProcessor::process(const cpp2::ListDrainersReq& req) { + auto space = req.get_space_id(); + CHECK_SPACE_ID_AND_RETURN(space); + folly::SharedMutex::ReadHolder rHolder(LockUtils::drainerLock()); + + const auto& drainerKey = MetaKeyUtils::drainerKey(space); + auto ret = doGet(drainerKey); + if (!nebula::ok(ret) && nebula::error(ret) != nebula::cpp2::ErrorCode::E_KEY_NOT_FOUND) { + auto retCode = nebula::error(ret); + LOG(ERROR) << "List drainer failed, error: " << apache::thrift::util::enumNameSafe(retCode); + handleErrorCode(retCode); + onFinished(); + return; + } + + // When the drainer does not exist, it returns success, but the drainers is empty. + std::vector drainers; + if (nebula::ok(ret)) { + auto drainerHosts = MetaKeyUtils::parseDrainerHosts(nebula::value(ret)); + + // For the slave cluster, meta knows the survival status of the drainer + auto activeHostsRet = + ActiveHostsMan::getActiveHosts(kvstore_, + FLAGS_heartbeat_interval_secs * FLAGS_expired_time_factor, + cpp2::HostRole::DRAINER); + if (!nebula::ok(activeHostsRet)) { + handleErrorCode(nebula::error(activeHostsRet)); + onFinished(); + return; + } + + auto activeHosts = std::move(nebula::value(activeHostsRet)); + for (auto& host : drainerHosts) { + cpp2::DrainerInfo drainer; + drainer.set_host(host); + if (std::find(activeHosts.begin(), activeHosts.end(), *drainer.host_ref()) != + activeHosts.end()) { + drainer.set_status(cpp2::HostStatus::ONLINE); + } else { + drainer.set_status(cpp2::HostStatus::OFFLINE); + } + drainers.emplace_back(std::move(drainer)); + } + } + resp_.set_drainers(std::move(drainers)); + handleErrorCode(nebula::cpp2::ErrorCode::SUCCEEDED); + onFinished(); +} + +} // namespace meta +} // namespace nebula diff --git a/src/meta/processors/drainer/DrainerProcessor.h b/src/meta/processors/drainer/DrainerProcessor.h new file mode 100644 index 00000000000..28720b5af85 --- /dev/null +++ b/src/meta/processors/drainer/DrainerProcessor.h @@ -0,0 +1,57 @@ +/* Copyright (c) 2021 vesoft inc. All rights reserved. + * + * This source code is licensed under Apache 2.0 License, + * attached with Common Clause Condition 1.0, found in the LICENSES directory. + */ + +#ifndef META_DRAINERROCESSOR_H_ +#define META_DRAINERROCESSOR_H_ + +#include "meta/processors/BaseProcessor.h" + +namespace nebula { +namespace meta { + +class AddDrainerProcessor : public BaseProcessor { + public: + static AddDrainerProcessor* instance(kvstore::KVStore* kvstore) { + return new AddDrainerProcessor(kvstore); + } + + void process(const cpp2::AddDrainerReq& req); + + private: + explicit AddDrainerProcessor(kvstore::KVStore* kvstore) + : BaseProcessor(kvstore) {} +}; + +class RemoveDrainerProcessor : public BaseProcessor { + public: + static RemoveDrainerProcessor* instance(kvstore::KVStore* kvstore) { + return new RemoveDrainerProcessor(kvstore); + } + + void process(const cpp2::RemoveDrainerReq& req); + + private: + explicit RemoveDrainerProcessor(kvstore::KVStore* kvstore) + : BaseProcessor(kvstore) {} +}; + +class ListDrainersProcessor : public BaseProcessor { + public: + static ListDrainersProcessor* instance(kvstore::KVStore* kvstore) { + return new ListDrainersProcessor(kvstore); + } + + void process(const cpp2::ListDrainersReq& req); + + private: + explicit ListDrainersProcessor(kvstore::KVStore* kvstore) + : BaseProcessor(kvstore) {} +}; + +} // namespace meta +} // namespace nebula + +#endif // META_DRAINERROCESSOR_H_ diff --git a/src/meta/processors/index/FTServiceProcessor.cpp b/src/meta/processors/index/FTServiceProcessor.cpp deleted file mode 100644 index f3116d97aee..00000000000 --- a/src/meta/processors/index/FTServiceProcessor.cpp +++ /dev/null @@ -1,81 +0,0 @@ -/* Copyright (c) 2020 vesoft inc. All rights reserved. - * - * This source code is licensed under Apache 2.0 License, - * attached with Common Clause Condition 1.0, found in the LICENSES directory. - */ - -#include "meta/processors/index/FTServiceProcessor.h" - -namespace nebula { -namespace meta { - -void SignInFTServiceProcessor::process(const cpp2::SignInFTServiceReq& req) { - folly::SharedMutex::WriteHolder wHolder(LockUtils::fulltextServicesLock()); - auto serviceKey = MetaKeyUtils::fulltextServiceKey(); - auto ret = doGet(serviceKey); - if (nebula::ok(ret)) { - LOG(ERROR) << "Fulltext already exists."; - handleErrorCode(nebula::cpp2::ErrorCode::E_EXISTED); - onFinished(); - return; - } else { - auto retCode = nebula::error(ret); - if (retCode != nebula::cpp2::ErrorCode::E_KEY_NOT_FOUND) { - LOG(ERROR) << "Sign in fulltext failed, error: " - << apache::thrift::util::enumNameSafe(retCode); - handleErrorCode(retCode); - onFinished(); - return; - } - } - - std::vector data; - data.emplace_back(std::move(serviceKey), - MetaKeyUtils::fulltextServiceVal(req.get_type(), req.get_clients())); - doSyncPutAndUpdate(std::move(data)); -} - -void SignOutFTServiceProcessor::process(const cpp2::SignOutFTServiceReq&) { - folly::SharedMutex::WriteHolder wHolder(LockUtils::fulltextServicesLock()); - auto serviceKey = MetaKeyUtils::fulltextServiceKey(); - auto ret = doGet(serviceKey); - if (!nebula::ok(ret)) { - auto retCode = nebula::error(ret); - if (retCode == nebula::cpp2::ErrorCode::E_KEY_NOT_FOUND) { - LOG(ERROR) << "Sign out fulltext failed, Fulltext not exists."; - } else { - LOG(ERROR) << "Sign out fulltext failed, error: " - << apache::thrift::util::enumNameSafe(retCode); - } - handleErrorCode(retCode); - onFinished(); - return; - } - - doSyncMultiRemoveAndUpdate({std::move(serviceKey)}); -} - -void ListFTClientsProcessor::process(const cpp2::ListFTClientsReq&) { - folly::SharedMutex::ReadHolder rHolder(LockUtils::fulltextServicesLock()); - const auto& prefix = MetaKeyUtils::fulltextServiceKey(); - auto iterRet = doPrefix(prefix); - if (!nebula::ok(iterRet)) { - auto retCode = nebula::error(iterRet); - LOG(ERROR) << "List fulltext failed, error: " << apache::thrift::util::enumNameSafe(retCode); - handleErrorCode(retCode); - onFinished(); - return; - } - - auto iter = nebula::value(iterRet).get(); - std::vector clients; - if (iter->valid()) { - clients = MetaKeyUtils::parseFTClients(iter->val()); - } - resp_.set_clients(std::move(clients)); - handleErrorCode(nebula::cpp2::ErrorCode::SUCCEEDED); - onFinished(); -} - -} // namespace meta -} // namespace nebula diff --git a/src/meta/processors/index/FTServiceProcessor.h b/src/meta/processors/index/FTServiceProcessor.h deleted file mode 100644 index c9124ca9767..00000000000 --- a/src/meta/processors/index/FTServiceProcessor.h +++ /dev/null @@ -1,55 +0,0 @@ -/* Copyright (c) 2020 vesoft inc. All rights reserved. - * - * This source code is licensed under Apache 2.0 License, - * attached with Common Clause Condition 1.0, found in the LICENSES directory. - */ -#ifndef META_FTISERVICEPROCESSOR_H_ -#define META_FTISERVICEPROCESSOR_H_ - -#include "meta/processors/BaseProcessor.h" - -namespace nebula { -namespace meta { - -class SignInFTServiceProcessor : public BaseProcessor { - public: - static SignInFTServiceProcessor* instance(kvstore::KVStore* kvstore) { - return new SignInFTServiceProcessor(kvstore); - } - - void process(const cpp2::SignInFTServiceReq& req); - - private: - explicit SignInFTServiceProcessor(kvstore::KVStore* kvstore) - : BaseProcessor(kvstore) {} -}; - -class SignOutFTServiceProcessor : public BaseProcessor { - public: - static SignOutFTServiceProcessor* instance(kvstore::KVStore* kvstore) { - return new SignOutFTServiceProcessor(kvstore); - } - - void process(const cpp2::SignOutFTServiceReq& req); - - private: - explicit SignOutFTServiceProcessor(kvstore::KVStore* kvstore) - : BaseProcessor(kvstore) {} -}; - -class ListFTClientsProcessor : public BaseProcessor { - public: - static ListFTClientsProcessor* instance(kvstore::KVStore* kvstore) { - return new ListFTClientsProcessor(kvstore); - } - - void process(const cpp2::ListFTClientsReq& req); - - private: - explicit ListFTClientsProcessor(kvstore::KVStore* kvstore) - : BaseProcessor(kvstore) {} -}; -} // namespace meta -} // namespace nebula - -#endif // META_FTISERVICEPROCESSOR_H_ diff --git a/src/meta/processors/listener/ListenerProcessor.cpp b/src/meta/processors/listener/ListenerProcessor.cpp index 01f8bcd1eb5..524f34b5869 100644 --- a/src/meta/processors/listener/ListenerProcessor.cpp +++ b/src/meta/processors/listener/ListenerProcessor.cpp @@ -18,8 +18,17 @@ void AddListenerProcessor::process(const cpp2::AddListenerReq& req) { auto space = req.get_space_id(); CHECK_SPACE_ID_AND_RETURN(space); auto type = req.get_type(); + + if (type == cpp2::ListenerType::SYNC && !req.space_name_ref().has_value()) { + LOG(ERROR) << "Add listener failed, invalid listener parameter."; + auto ret = nebula::cpp2::ErrorCode::E_INVALID_PARM; + handleErrorCode(ret); + onFinished(); + return; + } + const auto& hosts = req.get_hosts(); - auto ret = listenerExist(space, type); + auto ret = listenerExist(space, type, hosts); if (ret != nebula::cpp2::ErrorCode::E_LISTENER_NOT_FOUND) { if (ret == nebula::cpp2::ErrorCode::SUCCEEDED) { LOG(ERROR) << "Add listener failed, listener already exists."; @@ -51,11 +60,52 @@ void AddListenerProcessor::process(const cpp2::AddListenerReq& req) { parts.emplace_back(MetaKeyUtils::parsePartKeyPartId(iter->key())); iter->next(); } + + // When the type is sync, the drainer must have been registered + // Because each sync listener part uses a fixed drainer + std::vector drainerClients; + if (type == cpp2::ListenerType::SYNC) { + const auto& serviceKey = MetaKeyUtils::serviceKey(cpp2::ExternalServiceType::DRAINER); + auto sRet = doGet(serviceKey); + if (!nebula::ok(sRet)) { + auto retCode = nebula::error(sRet); + LOG(ERROR) << "List drainer service failed, error: " + << apache::thrift::util::enumNameSafe(retCode); + handleErrorCode(retCode); + onFinished(); + return; + } + + drainerClients = MetaKeyUtils::parseServiceClients(nebula::value(sRet)); + if (drainerClients.size() == 0) { + LOG(ERROR) << "Drainer service clients not exit."; + handleErrorCode(nebula::cpp2::ErrorCode::E_SERVICE_NOT_FOUND); + onFinished(); + return; + } + } + std::vector data; + // Sync listener tospace name + std::string spaceName; + if (type == cpp2::ListenerType::SYNC) { + spaceName = *req.space_name_ref(); + } + for (size_t i = 0; i < parts.size(); i++) { data.emplace_back(MetaKeyUtils::listenerKey(space, parts[i], type), - MetaKeyUtils::serializeHostAddr(hosts[i % hosts.size()])); + MetaKeyUtils::listenerVal(hosts[i % hosts.size()], spaceName)); + + // use the specified drainer client + // TODO(pandasheep) drainer maybe use user and passwd + if (type == cpp2::ListenerType::SYNC) { + auto key = MetaKeyUtils::listenerDrainerKey(space, parts[i]); + auto drainerClient = drainerClients[i % drainerClients.size()]; + auto val = MetaKeyUtils::listenerDrainerVal(drainerClient.get_host(), spaceName); + data.emplace_back(std::move(key), std::move(val)); + } } + doSyncPutAndUpdate(std::move(data)); } @@ -92,14 +142,42 @@ void RemoveListenerProcessor::process(const cpp2::RemoveListenerReq& req) { keys.emplace_back(iter->key()); iter->next(); } + + // remove listener drainer data + if (type == cpp2::ListenerType::SYNC) { + auto listenerDrainerPrefix = MetaKeyUtils::listenerDrainerPrefix(space); + auto literRet = doPrefix(listenerDrainerPrefix); + if (!nebula::ok(literRet)) { + auto retCode = nebula::error(literRet); + LOG(ERROR) << "List drainer client failed, error: " + << apache::thrift::util::enumNameSafe(retCode); + handleErrorCode(retCode); + onFinished(); + return; + } + auto liter = nebula::value(literRet).get(); + while (liter->valid()) { + keys.emplace_back(liter->key()); + liter->next(); + } + } + doSyncMultiRemoveAndUpdate(std::move(keys)); } -void ListListenerProcessor::process(const cpp2::ListListenerReq& req) { +void ListListenersProcessor::process(const cpp2::ListListenersReq& req) { auto space = req.get_space_id(); + auto type = req.get_type(); CHECK_SPACE_ID_AND_RETURN(space); folly::SharedMutex::ReadHolder rHolder(LockUtils::listenerLock()); - const auto& prefix = MetaKeyUtils::listenerPrefix(space); + + std::string prefix; + if (type == cpp2::ListenerType::ALL) { + prefix = MetaKeyUtils::listenerPrefix(space); + } else { + prefix = MetaKeyUtils::listenerPrefix(space, type); + } + auto iterRet = doPrefix(prefix); if (!nebula::ok(iterRet)) { auto retCode = nebula::error(iterRet); @@ -119,13 +197,16 @@ void ListListenerProcessor::process(const cpp2::ListListenerReq& req) { return; } - std::vector listeners; + std::vector listeners; auto activeHosts = std::move(nebula::value(activeHostsRet)); auto iter = nebula::value(iterRet).get(); while (iter->valid()) { cpp2::ListenerInfo listener; + if (type == cpp2::ListenerType::SYNC) { + listener.set_space_name(MetaKeyUtils::parseListenerSpacename(iter->val())); + } listener.set_type(MetaKeyUtils::parseListenerType(iter->key())); - listener.set_host(MetaKeyUtils::deserializeHostAddr(iter->val())); + listener.set_host(MetaKeyUtils::parseListenerHost(iter->val())); listener.set_part_id(MetaKeyUtils::parseListenerPart(iter->key())); if (std::find(activeHosts.begin(), activeHosts.end(), *listener.host_ref()) != activeHosts.end()) { @@ -141,5 +222,38 @@ void ListListenerProcessor::process(const cpp2::ListListenerReq& req) { onFinished(); } +// For the master cluster, the survival status of the drainer cluster is unknown +void ListListenerDrainersProcessor::process(const cpp2::ListListenerDrainersReq& req) { + auto space = req.get_space_id(); + CHECK_SPACE_ID_AND_RETURN(space); + folly::SharedMutex::ReadHolder rHolder(LockUtils::listenerLock()); + + auto prefix = MetaKeyUtils::listenerDrainerPrefix(space); + auto iterRet = doPrefix(prefix); + if (!nebula::ok(iterRet)) { + auto retCode = nebula::error(iterRet); + LOG(ERROR) << "List listener drainer failed, error: " + << apache::thrift::util::enumNameSafe(retCode); + handleErrorCode(retCode); + onFinished(); + return; + } + + std::unordered_map drainerClients; + auto iter = nebula::value(iterRet).get(); + if (iter->valid()) { + resp_.set_space_name(MetaKeyUtils::parseListenerDrainerSpacename(iter->val())); + } + while (iter->valid()) { + auto partId = MetaKeyUtils::parseListenerDrainerPart(iter->key()); + auto host = MetaKeyUtils::parseListenerDrainerHost(iter->val()); + drainerClients.emplace(partId, std::move(host)); + iter->next(); + } + resp_.set_drainerClients(std::move(drainerClients)); + handleErrorCode(nebula::cpp2::ErrorCode::SUCCEEDED); + onFinished(); +} + } // namespace meta } // namespace nebula diff --git a/src/meta/processors/listener/ListenerProcessor.h b/src/meta/processors/listener/ListenerProcessor.h index 9ce0efe89cd..60303f01df9 100644 --- a/src/meta/processors/listener/ListenerProcessor.h +++ b/src/meta/processors/listener/ListenerProcessor.h @@ -38,17 +38,30 @@ class RemoveListenerProcessor : public BaseProcessor { : BaseProcessor(kvstore) {} }; -class ListListenerProcessor : public BaseProcessor { +class ListListenersProcessor : public BaseProcessor { public: - static ListListenerProcessor* instance(kvstore::KVStore* kvstore) { - return new ListListenerProcessor(kvstore); + static ListListenersProcessor* instance(kvstore::KVStore* kvstore) { + return new ListListenersProcessor(kvstore); } - void process(const cpp2::ListListenerReq& req); + void process(const cpp2::ListListenersReq& req); private: - explicit ListListenerProcessor(kvstore::KVStore* kvstore) - : BaseProcessor(kvstore) {} + explicit ListListenersProcessor(kvstore::KVStore* kvstore) + : BaseProcessor(kvstore) {} +}; + +class ListListenerDrainersProcessor : public BaseProcessor { + public: + static ListListenerDrainersProcessor* instance(kvstore::KVStore* kvstore) { + return new ListListenerDrainersProcessor(kvstore); + } + + void process(const cpp2::ListListenerDrainersReq& req); + + private: + explicit ListListenerDrainersProcessor(kvstore::KVStore* kvstore) + : BaseProcessor(kvstore) {} }; } // namespace meta diff --git a/src/meta/processors/parts/DropSpaceProcessor.cpp b/src/meta/processors/parts/DropSpaceProcessor.cpp index a459dbd11cb..b1b1c9143f2 100644 --- a/src/meta/processors/parts/DropSpaceProcessor.cpp +++ b/src/meta/processors/parts/DropSpaceProcessor.cpp @@ -95,11 +95,33 @@ void DropSpaceProcessor::process(const cpp2::DropSpaceReq& req) { lstIter->next(); } - // 5. Delete related statis data - auto statiskey = MetaKeyUtils::statsKey(spaceId); - deleteKeys.emplace_back(statiskey); + // 5. Delete listener drainer meta data + auto lstDrainerPrefix = MetaKeyUtils::listenerDrainerPrefix(spaceId); + auto lstDrainerRet = doPrefix(lstDrainerPrefix); + if (!nebula::ok(lstDrainerRet)) { + auto retCode = nebula::error(lstDrainerRet); + LOG(ERROR) << "Drop space Failed, space " << spaceName + << " error: " << apache::thrift::util::enumNameSafe(retCode); + handleErrorCode(retCode); + onFinished(); + return; + } + + auto lstDrainerIter = nebula::value(lstDrainerRet).get(); + while (lstDrainerIter->valid()) { + deleteKeys.emplace_back(lstDrainerIter->key()); + lstDrainerIter->next(); + } + + // 6.Delete related drainer meta data + auto drainerKey = MetaKeyUtils::drainerKey(spaceId); + deleteKeys.emplace_back(drainerKey); + + // 7. Delete related statis data + auto statskey = MetaKeyUtils::statsKey(spaceId); + deleteKeys.emplace_back(statskey); - // 6. Delte related fulltext index meta data + // 8. Delte related fulltext index meta data auto ftPrefix = MetaKeyUtils::fulltextIndexPrefix(); auto ftRet = doPrefix(ftPrefix); if (!nebula::ok(ftRet)) { @@ -119,7 +141,7 @@ void DropSpaceProcessor::process(const cpp2::DropSpaceReq& req) { ftIter->next(); } - // 7. Delete local_id meta data + // 9. Delete local_id meta data auto localIdkey = MetaKeyUtils::localIdKey(spaceId); deleteKeys.emplace_back(localIdkey); diff --git a/src/meta/processors/parts/ListHostsProcessor.cpp b/src/meta/processors/parts/ListHostsProcessor.cpp index c812b59c0e3..0bad1df13b2 100644 --- a/src/meta/processors/parts/ListHostsProcessor.cpp +++ b/src/meta/processors/parts/ListHostsProcessor.cpp @@ -27,6 +27,8 @@ static cpp2::HostRole toHostRole(cpp2::ListHostType type) { return cpp2::HostRole::META; case cpp2::ListHostType::STORAGE: return cpp2::HostRole::STORAGE; + case cpp2::ListHostType::DRAINER: + return cpp2::HostRole::DRAINER; default: return cpp2::HostRole::UNKNOWN; } diff --git a/src/meta/processors/service/ServiceProcessor.cpp b/src/meta/processors/service/ServiceProcessor.cpp new file mode 100644 index 00000000000..fab6c45cb68 --- /dev/null +++ b/src/meta/processors/service/ServiceProcessor.cpp @@ -0,0 +1,105 @@ +/* Copyright (c) 2021 vesoft inc. All rights reserved. + * + * This source code is licensed under Apache 2.0 License, + * attached with Common Clause Condition 1.0, found in the LICENSES directory. + */ + +#include "meta/processors/service/ServiceProcessor.h" + +namespace nebula { +namespace meta { + +void SignInServiceProcessor::process(const cpp2::SignInServiceReq& req) { + folly::SharedMutex::WriteHolder wHolder(LockUtils::serviceLock()); + auto type = req.get_type(); + + auto serviceKey = MetaKeyUtils::serviceKey(type); + auto ret = doGet(serviceKey); + if (nebula::ok(ret)) { + LOG(ERROR) << "Service already exists."; + handleErrorCode(nebula::cpp2::ErrorCode::E_EXISTED); + onFinished(); + return; + } else { + auto retCode = nebula::error(ret); + if (retCode != nebula::cpp2::ErrorCode::E_KEY_NOT_FOUND) { + LOG(ERROR) << "Sign in service failed, error: " + << apache::thrift::util::enumNameSafe(retCode); + handleErrorCode(retCode); + onFinished(); + return; + } + } + + std::vector data; + data.emplace_back(std::move(serviceKey), MetaKeyUtils::serviceVal(req.get_clients())); + doSyncPutAndUpdate(std::move(data)); +} + +void SignOutServiceProcessor::process(const cpp2::SignOutServiceReq& req) { + folly::SharedMutex::WriteHolder wHolder(LockUtils::serviceLock()); + auto type = req.get_type(); + + auto serviceKey = MetaKeyUtils::serviceKey(type); + auto ret = doGet(serviceKey); + if (!nebula::ok(ret)) { + auto retCode = nebula::error(ret); + if (retCode == nebula::cpp2::ErrorCode::E_KEY_NOT_FOUND) { + LOG(ERROR) << "Sign out service failed, service not exists."; + } else { + LOG(ERROR) << "Sign out service failed, error: " + << apache::thrift::util::enumNameSafe(retCode); + } + handleErrorCode(retCode); + onFinished(); + return; + } + + doSyncMultiRemoveAndUpdate({std::move(serviceKey)}); +} + +void ListServiceClientsProcessor::process(const cpp2::ListServiceClientsReq& req) { + folly::SharedMutex::ReadHolder rHolder(LockUtils::serviceLock()); + auto type = req.get_type(); + + std::unordered_map> serviceClients; + if (type == cpp2::ExternalServiceType::ALL) { + const auto& prefix = MetaKeyUtils::servicePrefix(); + auto iterRet = doPrefix(prefix); + if (!nebula::ok(iterRet)) { + auto retCode = nebula::error(iterRet); + LOG(ERROR) << "List service failed, error: " << apache::thrift::util::enumNameSafe(retCode); + handleErrorCode(retCode); + onFinished(); + return; + } + auto iter = nebula::value(iterRet).get(); + while (iter->valid()) { + auto sType = MetaKeyUtils::parseServiceType(iter->key()); + auto client = MetaKeyUtils::parseServiceClients(iter->val()); + serviceClients.emplace(std::move(sType), std::move(client)); + iter->next(); + } + } else { + const auto& serviceKey = MetaKeyUtils::serviceKey(type); + auto ret = doGet(serviceKey); + if (!nebula::ok(ret) && nebula::error(ret) != nebula::cpp2::ErrorCode::E_KEY_NOT_FOUND) { + auto retCode = nebula::error(ret); + LOG(ERROR) << "List service failed, error: " << apache::thrift::util::enumNameSafe(retCode); + handleErrorCode(retCode); + onFinished(); + return; + } + + if (nebula::ok(ret)) { + serviceClients.emplace(type, MetaKeyUtils::parseServiceClients(nebula::value(ret))); + } + } + + resp_.set_clients(std::move(serviceClients)); + handleErrorCode(nebula::cpp2::ErrorCode::SUCCEEDED); + onFinished(); +} + +} // namespace meta +} // namespace nebula diff --git a/src/meta/processors/service/ServiceProcessor.h b/src/meta/processors/service/ServiceProcessor.h new file mode 100644 index 00000000000..8e16b042f02 --- /dev/null +++ b/src/meta/processors/service/ServiceProcessor.h @@ -0,0 +1,57 @@ +/* Copyright (c) 2020 vesoft inc. All rights reserved. + * + * This source code is licensed under Apache 2.0 License, + * attached with Common Clause Condition 1.0, found in the LICENSES directory. + */ + +#ifndef META_SERVICEPROCESSOR_H_ +#define META_SERVICEPROCESSOR_H_ + +#include "meta/processors/BaseProcessor.h" + +namespace nebula { +namespace meta { + +class SignInServiceProcessor : public BaseProcessor { + public: + static SignInServiceProcessor* instance(kvstore::KVStore* kvstore) { + return new SignInServiceProcessor(kvstore); + } + + void process(const cpp2::SignInServiceReq& req); + + private: + explicit SignInServiceProcessor(kvstore::KVStore* kvstore) + : BaseProcessor(kvstore) {} +}; + +class SignOutServiceProcessor : public BaseProcessor { + public: + static SignOutServiceProcessor* instance(kvstore::KVStore* kvstore) { + return new SignOutServiceProcessor(kvstore); + } + + void process(const cpp2::SignOutServiceReq& req); + + private: + explicit SignOutServiceProcessor(kvstore::KVStore* kvstore) + : BaseProcessor(kvstore) {} +}; + +class ListServiceClientsProcessor : public BaseProcessor { + public: + static ListServiceClientsProcessor* instance(kvstore::KVStore* kvstore) { + return new ListServiceClientsProcessor(kvstore); + } + + void process(const cpp2::ListServiceClientsReq& req); + + private: + explicit ListServiceClientsProcessor(kvstore::KVStore* kvstore) + : BaseProcessor(kvstore) {} +}; + +} // namespace meta +} // namespace nebula + +#endif // META_SERVICEPROCESSOR_H_ diff --git a/src/meta/test/CMakeLists.txt b/src/meta/test/CMakeLists.txt index 7d4f83988c6..a7e329db3a5 100644 --- a/src/meta/test/CMakeLists.txt +++ b/src/meta/test/CMakeLists.txt @@ -211,6 +211,21 @@ nebula_add_test( gtest ) +nebula_add_test( + NAME + drainer_test + SOURCES + DrainerTest.cpp + OBJECTS + ${meta_test_deps} + LIBRARIES + ${ROCKSDB_LIBRARIES} + ${THRIFT_LIBRARIES} + ${PROXYGEN_LIBRARIES} + wangle + gtest +) + nebula_add_test( NAME create_backup_test diff --git a/src/meta/test/DrainerTest.cpp b/src/meta/test/DrainerTest.cpp new file mode 100644 index 00000000000..7d81469ac5a --- /dev/null +++ b/src/meta/test/DrainerTest.cpp @@ -0,0 +1,154 @@ +/* Copyright (c) 2021 vesoft inc. All rights reserved. + * + * This source code is licensed under Apache 2.0 License, + * attached with Common Clause Condition 1.0, found in the LICENSES directory. + */ + +#include + +#include "common/base/Base.h" +#include "common/fs/TempDir.h" +#include "meta/processors/drainer/DrainerProcessor.h" +#include "meta/processors/parts/CreateSpaceProcessor.h" +#include "meta/test/TestUtils.h" + +namespace nebula { +namespace meta { + +TEST(DrainerTest, DrainerTest) { + fs::TempDir rootPath("/tmp/DrainerTest.XXXXXX"); + + // Prepare + std::unique_ptr kv(MockCluster::initMetaKV(rootPath.path())); + + { + // Register drainer machine + std::vector addresses; + for (int32_t i = 0; i < 3; i++) { + addresses.emplace_back(std::to_string(i), i); + } + TestUtils::registerHB(kv.get(), addresses, cpp2::HostRole::DRAINER); + } + { + // Register storage machine + std::vector addresses; + for (int32_t i = 3; i < 6; i++) { + addresses.emplace_back(std::to_string(i), i); + } + TestUtils::registerHB(kv.get(), addresses, cpp2::HostRole::STORAGE); + } + { + // Add Drainer, space not exist, failed + cpp2::AddDrainerReq req; + req.set_space_id(1); + + std::vector hosts; + for (int32_t i = 0; i < 3; i++) { + hosts.emplace_back(std::to_string(i), i); + } + req.set_hosts(std::move(hosts)); + auto* processor = AddDrainerProcessor::instance(kv.get()); + auto f = processor->getFuture(); + processor->process(req); + auto resp = std::move(f).get(); + ASSERT_NE(nebula::cpp2::ErrorCode::SUCCEEDED, resp.get_code()); + } + { + // Create space, succeeded + cpp2::SpaceDesc properties; + properties.set_space_name("first_space"); + properties.set_partition_num(9); + properties.set_replica_factor(1); + cpp2::CreateSpaceReq req; + req.set_properties(std::move(properties)); + + auto* processor = CreateSpaceProcessor::instance(kv.get()); + auto f = processor->getFuture(); + processor->process(req); + auto resp = std::move(f).get(); + ASSERT_EQ(nebula::cpp2::ErrorCode::SUCCEEDED, resp.get_code()); + ASSERT_EQ(1, resp.get_id().get_space_id()); + } + { + // Add Drainer, space exists, succeeded + cpp2::AddDrainerReq req; + req.set_space_id(1); + std::vector hosts; + for (int32_t i = 0; i < 3; i++) { + hosts.emplace_back(std::to_string(i), i); + } + req.set_hosts(std::move(hosts)); + + auto* processor = AddDrainerProcessor::instance(kv.get()); + auto f = processor->getFuture(); + processor->process(req); + auto resp = std::move(f).get(); + ASSERT_EQ(nebula::cpp2::ErrorCode::SUCCEEDED, resp.get_code()); + } + { + // Add Drainer, drainer exists in space, failed + cpp2::AddDrainerReq req; + req.set_space_id(1); + std::vector hosts; + for (int32_t i = 0; i < 3; i++) { + hosts.emplace_back(std::to_string(i), i); + } + req.set_hosts(std::move(hosts)); + + auto* processor = AddDrainerProcessor::instance(kv.get()); + auto f = processor->getFuture(); + processor->process(req); + auto resp = std::move(f).get(); + ASSERT_NE(nebula::cpp2::ErrorCode::SUCCEEDED, resp.get_code()); + } + { + // List drainer + cpp2::ListDrainersReq req; + req.set_space_id(1); + auto* processor = ListDrainersProcessor::instance(kv.get()); + auto f = processor->getFuture(); + processor->process(req); + auto resp = std::move(f).get(); + ASSERT_EQ(nebula::cpp2::ErrorCode::SUCCEEDED, resp.get_code()); + auto drainers = resp.get_drainers(); + ASSERT_EQ(3, drainers.size()); + + for (auto i = 0; i < 3; i++) { + auto drainer = drainers[i]; + ASSERT_EQ(HostAddr(std::to_string(i), i), drainer.get_host()); + ASSERT_EQ(cpp2::HostStatus::ONLINE, drainer.get_status()); + } + } + { + // remove drainer + cpp2::RemoveDrainerReq req; + req.set_space_id(1); + auto* processor = RemoveDrainerProcessor::instance(kv.get()); + auto f = processor->getFuture(); + processor->process(req); + auto resp = std::move(f).get(); + ASSERT_EQ(nebula::cpp2::ErrorCode::SUCCEEDED, resp.get_code()); + } + { + // List drainer, succeed, drainers is empty + cpp2::ListDrainersReq req; + req.set_space_id(1); + auto* processor = ListDrainersProcessor::instance(kv.get()); + auto f = processor->getFuture(); + processor->process(req); + auto resp = std::move(f).get(); + ASSERT_EQ(nebula::cpp2::ErrorCode::SUCCEEDED, resp.get_code()); + auto drainers = resp.get_drainers(); + ASSERT_EQ(0, drainers.size()); + } +} + +} // namespace meta +} // namespace nebula + +int main(int argc, char** argv) { + testing::InitGoogleTest(&argc, argv); + folly::init(&argc, &argv, true); + google::SetStderrLogging(google::INFO); + return RUN_ALL_TESTS(); +} diff --git a/src/meta/test/MetaClientTest.cpp b/src/meta/test/MetaClientTest.cpp index af328e63359..5ca421ed25f 100644 --- a/src/meta/test/MetaClientTest.cpp +++ b/src/meta/test/MetaClientTest.cpp @@ -1442,8 +1442,8 @@ TEST(MetaClientTest, FTServiceTest) { std::vector hosts = {{"0", 0}, {"1", 1}, {"2", 2}, {"3", 3}}; TestUtils::registerHB(kv, hosts); - std::vector clients; - cpp2::FTClient c1, c2; + std::vector clients; + cpp2::ServiceClient c1, c2; c1.set_host({"0", 0}); c1.set_user("u1"); c1.set_pwd("pwd"); @@ -1452,21 +1452,21 @@ TEST(MetaClientTest, FTServiceTest) { c2.set_user("u2"); clients.emplace_back(c2); { - cpp2::FTServiceType type = cpp2::FTServiceType::ELASTICSEARCH; - auto result = client->signInFTService(type, clients).get(); + cpp2::ExternalServiceType type = cpp2::ExternalServiceType::ELASTICSEARCH; + auto result = client->signInService(type, clients).get(); ASSERT_TRUE(result.ok()); } { - auto result = client->listFTClients().get(); + auto result = client->listServiceClients(cpp2::ExternalServiceType::ELASTICSEARCH).get(); ASSERT_TRUE(result.ok()); - ASSERT_EQ(clients, result.value()); + ASSERT_EQ(clients, result.value()[cpp2::ExternalServiceType::ELASTICSEARCH]); } { - auto result = client->signOutFTService().get(); + auto result = client->signOutService(cpp2::ExternalServiceType::ELASTICSEARCH).get(); ASSERT_TRUE(result.ok()); } { - auto result = client->listFTClients().get(); + auto result = client->listServiceClients(cpp2::ExternalServiceType::ELASTICSEARCH).get(); ASSERT_TRUE(result.ok()); ASSERT_TRUE(result.value().empty()); } @@ -2056,7 +2056,7 @@ TEST(MetaClientTest, ListenerTest) { ASSERT_TRUE(addRet.ok()) << addRet.status(); } { - auto listRet = client->listListener(space).get(); + auto listRet = client->listListeners(space, cpp2::ListenerType::ELASTICSEARCH).get(); ASSERT_TRUE(listRet.ok()) << listRet.status(); auto listeners = listRet.value(); ASSERT_EQ(9, listeners.size()); @@ -2076,7 +2076,7 @@ TEST(MetaClientTest, ListenerTest) { ASSERT_TRUE(removeRet.ok()) << removeRet.status(); } { - auto listRet = client->listListener(space).get(); + auto listRet = client->listListeners(space, cpp2::ListenerType::ELASTICSEARCH).get(); ASSERT_TRUE(listRet.ok()) << listRet.status(); auto listeners = listRet.value(); ASSERT_EQ(0, listeners.size()); @@ -2117,6 +2117,162 @@ TEST(MetaClientTest, VerifyClientTest) { FLAGS_enable_client_white_list = false; } +// Sign in drainr service and sync listener test +TEST(MetaClientTest, DrainerServiceAndListenerTest) { + FLAGS_heartbeat_interval_secs = 1; + fs::TempDir rootPath("/tmp/MetaClientDrainerServiceAndListenerTest.XXXXXX"); + + mock::MockCluster cluster; + cluster.startMeta(rootPath.path()); + uint32_t localMetaPort = cluster.metaServer_->port_; + auto* kv = cluster.metaKV_.get(); + auto localIp = cluster.localIP(); + + auto threadPool = std::make_shared(1); + auto localhosts = std::vector{HostAddr(localIp, localMetaPort)}; + auto client = std::make_shared(threadPool, localhosts); + client->waitForMetadReady(); + + std::vector hosts = {{"0", 0}, {"1", 1}, {"2", 2}, {"3", 3}}; + TestUtils::registerHB(kv, hosts); + + std::vector clients; + cpp2::ServiceClient c1, c2; + c1.set_host({"0", 0}); + clients.emplace_back(c1); + c2.set_host({"1", 1}); + clients.emplace_back(c2); + + TestUtils::createSomeHosts(kv); + meta::cpp2::SpaceDesc spaceDesc; + spaceDesc.set_space_name("default"); + spaceDesc.set_partition_num(9); + spaceDesc.set_replica_factor(3); + auto ret = client->createSpace(spaceDesc).get(); + ASSERT_TRUE(ret.ok()) << ret.status(); + GraphSpaceID space = ret.value(); + + std::vector listenerHosts = {{"1", 0}, {"1", 1}, {"1", 2}, {"1", 3}}; + std::string toSpaceName("tospacename"); + TestUtils::setupHB(kv, listenerHosts, cpp2::HostRole::LISTENER, gitInfoSha()); + { + // add sync listener,failed + auto addRet = + client->addListener(space, cpp2::ListenerType::SYNC, listenerHosts, &toSpaceName).get(); + ASSERT_FALSE(addRet.ok()) << addRet.status(); + } + { + // sign in drainer service + cpp2::ExternalServiceType type = cpp2::ExternalServiceType::DRAINER; + auto result = client->signInService(type, clients).get(); + ASSERT_TRUE(result.ok()); + } + { + auto result = client->listServiceClients(cpp2::ExternalServiceType::DRAINER).get(); + ASSERT_TRUE(result.ok()); + ASSERT_EQ(clients, result.value()[cpp2::ExternalServiceType::DRAINER]); + } + { + // add sync listener,succeed + auto addRet = + client->addListener(space, cpp2::ListenerType::SYNC, listenerHosts, &toSpaceName).get(); + ASSERT_TRUE(addRet.ok()) << addRet.status(); + } + { + auto listRet = client->listListeners(space, cpp2::ListenerType::SYNC).get(); + ASSERT_TRUE(listRet.ok()) << listRet.status(); + auto listeners = listRet.value(); + ASSERT_EQ(9, listeners.size()); + std::vector expected; + for (size_t i = 0; i < 9; i++) { + cpp2::ListenerInfo l; + l.set_type(cpp2::ListenerType::SYNC); + l.set_host(listenerHosts[i % 4]); + l.set_part_id(i + 1); + l.set_status(cpp2::HostStatus::ONLINE); + l.set_space_name(toSpaceName); + expected.emplace_back(std::move(l)); + } + ASSERT_EQ(expected, listeners); + } + { + auto removeRet = client->removeListener(space, cpp2::ListenerType::SYNC).get(); + ASSERT_TRUE(removeRet.ok()) << removeRet.status(); + } + { + auto listRet = client->listListeners(space, cpp2::ListenerType::ELASTICSEARCH).get(); + ASSERT_TRUE(listRet.ok()) << listRet.status(); + auto listeners = listRet.value(); + ASSERT_EQ(0, listeners.size()); + } + { + auto result = client->signOutService(cpp2::ExternalServiceType::DRAINER).get(); + ASSERT_TRUE(result.ok()); + } + { + auto result = client->listServiceClients(cpp2::ExternalServiceType::DRAINER).get(); + ASSERT_TRUE(result.ok()); + ASSERT_TRUE(result.value().empty()); + } +} + +// For the slave cluster, drainer test +TEST(MetaClientTest, DrainerTest) { + FLAGS_heartbeat_interval_secs = 1; + fs::TempDir rootPath("/tmp/MetaClientDrainerTest.XXXXXX"); + + mock::MockCluster cluster; + cluster.startMeta(rootPath.path(), HostAddr("127.0.0.1", 0)); + uint32_t localMetaPort = cluster.metaServer_->port_; + auto* kv = cluster.metaKV_.get(); + auto localIp = cluster.localIP(); + + auto threadPool = std::make_shared(1); + auto localhosts = std::vector{HostAddr(localIp, localMetaPort)}; + auto client = std::make_shared(threadPool, localhosts); + client->waitForMetadReady(); + + TestUtils::createSomeHosts(kv); + meta::cpp2::SpaceDesc spaceDesc; + spaceDesc.set_space_name("default"); + spaceDesc.set_partition_num(9); + spaceDesc.set_replica_factor(3); + auto ret = client->createSpace(spaceDesc).get(); + ASSERT_TRUE(ret.ok()) << ret.status(); + GraphSpaceID space = ret.value(); + + std::vector drainerHosts = {{"1", 0}, {"1", 1}, {"1", 2}, {"1", 3}}; + TestUtils::setupHB(kv, drainerHosts, cpp2::HostRole::DRAINER, gitInfoSha()); + { + auto addRet = client->addDrainer(space, drainerHosts).get(); + ASSERT_TRUE(addRet.ok()) << addRet.status(); + } + { + auto listRet = client->listDrainers(space).get(); + ASSERT_TRUE(listRet.ok()) << listRet.status(); + auto drainers = listRet.value(); + ASSERT_EQ(4, drainers.size()); + std::vector expected; + for (size_t i = 0; i < 4; i++) { + cpp2::DrainerInfo l; + l.set_host(drainerHosts[i % 4]); + l.set_status(cpp2::HostStatus::ONLINE); + expected.emplace_back(std::move(l)); + } + ASSERT_EQ(expected, drainers); + } + { + auto removeRet = client->removeDrainer(space).get(); + ASSERT_TRUE(removeRet.ok()) << removeRet.status(); + } + { + auto listRet = client->listDrainers(space).get(); + ASSERT_TRUE(listRet.ok()) << listRet.status(); + auto drainers = listRet.value(); + ASSERT_EQ(0, drainers.size()); + } +} + TEST(MetaClientTest, RocksdbOptionsTest) { FLAGS_heartbeat_interval_secs = 1; fs::TempDir rootPath("/tmp/RocksdbOptionsTest.XXXXXX"); diff --git a/src/meta/test/TestUtils.h b/src/meta/test/TestUtils.h index a9e2f28a715..3d3a21addf5 100644 --- a/src/meta/test/TestUtils.h +++ b/src/meta/test/TestUtils.h @@ -67,8 +67,10 @@ class TestUtils { return column; } - static void registerHB(kvstore::KVStore* kv, const std::vector& hosts) { - return setupHB(kv, hosts, cpp2::HostRole::STORAGE, gitInfoSha()); + static void registerHB(kvstore::KVStore* kv, + const std::vector& hosts, + cpp2::HostRole role = cpp2::HostRole::STORAGE) { + return setupHB(kv, hosts, role, gitInfoSha()); } static void setupHB(kvstore::KVStore* kv, diff --git a/src/mock/AdHocSchemaManager.cpp b/src/mock/AdHocSchemaManager.cpp index 70513cb4cdb..9347fffe6f7 100644 --- a/src/mock/AdHocSchemaManager.cpp +++ b/src/mock/AdHocSchemaManager.cpp @@ -300,12 +300,29 @@ StatusOr AdHocSchemaManager::getAllLatestVerEdgeSchema(GraphSpaceID return edgesSchema; } -StatusOr> AdHocSchemaManager::getFTClients() { - return ftClients_; +StatusOr> AdHocSchemaManager::getServiceClients( + nebula::meta::cpp2::ExternalServiceType type) { + UNUSED(type); + return serviceClients_; } -void AdHocSchemaManager::addFTClient(const nebula::meta::cpp2::FTClient& client) { - ftClients_.emplace_back(client); +StatusOr AdHocSchemaManager::getDrainerClient(GraphSpaceID space, PartitionID partId) { + UNUSED(space); + UNUSED(partId); + HostAddr clietn; + return clietn; +} + +StatusOr> AdHocSchemaManager::getDrainerServer( + GraphSpaceID spaceId) { + UNUSED(spaceId); + std::vector drainerServer; + return drainerServer; } + +void AdHocSchemaManager::addServiceClient(const nebula::meta::cpp2::ServiceClient& client) { + serviceClients_.emplace_back(client); +} + } // namespace mock } // namespace nebula diff --git a/src/mock/AdHocSchemaManager.h b/src/mock/AdHocSchemaManager.h index d5b4d4cca93..a8dbdea5d18 100644 --- a/src/mock/AdHocSchemaManager.h +++ b/src/mock/AdHocSchemaManager.h @@ -99,9 +99,15 @@ class AdHocSchemaManager final : public nebula::meta::SchemaManager { EdgeType edge, SchemaVer ver); - StatusOr> getFTClients() override; + StatusOr> getServiceClients( + nebula::meta::cpp2::ExternalServiceType type) override; - void addFTClient(const nebula::meta::cpp2::FTClient& client); + StatusOr getDrainerClient(GraphSpaceID space, PartitionID partId) override; + + StatusOr> getDrainerServer( + GraphSpaceID spaceId) override; + + void addServiceClient(const nebula::meta::cpp2::ServiceClient& client); StatusOr> getFTIndex(GraphSpaceID, int32_t) override { @@ -142,7 +148,7 @@ class AdHocSchemaManager final : public nebula::meta::SchemaManager { std::shared_ptr>> edgeSchemasInMap_; - std::vector ftClients_; + std::vector serviceClients_; int32_t partNum_; }; diff --git a/src/mock/MockCluster.cpp b/src/mock/MockCluster.cpp index 3e5c3de80d1..fe1a9f40efb 100644 --- a/src/mock/MockCluster.cpp +++ b/src/mock/MockCluster.cpp @@ -136,7 +136,7 @@ void MockCluster::initStorageKV(const char* dataPath, SchemaVer schemaVerCount, bool hasProp, bool hasListener, - const std::vector& clients, + const std::vector& clients, bool needCffBuilder) { FLAGS_heartbeat_interval_secs = 1; const std::vector parts{1, 2, 3, 4, 5, 6}; @@ -171,7 +171,8 @@ void MockCluster::initStorageKV(const char* dataPath, if (clients.empty()) { LOG(FATAL) << "full text client list is empty"; } - ret = metaClient_->signInFTService(meta::cpp2::FTServiceType::ELASTICSEARCH, clients).get(); + ret = + metaClient_->signInService(meta::cpp2::ExternalServiceType::ELASTICSEARCH, clients).get(); if (!ret.ok()) { LOG(FATAL) << "full text client sign in failed"; } diff --git a/src/mock/MockCluster.h b/src/mock/MockCluster.h index 076821cb916..98c82394179 100644 --- a/src/mock/MockCluster.h +++ b/src/mock/MockCluster.h @@ -87,7 +87,7 @@ class MockCluster { SchemaVer schemaVerCount = 1, bool hasProp = true, bool hasListener = false, - const std::vector& clients = {}, + const std::vector& clients = {}, bool needCffBuilder = false); std::shared_ptr getWorkers(); diff --git a/src/parser/AdminSentences.cpp b/src/parser/AdminSentences.cpp index 0b15b7961c2..687827e00fb 100644 --- a/src/parser/AdminSentences.cpp +++ b/src/parser/AdminSentences.cpp @@ -214,11 +214,20 @@ std::string AddListenerSentence::toString() const { case meta::cpp2::ListenerType::ELASTICSEARCH: buf += "ELASTICSEARCH "; break; + case meta::cpp2::ListenerType::SYNC: + buf += "SYNC "; + break; case meta::cpp2::ListenerType::UNKNOWN: + case meta::cpp2::ListenerType::ALL: LOG(FATAL) << "Unknown listener type."; break; } + buf += listeners_->toString(); + if (type_ == meta::cpp2::ListenerType::SYNC && spaceName_) { + buf += " TO SPACE "; + buf += *spaceName_; + } return buf; } @@ -230,6 +239,30 @@ std::string RemoveListenerSentence::toString() const { case meta::cpp2::ListenerType::ELASTICSEARCH: buf += "ELASTICSEARCH "; break; + case meta::cpp2::ListenerType::SYNC: + buf += "SYNC "; + break; + case meta::cpp2::ListenerType::UNKNOWN: + case meta::cpp2::ListenerType::ALL: + DLOG(FATAL) << "Unknown listener type."; + break; + } + return buf; +} + +std::string ShowListenerSentence::toString() const { + std::string buf; + buf.reserve(64); + buf += "SHOW LISTENER "; + switch (type_) { + case meta::cpp2::ListenerType::ELASTICSEARCH: + buf += "ELASTICSEARCH "; + break; + case meta::cpp2::ListenerType::SYNC: + buf += "SYNC "; + break; + case meta::cpp2::ListenerType::ALL: + break; case meta::cpp2::ListenerType::UNKNOWN: DLOG(FATAL) << "Unknown listener type."; break; @@ -237,7 +270,27 @@ std::string RemoveListenerSentence::toString() const { return buf; } -std::string ShowListenerSentence::toString() const { return "SHOW LISTENER"; } +std::string AddDrainerSentence::toString() const { + std::string buf; + buf.reserve(64); + buf += "ADD DRAINER "; + buf += drainers_->toString(); + return buf; +} + +std::string RemoveDrainerSentence::toString() const { + std::string buf; + buf.reserve(64); + buf += "REMOVE DRAINER "; + return buf; +} + +std::string ShowDrainersSentence::toString() const { + std::string buf; + buf.reserve(64); + buf += "SHOW DRAINERS "; + return buf; +} std::string AdminJobSentence::toString() const { switch (op_) { @@ -299,12 +352,31 @@ void AdminJobSentence::addPara(const NameLabelList ¶s) { std::string ShowStatsSentence::toString() const { return folly::stringPrintf("SHOW STATS"); } -std::string ShowTSClientsSentence::toString() const { return "SHOW TEXT SEARCH CLIENTS"; } +std::string ShowServiceClientsSentence::toString() const { + switch (type_) { + case meta::cpp2::ExternalServiceType::ELASTICSEARCH: + return "SHOW TEXT SEARCH CLIENTS"; + case meta::cpp2::ExternalServiceType::DRAINER: + return "SHOW DRAINER CLIENTS"; + default: + LOG(FATAL) << "Unknown service type " << static_cast(type_); + } +} -std::string SignInTextServiceSentence::toString() const { +std::string SignInServiceSentence::toString() const { std::string buf; buf.reserve(256); - buf += "SIGN IN TEXT SERVICE "; + switch (type_) { + case meta::cpp2::ExternalServiceType::ELASTICSEARCH: + buf += "SIGN IN TEXT SERVICE "; + break; + case meta::cpp2::ExternalServiceType::DRAINER: + buf += "SIGN IN DRAINER SERVICE "; + break; + default: + LOG(FATAL) << "Unknown service type " << static_cast(type_); + } + for (auto &client : clients_->clients()) { buf += "("; buf += client.get_host().host; @@ -329,7 +401,16 @@ std::string SignInTextServiceSentence::toString() const { return buf; } -std::string SignOutTextServiceSentence::toString() const { return "SIGN OUT TEXT SERVICE"; } +std::string SignOutServiceSentence::toString() const { + switch (type_) { + case meta::cpp2::ExternalServiceType::ELASTICSEARCH: + return "SIGN OUT TEXT SERVICE"; + case meta::cpp2::ExternalServiceType::DRAINER: + return "SIGN OUT DRAINER SERVICE"; + default: + LOG(FATAL) << "Unknown service type " << static_cast(type_); + } +} std::string ShowSessionsSentence::toString() const { if (isSetSessionID()) { diff --git a/src/parser/AdminSentences.h b/src/parser/AdminSentences.h index 328414bbf67..1d20f299616 100644 --- a/src/parser/AdminSentences.h +++ b/src/parser/AdminSentences.h @@ -510,14 +510,19 @@ class DropSnapshotSentence final : public Sentence { class AddListenerSentence final : public Sentence { public: - AddListenerSentence(const meta::cpp2::ListenerType& type, HostList* hosts) { + AddListenerSentence(const meta::cpp2::ListenerType& type, + HostList* hosts, + std::string* spaceName = nullptr) { kind_ = Kind::kAddListener; type_ = type; listeners_.reset(hosts); + spaceName_.reset(spaceName); } meta::cpp2::ListenerType type() const { return type_; } + const std::string* spaceName() const { return spaceName_.get(); } + HostList* listeners() const { return listeners_.get(); } std::string toString() const override; @@ -525,6 +530,7 @@ class AddListenerSentence final : public Sentence { private: meta::cpp2::ListenerType type_; std::unique_ptr listeners_; + std::unique_ptr spaceName_; }; class RemoveListenerSentence final : public Sentence { @@ -544,7 +550,43 @@ class RemoveListenerSentence final : public Sentence { class ShowListenerSentence final : public Sentence { public: - ShowListenerSentence() { kind_ = Kind::kShowListener; } + explicit ShowListenerSentence(const meta::cpp2::ListenerType& type) : type_(type) { + kind_ = Kind::kShowListener; + } + + std::string toString() const override; + + meta::cpp2::ListenerType type() const { return type_; } + + private: + meta::cpp2::ListenerType type_; +}; + +class AddDrainerSentence final : public Sentence { + public: + explicit AddDrainerSentence(HostList* hosts) { + kind_ = Kind::kAddDrainer; + drainers_.reset(hosts); + } + + HostList* drainers() const { return drainers_.get(); } + + std::string toString() const override; + + private: + std::unique_ptr drainers_; +}; + +class RemoveDrainerSentence final : public Sentence { + public: + RemoveDrainerSentence() { kind_ = Kind::kRemoveDrainer; } + + std::string toString() const override; +}; + +class ShowDrainersSentence final : public Sentence { + public: + ShowDrainersSentence() { kind_ = Kind::kShowDrainers; } std::string toString() const override; }; @@ -581,14 +623,14 @@ class ShowStatsSentence final : public Sentence { std::string toString() const override; }; -class TSClientList final { +class ServiceClientList final { public: - void addClient(nebula::meta::cpp2::FTClient* client) { clients_.emplace_back(client); } + void addClient(nebula::meta::cpp2::ServiceClient* client) { clients_.emplace_back(client); } std::string toString() const; - std::vector clients() const { - std::vector result; + std::vector clients() const { + std::vector result; result.reserve(clients_.size()); for (auto& client : clients_) { result.emplace_back(*client); @@ -597,35 +639,55 @@ class TSClientList final { } private: - std::vector> clients_; + std::vector> clients_; }; -class ShowTSClientsSentence final : public Sentence { +class ShowServiceClientsSentence final : public Sentence { public: - ShowTSClientsSentence() { kind_ = Kind::kShowTSClients; } + explicit ShowServiceClientsSentence(const meta::cpp2::ExternalServiceType& type) : type_(type) { + kind_ = Kind::kShowServiceClients; + } + std::string toString() const override; + + meta::cpp2::ExternalServiceType getType() { return type_; } + + private: + meta::cpp2::ExternalServiceType type_; }; -class SignInTextServiceSentence final : public Sentence { +class SignInServiceSentence final : public Sentence { public: - explicit SignInTextServiceSentence(TSClientList* clients) { - kind_ = Kind::kSignInTSService; + explicit SignInServiceSentence(const meta::cpp2::ExternalServiceType& type, + ServiceClientList* clients) + : type_(type) { + kind_ = Kind::kSignInService; clients_.reset(clients); } std::string toString() const override; - TSClientList* clients() const { return clients_.get(); } + ServiceClientList* clients() const { return clients_.get(); } + + meta::cpp2::ExternalServiceType getType() { return type_; } private: - std::unique_ptr clients_; + std::unique_ptr clients_; + meta::cpp2::ExternalServiceType type_; }; -class SignOutTextServiceSentence final : public Sentence { +class SignOutServiceSentence final : public Sentence { public: - SignOutTextServiceSentence() { kind_ = Kind::kSignOutTSService; } + explicit SignOutServiceSentence(const meta::cpp2::ExternalServiceType& type) : type_(type) { + kind_ = Kind::kSignOutService; + } std::string toString() const override; + + meta::cpp2::ExternalServiceType getType() { return type_; } + + private: + meta::cpp2::ExternalServiceType type_; }; class ShowSessionsSentence final : public Sentence { diff --git a/src/parser/Sentence.h b/src/parser/Sentence.h index 36b66629c9c..0b66fe7413d 100644 --- a/src/parser/Sentence.h +++ b/src/parser/Sentence.h @@ -78,7 +78,7 @@ class Sentence { kShowGroups, kShowZones, kShowStats, - kShowTSClients, + kShowServiceClients, kShowFTIndexes, kDeleteVertices, kDeleteTags, @@ -128,8 +128,11 @@ class Sentence { kAddListener, kRemoveListener, kShowListener, - kSignInTSService, - kSignOutTSService, + kAddDrainer, + kRemoveDrainer, + kShowDrainers, + kSignInService, + kSignOutService, kCreateFTIndex, kDropFTIndex, kShowSessions, diff --git a/src/parser/parser.yy b/src/parser/parser.yy index e6eb5dc2ec9..73579399904 100644 --- a/src/parser/parser.yy +++ b/src/parser/parser.yy @@ -145,8 +145,8 @@ static constexpr size_t kCommentLengthLimit = 256; nebula::TextSearchArgument *text_search_argument; nebula::TextSearchArgument *base_text_search_argument; nebula::TextSearchArgument *fuzzy_text_search_argument; - nebula::meta::cpp2::FTClient *text_search_client_item; - nebula::TSClientList *text_search_client_list; + nebula::meta::cpp2::ServiceClient *service_client_item; + nebula::ServiceClientList *service_client_list; nebula::QueryUniqueIdentifier *query_unique_identifier; } @@ -193,7 +193,7 @@ static constexpr size_t kCommentLengthLimit = 256; %token KW_UNWIND KW_SKIP KW_OPTIONAL %token KW_CASE KW_THEN KW_ELSE KW_END %token KW_GROUP KW_ZONE KW_GROUPS KW_ZONES KW_INTO -%token KW_LISTENER KW_ELASTICSEARCH KW_FULLTEXT +%token KW_LISTENER KW_ELASTICSEARCH KW_FULLTEXT KW_SYNC KW_DRAINER KW_DRAINERS %token KW_AUTO KW_FUZZY KW_PREFIX KW_REGEXP KW_WILDCARD %token KW_TEXT KW_SEARCH KW_CLIENTS KW_SIGN KW_SERVICE KW_TEXT_SEARCH %token KW_ANY KW_SINGLE KW_NONE @@ -320,8 +320,8 @@ static constexpr size_t kCommentLengthLimit = 256; %type text_search_argument %type base_text_search_argument %type fuzzy_text_search_argument -%type text_search_client_item -%type text_search_client_list +%type service_client_item +%type service_client_list %type legal_integer unary_integer rank port job_concurrency @@ -358,6 +358,7 @@ static constexpr size_t kCommentLengthLimit = 256; %type add_host_into_zone_sentence drop_host_from_zone_sentence %type create_snapshot_sentence drop_snapshot_sentence %type add_listener_sentence remove_listener_sentence list_listener_sentence +%type add_drainer_sentence remove_drainer_sentence list_drainer_sentence %type admin_job_sentence %type create_user_sentence alter_user_sentence drop_user_sentence change_password_sentence @@ -383,7 +384,7 @@ static constexpr size_t kCommentLengthLimit = 256; %type seq_sentences %type explain_sentence %type sentences -%type sign_in_text_search_service_sentence sign_out_text_search_service_sentence +%type sign_in_service_sentence sign_out_service_sentence %type opt_if_not_exists %type opt_if_exists @@ -503,6 +504,9 @@ unreserved_keyword | KW_ZONES { $$ = new std::string("zones"); } | KW_LISTENER { $$ = new std::string("listener"); } | KW_ELASTICSEARCH { $$ = new std::string("elasticsearch"); } + | KW_SYNC { $$ = new std::string("sync"); } + | KW_DRAINER { $$ = new std::string("drainer"); } + | KW_DRAINERS { $$ = new std::string("drainers"); } | KW_FULLTEXT { $$ = new std::string("fulltext"); } | KW_STATS { $$ = new std::string("stats"); } | KW_STATUS { $$ = new std::string("status"); } @@ -1761,15 +1765,14 @@ match_limit } ; - -text_search_client_item +service_client_item : L_PAREN host_item R_PAREN { - $$ = new nebula::meta::cpp2::FTClient(); + $$ = new nebula::meta::cpp2::ServiceClient(); $$->set_host(*$2); delete $2; } | L_PAREN host_item COMMA STRING COMMA STRING R_PAREN { - $$ = new nebula::meta::cpp2::FTClient(); + $$ = new nebula::meta::cpp2::ServiceClient(); $$->set_host(*$2); $$->set_user(*$4); $$->set_pwd(*$6); @@ -1779,29 +1782,35 @@ text_search_client_item } ; -text_search_client_list - : text_search_client_item { - $$ = new TSClientList(); +service_client_list + : service_client_item { + $$ = new ServiceClientList(); $$->addClient($1); } - | text_search_client_list COMMA text_search_client_item { + | service_client_list COMMA service_client_item { $$ = $1; $$->addClient($3); } - | text_search_client_list COMMA { + | service_client_list COMMA { $$ = $1; } ; -sign_in_text_search_service_sentence - : KW_SIGN KW_IN KW_TEXT KW_SERVICE text_search_client_list { - $$ = new SignInTextServiceSentence($5); +sign_in_service_sentence + : KW_SIGN KW_IN KW_TEXT KW_SERVICE service_client_list { + $$ = new SignInServiceSentence(meta::cpp2::ExternalServiceType::ELASTICSEARCH, $5); + } + | KW_SIGN KW_IN KW_DRAINER KW_SERVICE service_client_list { + $$ = new SignInServiceSentence(meta::cpp2::ExternalServiceType::DRAINER, $5); } ; -sign_out_text_search_service_sentence +sign_out_service_sentence : KW_SIGN KW_OUT KW_TEXT KW_SERVICE { - $$ = new SignOutTextServiceSentence(); + $$ = new SignOutServiceSentence(meta::cpp2::ExternalServiceType::ELASTICSEARCH); + } + | KW_SIGN KW_OUT KW_DRAINER KW_SERVICE { + $$ = new SignOutServiceSentence(meta::cpp2::ExternalServiceType::DRAINER); } ; @@ -3092,7 +3101,10 @@ show_sentence $$ = new ShowStatsSentence(); } | KW_SHOW KW_TEXT KW_SEARCH KW_CLIENTS { - $$ = new ShowTSClientsSentence(); + $$ = new ShowServiceClientsSentence(meta::cpp2::ExternalServiceType::ELASTICSEARCH); + } + | KW_SHOW KW_DRAINER KW_CLIENTS { + $$ = new ShowServiceClientsSentence(meta::cpp2::ExternalServiceType::DRAINER); } | KW_SHOW KW_FULLTEXT KW_INDEXES { $$ = new ShowFTIndexesSentence(); @@ -3112,6 +3124,7 @@ list_host_type : KW_GRAPH { $$ = meta::cpp2::ListHostType::GRAPH; } | KW_META { $$ = meta::cpp2::ListHostType::META; } | KW_STORAGE { $$ = meta::cpp2::ListHostType::STORAGE; } + | KW_DRAINER { $$ = meta::cpp2::ListHostType::DRAINER; } ; config_module_enum @@ -3439,17 +3452,47 @@ add_listener_sentence : KW_ADD KW_LISTENER KW_ELASTICSEARCH host_list { $$ = new AddListenerSentence(meta::cpp2::ListenerType::ELASTICSEARCH, $4); } + | KW_ADD KW_LISTENER KW_SYNC host_list KW_TO KW_SPACE name_label { + $$ = new AddListenerSentence(meta::cpp2::ListenerType::SYNC, $4, $7); + } ; remove_listener_sentence : KW_REMOVE KW_LISTENER KW_ELASTICSEARCH { $$ = new RemoveListenerSentence(meta::cpp2::ListenerType::ELASTICSEARCH); } + | KW_REMOVE KW_LISTENER KW_SYNC { + $$ = new RemoveListenerSentence(meta::cpp2::ListenerType::SYNC); + } ; list_listener_sentence : KW_SHOW KW_LISTENER { - $$ = new ShowListenerSentence(); + $$ = new ShowListenerSentence(meta::cpp2::ListenerType::ALL); + } + | KW_SHOW KW_LISTENER KW_ELASTICSEARCH { + $$ = new ShowListenerSentence(meta::cpp2::ListenerType::ELASTICSEARCH); + } + | KW_SHOW KW_LISTENER KW_SYNC { + $$ = new ShowListenerSentence(meta::cpp2::ListenerType::SYNC); + } + ; + +add_drainer_sentence + : KW_ADD KW_DRAINER host_list { + $$ = new AddDrainerSentence($3); + } + ; + +remove_drainer_sentence + : KW_REMOVE KW_DRAINER { + $$ = new RemoveDrainerSentence(); + } + ; + +list_drainer_sentence + : KW_SHOW KW_DRAINERS { + $$ = new ShowDrainersSentence(); } ; @@ -3537,10 +3580,13 @@ maintain_sentence | add_listener_sentence { $$ = $1; } | remove_listener_sentence { $$ = $1; } | list_listener_sentence { $$ = $1; } + | add_drainer_sentence { $$ = $1; } + | remove_drainer_sentence { $$ = $1; } + | list_drainer_sentence { $$ = $1; } | create_snapshot_sentence { $$ = $1; } | drop_snapshot_sentence { $$ = $1; } - | sign_in_text_search_service_sentence { $$ = $1; } - | sign_out_text_search_service_sentence { $$ = $1; } + | sign_in_service_sentence { $$ = $1; } + | sign_out_service_sentence { $$ = $1; } ; sentence diff --git a/src/parser/scanner.lex b/src/parser/scanner.lex index 6c316e4b2bf..5705b6a562e 100644 --- a/src/parser/scanner.lex +++ b/src/parser/scanner.lex @@ -233,6 +233,9 @@ IP_OCTET ([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5]) "INTO" { return TokenType::KW_INTO; } "LISTENER" { return TokenType::KW_LISTENER; } "ELASTICSEARCH" { return TokenType::KW_ELASTICSEARCH; } +"SYNC" { return TokenType::KW_SYNC; } +"DRAINER" { return TokenType::KW_DRAINER; } +"DRAINERS" { return TokenType::KW_DRAINERS; } "FULLTEXT" { return TokenType::KW_FULLTEXT; } "AUTO" { return TokenType::KW_AUTO; } "FUZZY" { return TokenType::KW_FUZZY; } diff --git a/src/parser/test/CMakeLists.txt b/src/parser/test/CMakeLists.txt index 86016b7ba1e..18d169fe3ae 100644 --- a/src/parser/test/CMakeLists.txt +++ b/src/parser/test/CMakeLists.txt @@ -22,6 +22,7 @@ set(PARSER_TEST_LIBS $ $ $ + $ $ $ $ diff --git a/src/parser/test/ParserTest.cpp b/src/parser/test/ParserTest.cpp index 87b2bcd7240..db2ab8aba85 100644 --- a/src/parser/test/ParserTest.cpp +++ b/src/parser/test/ParserTest.cpp @@ -1446,6 +1446,11 @@ TEST_F(ParserTest, AdminOperation) { auto result = parse(query); ASSERT_TRUE(result.ok()) << result.status(); } + { + std::string query = "SHOW HOSTS drainer"; + auto result = parse(query); + ASSERT_TRUE(result.ok()) << result.status(); + } { std::string query = "SHOW SPACES"; auto result = parse(query); @@ -2917,6 +2922,11 @@ TEST_F(ParserTest, FullTextServiceTest) { auto result = parse(query); ASSERT_TRUE(result.ok()) << result.status(); } + { + std::string query = "SHOW LISTENER ELASTICSEARCH"; + auto result = parse(query); + ASSERT_TRUE(result.ok()) << result.status(); + } { std::string query = "SIGN IN TEXT SERVICE (127.0.0.1:9200)"; auto result = parse(query); @@ -2951,6 +2961,82 @@ TEST_F(ParserTest, FullTextServiceTest) { } } +TEST_F(ParserTest, SyncServiceTest) { + { + std::string query = "ADD LISTENER SYNC 127.0.0.1:12000"; + auto result = parse(query); + ASSERT_FALSE(result.ok()) << result.status(); + } + { + std::string query = "ADD LISTENER SYNC 127.0.0.1:12000 TO SPACE default_space"; + auto result = parse(query); + ASSERT_TRUE(result.ok()) << result.status(); + } + { + std::string query = "ADD LISTENER SYNC 127.0.0.1:12000, 127.0.0.1:12001 TO SPACE default_space"; + auto result = parse(query); + ASSERT_TRUE(result.ok()) << result.status(); + } + { + std::string query = "REMOVE LISTENER SYNC"; + auto result = parse(query); + ASSERT_TRUE(result.ok()) << result.status(); + } + { + std::string query = "SHOW LISTENER"; + auto result = parse(query); + ASSERT_TRUE(result.ok()) << result.status(); + } + { + std::string query = "SHOW LISTENER SYNC"; + auto result = parse(query); + ASSERT_TRUE(result.ok()) << result.status(); + } + { + std::string query = "SIGN IN DRAINER SERVICE (127.0.0.1:9200)"; + auto result = parse(query); + ASSERT_TRUE(result.ok()) << result.status(); + } + { + std::string query = "SIGN IN DRAINER SERVICE (127.0.0.1:9200), (127.0.0.1:9300)"; + auto result = parse(query); + ASSERT_TRUE(result.ok()) << result.status(); + } + { + std::string query = "SIGN OUT DRAINER SERVICE"; + auto result = parse(query); + ASSERT_TRUE(result.ok()) << result.status(); + } + { + std::string query = "SHOW DRAINER CLIENTS"; + auto result = parse(query); + ASSERT_TRUE(result.ok()); + } +} + +TEST_F(ParserTest, DrainerTest) { + { + std::string query = "ADD DRAINER 127.0.0.1:12000"; + auto result = parse(query); + ASSERT_TRUE(result.ok()) << result.status(); + } + { + std::string query = "ADD DRAINER 127.0.0.1:12000, 127.0.0.1:12001"; + auto result = parse(query); + ASSERT_TRUE(result.ok()) << result.status(); + } + { + std::string query = "REMOVE DRAINER"; + auto result = parse(query); + ASSERT_TRUE(result.ok()) << result.status(); + } + { + std::string query = "SHOW DRAINERS"; + auto result = parse(query); + ASSERT_TRUE(result.ok()) << result.status(); + } +} + TEST_F(ParserTest, SessionTest) { { std::string query = "SHOW SESSIONS"; diff --git a/src/storage/test/CMakeLists.txt b/src/storage/test/CMakeLists.txt index d976ab2b209..f8443a1e861 100644 --- a/src/storage/test/CMakeLists.txt +++ b/src/storage/test/CMakeLists.txt @@ -27,6 +27,7 @@ set(storage_test_deps $ $ $ + $ $ $ $ diff --git a/src/storage/test/ElasticSearchBulkInsertTest.cpp b/src/storage/test/ElasticSearchBulkInsertTest.cpp index a849785a015..a438077c279 100644 --- a/src/storage/test/ElasticSearchBulkInsertTest.cpp +++ b/src/storage/test/ElasticSearchBulkInsertTest.cpp @@ -168,7 +168,7 @@ class ElasticSearchBasicTest : public ::testing::Test { TEST_F(ElasticSearchBasicTest, SimpleTest) { FLAGS_heartbeat_interval_secs = 1; - meta::cpp2::FTClient ftClient; + meta::cpp2::ServiceClient ftClient; ftClient.set_host(HostAddr("127.0.0.1", esPort_)); const nebula::ClusterID kClusterId = 10; mock::MockCluster cluster; diff --git a/src/tools/db-dump/CMakeLists.txt b/src/tools/db-dump/CMakeLists.txt index b04c86360cc..7bd7470e53a 100644 --- a/src/tools/db-dump/CMakeLists.txt +++ b/src/tools/db-dump/CMakeLists.txt @@ -23,6 +23,7 @@ set(tools_test_deps $ $ $ + $ $ $ $ diff --git a/src/tools/db-upgrade/CMakeLists.txt b/src/tools/db-upgrade/CMakeLists.txt index 584d03acf2d..8de5e48583c 100644 --- a/src/tools/db-upgrade/CMakeLists.txt +++ b/src/tools/db-upgrade/CMakeLists.txt @@ -31,6 +31,7 @@ nebula_add_executable( $ $ $ + $ $ $ $ diff --git a/src/tools/meta-dump/CMakeLists.txt b/src/tools/meta-dump/CMakeLists.txt index 8ba15751e2b..8935b721224 100644 --- a/src/tools/meta-dump/CMakeLists.txt +++ b/src/tools/meta-dump/CMakeLists.txt @@ -28,6 +28,7 @@ nebula_add_executable( $ $ $ + $ $ $ $ diff --git a/src/tools/simple-kv-verify/CMakeLists.txt b/src/tools/simple-kv-verify/CMakeLists.txt index 32e8eafa5ee..2368288a048 100644 --- a/src/tools/simple-kv-verify/CMakeLists.txt +++ b/src/tools/simple-kv-verify/CMakeLists.txt @@ -25,6 +25,7 @@ nebula_add_executable( $ $ $ + $ $ $ $ diff --git a/src/tools/storage-perf/CMakeLists.txt b/src/tools/storage-perf/CMakeLists.txt index ea4d1dfab25..4076143e85f 100644 --- a/src/tools/storage-perf/CMakeLists.txt +++ b/src/tools/storage-perf/CMakeLists.txt @@ -23,6 +23,7 @@ set(perf_test_deps $ $ $ + $ $ $ $ diff --git a/tests/admin/test_listener.py b/tests/admin/test_listener.py index 17a92bb3333..7472e6376d0 100644 --- a/tests/admin/test_listener.py +++ b/tests/admin/test_listener.py @@ -32,11 +32,54 @@ def test_listener(self): self.check_resp_succeeded(resp) self.search_result(resp, [[1, "ELASTICSEARCH", '"127.0.0.1":8899', "OFFLINE"]]) + # Show listener + resp = self.client.execute('SHOW LISTENER ELASTICSEARCH;') + self.check_resp_succeeded(resp) + self.search_result(resp, [[1, "ELASTICSEARCH", '"127.0.0.1":8899', "OFFLINE"]]) + # Remove listener resp = self.client.execute('REMOVE LISTENER ELASTICSEARCH;') self.check_resp_succeeded(resp) + # CHECK listener + resp = self.client.execute('SHOW LISTENER ELASTICSEARCH;') + self.check_resp_succeeded(resp) + self.check_result(resp, []) + + # Sync listener need sign in drainer service first + resp = self.client.execute('SIGN IN DRAINER SERVICE (127.0.0.1:7899);') + self.check_resp_succeeded(resp) + + # CHECK drainer clients + resp = self.client.execute('SHOW DRAINER CLIENTS;') + self.check_resp_succeeded(resp) + + # Add on same as storage host + resp = self.client.execute('ADD LISTENER SYNC {}:{} TO SPACE default_space'.format(storage_ip, storage_port)) + self.check_resp_failed(resp) + + # Add non-existen host + resp = self.client.execute('ADD LISTENER SYNC 127.0.0.1:7899 TO SPACE default_space') + self.check_resp_succeeded(resp) + + # Show listener + resp = self.client.execute('SHOW LISTENER SYNC;') + self.check_resp_succeeded(resp) + self.search_result(resp, [[1, "SYNC", '"127.0.0.1":7899', "default_space", "OFFLINE"]]) + + # Remove listener + resp = self.client.execute('REMOVE LISTENER SYNC;') + self.check_resp_succeeded(resp) + # CHECK listener resp = self.client.execute('SHOW LISTENER;') self.check_resp_succeeded(resp) self.check_result(resp, []) + + # Sign out drainer service + resp = self.client.execute('SIGN OUT DRAINER SERVICE;') + self.check_resp_succeeded(resp) + + # CHECK drainer clients + resp = self.client.execute('SHOW DRAINER CLIENTS;') + self.check_resp_succeeded(resp)