diff --git a/ydb/core/mind/hive/monitoring.cpp b/ydb/core/mind/hive/monitoring.cpp index 9248554b1027..3e945ccddeca 100644 --- a/ydb/core/mind/hive/monitoring.cpp +++ b/ydb/core/mind/hive/monitoring.cpp @@ -2883,6 +2883,7 @@ class TReassignTabletWaitActor : public TActor, public ui32 TabletsTotal = 0; ui32 TabletsDone = 0; THive* Hive; + NJson::TJsonValue Response; static constexpr NKikimrServices::TActivity::EType ActorActivityType() { return NKikimrServices::TActivity::HIVE_MON_REQUEST; @@ -2912,9 +2913,14 @@ class TReassignTabletWaitActor : public TActor, public ++TabletsTotal; } + void AddContext(const TString& key, const TString& value) { + Response[key] = value; + } + void CheckCompletion() { if (TabletsDone >= TabletsTotal) { - Send(Source, new NMon::TEvRemoteJsonInfoRes(TStringBuilder() << "{\"total\":" << TabletsDone << "}")); + Response["total"] = TabletsDone; + Send(Source, new NMon::TEvRemoteJsonInfoRes(NJson::WriteJson(Response, false))); PassAway(); } } @@ -2934,32 +2940,77 @@ class TReassignTabletWaitActor : public TActor, public class TTxMonEvent_ReassignTablet : public TTransactionBase { public: + struct TTabletFilter { + struct TAllTablets {}; + + std::variant TabletId; + TTabletTypes::EType TabletType = TTabletTypes::TypeInvalid; + + explicit TTabletFilter(const TCgiParameters& cgi) { + auto tablet = cgi.Get("tablet"); + if (tablet == "all") { + TabletId = TAllTablets(); + } else if (auto tabletId = TryFromString(tablet)) { + TabletId = *tabletId; + } + + TabletType = (TTabletTypes::EType)FromStringWithDefault(cgi.Get("type"), TabletType); + } + + bool CheckTabletId(const TLeaderTabletInfo* tablet) const { + if (auto* tabletId = std::get_if(&TabletId)) { + return tablet->Id == *tabletId; + } + return true; + } + + bool CheckTabletType(const TLeaderTabletInfo* tablet) const { + return TabletType == TTabletTypes::TypeInvalid || TabletType == tablet->Type; + } + + bool operator()(const TLeaderTabletInfo* tablet) const { + return CheckTabletId(tablet) && CheckTabletType(tablet); + } + + bool IsValidFilter(TString& error) const { + if (std::holds_alternative(TabletId)) { + error = "must specify tablet"; + return false; + } + return true; + } + + bool AllowsEverything() const { + return std::holds_alternative(TabletId) && TabletType == TTabletTypes::TypeInvalid; + } + + }; + TAutoPtr Event; const TActorId Source; - TTabletId TabletId = 0; - TTabletTypes::EType TabletType = TTabletTypes::TypeInvalid; + TTabletFilter TabletFilter; TVector TabletChannels; ui32 GroupId = 0; TVector ForcedGroupIds; - int TabletPercent = 100; TString Error; bool Wait = true; bool Async = false; + bool BypassLimit = false; + + static constexpr size_t REASSIGN_SOFT_LIMIT = 500; TTxMonEvent_ReassignTablet(const TActorId& source, NMon::TEvRemoteHttpInfo::TPtr& ev, TSelf* hive) : TBase(hive) , Event(ev->Release()) , Source(source) + , TabletFilter(Event->Cgi()) { - TabletId = FromStringWithDefault(Event->Cgi().Get("tablet"), TabletId); - TabletType = (TTabletTypes::EType)FromStringWithDefault(Event->Cgi().Get("type"), TabletType); TabletChannels = Scan(SplitString(Event->Cgi().Get("channel"), ",")); - TabletPercent = FromStringWithDefault(Event->Cgi().Get("percent"), TabletPercent); GroupId = FromStringWithDefault(Event->Cgi().Get("group"), GroupId); ForcedGroupIds = Scan(SplitString(Event->Cgi().Get("forcedGroup"), ",")); - TabletPercent = std::min(std::abs(TabletPercent), 100); Wait = FromStringWithDefault(Event->Cgi().Get("wait"), Wait); Async = FromStringWithDefault(Event->Cgi().Get("async"), Async); + BypassLimit = FromStringWithDefault(Event->Cgi().Get("bypassLimit"), BypassLimit); } TTxType GetTxType() const override { return NHive::TXTYPE_MON_REASSIGN_TABLET; } @@ -2993,29 +3044,17 @@ class TTxMonEvent_ReassignTablet : public TTransactionBase { Error = "cannot provide force groups in async mode"; return true; } - TVector tablets; - if (TabletId != 0) { - TLeaderTabletInfo* tablet = Self->FindTablet(TabletId); - if (tablet != nullptr) { - tablets.push_back(tablet); - } - } else if (TabletType != TTabletTypes::TypeInvalid) { - for (auto& pr : Self->Tablets) { - if (pr.second.Type == TabletType) { - tablets.push_back(&pr.second); - } - } - } else { - for (auto& pr : Self->Tablets) { - tablets.push_back(&pr.second); - } + if (!TabletFilter.IsValidFilter(Error)) { + return true; } - if (TabletPercent != 100) { - std::sort(tablets.begin(), tablets.end(), [this](TLeaderTabletInfo* a, TLeaderTabletInfo* b) -> bool { - return GetMaxTimestamp(a) < GetMaxTimestamp(b); - }); - tablets.resize(tablets.size() * TabletPercent / 100); + if (TabletFilter.AllowsEverything() && GroupId == 0) { + Error = "cannot reassign all tablets"; + return true; } + auto tablets = Self->Tablets + | std::views::transform([](auto&& p) -> TLeaderTabletInfo* { return &p.second; }) + | std::views::filter(TabletFilter); + TVector> operations; TActorId waitActorId; TReassignTabletWaitActor* waitActor = nullptr; @@ -3054,6 +3093,12 @@ class TTxMonEvent_ReassignTablet : public TTransactionBase { waitActor->AddTablet(tablet); } operations.emplace_back(new TEvHive::TEvReassignTablet(tablet->Id, channels, forcedGroupIds, Async)); + if (!BypassLimit && operations.size() >= REASSIGN_SOFT_LIMIT) { + if (Wait) { + waitActor->AddContext("limit_reached", "true"); + } + break; + } } if (Wait) { waitActor->CheckCompletion();