Skip to content

Commit

Permalink
issue-1795: implemented Unsafe{Delete,Update,Get}Node to use it in pr…
Browse files Browse the repository at this point in the history
…ivate api for manual interventions - e.g. to clean up OrphanNodes (#2165)

* issue-1795: implemented Unsafe{Delete,Update,Get}Node to use it in private api for manual interventions - e.g. to clean up OrphanNodes

* issue-1795: tiny cleanup

* issue-1795: private api Unsafe{Delete,Update,Get}Node actions + ut

* issue-1795: cleanup
  • Loading branch information
qkrorlqr committed Sep 29, 2024
1 parent 8cc58f7 commit 83379f6
Show file tree
Hide file tree
Showing 12 changed files with 730 additions and 5 deletions.
12 changes: 12 additions & 0 deletions cloud/filestore/libs/storage/api/tablet.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,9 @@ namespace NCloud::NFileStore::NStorage {
xxx(GetStorageConfig, __VA_ARGS__) \
xxx(GetNodeAttrBatch, __VA_ARGS__) \
xxx(WriteCompactionMap, __VA_ARGS__) \
xxx(UnsafeDeleteNode, __VA_ARGS__) \
xxx(UnsafeUpdateNode, __VA_ARGS__) \
xxx(UnsafeGetNode, __VA_ARGS__) \
// FILESTORE_TABLET_REQUESTS

////////////////////////////////////////////////////////////////////////////////
Expand Down Expand Up @@ -97,6 +100,15 @@ struct TEvIndexTablet
EvWriteCompactionMapRequest = EvBegin + 33,
EvWriteCompactionMapResponse,

EvUnsafeDeleteNodeRequest = EvBegin + 35,
EvUnsafeDeleteNodeResponse,

EvUnsafeUpdateNodeRequest = EvBegin + 37,
EvUnsafeUpdateNodeResponse,

EvUnsafeGetNodeRequest = EvBegin + 39,
EvUnsafeGetNodeResponse,

EvEnd
};

Expand Down
12 changes: 12 additions & 0 deletions cloud/filestore/libs/storage/service/service_actor.h
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,18 @@ class TStorageServiceActor final
TRequestInfoPtr requestInfo,
TString input);

NActors::IActorPtr CreateUnsafeDeleteNodeActionActor(
TRequestInfoPtr requestInfo,
TString input);

NActors::IActorPtr CreateUnsafeUpdateNodeActionActor(
TRequestInfoPtr requestInfo,
TString input);

NActors::IActorPtr CreateUnsafeGetNodeActionActor(
TRequestInfoPtr requestInfo,
TString input);

private:
void RenderSessions(IOutputStream& out);
void RenderLocalFileStores(IOutputStream& out);
Expand Down
12 changes: 12 additions & 0 deletions cloud/filestore/libs/storage/service/service_actor_actions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,18 @@ void TStorageServiceActor::HandleExecuteAction(
"writecompactionmap",
&TStorageServiceActor::CreateWriteCompactionMapActionActor
},
{
"unsafedeletenode",
&TStorageServiceActor::CreateUnsafeDeleteNodeActionActor
},
{
"unsafeupdatenode",
&TStorageServiceActor::CreateUnsafeUpdateNodeActionActor
},
{
"unsafegetnode",
&TStorageServiceActor::CreateUnsafeGetNodeActionActor
},
};

auto it = actions.find(action);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
#include "service_actor.h"

#include <cloud/filestore/libs/storage/api/service.h>
#include <cloud/filestore/libs/storage/api/tablet.h>
#include <cloud/filestore/libs/storage/api/tablet_proxy.h>
#include <cloud/filestore/libs/storage/core/public.h>
#include <cloud/filestore/private/api/protos/tablet.pb.h>

#include <contrib/ydb/library/actors/core/actor_bootstrapped.h>

#include <google/protobuf/util/json_util.h>

namespace NCloud::NFileStore::NStorage {

using namespace NActors;

using namespace NKikimr;

namespace {

////////////////////////////////////////////////////////////////////////////////

template <typename TRequest, typename TResponse>
class TTabletActionActor final
: public TActorBootstrapped<TTabletActionActor<TRequest, TResponse>>
{
private:
const TRequestInfoPtr RequestInfo;
const TString Input;

using TBase = TActorBootstrapped<TTabletActionActor<TRequest, TResponse>>;

public:
TTabletActionActor(
TRequestInfoPtr requestInfo,
TString input);

void Bootstrap(const TActorContext& ctx);

private:
void ReplyAndDie(
const TActorContext& ctx,
const TResponse::ProtoRecordType& responseRecord);

private:
STFUNC(StateWork)
{
switch (ev->GetTypeRewrite()) {
HFunc(TResponse, HandleResponse);

default:
HandleUnexpectedEvent(ev, TFileStoreComponents::SERVICE);
break;
}
}

void HandleResponse(const TResponse::TPtr& ev, const TActorContext& ctx);
};

////////////////////////////////////////////////////////////////////////////////

template <typename TRequest, typename TResponse>
TTabletActionActor<TRequest, TResponse>::TTabletActionActor(
TRequestInfoPtr requestInfo,
TString input)
: RequestInfo(std::move(requestInfo))
, Input(std::move(input))
{}

template <typename TRequest, typename TResponse>
void TTabletActionActor<TRequest, TResponse>::Bootstrap(
const TActorContext& ctx)
{
typename TRequest::ProtoRecordType request;
if (!google::protobuf::util::JsonStringToMessage(Input, &request).ok()) {
ReplyAndDie(
ctx,
TErrorResponse(E_ARGUMENT, "Failed to parse input"));
return;
}

if (!request.GetFileSystemId()) {
ReplyAndDie(
ctx,
TErrorResponse(E_ARGUMENT, "FileSystem id should be supplied"));
return;
}

auto requestToTablet = std::make_unique<TRequest>();
requestToTablet->Record = std::move(request);

NCloud::Send(
ctx,
MakeIndexTabletProxyServiceId(),
std::move(requestToTablet));

TBase::Become(&TTabletActionActor<TRequest, TResponse>::StateWork);
}

template <typename TRequest, typename TResponse>
void TTabletActionActor<TRequest, TResponse>::ReplyAndDie(
const TActorContext& ctx,
const TResponse::ProtoRecordType& response)
{
auto msg = std::make_unique<TEvService::TEvExecuteActionResponse>(
response.GetError());

google::protobuf::util::MessageToJsonString(
response,
msg->Record.MutableOutput());

NCloud::Reply(ctx, *RequestInfo, std::move(msg));
TBase::Die(ctx);
}

////////////////////////////////////////////////////////////////////////////////

template <typename TRequest, typename TResponse>
void TTabletActionActor<TRequest, TResponse>::HandleResponse(
const TResponse::TPtr& ev,
const TActorContext& ctx)
{
ReplyAndDie(ctx, ev->Get()->Record);
}

} // namespace

IActorPtr TStorageServiceActor::CreateUnsafeDeleteNodeActionActor(
TRequestInfoPtr requestInfo,
TString input)
{
using TUnsafeDeleteNodeActor = TTabletActionActor<
TEvIndexTablet::TEvUnsafeDeleteNodeRequest,
TEvIndexTablet::TEvUnsafeDeleteNodeResponse>;
return std::make_unique<TUnsafeDeleteNodeActor>(
std::move(requestInfo),
std::move(input));
}

IActorPtr TStorageServiceActor::CreateUnsafeUpdateNodeActionActor(
TRequestInfoPtr requestInfo,
TString input)
{
using TUnsafeUpdateNodeActor = TTabletActionActor<
TEvIndexTablet::TEvUnsafeUpdateNodeRequest,
TEvIndexTablet::TEvUnsafeUpdateNodeResponse>;
return std::make_unique<TUnsafeUpdateNodeActor>(
std::move(requestInfo),
std::move(input));
}

IActorPtr TStorageServiceActor::CreateUnsafeGetNodeActionActor(
TRequestInfoPtr requestInfo,
TString input)
{
using TUnsafeGetNodeActor = TTabletActionActor<
TEvIndexTablet::TEvUnsafeGetNodeRequest,
TEvIndexTablet::TEvUnsafeGetNodeResponse>;
return std::make_unique<TUnsafeGetNodeActor>(
std::move(requestInfo),
std::move(input));
}

} // namespace NCloud::NFileStore::NStorage
116 changes: 115 additions & 1 deletion cloud/filestore/libs/storage/service/service_actor_actions_ut.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,10 @@
#include <cloud/filestore/libs/storage/testlib/tablet_client.h>
#include <cloud/filestore/libs/storage/testlib/test_env.h>
#include <cloud/filestore/private/api/protos/actions.pb.h>
#include <cloud/filestore/private/api/protos/tablet.pb.h>

#include <contrib/libs/protobuf/src/google/protobuf/stubs/stringpiece.h>
#include <google/protobuf/util/json_util.h>

namespace NCloud::NFileStore::NStorage {

Expand Down Expand Up @@ -83,7 +85,8 @@ Y_UNIT_TEST_SUITE(TStorageServiceActionsTest)
ui32 nodeIdx = env.CreateNode("nfs");

TServiceClient service(env.GetRuntime(), nodeIdx);
auto response = service.AssertExecuteActionFailed("NonExistingAction", "{}");
auto response =
service.AssertExecuteActionFailed("NonExistingAction", "{}");

UNIT_ASSERT_VALUES_UNEQUAL(S_OK, response->GetStatus());
}
Expand Down Expand Up @@ -179,6 +182,117 @@ Y_UNIT_TEST_SUITE(TStorageServiceActionsTest)
true);
}
}

