Skip to content

Commit

Permalink
vdisk evict
Browse files Browse the repository at this point in the history
  • Loading branch information
StekPerepolnen committed May 31, 2024
1 parent 459b977 commit 0f363b2
Show file tree
Hide file tree
Showing 4 changed files with 279 additions and 1 deletion.
3 changes: 2 additions & 1 deletion ydb/core/viewer/json_handlers_vdisk.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,15 @@
#include "json_vdiskstat.h"
#include "json_getblob.h"
#include "json_blobindexstat.h"

#include "json_vdisk_evict.h"

namespace NKikimr::NViewer {

void InitVDiskJsonHandlers(TJsonHandlers& jsonHandlers) {
jsonHandlers.AddHandler("/vdisk/vdiskstat", new TJsonHandler<TJsonVDiskStat>);
jsonHandlers.AddHandler("/vdisk/getblob", new TJsonHandler<TJsonGetBlob>);
jsonHandlers.AddHandler("/vdisk/blobindexstat", new TJsonHandler<TJsonBlobIndexStat>);
jsonHandlers.AddHandler("/vdisk/evict", new TJsonHandler<TJsonVDiskEvict>);
}

}
15 changes: 15 additions & 0 deletions ydb/core/viewer/json_pipe_req.h
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,21 @@ class TViewerPipeClient : public TActorBootstrapped<TDerived> {
SendRequestToPipe(pipeClient, request.Release());
}

void RequestBSControllerVDiskEvict(ui32 groupId, ui32 groupGeneration, ui32 failRealmIdx, ui32 failDomainIdx, ui32 vdiskIdx, bool force = false) {
TActorId pipeClient = ConnectTabletPipe(GetBSControllerId());
THolder<TEvBlobStorage::TEvControllerConfigRequest> request = MakeHolder<TEvBlobStorage::TEvControllerConfigRequest>();
auto* evictVDisk = request->Record.MutableRequest()->AddCommand()->MutableReassignGroupDisk();
evictVDisk->SetGroupId(groupId);
evictVDisk->SetGroupGeneration(groupGeneration);
evictVDisk->SetFailRealmIdx(failRealmIdx);
evictVDisk->SetFailDomainIdx(failDomainIdx);
evictVDisk->SetVDiskIdx(vdiskIdx);
if (force) {
request->Record.MutableRequest()->SetIgnoreDegradedGroupsChecks(true);
}
SendRequestToPipe(pipeClient, request.Release());
}

