diff --git a/cloud/filestore/config/storage.proto b/cloud/filestore/config/storage.proto index a64b4763fa..a45e1ea15b 100644 --- a/cloud/filestore/config/storage.proto +++ b/cloud/filestore/config/storage.proto @@ -326,4 +326,7 @@ message TStorageConfig // Enables ThreeStageWrite for unaligned requests. optional bool UnalignedThreeStageWriteEnabled = 366; + + // Number of ranges with zero compaction stats to delete per tx. + optional uint32 MaxZeroCompactionRangesToDeletePerTx = 367; } diff --git a/cloud/filestore/libs/storage/api/tablet.h b/cloud/filestore/libs/storage/api/tablet.h index 965f9d1cb0..977878d77b 100644 --- a/cloud/filestore/libs/storage/api/tablet.h +++ b/cloud/filestore/libs/storage/api/tablet.h @@ -31,6 +31,7 @@ namespace NCloud::NFileStore::NStorage { xxx(ConfigureAsFollower, __VA_ARGS__) \ xxx(GetStorageConfig, __VA_ARGS__) \ xxx(GetNodeAttrBatch, __VA_ARGS__) \ + xxx(WriteCompactionMap, __VA_ARGS__) \ // FILESTORE_TABLET_REQUESTS //////////////////////////////////////////////////////////////////////////////// @@ -93,6 +94,9 @@ struct TEvIndexTablet EvGetNodeAttrBatchRequest = EvBegin + 31, EvGetNodeAttrBatchResponse, + EvWriteCompactionMapRequest = EvBegin + 33, + EvWriteCompactionMapResponse, + EvEnd }; diff --git a/cloud/filestore/libs/storage/core/config.cpp b/cloud/filestore/libs/storage/core/config.cpp index 5a93a010a5..07bd74e62b 100644 --- a/cloud/filestore/libs/storage/core/config.cpp +++ b/cloud/filestore/libs/storage/core/config.cpp @@ -176,6 +176,8 @@ namespace { xxx(NodeType, TString, {} )\ xxx(BlobCompressionRate, ui32, 0 )\ xxx(BlobCompressionCodec, TString, "lz4" )\ + \ + xxx(MaxZeroCompactionRangesToDeletePerTx, ui32, 10000 )\ // FILESTORE_STORAGE_CONFIG #define FILESTORE_DECLARE_CONFIG(name, type, value) \ diff --git a/cloud/filestore/libs/storage/core/config.h b/cloud/filestore/libs/storage/core/config.h index 4eb445aee0..0439de8281 100644 --- a/cloud/filestore/libs/storage/core/config.h +++ b/cloud/filestore/libs/storage/core/config.h @@ -224,6 +224,8 @@ class TStorageConfig ui64 GetTrimBytesItemCount() const; + ui32 GetMaxZeroCompactionRangesToDeletePerTx() const; + void Dump(IOutputStream& out) const; void DumpHtml(IOutputStream& out) const; void DumpOverridesHtml(IOutputStream& out) const; diff --git a/cloud/filestore/libs/storage/service/service_actor.h b/cloud/filestore/libs/storage/service/service_actor.h index 8608f707d8..b5e0821618 100644 --- a/cloud/filestore/libs/storage/service/service_actor.h +++ b/cloud/filestore/libs/storage/service/service_actor.h @@ -185,6 +185,10 @@ class TStorageServiceActor final TRequestInfoPtr requestInfo, TString input); + NActors::IActorPtr CreateWriteCompactionMapActionActor( + TRequestInfoPtr requestInfo, + TString input); + private: void RenderSessions(IOutputStream& out); void RenderLocalFileStores(IOutputStream& out); diff --git a/cloud/filestore/libs/storage/service/service_actor_actions.cpp b/cloud/filestore/libs/storage/service/service_actor_actions.cpp index 7892b6968d..990dafb6a7 100644 --- a/cloud/filestore/libs/storage/service/service_actor_actions.cpp +++ b/cloud/filestore/libs/storage/service/service_actor_actions.cpp @@ -68,6 +68,10 @@ void TStorageServiceActor::HandleExecuteAction( "getstorageconfig", &TStorageServiceActor::CreateGetStorageConfigActionActor }, + { + "writecompactionmap", + &TStorageServiceActor::CreateWriteCompactionMapActionActor + }, }; auto it = actions.find(action); diff --git a/cloud/filestore/libs/storage/service/service_actor_actions_write_compaction_map.cpp b/cloud/filestore/libs/storage/service/service_actor_actions_write_compaction_map.cpp new file mode 100644 index 0000000000..befbd476a2 --- /dev/null +++ b/cloud/filestore/libs/storage/service/service_actor_actions_write_compaction_map.cpp @@ -0,0 +1,138 @@ +#include "service_actor.h" + +#include +#include +#include +#include +#include + +#include + +#include + +namespace NCloud::NFileStore::NStorage { + +using namespace NActors; + +using namespace NKikimr; + +namespace { + +//////////////////////////////////////////////////////////////////////////////// + +class TWriteCompactionMapActionActor final + : public TActorBootstrapped +{ +private: + const TRequestInfoPtr RequestInfo; + const TString Input; + +public: + TWriteCompactionMapActionActor( + TRequestInfoPtr requestInfo, + TString input); + + void Bootstrap(const TActorContext& ctx); + +private: + void ReplyAndDie( + const TActorContext& ctx, + const NProtoPrivate::TWriteCompactionMapResponse& response); +private: + STFUNC(StateWork); + + void HandleWriteCompactionMapResponse( + const TEvIndexTablet::TEvWriteCompactionMapResponse::TPtr& ev, + const TActorContext& ctx); +}; + +//////////////////////////////////////////////////////////////////////////////// + +TWriteCompactionMapActionActor::TWriteCompactionMapActionActor( + TRequestInfoPtr requestInfo, + TString input) + : RequestInfo(std::move(requestInfo)) + , Input(std::move(input)) +{} + +void TWriteCompactionMapActionActor::Bootstrap(const TActorContext& ctx) +{ + NProtoPrivate::TWriteCompactionMapRequest 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(); + + requestToTablet->Record = std::move(request); + + NCloud::Send( + ctx, + MakeIndexTabletProxyServiceId(), + std::move(requestToTablet)); + + Become(&TThis::StateWork); +} + +void TWriteCompactionMapActionActor::ReplyAndDie( + const TActorContext& ctx, + const NProtoPrivate::TWriteCompactionMapResponse& response) +{ + auto msg = std::make_unique( + response.GetError()); + + google::protobuf::util::MessageToJsonString( + response, + msg->Record.MutableOutput()); + + NCloud::Reply(ctx, *RequestInfo, std::move(msg)); + Die(ctx); +} + +//////////////////////////////////////////////////////////////////////////////// + +void TWriteCompactionMapActionActor::HandleWriteCompactionMapResponse( + const TEvIndexTablet::TEvWriteCompactionMapResponse::TPtr& ev, + const TActorContext& ctx) +{ + ReplyAndDie(ctx, ev->Get()->Record); +} + +//////////////////////////////////////////////////////////////////////////////// + +STFUNC(TWriteCompactionMapActionActor::StateWork) +{ + switch (ev->GetTypeRewrite()) { + HFunc( + TEvIndexTablet::TEvWriteCompactionMapResponse, + HandleWriteCompactionMapResponse); + + default: + HandleUnexpectedEvent(ev, TFileStoreComponents::SERVICE); + break; + } +} + +} // namespace + +IActorPtr TStorageServiceActor::CreateWriteCompactionMapActionActor( + TRequestInfoPtr requestInfo, + TString input) +{ + return std::make_unique( + std::move(requestInfo), + std::move(input)); +} + +} // namespace NCloud::NFileStore::NStorage diff --git a/cloud/filestore/libs/storage/service/service_ut.cpp b/cloud/filestore/libs/storage/service/service_ut.cpp index 0202f9291c..f9420e6289 100644 --- a/cloud/filestore/libs/storage/service/service_ut.cpp +++ b/cloud/filestore/libs/storage/service/service_ut.cpp @@ -5212,6 +5212,69 @@ Y_UNIT_TEST_SUITE(TStorageServiceTest) nodeId1, createHandleResponse->Record.GetNodeAttr().GetId()); } + + Y_UNIT_TEST(ShouldWriteCompactionMap) + { + TTestEnv env; + env.CreateSubDomain("nfs"); + + ui32 nodeIdx = env.CreateNode("nfs"); + + ui64 tabletId = -1; + ui32 lastCompactionMapRangeId = 0; + env.GetRuntime().SetEventFilter( + [&] (auto& runtime, TAutoPtr& event) { + Y_UNUSED(runtime); + switch (event->GetTypeRewrite()) { + case TEvSSProxy::EvDescribeFileStoreResponse: { + using TDesc = TEvSSProxy::TEvDescribeFileStoreResponse; + const auto* msg = event->Get(); + const auto& desc = + msg->PathDescription.GetFileStoreDescription(); + tabletId = desc.GetIndexTabletId(); + break; + } + case TEvIndexTabletPrivate:: + EvLoadCompactionMapChunkCompleted: { + lastCompactionMapRangeId = Max( + event + ->Get() + ->LastRangeId, + lastCompactionMapRangeId); + break; + } + } + + return false; + }); + + TServiceClient service(env.GetRuntime(), nodeIdx); + service.CreateFileStore("test", 1'000); + + NProtoPrivate::TWriteCompactionMapRequest request; + request.SetFileSystemId("test"); + for (ui32 i = 4; i < 30; ++i) { + NProtoPrivate::TCompactionRangeStats range; + range.SetRangeId(i); + range.SetBlobCount(1); + range.SetDeletionCount(2); + *request.AddRanges() = range; + } + + TString buf; + google::protobuf::util::MessageToJsonString(request, &buf); + auto jsonResponse = service.ExecuteAction("writecompactionmap", buf); + NProtoPrivate::TWriteCompactionMapResponse response; + UNIT_ASSERT(google::protobuf::util::JsonStringToMessage( + jsonResponse->Record.GetOutput(), + &response).ok()); + + TIndexTabletClient tablet(env.GetRuntime(), nodeIdx, tabletId); + tablet.RebootTablet(); + + UNIT_ASSERT_VALUES_EQUAL(lastCompactionMapRangeId, 29); + } } } // namespace NCloud::NFileStore::NStorage diff --git a/cloud/filestore/libs/storage/service/ya.make b/cloud/filestore/libs/storage/service/ya.make index 5dda4767ce..27ca295533 100644 --- a/cloud/filestore/libs/storage/service/ya.make +++ b/cloud/filestore/libs/storage/service/ya.make @@ -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_write_compaction_map.cpp service_actor_actions.cpp service_actor_alterfs.cpp service_actor_complete.cpp diff --git a/cloud/filestore/libs/storage/tablet/tablet_actor.cpp b/cloud/filestore/libs/storage/tablet/tablet_actor.cpp index 6d74bbfd41..5bc8702b82 100644 --- a/cloud/filestore/libs/storage/tablet/tablet_actor.cpp +++ b/cloud/filestore/libs/storage/tablet/tablet_actor.cpp @@ -611,6 +611,11 @@ void TIndexTabletActor::HandleForcedOperation( break; } + case NProtoPrivate::TForcedOperationRequest::E_DELETE_EMPTY_RANGES: { + mode = EMode::DeleteZeroCompactionRanges; + break; + } + default: { e = MakeError(E_ARGUMENT, "unsupported mode"); } @@ -630,9 +635,19 @@ void TIndexTabletActor::HandleForcedOperation( using TResponse = TEvIndexTablet::TEvForcedOperationResponse; auto response = std::make_unique(std::move(e)); if (e.GetCode() == S_OK) { - TVector ranges = request.GetProcessAllRanges() - ? GetAllCompactionRanges() - : GetNonEmptyCompactionRanges(); + TVector ranges; + if (mode == EMode::DeleteZeroCompactionRanges) { + const auto& zeroRanges = RangesWithEmptyCompactionScore; + ui32 i = 0; + while (i < zeroRanges.size()) { + ranges.push_back(i); + i += Config->GetMaxZeroCompactionRangesToDeletePerTx(); + } + } else { + ranges = request.GetProcessAllRanges() + ? GetAllCompactionRanges() + : GetNonEmptyCompactionRanges(); + } response->Record.SetRangeCount(ranges.size()); EnqueueForcedRangeOperation(mode, std::move(ranges)); EnqueueForcedRangeOperationIfNeeded(ctx); diff --git a/cloud/filestore/libs/storage/tablet/tablet_actor.h b/cloud/filestore/libs/storage/tablet/tablet_actor.h index bb045b73f1..80e479bbab 100644 --- a/cloud/filestore/libs/storage/tablet/tablet_actor.h +++ b/cloud/filestore/libs/storage/tablet/tablet_actor.h @@ -257,6 +257,8 @@ class TIndexTabletActor final const NBlockCodecs::ICodec* BlobCodec; + TVector RangesWithEmptyCompactionScore; + public: TIndexTabletActor( const NActors::TActorId& owner, diff --git a/cloud/filestore/libs/storage/tablet/tablet_actor_compactionforced.cpp b/cloud/filestore/libs/storage/tablet/tablet_actor_compactionforced.cpp index d82164ce96..bc60dc17d1 100644 --- a/cloud/filestore/libs/storage/tablet/tablet_actor_compactionforced.cpp +++ b/cloud/filestore/libs/storage/tablet/tablet_actor_compactionforced.cpp @@ -235,6 +235,16 @@ struct TCleanupRequestConstructor } }; +struct TDeleteZeroCompactionRangesRequestConstructor +{ + std::unique_ptr + operator()(const ui32 range) const + { + return std::make_unique< + TEvIndexTabletPrivate::TEvDeleteZeroCompactionRangesRequest>(range); + } +}; + using TForcedCompactionActor = TForcedOperationActor< TEvIndexTabletPrivate::TEvCompactionResponse, TCompactionRequestConstructor>; @@ -243,6 +253,10 @@ using TForcedCleanupActor = TForcedOperationActor< TEvIndexTabletPrivate::TEvCleanupResponse, TCleanupRequestConstructor>; +using TDeleteRangesWithEmptyScoreActor = TForcedOperationActor< + TEvIndexTabletPrivate::TEvDeleteZeroCompactionRangesResponse, + TDeleteZeroCompactionRangesRequestConstructor>; + } // namespace //////////////////////////////////////////////////////////////////////////////// @@ -327,6 +341,14 @@ void TIndexTabletActor::HandleForcedRangeOperation( *GetForcedRangeOperationState(), std::move(requestInfo)); break; + case TEvIndexTabletPrivate::EForcedRangeOperationMode::DeleteZeroCompactionRanges: + actor = std::make_unique( + ctx.SelfID, + LogTag, + Config->GetCompactionRetryTimeout(), + *GetForcedRangeOperationState(), + std::move(requestInfo)); + break; default: TABLET_VERIFY_C(false, "unexpected forced compaction mode"); diff --git a/cloud/filestore/libs/storage/tablet/tablet_actor_delete_zero_compaction_ranges.cpp b/cloud/filestore/libs/storage/tablet/tablet_actor_delete_zero_compaction_ranges.cpp new file mode 100644 index 0000000000..3b3df48ef2 --- /dev/null +++ b/cloud/filestore/libs/storage/tablet/tablet_actor_delete_zero_compaction_ranges.cpp @@ -0,0 +1,77 @@ +#include "tablet_actor.h" + +namespace NCloud::NFileStore::NStorage { + +using namespace NActors; + +using namespace NKikimr; +using namespace NKikimr::NTabletFlatExecutor; + +//////////////////////////////////////////////////////////////////////////////// + +void TIndexTabletActor::HandleDeleteZeroCompactionRanges( + const TEvIndexTabletPrivate::TEvDeleteZeroCompactionRangesRequest::TPtr& ev, + const TActorContext& ctx) +{ + auto* msg = ev->Get(); + + auto requestInfo = CreateRequestInfo( + ev->Sender, + ev->Cookie, + msg->CallContext); + + ExecuteTx( + ctx, + std::move(requestInfo), + msg->RangeId); +} + +//////////////////////////////////////////////////////////////////////////////// + +bool TIndexTabletActor::PrepareTx_DeleteZeroCompactionRanges( + const TActorContext& ctx, + TTransactionContext& tx, + TTxIndexTablet::TDeleteZeroCompactionRanges& args) +{ + Y_UNUSED(ctx); + Y_UNUSED(tx); + Y_UNUSED(args); + + return true; +} + +void TIndexTabletActor::ExecuteTx_DeleteZeroCompactionRanges( + const TActorContext& ctx, + TTransactionContext& tx, + TTxIndexTablet::TDeleteZeroCompactionRanges& args) +{ + Y_UNUSED(ctx); + + TIndexTabletDatabase db(tx.DB); + + TVector ranges( + Reserve(Config->GetMaxZeroCompactionRangesToDeletePerTx())); + ui32 rangeCount = RangesWithEmptyCompactionScore.size(); + ui32 rangesPerTx = Config->GetMaxZeroCompactionRangesToDeletePerTx(); + for (ui32 i = args.StartIndex; + i < Min(args.StartIndex + rangesPerTx, rangeCount); ++i) + { + ui32 range = RangesWithEmptyCompactionScore[i]; + db.WriteCompactionMap( + range, + GetCompactionStats(range).BlobsCount, + GetCompactionStats(range).DeletionsCount); + } + +} + +void TIndexTabletActor::CompleteTx_DeleteZeroCompactionRanges( + const TActorContext& ctx, + TTxIndexTablet::TDeleteZeroCompactionRanges& args) +{ + auto response = std::make_unique< + TEvIndexTabletPrivate::TEvDeleteZeroCompactionRangesResponse>(); + NCloud::Reply(ctx, *args.RequestInfo, std::move(response)); +} + +} // namespace NCloud::NFileStore::NStorage diff --git a/cloud/filestore/libs/storage/tablet/tablet_actor_loadstate.cpp b/cloud/filestore/libs/storage/tablet/tablet_actor_loadstate.cpp index 83a03224cc..e5bba016c5 100644 --- a/cloud/filestore/libs/storage/tablet/tablet_actor_loadstate.cpp +++ b/cloud/filestore/libs/storage/tablet/tablet_actor_loadstate.cpp @@ -464,6 +464,9 @@ void TIndexTabletActor::CompleteTx_LoadCompactionMapChunk( LoadCompactionMap(args.CompactionMap); for (const auto& x: args.CompactionMap) { args.LastRangeId = Max(args.LastRangeId, x.RangeId); + if (!x.Stats.BlobsCount && !x.Stats.DeletionsCount) { + RangesWithEmptyCompactionScore.push_back(x.RangeId); + } } using TNotification = diff --git a/cloud/filestore/libs/storage/tablet/tablet_actor_monitoring.cpp b/cloud/filestore/libs/storage/tablet/tablet_actor_monitoring.cpp index d70fb5d0c2..85b7f41fc0 100644 --- a/cloud/filestore/libs/storage/tablet/tablet_actor_monitoring.cpp +++ b/cloud/filestore/libs/storage/tablet/tablet_actor_monitoring.cpp @@ -253,6 +253,24 @@ void BuildForceCompactionButton(IOutputStream& out, ui64 tabletId) "Force cleanup", "Are you sure you want to force cleanup for ALL ranges?", "forceCleanupAll();"); + + out << "