Y_UNIT_TEST(ShouldPerformUnsafeNodeManipulations)
{
NProto::TStorageConfig config;
TTestEnv env{{}, config};
env.CreateSubDomain("nfs");

ui32 nodeIdx = env.CreateNode("nfs");

TServiceClient service(env.GetRuntime(), nodeIdx);

const TString fsId = "test";
service.CreateFileStore(fsId, 1'000);

auto headers = service.InitSession("test", "client");

service.CreateNode(headers, TCreateNodeArgs::File(RootNodeId, "file1"));
service.CreateNode(headers, TCreateNodeArgs::File(RootNodeId, "file2"));

ui64 id1 = 0;
ui64 id2 = 0;
ui64 id3 = 0;

{
auto r = service.ListNodes(headers, fsId, RootNodeId)->Record;
UNIT_ASSERT_VALUES_EQUAL(2, r.NodesSize());
UNIT_ASSERT_VALUES_EQUAL(0, r.GetNodes(0).GetSize());
UNIT_ASSERT_VALUES_EQUAL(0, r.GetNodes(1).GetSize());

id1 = r.GetNodes(0).GetId();
id2 = r.GetNodes(1).GetId();

UNIT_ASSERT_VALUES_UNEQUAL(0, id1);
UNIT_ASSERT_VALUES_UNEQUAL(0, id2);
}

id3 = Max(id1, id2) + 1;

{
NProtoPrivate::TUnsafeUpdateNodeRequest request;
request.SetFileSystemId(fsId);
auto* node = request.MutableNode();
node->SetId(id3);
node->SetSize(333);
TString buf;
google::protobuf::util::MessageToJsonString(request, &buf);
service.ExecuteAction("UnsafeUpdateNode", buf);
}

{
NProtoPrivate::TUnsafeGetNodeRequest request;
request.SetFileSystemId(fsId);
request.SetId(id3);
TString buf;
google::protobuf::util::MessageToJsonString(request, &buf);
auto r = service.ExecuteAction("UnsafeGetNode", buf)->Record;

NProtoPrivate::TUnsafeGetNodeResponse response;
UNIT_ASSERT(google::protobuf::util::JsonStringToMessage(
r.GetOutput(),
&response).ok());

UNIT_ASSERT_VALUES_EQUAL(id3, response.GetNode().GetId());
UNIT_ASSERT_VALUES_EQUAL(333, response.GetNode().GetSize());
}

{
NProtoPrivate::TUnsafeUpdateNodeRequest request;
request.SetFileSystemId(fsId);
auto* node = request.MutableNode();
node->SetId(id2);
node->SetSize(222);
TString buf;
google::protobuf::util::MessageToJsonString(request, &buf);
service.ExecuteAction("UnsafeUpdateNode", buf);
}

{
auto r = service.ListNodes(headers, fsId, RootNodeId)->Record;
UNIT_ASSERT_VALUES_EQUAL(2, r.NodesSize());
UNIT_ASSERT_VALUES_EQUAL(0, r.GetNodes(0).GetSize());
UNIT_ASSERT_VALUES_EQUAL(222, r.GetNodes(1).GetSize());
}

{
NProtoPrivate::TUnsafeDeleteNodeRequest request;
request.SetFileSystemId(fsId);
request.SetId(id3);
TString buf;
google::protobuf::util::MessageToJsonString(request, &buf);
service.ExecuteAction("UnsafeDeleteNode", buf);
}

{
NProtoPrivate::TUnsafeGetNodeRequest request;
request.SetFileSystemId(fsId);
request.SetId(id3);
TString buf;
google::protobuf::util::MessageToJsonString(request, &buf);
auto r = service.ExecuteAction("UnsafeGetNode", buf)->Record;

NProtoPrivate::TUnsafeGetNodeResponse response;
UNIT_ASSERT(google::protobuf::util::JsonStringToMessage(
r.GetOutput(),
&response).ok());

UNIT_ASSERT(!response.HasNode());
}

service.DestroySession(headers);
}
}

} // namespace NCloud::NFileStore::NStorage
1 change: 1 addition & 0 deletions cloud/filestore/libs/storage/service/ya.make
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ SRCS(
service_actor_actions_get_storage_config_fields.cpp
service_actor_actions_get_storage_config.cpp
service_actor_actions_reassign_tablet.cpp
service_actor_actions_unsafe_node_ops.cpp
service_actor_actions_write_compaction_map.cpp
service_actor_actions.cpp
service_actor_alterfs.cpp
Expand Down
Loading

0 comments on commit 83379f6

Please sign in to comment.