void RequestSchemeCacheNavigate(const TString& path) {
THolder<NSchemeCache::TSchemeCacheNavigate> request = MakeHolder<NSchemeCache::TSchemeCacheNavigate>();
NSchemeCache::TSchemeCacheNavigate::TEntry entry;
Expand Down
261 changes: 261 additions & 0 deletions ydb/core/viewer/json_vdisk_evict.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,261 @@
#pragma once
#include <ydb/library/actors/core/actor_bootstrapped.h>
#include <ydb/library/actors/core/interconnect.h>
#include <ydb/library/actors/core/mon.h>
#include <ydb/library/services/services.pb.h>
#include <ydb/core/viewer/json/json.h>
#include <ydb/core/util/wildcard.h>
#include <library/cpp/json/json_writer.h>
#include "viewer.h"
#include "json_pipe_req.h"

namespace NKikimr {
namespace NViewer {

using namespace NActors;

class TJsonVDiskEvict : public TViewerPipeClient<TJsonVDiskEvict> {
enum EEv {
EvRetryNodeRequest = EventSpaceBegin(NActors::TEvents::ES_PRIVATE),
EvEnd
};

static_assert(EvEnd < EventSpaceEnd(NActors::TEvents::ES_PRIVATE), "expect EvEnd < EventSpaceEnd(TEvents::ES_PRIVATE)");

struct TEvRetryNodeRequest : NActors::TEventLocal<TEvRetryNodeRequest, EvRetryNodeRequest> {
TEvRetryNodeRequest()
{}
};

protected:
using TThis = TJsonVDiskEvict;
using TBase = TViewerPipeClient<TThis>;
IViewer* Viewer;
NMon::TEvHttpInfo::TPtr Event;
TJsonSettings JsonSettings;
bool AllEnums = false;
ui32 Timeout = 0;
ui32 ActualRetries = 0;
ui32 Retries = 0;
TDuration RetryPeriod = TDuration::MilliSeconds(500);

std::unique_ptr<TEvBlobStorage::TEvControllerConfigResponse> Response;

ui32 GroupId = 0;
ui32 GroupGeneration = 0;
ui32 FailRealmIdx = 0;
ui32 FailDomainIdx = 0;
ui32 VdiskIdx = 0;
bool Force = false;

public:
static constexpr NKikimrServices::TActivity::EType ActorActivityType() {
return NKikimrServices::TActivity::VIEWER_HANDLER;
}

TJsonVDiskEvict(IViewer* viewer, NMon::TEvHttpInfo::TPtr& ev)
: Viewer(viewer)
, Event(ev)
{}

template <class T>
inline T ParseRequiredParam(const TCgiParameters& params, const std::string& s, const T& def) {
auto res = FromStringWithDefault<T>(params.Get(s), def);
if (res == def) {
TBase::Send(Event->Sender, new NMon::TEvHttpInfoRes(
Viewer->GetHTTPBADREQUEST(Event->Get(), "text/plain", TStringBuilder() << "field '" << s << "' is required"),
0, NMon::IEvHttpInfoRes::EContentType::Custom));
PassAway();
return def;
}
return res;
}

void Bootstrap() {
const auto& params(Event->Get()->Request.GetParams());
GroupId = ParseRequiredParam<ui32>(params, "group_id", Max<ui32>());
GroupGeneration = ParseRequiredParam<ui32>(params, "group_generation_id", Max<ui32>());
FailRealmIdx = ParseRequiredParam<ui32>(params, "fail_realm_idx", Max<ui32>());
FailDomainIdx = ParseRequiredParam<ui32>(params, "fail_domain_ids", Max<ui32>());
VdiskIdx = ParseRequiredParam<ui32>(params, "vdisk_idx", Max<ui32>());

if (Event->Get()->Request.GetMethod() != HTTP_METHOD_POST) {
TBase::Send(Event->Sender, new NMon::TEvHttpInfoRes(
Viewer->GetHTTPBADREQUEST(Event->Get(), "text/plain", "Only POST method is allowed"),
0, NMon::IEvHttpInfoRes::EContentType::Custom));
return PassAway();
}
TBase::InitConfig(params);

Force = FromStringWithDefault<bool>(params.Get("force"), false);
Timeout = FromStringWithDefault<ui32>(params.Get("timeout"), 10000);
Retries = FromStringWithDefault<ui32>(params.Get("retries"), 0);
RetryPeriod = TDuration::MilliSeconds(FromStringWithDefault<ui32>(params.Get("retry_period"), RetryPeriod.MilliSeconds()));

SendRequest();

TBase::Become(&TThis::StateWork, TDuration::MilliSeconds(Timeout), new TEvents::TEvWakeup());
}

STATEFN(StateWork) {
switch (ev->GetTypeRewrite()) {
hFunc(TEvBlobStorage::TEvControllerConfigResponse, Handle);
cFunc(TEvRetryNodeRequest::EventType, HandleRetry);
cFunc(TEvents::TEvUndelivered::EventType, Undelivered);
cFunc(TEvents::TSystem::Wakeup, HandleTimeout);
}
}

void SendRequest() {
RequestBSControllerVDiskEvict(GroupId, GroupGeneration, FailRealmIdx, FailDomainIdx, VdiskIdx, Force);
}

bool RetryRequest() {
if (Retries) {
if (++ActualRetries <= Retries) {
TBase::Schedule(RetryPeriod, new TEvRetryNodeRequest());
return true;
}
}
return false;
}

void Undelivered() {
if (!RetryRequest()) {
TBase::RequestDone();
}
}

void Handle(TEvBlobStorage::TEvControllerConfigResponse::TPtr& ev) {
Response.reset(ev->Release().Release());
ReplyAndPassAway();
}

void HandleRetry() {
SendRequest();
}

void HandleTimeout() {
Send(Event->Sender, new NMon::TEvHttpInfoRes(
Viewer->GetHTTPGATEWAYTIMEOUT(Event->Get(), "text/plain", "Timeout receiving response from NodeWarden"),
0, NMon::IEvHttpInfoRes::EContentType::Custom));
}

void PassAway() override {
TBase::PassAway();
}

void TryToTranslateFromBSC2Human(TString& bscError, bool& forceRetryPossible) {
if (IsMatchesWildcard(bscError, "GroupId# * ExpectedStatus# *")) {
TStringBuf groupId = TStringBuf(bscError).After(' ').Before(' ');
TStringBuf expectedStatus = TStringBuf(bscError).After('#').After('#').After(' ');
if (expectedStatus == "DEGRADED") {
bscError = TStringBuilder() << "Calling this operation will cause at least group " << groupId << " to go into a degraded state";
forceRetryPossible = true;
return;
}
if (expectedStatus == "DISINTEGRATED") {
bscError = TStringBuilder() << "Calling this operation will cause at least group " << groupId << " to go into a dead state";
return;
}
}
forceRetryPossible = false;
}

void ReplyAndPassAway() {
NJson::TJsonValue json;
if (Response != nullptr) {
if (Response->Record.GetResponse().GetSuccess()) {
json["result"] = true;
} else {
json["result"] = false;
TString error = Response->Record.GetResponse().GetErrorDescription();
bool forceRetryPossible = false;
TryToTranslateFromBSC2Human(error, forceRetryPossible);
json["error"] = error;
if (forceRetryPossible) {
json["forceRetryPossible"] = true;
}
}
json["debugMessage"] = Response->Record.ShortDebugString();
} else {
json["result"] = false;
json["error"] = "No response was received from BSC";
}
TBase::Send(Event->Sender, new NMon::TEvHttpInfoRes(Viewer->GetHTTPOKJSON(Event->Get(), NJson::WriteJson(json)), 0, NMon::IEvHttpInfoRes::EContentType::Custom));
PassAway();
}
};

template <>
YAML::Node TJsonRequestSwagger<TJsonVDiskEvict>::GetSwagger() {
return YAML::Load(R"___(
post:
tags:
- pdisk
summary: VDisk evict
description: PDisk evict
parameters:
- name: group_id
in: query
description: group identifier
required: true
type: integer
- name: group_generation_id
in: query
description: group generation identifier
required: true
type: integer
- name: fail_realm_idx
in: query
description: fail realm identifier
required: true
type: integer
- name: fail_domain_ids
in: query
description: fail domain identifier
required: true
type: integer
- name: vdisk_idx
in: query
description: vdisk idx identifier
required: true
type: integer
- name: timeout
in: query
description: timeout in ms
required: false
type: integer
- name: force
in: query
description: attempt forced operation, ignore warnings
required: false
type: boolean
responses:
200:
description: OK
content:
application/json:
schema:
type: object
properties:
result:
type: boolean
description: was operation successful or not
error:
type: string
description: details about failed operation
forceRetryPossible:
type: boolean
description: if true, operation can be retried with force flag
400:
description: Bad Request
403:
description: Forbidden
504:
description: Gateway Timeout
)___");
}

}
}
1 change: 1 addition & 0 deletions ydb/core/viewer/ya.make
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ SRCS(
json_topicinfo.h
json_pqconsumerinfo.h
json_vdisk_req.h
json_vdisk_evict.h
json_vdiskinfo.h
json_vdiskstat.h
json_wb_req.h
Expand Down

0 comments on commit 0f363b2

Please sign in to comment.