Force Delete zero compaction ranges

" + << "
" + << "" + << "" + << "" + << "" + << "
"; + + BuildConfirmActionDialog( + out, + "force-delete-zero-compaction-ranges", + "Force delete zero compaction ranges", + "Are you sure you want to delete ALL zero compaction ranges?", + "forceDeleteZeroCompactionRanges();"); } //////////////////////////////////////////////////////////////////////////////// @@ -765,6 +783,14 @@ void GenerateActionsJS(IOutputStream& out) } )___"; + + out << R"___( + + )___"; } //////////////////////////////////////////////////////////////////////////////// @@ -1179,6 +1205,23 @@ void TIndexTabletActor::HandleHttpInfo_ForceOperation( return; } + TEvIndexTabletPrivate::EForcedRangeOperationMode mode; + if (params.Get("mode") == "cleanup") { + mode = TEvIndexTabletPrivate::EForcedRangeOperationMode::Cleanup; + } else if (params.Get("mode") == "compaction") { + mode = TEvIndexTabletPrivate::EForcedRangeOperationMode::Compaction; + } else if (params.Get("mode") == "deleteZeroCompactionRanges") { + mode = TEvIndexTabletPrivate::EForcedRangeOperationMode + ::DeleteZeroCompactionRanges; + } else { + RejectHttpRequest( + ctx, + TabletID(), + *requestInfo, + TStringBuilder() << "Invalid mode: " << params.Get("mode")); + return; + } + TVector ranges; if (params.Has("RangeIndex") && params.Has("RangesCount")) { ui64 rangeIndex = 0; @@ -1199,14 +1242,15 @@ void TIndexTabletActor::HandleHttpInfo_ForceOperation( rangeIndex + rangesCount, 1)); } else { - ranges = GetNonEmptyCompactionRanges(); + if (mode == TEvIndexTabletPrivate::EForcedRangeOperationMode + ::DeleteZeroCompactionRanges) + { + ranges = RangesWithEmptyCompactionScore; + } else { + ranges = GetNonEmptyCompactionRanges(); + } } - TEvIndexTabletPrivate::EForcedRangeOperationMode mode = - params.Get("mode") == "cleanup" - ? TEvIndexTabletPrivate::EForcedRangeOperationMode::Cleanup - : TEvIndexTabletPrivate::EForcedRangeOperationMode::Compaction; - EnqueueForcedRangeOperation(mode, std::move(ranges)); EnqueueForcedRangeOperationIfNeeded(ctx); diff --git a/cloud/filestore/libs/storage/tablet/tablet_actor_write_compactionmap.cpp b/cloud/filestore/libs/storage/tablet/tablet_actor_write_compactionmap.cpp new file mode 100644 index 0000000000..7a0539a52c --- /dev/null +++ b/cloud/filestore/libs/storage/tablet/tablet_actor_write_compactionmap.cpp @@ -0,0 +1,74 @@ +#include "tablet_actor.h" + +namespace NCloud::NFileStore::NStorage { + +using namespace NActors; + +using namespace NKikimr; +using namespace NKikimr::NTabletFlatExecutor; + +//////////////////////////////////////////////////////////////////////////////// + +void TIndexTabletActor::HandleWriteCompactionMap( + const TEvIndexTablet::TEvWriteCompactionMapRequest::TPtr& ev, + const TActorContext& ctx) +{ + const auto& msg = ev->Get(); + + auto requestInfo = CreateRequestInfo( + ev->Sender, + ev->Cookie, + msg->CallContext); + + TVector ranges( + Reserve(msg->Record.GetRanges().size())); + for (const auto& range: msg->Record.GetRanges()) { + ranges.push_back(range); + } + + ExecuteTx( + ctx, + std::move(requestInfo), + std::move(ranges)); +} + +//////////////////////////////////////////////////////////////////////////////// + +bool TIndexTabletActor::PrepareTx_WriteCompactionMap( + const TActorContext& ctx, + TTransactionContext& tx, + TTxIndexTablet::TWriteCompactionMap& args) +{ + Y_UNUSED(ctx); + Y_UNUSED(tx); + Y_UNUSED(args); + + return true; +} + +void TIndexTabletActor::ExecuteTx_WriteCompactionMap( + const TActorContext& ctx, + TTransactionContext& tx, + TTxIndexTablet::TWriteCompactionMap& args) +{ + Y_UNUSED(ctx); + + TIndexTabletDatabase db(tx.DB); + for (const auto& range: args.Ranges) { + db.ForceWriteCompactionMap( + range.GetRangeId(), + range.GetBlobCount(), + range.GetDeletionCount()); + } +} + +void TIndexTabletActor::CompleteTx_WriteCompactionMap( + const TActorContext& ctx, + TTxIndexTablet::TWriteCompactionMap& args) +{ + auto response = std::make_unique< + TEvIndexTablet::TEvWriteCompactionMapResponse>(); + NCloud::Reply(ctx, *args.RequestInfo, std::move(response)); +} + +} // namespace NCloud::NFileStore::NStorage diff --git a/cloud/filestore/libs/storage/tablet/tablet_database.cpp b/cloud/filestore/libs/storage/tablet/tablet_database.cpp index ec8e7d58c7..c0fcfa2b7c 100644 --- a/cloud/filestore/libs/storage/tablet/tablet_database.cpp +++ b/cloud/filestore/libs/storage/tablet/tablet_database.cpp @@ -1598,6 +1598,19 @@ bool TIndexTabletDatabase::ReadCheckpointBlobs( //////////////////////////////////////////////////////////////////////////////// // CompactionMap +void TIndexTabletDatabase::ForceWriteCompactionMap( + ui32 rangeId, + ui32 blobsCount, + ui32 deletionsCount) +{ + using TTable = TIndexTabletSchema::CompactionMap; + + Table() + .Key(rangeId) + .Update(NIceDb::TUpdate(blobsCount)) + .Update(NIceDb::TUpdate(deletionsCount)); +} + void TIndexTabletDatabase::WriteCompactionMap( ui32 rangeId, ui32 blobsCount, @@ -1606,10 +1619,7 @@ void TIndexTabletDatabase::WriteCompactionMap( using TTable = TIndexTabletSchema::CompactionMap; if (blobsCount || deletionsCount) { - Table() - .Key(rangeId) - .Update(NIceDb::TUpdate(blobsCount)) - .Update(NIceDb::TUpdate(deletionsCount)); + ForceWriteCompactionMap(rangeId, blobsCount, deletionsCount); } else { Table().Key(rangeId).Delete(); } diff --git a/cloud/filestore/libs/storage/tablet/tablet_database.h b/cloud/filestore/libs/storage/tablet/tablet_database.h index 1575ada051..72a164957e 100644 --- a/cloud/filestore/libs/storage/tablet/tablet_database.h +++ b/cloud/filestore/libs/storage/tablet/tablet_database.h @@ -464,6 +464,7 @@ FILESTORE_FILESYSTEM_STATS(FILESTORE_DECLARE_STATS) // CompactionMap // + void ForceWriteCompactionMap(ui32 rangeId, ui32 blobsCount, ui32 deletionsCount); void WriteCompactionMap(ui32 rangeId, ui32 blobsCount, ui32 deletionsCount); bool ReadCompactionMap(TVector& compactionMap); bool ReadCompactionMap( diff --git a/cloud/filestore/libs/storage/tablet/tablet_private.h b/cloud/filestore/libs/storage/tablet/tablet_private.h index 8dd92ffe89..f9b0abc86f 100644 --- a/cloud/filestore/libs/storage/tablet/tablet_private.h +++ b/cloud/filestore/libs/storage/tablet/tablet_private.h @@ -41,6 +41,7 @@ namespace NCloud::NFileStore::NStorage { #define FILESTORE_TABLET_REQUESTS_PRIVATE_SYNC(xxx, ...) \ xxx(AddBlob, __VA_ARGS__) \ xxx(Cleanup, __VA_ARGS__) \ + xxx(DeleteZeroCompactionRanges, __VA_ARGS__) \ xxx(DeleteGarbage, __VA_ARGS__) \ xxx(TruncateRange, __VA_ARGS__) \ xxx(ZeroRange, __VA_ARGS__) \ @@ -430,6 +431,25 @@ struct TEvIndexTabletPrivate using TCompactionCompleted = TOperationCompleted; + // + // DeleteZeroCompactionRanges + // + + struct TDeleteZeroCompactionRangesRequest + { + const ui32 RangeId; + + explicit TDeleteZeroCompactionRangesRequest(ui32 rangeId) + : RangeId(rangeId) + {} + }; + + struct TDeleteZeroCompactionRangesResponse + { + }; + + using TDeleteZeroCompactionRangesCompleted = TOperationCompleted; + // // LoadCompactionMapChunk // @@ -475,6 +495,7 @@ struct TEvIndexTabletPrivate { Compaction = 0, Cleanup = 1, + DeleteZeroCompactionRanges = 2, }; struct TForcedRangeOperationRequest diff --git a/cloud/filestore/libs/storage/tablet/tablet_tx.h b/cloud/filestore/libs/storage/tablet/tablet_tx.h index dfe844b777..0c50e99479 100644 --- a/cloud/filestore/libs/storage/tablet/tablet_tx.h +++ b/cloud/filestore/libs/storage/tablet/tablet_tx.h @@ -115,6 +115,8 @@ namespace NCloud::NFileStore::NStorage { xxx(AddBlob, __VA_ARGS__) \ xxx(Cleanup, __VA_ARGS__) \ xxx(Compaction, __VA_ARGS__) \ + xxx(DeleteZeroCompactionRanges, __VA_ARGS__) \ + xxx(WriteCompactionMap, __VA_ARGS__) \ xxx(DeleteGarbage, __VA_ARGS__) \ xxx(DumpCompactionRange, __VA_ARGS__) \ xxx(FlushBytes, __VA_ARGS__) \ @@ -1654,6 +1656,48 @@ struct TTxIndexTablet } }; + // + // DeleteZeroCompactionRanges + // + + struct TDeleteZeroCompactionRanges + { + const TRequestInfoPtr RequestInfo; + const ui32 StartIndex; + + TDeleteZeroCompactionRanges( + TRequestInfoPtr requestInfo, + ui32 startIndex) + : RequestInfo(std::move(requestInfo)) + , StartIndex(startIndex) + {} + + void Clear() + { + } + }; + + // + // WriteCompactionMap + // + + struct TWriteCompactionMap + { + const TRequestInfoPtr RequestInfo; + const TVector Ranges; + + TWriteCompactionMap( + TRequestInfoPtr requestInfo, + TVector ranges) + : RequestInfo(std::move(requestInfo)) + , Ranges(std::move(ranges)) + {} + + void Clear() + { + } + }; + // // DeleteGarbage // diff --git a/cloud/filestore/libs/storage/tablet/tablet_ut_data.cpp b/cloud/filestore/libs/storage/tablet/tablet_ut_data.cpp index a9ceafc120..b95559f325 100644 --- a/cloud/filestore/libs/storage/tablet/tablet_ut_data.cpp +++ b/cloud/filestore/libs/storage/tablet/tablet_ut_data.cpp @@ -3294,6 +3294,92 @@ Y_UNIT_TEST_SUITE(TIndexTabletTest_Data) } } + TABLET_TEST(ShouldDoForcedDeletionOfZeroCompactionRanges) + { + NProto::TStorageConfig storageConfig; + storageConfig.SetMaxZeroCompactionRangesToDeletePerTx(10); + + TTestEnv env({}, std::move(storageConfig)); + env.CreateSubDomain("nfs"); + + ui32 requests = 0; + ui32 lastCompactionMapRangeId = 0; + env.GetRuntime().SetEventFilter( + [&] (auto& runtime, TAutoPtr& event) + { + Y_UNUSED(runtime); + + switch (event->GetTypeRewrite()) { + case TEvIndexTabletPrivate + ::EvDeleteZeroCompactionRangesRequest: + { + ++requests; + break; + } + case TEvIndexTabletPrivate::EvLoadCompactionMapChunkCompleted: { + lastCompactionMapRangeId = Max( + event->Get< + TEvIndexTabletPrivate + ::TEvLoadCompactionMapChunkCompleted>()->LastRangeId, + lastCompactionMapRangeId); + break; + } + } + return false; + }); + + ui32 nodeIdx = env.CreateNode("nfs"); + ui64 tabletId = env.BootIndexTablet(nodeIdx); + + TIndexTabletClient tablet( + env.GetRuntime(), + nodeIdx, + tabletId, + tabletConfig); + tablet.InitSession("client", "session"); + tablet.RebootTablet(); + + TVector ranges; + for (ui32 i = 0; i < 50; ++i) { + NProtoPrivate::TCompactionRangeStats range; + range.SetRangeId(i); + range.SetBlobCount(0); + range.SetDeletionCount(0); + ranges.push_back(range); + } + for (ui32 i = 100; i < 200; ++i) { + NProtoPrivate::TCompactionRangeStats range; + range.SetRangeId(i); + range.SetBlobCount(0); + range.SetDeletionCount(0); + ranges.push_back(range); + } + for (ui32 i = 60; i < 80; ++i) { + NProtoPrivate::TCompactionRangeStats range; + range.SetRangeId(i); + range.SetBlobCount(1); + range.SetDeletionCount(2); + ranges.push_back(range); + } + tablet.WriteCompactionMap(ranges); + tablet.RebootTablet(); + + tablet.ForcedOperation( + NProtoPrivate::TForcedOperationRequest::E_DELETE_EMPTY_RANGES); + + UNIT_ASSERT_VALUES_EQUAL(requests, 15); + UNIT_ASSERT_VALUES_EQUAL(lastCompactionMapRangeId, 199); + tablet.AssertForcedRangeOperationFailed( + TVector{}, + TEvIndexTabletPrivate + ::EForcedRangeOperationMode::DeleteZeroCompactionRanges); + + lastCompactionMapRangeId = 0; + tablet.RebootTablet(); + UNIT_ASSERT_VALUES_EQUAL(lastCompactionMapRangeId, 79); + } + + TABLET_TEST(ShouldDumpCompactionRangeBlobs) { TTestEnv env; diff --git a/cloud/filestore/libs/storage/tablet/ya.make b/cloud/filestore/libs/storage/tablet/ya.make index b625df8953..27d9d978b8 100644 --- a/cloud/filestore/libs/storage/tablet/ya.make +++ b/cloud/filestore/libs/storage/tablet/ya.make @@ -29,6 +29,7 @@ SRCS( tablet_actor_createhandle.cpp tablet_actor_createnode.cpp tablet_actor_createsession.cpp + tablet_actor_delete_zero_compaction_ranges.cpp tablet_actor_deletecheckpoint.cpp tablet_actor_deletegarbage.cpp tablet_actor_destroycheckpoint.cpp @@ -68,6 +69,7 @@ SRCS( tablet_actor_writebatch.cpp tablet_actor_writeblob.cpp tablet_actor_writedata.cpp + tablet_actor_write_compactionmap.cpp tablet_actor_zerorange.cpp tablet_counters.cpp tablet_database.cpp diff --git a/cloud/filestore/libs/storage/testlib/tablet_client.h b/cloud/filestore/libs/storage/testlib/tablet_client.h index 374ea6074a..a3a373a265 100644 --- a/cloud/filestore/libs/storage/testlib/tablet_client.h +++ b/cloud/filestore/libs/storage/testlib/tablet_client.h @@ -288,6 +288,34 @@ class TIndexTabletClient return request; } + auto CreateForcedOperationRequest( + NProtoPrivate::TForcedOperationRequest::EForcedOperationType type) + { + NProtoPrivate::TForcedOperationRequest request; + request.SetOpType(type); + auto requestToTablet = + std::make_unique(); + + requestToTablet->Record = std::move(request); + return requestToTablet; + } + + auto CreateWriteCompactionMapRequest( + const TVector& ranges) + { + NProtoPrivate::TWriteCompactionMapRequest request; + for (auto range: ranges) + { + *request.AddRanges() = range; + } + + auto requestToTablet = + std::make_unique(); + + requestToTablet->Record = std::move(request); + return requestToTablet; + } + // // TEvIndexTabletPrivate // diff --git a/cloud/filestore/private/api/protos/tablet.proto b/cloud/filestore/private/api/protos/tablet.proto index 8b8828a158..b6e6eb8aa7 100644 --- a/cloud/filestore/private/api/protos/tablet.proto +++ b/cloud/filestore/private/api/protos/tablet.proto @@ -469,6 +469,7 @@ message TForcedOperationRequest { E_COMPACTION = 0; E_CLEANUP = 1; + E_DELETE_EMPTY_RANGES = 2; }; // Optional request headers. @@ -578,3 +579,18 @@ message TGetNodeAttrBatchResponse // Individual node responses. repeated NProto.TGetNodeAttrResponse Responses = 2; } + +//////////////////////////////////////////////////////////////////////////////// +// WriteCompactionMap request/response. + +message TWriteCompactionMapRequest +{ + string FileSystemId = 1; + repeated TCompactionRangeStats Ranges = 2; +} + +message TWriteCompactionMapResponse +{ + // Optional error, set only if error happened. + NCloud.NProto.TError Error = 1; +}