Alexey Efimov 6 месяцев назад
Родитель
Сommit
e5a8411bf5

+ 98 - 26
ydb/core/viewer/json_pipe_req.cpp

@@ -93,26 +93,34 @@ void TViewerPipeClient::SendDelayedRequests() {
     }
 }
 
-TPathId TViewerPipeClient::GetPathId(TEvTxProxySchemeCache::TEvNavigateKeySetResult::TPtr& ev) {
-    if (ev->Get()->Request->ResultSet.size() == 1) {
-        if (ev->Get()->Request->ResultSet.begin()->Self) {
-            const auto& info = ev->Get()->Request->ResultSet.begin()->Self->Info;
+TPathId TViewerPipeClient::GetPathId(const TEvTxProxySchemeCache::TEvNavigateKeySetResult& ev) {
+    if (ev.Request->ResultSet.size() == 1) {
+        if (ev.Request->ResultSet.begin()->Self) {
+            const auto& info = ev.Request->ResultSet.begin()->Self->Info;
             return TPathId(info.GetSchemeshardId(), info.GetPathId());
         }
-        if (ev->Get()->Request->ResultSet.begin()->TableId) {
-            return ev->Get()->Request->ResultSet.begin()->TableId.PathId;
+        if (ev.Request->ResultSet.begin()->TableId) {
+            return ev.Request->ResultSet.begin()->TableId.PathId;
         }
     }
     return {};
 }
 
-TString TViewerPipeClient::GetPath(TEvTxProxySchemeCache::TEvNavigateKeySetResult::TPtr& ev) {
-    if (ev->Get()->Request->ResultSet.size() == 1) {
-        return CanonizePath(ev->Get()->Request->ResultSet.begin()->Path);
+TString TViewerPipeClient::GetPath(const TEvTxProxySchemeCache::TEvNavigateKeySetResult& ev) {
+    if (ev.Request->ResultSet.size() == 1) {
+        return CanonizePath(ev.Request->ResultSet.begin()->Path);
     }
     return {};
 }
 
+TPathId TViewerPipeClient::GetPathId(TEvTxProxySchemeCache::TEvNavigateKeySetResult::TPtr& ev) {
+    return GetPathId(*ev->Get());
+}
+
+TString TViewerPipeClient::GetPath(TEvTxProxySchemeCache::TEvNavigateKeySetResult::TPtr& ev) {
+    return GetPath(*ev->Get());
+}
+
 bool TViewerPipeClient::IsSuccess(const std::unique_ptr<TEvTxProxySchemeCache::TEvNavigateKeySetResult>& ev) {
     return (ev->Request->ResultSet.size() == 1) && (ev->Request->ResultSet.begin()->Status == NSchemeCache::TSchemeCacheNavigate::EStatus::Ok);
 }
@@ -147,6 +155,23 @@ TString TViewerPipeClient::GetError(const std::unique_ptr<TEvTxProxySchemeCache:
     }
 }
 
+bool TViewerPipeClient::IsSuccess(const std::unique_ptr<TEvStateStorage::TEvBoardInfo>& ev) {
+    return ev->Status == TEvStateStorage::TEvBoardInfo::EStatus::Ok;
+}
+
+TString TViewerPipeClient::GetError(const std::unique_ptr<TEvStateStorage::TEvBoardInfo>& ev) {
+    switch (ev->Status) {
+        case TEvStateStorage::TEvBoardInfo::EStatus::Unknown:
+            return "Unknown";
+        case TEvStateStorage::TEvBoardInfo::EStatus::Ok:
+            return "Ok";
+        case TEvStateStorage::TEvBoardInfo::EStatus::NotAvailable:
+            return "NotAvailable";
+        default:
+            return ::ToString(static_cast<int>(ev->Status));
+    }
+}
+
 void TViewerPipeClient::RequestHiveDomainStats(NNodeWhiteboard::TTabletId hiveId) {
     TActorId pipeClient = ConnectTabletPipe(hiveId);
     THolder<TEvHive::TEvRequestHiveDomainStats> request = MakeHolder<TEvHive::TEvRequestHiveDomainStats>();
@@ -540,10 +565,10 @@ void TViewerPipeClient::RequestStateStorageMetadataCacheEndpointsLookup(const TS
     ++Requests;
 }
 
-std::vector<TNodeId> TViewerPipeClient::GetNodesFromBoardReply(TEvStateStorage::TEvBoardInfo::TPtr& ev) {
+std::vector<TNodeId> TViewerPipeClient::GetNodesFromBoardReply(const TEvStateStorage::TEvBoardInfo& ev) {
     std::vector<TNodeId> databaseNodes;
-    if (ev->Get()->Status == TEvStateStorage::TEvBoardInfo::EStatus::Ok) {
-        for (const auto& [actorId, infoEntry] : ev->Get()->InfoEntries) {
+    if (ev.Status == TEvStateStorage::TEvBoardInfo::EStatus::Ok) {
+        for (const auto& [actorId, infoEntry] : ev.InfoEntries) {
             databaseNodes.emplace_back(actorId.NodeId());
         }
     }
@@ -552,11 +577,20 @@ std::vector<TNodeId> TViewerPipeClient::GetNodesFromBoardReply(TEvStateStorage::
     return databaseNodes;
 }
 
+std::vector<TNodeId> TViewerPipeClient::GetNodesFromBoardReply(TEvStateStorage::TEvBoardInfo::TPtr& ev) {
+    return GetNodesFromBoardReply(*ev->Get());
+}
+
 void TViewerPipeClient::InitConfig(const TCgiParameters& params) {
     Followers = FromStringWithDefault(params.Get("followers"), Followers);
     Metrics = FromStringWithDefault(params.Get("metrics"), Metrics);
     WithRetry = FromStringWithDefault(params.Get("with_retry"), WithRetry);
     MaxRequestsInFlight = FromStringWithDefault(params.Get("max_requests_in_flight"), MaxRequestsInFlight);
+    Database = params.Get("database");
+    if (!Database) {
+        Database = params.Get("tenant");
+    }
+    Direct = FromStringWithDefault<bool>(params.Get("direct"), Direct);
 }
 
 void TViewerPipeClient::InitConfig(const TRequestSettings& settings) {
@@ -653,23 +687,43 @@ void TViewerPipeClient::Handle(TEvTabletPipe::TEvClientConnected::TPtr& ev) {
     }
 }
 
+void TViewerPipeClient::HandleResolveResource(TEvTxProxySchemeCache::TEvNavigateKeySetResult::TPtr& ev) {
+    ResourceNavigateResponse->Set(std::move(ev));
+    if (ResourceNavigateResponse->IsOk()) {
+        TSchemeCacheNavigate::TEntry& entry(ResourceNavigateResponse->Get()->Request->ResultSet.front());
+        SharedDatabase = CanonizePath(entry.Path);
+        if (SharedDatabase == AppData()->TenantName) {
+            Direct = true;
+            return Bootstrap(); // retry bootstrap without redirect this time
+        }
+        DatabaseBoardInfoResponse = MakeRequestStateStorageEndpointsLookup(SharedDatabase);
+    } else {
+        ReplyAndPassAway(GetHTTPBADREQUEST("text/plain", "Failed to resolve database - shared database not found"));
+    }
+}
+
 void TViewerPipeClient::HandleResolveDatabase(TEvTxProxySchemeCache::TEvNavigateKeySetResult::TPtr& ev) {
-    if (ev->Get()->Request->ResultSet.size() == 1 && ev->Get()->Request->ResultSet.begin()->Status == NSchemeCache::TSchemeCacheNavigate::EStatus::Ok) {
-        TSchemeCacheNavigate::TEntry& entry(ev->Get()->Request->ResultSet.front());
-        if (entry.DomainInfo) {
-            if (entry.DomainInfo->ResourcesDomainKey && entry.DomainInfo->DomainKey != entry.DomainInfo->ResourcesDomainKey) {
-                RequestSchemeCacheNavigate(TPathId(entry.DomainInfo->ResourcesDomainKey));
-            } else {
-                RequestStateStorageEndpointsLookup(CanonizePath(entry.Path));
-            }
+    DatabaseNavigateResponse->Set(std::move(ev));
+    if (DatabaseNavigateResponse->IsOk()) {
+        TSchemeCacheNavigate::TEntry& entry(DatabaseNavigateResponse->Get()->Request->ResultSet.front());
+        if (entry.DomainInfo && entry.DomainInfo->ResourcesDomainKey && entry.DomainInfo->DomainKey != entry.DomainInfo->ResourcesDomainKey) {
+            ResourceNavigateResponse = MakeRequestSchemeCacheNavigate(TPathId(entry.DomainInfo->ResourcesDomainKey));
+            Become(&TViewerPipeClient::StateResolveResource);
+            return;
         }
+        DatabaseBoardInfoResponse = MakeRequestStateStorageEndpointsLookup(CanonizePath(entry.Path));
     } else {
-        ReplyAndPassAway(GetHTTPBADREQUEST("text/plain", "Failed to resolve database"));
+        ReplyAndPassAway(GetHTTPBADREQUEST("text/plain", "Failed to resolve database - not found"));
     }
 }
 
-void TViewerPipeClient::HandleResolveDatabase(TEvStateStorage::TEvBoardInfo::TPtr& ev) {
-    ReplyAndPassAway(MakeForward(GetNodesFromBoardReply(ev)));
+void TViewerPipeClient::HandleResolve(TEvStateStorage::TEvBoardInfo::TPtr& ev) {
+    DatabaseBoardInfoResponse->Set(std::move(ev));
+    if (DatabaseBoardInfoResponse->IsOk()) {
+        ReplyAndPassAway(MakeForward(GetNodesFromBoardReply(DatabaseBoardInfoResponse->GetRef())));
+    } else {
+        ReplyAndPassAway(GetHTTPBADREQUEST("text/plain", "Failed to resolve database - no nodes found"));
+    }
 }
 
 void TViewerPipeClient::HandleTimeout() {
@@ -678,15 +732,33 @@ void TViewerPipeClient::HandleTimeout() {
 
 STATEFN(TViewerPipeClient::StateResolveDatabase) {
     switch (ev->GetTypeRewrite()) {
-        hFunc(TEvStateStorage::TEvBoardInfo, HandleResolveDatabase);
+        hFunc(TEvStateStorage::TEvBoardInfo, HandleResolve);
         hFunc(TEvTxProxySchemeCache::TEvNavigateKeySetResult, HandleResolveDatabase);
         cFunc(TEvents::TEvWakeup::EventType, HandleTimeout);
     }
 }
 
+STATEFN(TViewerPipeClient::StateResolveResource) {
+    switch (ev->GetTypeRewrite()) {
+        hFunc(TEvStateStorage::TEvBoardInfo, HandleResolve);
+        hFunc(TEvTxProxySchemeCache::TEvNavigateKeySetResult, HandleResolveResource);
+        cFunc(TEvents::TEvWakeup::EventType, HandleTimeout);
+    }
+}
+
 void TViewerPipeClient::RedirectToDatabase(const TString& database) {
-    RequestSchemeCacheNavigate(database);
-    Become(&TViewerPipeClient::StateResolveDatabase, TDuration::MilliSeconds(1000), new TEvents::TEvWakeup());
+    DatabaseNavigateResponse = MakeRequestSchemeCacheNavigate(database);
+    Become(&TViewerPipeClient::StateResolveDatabase);
+}
+
+bool TViewerPipeClient::NeedToRedirect() {
+    Direct |= !Event->Get()->Request.GetHeader("X-Forwarded-From-Node").empty(); // we're already forwarding
+    Direct |= (Database == AppData()->TenantName) || Database.empty(); // we're already on the right node or don't use database filter
+    if (Database && !Direct) {
+        RedirectToDatabase(Database); // to find some dynamic node and redirect query there
+        return true;
+    }
+    return false;
 }
 
 void TViewerPipeClient::PassAway() {

+ 18 - 1
ydb/core/viewer/json_pipe_req.h

@@ -37,6 +37,9 @@ protected:
     bool Followers = true;
     bool Metrics = true;
     bool WithRetry = true;
+    TString Database;
+    TString SharedDatabase;
+    bool Direct = false;
     ui32 Requests = 0;
     ui32 MaxRequestsInFlight = 200;
     NWilson::TSpan Span;
@@ -158,6 +161,10 @@ protected:
         }
     };
 
+    std::optional<TRequestResponse<TEvTxProxySchemeCache::TEvNavigateKeySetResult>> DatabaseNavigateResponse;
+    std::optional<TRequestResponse<TEvTxProxySchemeCache::TEvNavigateKeySetResult>> ResourceNavigateResponse;
+    std::optional<TRequestResponse<TEvStateStorage::TEvBoardInfo>> DatabaseBoardInfoResponse;
+
     NTabletPipe::TClientConfig GetPipeClientConfig();
 
     ~TViewerPipeClient();
@@ -211,12 +218,18 @@ protected:
         return MakeBSControllerID();
     }
 
+    static TPathId GetPathId(const TEvTxProxySchemeCache::TEvNavigateKeySetResult& ev);
+    static TString GetPath(const TEvTxProxySchemeCache::TEvNavigateKeySetResult& ev);
+
     static TPathId GetPathId(TEvTxProxySchemeCache::TEvNavigateKeySetResult::TPtr& ev);
     static TString GetPath(TEvTxProxySchemeCache::TEvNavigateKeySetResult::TPtr& ev);
 
     static bool IsSuccess(const std::unique_ptr<TEvTxProxySchemeCache::TEvNavigateKeySetResult>& ev);
     static TString GetError(const std::unique_ptr<TEvTxProxySchemeCache::TEvNavigateKeySetResult>& ev);
 
+    static bool IsSuccess(const std::unique_ptr<TEvStateStorage::TEvBoardInfo>& ev);
+    static TString GetError(const std::unique_ptr<TEvStateStorage::TEvBoardInfo>& ev);
+
     TRequestResponse<TEvHive::TEvResponseHiveDomainStats> MakeRequestHiveDomainStats(TTabletId hiveId);
     TRequestResponse<TEvHive::TEvResponseHiveStorageStats> MakeRequestHiveStorageStats(TTabletId hiveId);
     TRequestResponse<TEvHive::TEvResponseHiveNodeStats> MakeRequestHiveNodeStats(TTabletId hiveId, TEvHive::TEvRequestHiveNodeStats* request);
@@ -252,6 +265,7 @@ protected:
     void RequestStateStorageMetadataCacheEndpointsLookup(const TString& path);
     TRequestResponse<TEvStateStorage::TEvBoardInfo> MakeRequestStateStorageEndpointsLookup(const TString& path, ui64 cookie = 0);
     std::vector<TNodeId> GetNodesFromBoardReply(TEvStateStorage::TEvBoardInfo::TPtr& ev);
+    std::vector<TNodeId> GetNodesFromBoardReply(const TEvStateStorage::TEvBoardInfo& ev);
     void InitConfig(const TCgiParameters& params);
     void InitConfig(const TRequestSettings& settings);
     void ClosePipes();
@@ -285,9 +299,12 @@ protected:
     void AddEvent(const TString& name);
     void Handle(TEvTabletPipe::TEvClientConnected::TPtr& ev);
     void HandleResolveDatabase(TEvTxProxySchemeCache::TEvNavigateKeySetResult::TPtr& ev);
-    void HandleResolveDatabase(TEvStateStorage::TEvBoardInfo::TPtr& ev);
+    void HandleResolveResource(TEvTxProxySchemeCache::TEvNavigateKeySetResult::TPtr& ev);
+    void HandleResolve(TEvStateStorage::TEvBoardInfo::TPtr& ev);
     STATEFN(StateResolveDatabase);
+    STATEFN(StateResolveResource);
     void RedirectToDatabase(const TString& database);
+    bool NeedToRedirect();
     void HandleTimeout();
     void PassAway() override;
 };

+ 42 - 45
ydb/core/viewer/storage_groups.h

@@ -122,7 +122,6 @@ public:
     using TFieldsType = std::bitset<+EGroupFields::COUNT>;
 
     // Common
-    std::optional<TRequestResponse<TEvTxProxySchemeCache::TEvNavigateKeySetResult>> DatabaseNavigateResult;
     std::unordered_map<TPathId, TRequestResponse<TEvTxProxySchemeCache::TEvNavigateKeySetResult>> NavigateKeySetResult;
     std::unordered_map<TPathId, TTabletId> PathId2HiveId;
     std::unordered_map<TTabletId, TRequestResponse<TEvHive::TEvResponseHiveStorageStats>> HiveStorageStats;
@@ -145,8 +144,6 @@ public:
     ui64 PDiskStateRequestsInFlight = 0;
 
     ui32 Timeout = 0;
-    TString Database;
-    bool Direct = false;
     TString Filter;
     std::unordered_set<TString> DatabaseStoragePools;
     std::unordered_set<TString> FilterStoragePools;
@@ -610,18 +607,11 @@ public:
         : TBase(viewer, ev)
     {
         const auto& params(Event->Get()->Request.GetParams());
-        InitConfig(params);
         Timeout = FromStringWithDefault<ui32>(params.Get("timeout"), 10000);
-        Database = params.Get("tenant");
-        if (Database.empty()) {
-            Database = params.Get("database");
-        }
         if (!Database.empty()) {
             FieldsRequired.set(+EGroupFields::PoolName);
             NeedFilter = true;
         }
-        Direct = FromStringWithDefault<bool>(params.Get("direct"), Direct);
-
         FieldsRequired.set(+EGroupFields::GroupId);
         TString filterStoragePool = params.Get("pool");
         if (!filterStoragePool.empty()) {
@@ -726,32 +716,35 @@ public:
 
 public:
     void Bootstrap() override {
-        Direct |= !TBase::Event->Get()->Request.GetHeader("X-Forwarded-From-Node").empty(); // we're already forwarding
-        Direct |= (Database == AppData()->TenantName) || Database.empty(); // we're already on the right node or don't use database filter
-
-        if (Database && !Direct) {
-            return RedirectToDatabase(Database); // to find some dynamic node and redirect query there
+        if (TBase::NeedToRedirect()) {
+            return;
+        }
+        if (Database) {
+            if (!DatabaseNavigateResponse) {
+                DatabaseNavigateResponse = MakeRequestSchemeCacheNavigate(Database, 0);
+            } else {
+                auto pathId = GetPathId(DatabaseNavigateResponse->GetRef());
+                auto result = NavigateKeySetResult.emplace(pathId, std::move(*DatabaseNavigateResponse));
+                ProcessNavigate(result.first->second, true);
+            }
+        }
+        if (FallbackToWhiteboard) {
+            RequestWhiteboard();
         } else {
-            if (Database) {
-                DatabaseNavigateResult = MakeRequestSchemeCacheNavigate(Database, 0);
+            if (FieldsNeeded(FieldsBsGroups)) {
+                GetGroupsResponse = RequestBSControllerGroups();
             }
-            if (FallbackToWhiteboard) {
-                RequestWhiteboard();
-            } else {
-                if (FieldsNeeded(FieldsBsGroups)) {
-                    GetGroupsResponse = RequestBSControllerGroups();
-                }
-                if (FieldsNeeded(FieldsBsPools)) {
-                    GetStoragePoolsResponse = RequestBSControllerPools();
-                }
-                if (FieldsNeeded(FieldsBsVSlots)) {
-                    GetVSlotsResponse = RequestBSControllerVSlots();
-                }
-                if (FieldsNeeded(FieldsBsPDisks)) {
-                    GetPDisksResponse = RequestBSControllerPDisks();
-                }
+            if (FieldsNeeded(FieldsBsPools)) {
+                GetStoragePoolsResponse = RequestBSControllerPools();
+            }
+            if (FieldsNeeded(FieldsBsVSlots)) {
+                GetVSlotsResponse = RequestBSControllerVSlots();
+            }
+            if (FieldsNeeded(FieldsBsPDisks)) {
+                GetPDisksResponse = RequestBSControllerPDisks();
             }
         }
+
         if (Requests == 0) {
             return ReplyAndPassAway();
         }
@@ -1100,19 +1093,7 @@ public:
         }
     }
 
-    void Handle(TEvTxProxySchemeCache::TEvNavigateKeySetResult::TPtr& ev) {
-        bool firstNavigate = (ev->Cookie == 0);
-        TPathId pathId = GetPathId(ev);
-        if (firstNavigate && DatabaseNavigateResult.has_value() && pathId) {
-            NavigateKeySetResult.emplace(pathId, std::move(*DatabaseNavigateResult));
-        }
-        auto itNavigateKeySetResult = NavigateKeySetResult.find(pathId);
-        if (itNavigateKeySetResult == NavigateKeySetResult.end()) {
-            BLOG_W("Invalid NavigateKeySetResult PathId: " << pathId << " Path: " << CanonizePath(ev->Get()->Request->ResultSet.begin()->Path));
-            return RequestDone();
-        }
-        auto& navigateResult(itNavigateKeySetResult->second);
-        navigateResult.Set(std::move(ev));
+    void ProcessNavigate(TRequestResponse<TEvTxProxySchemeCache::TEvNavigateKeySetResult>& navigateResult, bool firstNavigate) {
         if (navigateResult.IsOk()) {
             TString path = CanonizePath(navigateResult->Request->ResultSet.begin()->Path);
             TIntrusiveConstPtr<TSchemeCacheNavigate::TDomainDescription> domainDescription = navigateResult->Request->ResultSet.begin()->DomainDescription;
@@ -1134,6 +1115,22 @@ public:
                 }
             }
         }
+    }
+
+    void Handle(TEvTxProxySchemeCache::TEvNavigateKeySetResult::TPtr& ev) {
+        bool firstNavigate = (ev->Cookie == 0);
+        TPathId pathId = GetPathId(ev);
+        if (firstNavigate && DatabaseNavigateResponse && pathId) {
+            NavigateKeySetResult.emplace(pathId, std::move(*DatabaseNavigateResponse));
+        }
+        auto itNavigateKeySetResult = NavigateKeySetResult.find(pathId);
+        if (itNavigateKeySetResult == NavigateKeySetResult.end()) {
+            BLOG_W("Invalid NavigateKeySetResult PathId: " << pathId << " Path: " << CanonizePath(ev->Get()->Request->ResultSet.begin()->Path));
+            return RequestDone();
+        }
+        auto& navigateResult(itNavigateKeySetResult->second);
+        navigateResult.Set(std::move(ev));
+        ProcessNavigate(navigateResult, firstNavigate);
         RequestDone();
     }
 

+ 7 - 18
ydb/core/viewer/viewer_acl.h

@@ -22,28 +22,17 @@ public:
     {}
 
     void Bootstrap() override {
+        if (NeedToRedirect()) {
+            return;
+        }
         const auto& params(Event->Get()->Request.GetParams());
         Timeout = FromStringWithDefault<ui32>(params.Get("timeout"), 10000);
-        TString database;
-        if (params.Has("database")) {
-            database = params.Get("database");
-        }
-        bool direct = false;;
-        if (params.Has("direct")) {
-            direct = FromStringWithDefault<bool>(params.Get("direct"), direct);
-        }
-        direct |= !TBase::Event->Get()->Request.GetHeader("X-Forwarded-From-Node").empty(); // we're already forwarding
-        direct |= (database == AppData()->TenantName); // we're already on the right node or don't use database filter
-        if (database && !direct) {
-            return RedirectToDatabase(database); // to find some dynamic node and redirect query there
+        if (params.Has("path")) {
+            RequestSchemeCacheNavigate(params.Get("path"));
         } else {
-            if (params.Has("path")) {
-                RequestSchemeCacheNavigate(params.Get("path"));
-            } else {
-                return ReplyAndPassAway(Viewer->GetHTTPBADREQUEST(Event->Get(), "text/plain", "field 'path' is required"));
-            }
-            MergeRules = FromStringWithDefault<bool>(params.Get("merge_rules"), MergeRules);
+            return ReplyAndPassAway(Viewer->GetHTTPBADREQUEST(Event->Get(), "text/plain", "field 'path' is required"));
         }
+        MergeRules = FromStringWithDefault<bool>(params.Get("merge_rules"), MergeRules);
 
         Become(&TThis::StateRequestedDescribe, TDuration::MilliSeconds(Timeout), new TEvents::TEvWakeup());
     }

+ 10 - 73
ydb/core/viewer/viewer_autocomplete.h

@@ -33,7 +33,6 @@ class TJsonAutocomplete : public TViewerPipeClient {
         {}
     };
     THashMap<TString, TSchemaWordData> Dictionary;
-    TString Database;
     TVector<TString> Tables;
     TVector<TString> Paths;
     TString Prefix;
@@ -43,7 +42,6 @@ class TJsonAutocomplete : public TViewerPipeClient {
 
     std::optional<TNodeId> SubscribedNodeId;
     std::vector<TNodeId> TenantDynamicNodes;
-    bool Direct = false;
 public:
     TJsonAutocomplete(IViewer* viewer, NMon::TEvHttpInfo::TPtr& ev)
         : TBase(viewer, ev)
@@ -154,75 +152,28 @@ public:
         return request;
     }
 
+    void SendSchemeCacheRequest() {
+        SendRequest(MakeSchemeCacheID(), new TEvTxProxySchemeCache::TEvNavigateKeySet(MakeSchemeCacheRequest()));
+    }
+
     void Bootstrap() override {
         if (ViewerRequest) {
             // handle proxied request
             SendSchemeCacheRequest();
-        } else if (!Database) {
-            // autocomplete database list via console request
-            RequestConsoleListTenants();
         } else {
-            Direct |= !TBase::Event->Get()->Request.GetHeader("X-Forwarded-From-Node").empty(); // we're already forwarding
-            Direct |= (Database == AppData()->TenantName) || Database.empty(); // we're already on the right node or don't use database filter
-            if (Database && !Direct) {
-                // proxy request to a dynamic node of the specified database
-                return RedirectToDatabase(Database);
+            if (NeedToRedirect()) {
+                return;
             }
-            if (Requests == 0) {
-                // perform autocomplete without proxying
-                SendSchemeCacheRequest();
+            if (!Database) {
+                // autocomplete database list via console request
+                RequestConsoleListTenants();
             }
+            SendSchemeCacheRequest();
         }
 
         Become(&TThis::StateRequestedDescribe, TDuration::MilliSeconds(Timeout), new TEvents::TEvWakeup());
     }
 
-    void Connected(TEvInterconnect::TEvNodeConnected::TPtr &) {}
-
-    void Undelivered(TEvents::TEvUndelivered::TPtr &ev) {
-        if (!Direct && ev->Get()->SourceType == NViewer::TEvViewer::EvViewerRequest) {
-            Direct = true;
-            SendSchemeCacheRequest(); // fallback
-            RequestDone();
-        }
-    }
-
-    void Disconnected(TEvInterconnect::TEvNodeDisconnected::TPtr &) {
-        if (!Direct) {
-            Direct = true;
-            SendSchemeCacheRequest(); // fallback
-            RequestDone();
-        }
-    }
-
-    void SendSchemeCacheRequest() {
-        SendRequest(MakeSchemeCacheID(), new TEvTxProxySchemeCache::TEvNavigateKeySet(MakeSchemeCacheRequest()));
-    }
-
-    void SendDynamicNodeAutocompleteRequest() {
-        ui64 hash = std::hash<TString>()(Event->Get()->Request.GetRemoteAddr());
-
-        auto itPos = std::next(TenantDynamicNodes.begin(), hash % TenantDynamicNodes.size());
-        std::nth_element(TenantDynamicNodes.begin(), itPos, TenantDynamicNodes.end());
-
-        TNodeId nodeId = *itPos;
-        SubscribedNodeId = nodeId;
-        TActorId viewerServiceId = MakeViewerID(nodeId);
-
-        THolder<TEvViewer::TEvViewerRequest> request = MakeHolder<TEvViewer::TEvViewerRequest>();
-        request->Record.SetTimeout(Timeout);
-        auto autocompleteRequest = request->Record.MutableAutocompleteRequest();
-        autocompleteRequest->SetDatabase(Database);
-        for (TString& path: Paths) {
-            autocompleteRequest->AddTables(path);
-        }
-        autocompleteRequest->SetPrefix(Prefix);
-        autocompleteRequest->SetLimit(Limit);
-
-        ViewerWhiteboardCookie cookie(NKikimrViewer::TEvViewerRequest::kAutocompleteRequest, nodeId);
-        SendRequest(viewerServiceId, request.Release(), IEventHandle::FlagTrackDelivery | IEventHandle::FlagSubscribeOnSession, cookie.ToUi64());
-    }
-
     void PassAway() override {
         if (SubscribedNodeId.has_value()) {
             Send(TActivationContext::InterconnectProxy(SubscribedNodeId.value()), new TEvents::TEvUnsubscribe());
@@ -235,10 +186,6 @@ public:
         switch (ev->GetTypeRewrite()) {
             hFunc(NConsole::TEvConsole::TEvListTenantsResponse, Handle);
             hFunc(TEvTxProxySchemeCache::TEvNavigateKeySetResult, Handle);
-            hFunc(TEvents::TEvUndelivered, Undelivered);
-            hFunc(TEvInterconnect::TEvNodeConnected, Connected);
-            hFunc(TEvInterconnect::TEvNodeDisconnected, Disconnected);
-            hFunc(TEvViewer::TEvViewerResponse, Handle);
             cFunc(TEvents::TSystem::Wakeup, HandleTimeout);
         }
     }
@@ -399,16 +346,6 @@ public:
         PassAway();
     }
 
-    void Handle(TEvViewer::TEvViewerResponse::TPtr& ev) {
-        if (ev.Get()->Get()->Record.HasAutocompleteResponse()) {
-            ProxyResult = ev.Release()->Release();
-        } else {
-            Direct = true;
-            SendSchemeCacheRequest(); // fallback
-        }
-        RequestDone();
-    }
-
     void HandleTimeout() {
         if (ViewerRequest) {
             Result.add_error("Request timed out");

+ 9 - 22
ydb/core/viewer/viewer_check_access.h

@@ -22,33 +22,20 @@ public:
     {}
 
     void Bootstrap() override {
+        if (NeedToRedirect()) {
+            return;
+        }
         const auto& params(Event->Get()->Request.GetParams());
         ui32 timeout = FromStringWithDefault<ui32>(params.Get("timeout"), 10000);
-        TString database;
-        if (params.Has("database")) {
-            database = params.Get("database");
+        if (params.Has("permissions")) {
+            Split(params.Get("permissions"), ",", Permissions);
         } else {
-            return ReplyAndPassAway(GetHTTPBADREQUEST("text/plain", "field 'database' is required"));
-        }
-        bool direct = false;;
-        if (params.Has("direct")) {
-            direct = FromStringWithDefault<bool>(params.Get("direct"), direct);
+            return ReplyAndPassAway(GetHTTPBADREQUEST("text/plain", "field 'permissions' is required"));
         }
-        direct |= !TBase::Event->Get()->Request.GetHeader("X-Forwarded-From-Node").empty(); // we're already forwarding
-        direct |= (database == AppData()->TenantName); // we're already on the right node or don't use database filter
-        if (database && !direct) {
-            return RedirectToDatabase(database); // to find some dynamic node and redirect query there
+        if (params.Has("path")) {
+            RequestSchemeCacheNavigate(params.Get("path"));
         } else {
-            if (params.Has("permissions")) {
-                Split(params.Get("permissions"), ",", Permissions);
-            } else {
-                return ReplyAndPassAway(GetHTTPBADREQUEST("text/plain", "field 'permissions' is required"));
-            }
-            if (params.Has("path")) {
-                RequestSchemeCacheNavigate(params.Get("path"));
-            } else {
-                return ReplyAndPassAway(GetHTTPBADREQUEST("text/plain", "field 'path' is required"));
-            }
+            return ReplyAndPassAway(GetHTTPBADREQUEST("text/plain", "field 'path' is required"));
         }
         Become(&TThis::StateRequestedNavigate, TDuration::MilliSeconds(timeout), new TEvents::TEvWakeup());
     }

+ 5 - 12
ydb/core/viewer/viewer_feature_flags.h

@@ -12,7 +12,6 @@ class TJsonFeatureFlags : public TViewerPipeClient {
     using TBase = TViewerPipeClient;
     TJsonSettings JsonSettings;
     ui32 Timeout = 0;
-    TString FilterDatabase;
     THashSet<TString> FilterFeatures;
     bool ChangedOnly = false;
     TRequestResponse<NConsole::TEvConsole::TEvListTenantsResponse> TenantsResponse;
@@ -26,23 +25,17 @@ public:
     {}
 
     void Bootstrap() override {
+        if (NeedToRedirect()) {
+            return;
+        }
         const auto& params(Event->Get()->Request.GetParams());
         JsonSettings.EnumAsNumbers = !FromStringWithDefault<bool>(params.Get("enums"), true);
         JsonSettings.UI64AsString = !FromStringWithDefault<bool>(params.Get("ui64"), false);
-        FilterDatabase = params.Get("database");
         StringSplitter(params.Get("features")).Split(',').SkipEmpty().Collect(&FilterFeatures);
-        bool direct = FromStringWithDefault<bool>(params.Get("direct"), false);
         Timeout = FromStringWithDefault<ui32>(params.Get("timeout"), 10000);
         ChangedOnly = FromStringWithDefault<bool>(params.Get("changed"), ChangedOnly);
-
-        direct |= !TBase::Event->Get()->Request.GetHeader("X-Forwarded-From-Node").empty(); // we're already forwarding
-        direct |= (FilterDatabase == AppData()->TenantName); // we're already on the right node
-        if (FilterDatabase && !direct) {
-            return RedirectToDatabase(FilterDatabase); // to find some dynamic node and redirect query there
-        }
-
-        if (FilterDatabase) {
-            PathNameNavigateKeySetResults[FilterDatabase] = MakeRequestSchemeCacheNavigate(FilterDatabase);
+        if (Database && DatabaseNavigateResponse) {
+            PathNameNavigateKeySetResults[Database] = std::move(*DatabaseNavigateResponse);
         } else {
             TenantsResponse = MakeRequestConsoleListTenants();
         }

+ 41 - 50
ydb/core/viewer/viewer_nodes.h

@@ -61,9 +61,7 @@ class TJsonNodes : public TViewerPipeClient {
 
     std::optional<TRequestResponse<TEvInterconnect::TEvNodesInfo>> NodesInfoResponse;
     std::optional<TRequestResponse<TEvWhiteboard::TEvNodeStateResponse>> NodeStateResponse;
-    std::optional<TRequestResponse<TEvTxProxySchemeCache::TEvNavigateKeySetResult>> DatabaseNavigateResponse;
     std::optional<TRequestResponse<TEvStateStorage::TEvBoardInfo>> DatabaseBoardInfoResponse;
-    std::optional<TRequestResponse<TEvTxProxySchemeCache::TEvNavigateKeySetResult>> ResourceNavigateResponse;
     std::optional<TRequestResponse<TEvStateStorage::TEvBoardInfo>> ResourceBoardInfoResponse;
     std::optional<TRequestResponse<TEvTxProxySchemeCache::TEvNavigateKeySetResult>> PathNavigateResponse;
     std::unordered_map<TTabletId, TRequestResponse<TEvHive::TEvResponseHiveNodeStats>> HiveNodeStats;
@@ -95,9 +93,7 @@ class TJsonNodes : public TViewerPipeClient {
 
     ETimeoutTag CurrentTimeoutState = NoTimeout;
 
-    TString Database;
     TString SharedDatabase;
-    bool Direct = false;
     bool FilterDatabase = false;
     bool HasDatabaseNodes = false;
     TPathId FilterPathId;
@@ -556,10 +552,6 @@ public:
         if (UptimeSeconds || ProblemNodesOnly || !Filter.empty()) {
             FieldsRequired.set(+ENodeFields::SystemState);
         }
-        Database = params.Get("database");
-        if (!Database) {
-            Database = params.Get("tenant");
-        }
         FilterPath = params.Get("path");
         if (FilterPath && !Database) {
             Database = FilterPath;
@@ -667,55 +659,54 @@ public:
     }
 
     void Bootstrap() override {
-        Direct |= !TBase::Event->Get()->Request.GetHeader("X-Forwarded-From-Node").empty(); // we're already forwarding
-        Direct |= (Database == AppData()->TenantName) || Database.empty(); // we're already on the right node or don't use database filter
+        if (TBase::NeedToRedirect()) {
+            return;
+        }
 
-        if (Database && !Direct) {
-            return RedirectToDatabase(Database); // to find some dynamic node and redirect query there
-        } else {
-            if (FieldsNeeded(FieldsNodeInfo)) {
-                NodesInfoResponse = MakeRequest<TEvInterconnect::TEvNodesInfo>(GetNameserviceActorId(), new TEvInterconnect::TEvListNodes());
-                NodeStateResponse = MakeWhiteboardRequest(TActivationContext::ActorSystem()->NodeId, new TEvWhiteboard::TEvNodeStateRequest());
-            }
-            if (FilterStoragePool || !FilterGroupIds.empty()) {
-                FilterDatabase = false; // we disable database filter if we're filtering by pool or group
-            }
-            if (FilterDatabase) {
+        if (FieldsNeeded(FieldsNodeInfo)) {
+            NodesInfoResponse = MakeRequest<TEvInterconnect::TEvNodesInfo>(GetNameserviceActorId(), new TEvInterconnect::TEvListNodes());
+            NodeStateResponse = MakeWhiteboardRequest(TActivationContext::ActorSystem()->NodeId, new TEvWhiteboard::TEvNodeStateRequest());
+        }
+        if (FilterStoragePool || !FilterGroupIds.empty()) {
+            FilterDatabase = false; // we disable database filter if we're filtering by pool or group
+        }
+        if (FilterDatabase) {
+            if (!DatabaseNavigateResponse) {
                 DatabaseNavigateResponse = MakeRequestSchemeCacheNavigate(Database, ENavigateRequestDatabase);
-                if (!FieldsNeeded(FieldsHiveNodeStat) && !(FilterPath && FieldsNeeded(FieldsTablets))) {
-                    DatabaseBoardInfoResponse = MakeRequestStateStorageEndpointsLookup(Database, EBoardInfoRequestDatabase);
-                }
             }
-            if (FilterPath && FieldsNeeded(FieldsTablets)) {
-                PathNavigateResponse = MakeRequestSchemeCacheNavigate(FilterPath, ENavigateRequestPath);
+            if (!FieldsNeeded(FieldsHiveNodeStat) && !(FilterPath && FieldsNeeded(FieldsTablets))) {
+                DatabaseBoardInfoResponse = MakeRequestStateStorageEndpointsLookup(Database, EBoardInfoRequestDatabase);
             }
-            if (FilterStoragePool) {
-                StoragePoolsResponse = RequestBSControllerPools();
-                GroupsResponse = RequestBSControllerGroups();
-                VSlotsResponse = RequestBSControllerVSlots();
-                FilterStorageStage = EFilterStorageStage::Pools;
-            } else if (!FilterGroupIds.empty()) {
-                VSlotsResponse = RequestBSControllerVSlots();
-                FilterStorageStage = EFilterStorageStage::VSlots;
-            }
-            if (With != EWith::Everything) {
+        }
+        if (FilterPath && FieldsNeeded(FieldsTablets)) {
+            PathNavigateResponse = MakeRequestSchemeCacheNavigate(FilterPath, ENavigateRequestPath);
+        }
+        if (FilterStoragePool) {
+            StoragePoolsResponse = RequestBSControllerPools();
+            GroupsResponse = RequestBSControllerGroups();
+            VSlotsResponse = RequestBSControllerVSlots();
+            FilterStorageStage = EFilterStorageStage::Pools;
+        } else if (!FilterGroupIds.empty()) {
+            VSlotsResponse = RequestBSControllerVSlots();
+            FilterStorageStage = EFilterStorageStage::VSlots;
+        }
+        if (With != EWith::Everything) {
+            PDisksResponse = RequestBSControllerPDisks();
+        }
+        if (ProblemNodesOnly || GroupBy == ENodeFields::Uptime) {
+            FieldsRequired.set(+ENodeFields::SystemState);
+            TTabletId rootHiveId = AppData()->DomainsInfo->GetHive();
+            HivesToAsk.push_back(rootHiveId);
+            if (!PDisksResponse) {
                 PDisksResponse = RequestBSControllerPDisks();
             }
-            if (ProblemNodesOnly || GroupBy == ENodeFields::Uptime) {
-                FieldsRequired.set(+ENodeFields::SystemState);
-                TTabletId rootHiveId = AppData()->DomainsInfo->GetHive();
-                HivesToAsk.push_back(rootHiveId);
-                if (!PDisksResponse) {
-                    PDisksResponse = RequestBSControllerPDisks();
-                }
-            }
-            if (FieldsNeeded(FieldsHiveNodeStat) && !FilterDatabase && !FilterPath) {
-                TTabletId rootHiveId = AppData()->DomainsInfo->GetHive();
-                HivesToAsk.push_back(rootHiveId);
-            }
-            Schedule(TDuration::MilliSeconds(Timeout * 50 / 100), new TEvents::TEvWakeup(TimeoutTablets)); // 50% timeout (for tablets)
-            TBase::Become(&TThis::StateWork, TDuration::MilliSeconds(Timeout), new TEvents::TEvWakeup(TimeoutFinal));
         }
+        if (FieldsNeeded(FieldsHiveNodeStat) && !FilterDatabase && !FilterPath) {
+            TTabletId rootHiveId = AppData()->DomainsInfo->GetHive();
+            HivesToAsk.push_back(rootHiveId);
+        }
+        Schedule(TDuration::MilliSeconds(Timeout * 50 / 100), new TEvents::TEvWakeup(TimeoutTablets)); // 50% timeout (for tablets)
+        TBase::Become(&TThis::StateWork, TDuration::MilliSeconds(Timeout), new TEvents::TEvWakeup(TimeoutFinal));
     }
 
     void InvalidateNodes() {

+ 4 - 11
ydb/core/viewer/viewer_query.h

@@ -23,13 +23,11 @@ class TJsonQuery : public TViewerPipeClient {
     ui32 Timeout = 0;
     std::vector<std::vector<Ydb::ResultSet>> ResultSets;
     TString Query;
-    TString Database;
     TString Action;
     TString Stats;
     TString Syntax;
     TString QueryId;
     TString TransactionMode;
-    bool Direct = false;
     bool IsBase64Encode = true;
     int LimitRows = 10000;
     int TotalRows = 0;
@@ -146,8 +144,10 @@ public:
     }
 
     void Bootstrap() override {
+        if (NeedToRedirect()) {
+            return;
+        }
         const auto& params(Event->Get()->Request.GetParams());
-        InitConfig(params);
         ParseCgiParameters(params);
         if (IsPostContent()) {
             TStringBuf content = Event->Get()->Request.GetPostContent();
@@ -159,14 +159,7 @@ public:
             return TBase::ReplyAndPassAway(GetHTTPBADREQUEST("text/plain", "Query is empty"), "EmptyQuery");
         }
 
-        Direct |= !TBase::Event->Get()->Request.GetHeader("X-Forwarded-From-Node").empty(); // we're already forwarding
-        Direct |= (Database == AppData()->TenantName); // we're already on the right node
-
-        if (Database && !Direct) {
-            return RedirectToDatabase(Database); // to find some dynamic node and redirect query there
-        } else {
-            SendKpqProxyRequest();
-        }
+        SendKpqProxyRequest();
         Become(&TThis::StateWork, TDuration::MilliSeconds(Timeout), new TEvents::TEvWakeup());
     }
 

+ 4 - 76
ydb/core/viewer/viewer_render.h

@@ -17,13 +17,8 @@ class TJsonRender : public TViewerPipeClient {
     TEvViewer::TEvViewerRequest::TPtr ViewerRequest;
     ui32 Timeout = 0;
     std::vector<TString> Metrics;
-    TString Database;
     TCgiParameters Params;
 
-    std::optional<TNodeId> SubscribedNodeId;
-    std::vector<TNodeId> TenantDynamicNodes;
-    bool Direct = false;
-    bool MadeProxyRequest = false;
 public:
     TJsonRender(IViewer* viewer, NMon::TEvHttpInfo::TPtr& ev)
         : TViewerPipeClient(viewer, ev)
@@ -31,8 +26,6 @@ public:
         const auto& params(Event->Get()->Request.GetParams());
 
         InitConfig(params);
-        Database = params.Get("database");
-        Direct = FromStringWithDefault<bool>(params.Get("direct"), Direct);
         Timeout = FromStringWithDefault<ui32>(params.Get("timeout"), 30000);
     }
 
@@ -48,6 +41,9 @@ public:
     }
 
     void Bootstrap() override {
+        if (NeedToRedirect()) {
+            return;
+        }
         auto postData = Event
             ? Event->Get()->Request.GetPostContent()
             : ViewerRequest->Get()->Record.GetRenderRequest().GetContent();
@@ -67,15 +63,7 @@ public:
                     ++num;
                 }
             }
-            //StringSplitter(Params.Get("target")).Split(',').SkipEmpty().Collect(&Metrics);
-            Direct |= !TBase::Event->Get()->Request.GetHeader("X-Forwarded-From-Node").empty(); // we're already forwarding
-            Direct |= (Database == AppData()->TenantName); // we're already on the right node
-            if (Database && !Direct) {
-                return RedirectToDatabase(Database); // to find some dynamic node and redirect query there
-            }
-            if (Requests == 0) {
-                SendGraphRequest();
-            }
+            SendGraphRequest();
         } else {
             ReplyAndPassAway(Viewer->GetHTTPBADREQUEST(Event->Get(), {}, "Bad Request"));
             return;
@@ -84,65 +72,14 @@ public:
         Become(&TThis::StateWork, TDuration::MilliSeconds(Timeout), new TEvents::TEvWakeup());
     }
 
-    void PassAway() override {
-        if (SubscribedNodeId.has_value()) {
-            Send(TActivationContext::InterconnectProxy(SubscribedNodeId.value()), new TEvents::TEvUnsubscribe());
-        }
-        TBase::PassAway();
-        BLOG_TRACE("PassAway()");
-    }
-
     STATEFN(StateWork) {
         switch (ev->GetTypeRewrite()) {
-            hFunc(TEvents::TEvUndelivered, Undelivered);
-            hFunc(TEvInterconnect::TEvNodeConnected, Connected);
-            hFunc(TEvInterconnect::TEvNodeDisconnected, Disconnected);
-            hFunc(TEvViewer::TEvViewerResponse, Handle);
             hFunc(NGraph::TEvGraph::TEvMetricsResult, Handle);
-
             cFunc(TEvents::TSystem::Wakeup, HandleTimeout);
         }
     }
 
-    void Connected(TEvInterconnect::TEvNodeConnected::TPtr &) {}
-
-    void Undelivered(TEvents::TEvUndelivered::TPtr &ev) {
-        if (ev->Get()->SourceType == NViewer::TEvViewer::EvViewerRequest) {
-            SendGraphRequest();
-        }
-    }
-
-    void Disconnected(TEvInterconnect::TEvNodeDisconnected::TPtr &) {
-        SendGraphRequest();
-    }
-
-    void SendDynamicNodeRenderRequest() {
-        ui64 hash = std::hash<TString>()(Event->Get()->Request.GetRemoteAddr());
-
-        auto itPos = std::next(TenantDynamicNodes.begin(), hash % TenantDynamicNodes.size());
-        std::nth_element(TenantDynamicNodes.begin(), itPos, TenantDynamicNodes.end());
-
-        TNodeId nodeId = *itPos;
-        SubscribedNodeId = nodeId;
-        TActorId viewerServiceId = MakeViewerID(nodeId);
-
-        THolder<TEvViewer::TEvViewerRequest> request = MakeHolder<TEvViewer::TEvViewerRequest>();
-        request->Record.SetTimeout(Timeout);
-        auto renderRequest = request->Record.MutableRenderRequest();
-        renderRequest->SetUri(TString(Event->Get()->Request.GetUri()));
-
-        TStringBuf content = Event->Get()->Request.GetPostContent();
-        renderRequest->SetContent(TString(content));
-
-        ViewerWhiteboardCookie cookie(NKikimrViewer::TEvViewerRequest::kRenderRequest, nodeId);
-        SendRequest(viewerServiceId, request.Release(), IEventHandle::FlagTrackDelivery | IEventHandle::FlagSubscribeOnSession, cookie.ToUi64());
-    }
-
     void SendGraphRequest() {
-        if (MadeProxyRequest) {
-            return;
-        }
-        MadeProxyRequest = true;
         NKikimrGraph::TEvGetMetrics getRequest;
         if (Metrics.size() > 0) {
             for (const auto& metric : Metrics) {
@@ -225,15 +162,6 @@ public:
         HandleRenderResponse(ev->Get()->Record);
     }
 
-    void Handle(TEvViewer::TEvViewerResponse::TPtr& ev) {
-        auto& record = ev.Get()->Get()->Record;
-        if (record.HasRenderResponse()) {
-            HandleRenderResponse(*(record.MutableRenderResponse()));
-        } else {
-            SendGraphRequest(); // fallback
-        }
-    }
-
     void HandleTimeout() {
         if (Event) {
             ReplyAndPassAway(Viewer->GetHTTPGATEWAYTIMEOUT(Event->Get()));

Некоторые файлы не были показаны из-за большого количества измененных файлов