Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Merged
merged 4 commits into from
Sep 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading