diff --git a/cloud/filestore/libs/storage/api/tablet.h b/cloud/filestore/libs/storage/api/tablet.h index 1830aeb72e2..a5110d05bda 100644 --- a/cloud/filestore/libs/storage/api/tablet.h +++ b/cloud/filestore/libs/storage/api/tablet.h @@ -22,6 +22,7 @@ namespace NCloud::NFileStore::NStorage { xxx(GetFileSystemConfig, __VA_ARGS__) \ xxx(GetStorageConfigFields, __VA_ARGS__) \ xxx(ChangeStorageConfig, __VA_ARGS__) \ + xxx(DescribeData, __VA_ARGS__) \ // FILESTORE_TABLET_REQUESTS //////////////////////////////////////////////////////////////////////////////// @@ -57,6 +58,9 @@ struct TEvIndexTablet EvChangeStorageConfigRequest = EvBegin + 13, EvChangeStorageConfigResponse, + EvDescribeDataRequest = EvBegin + 15, + EvDescribeDataResponse, + EvEnd }; diff --git a/cloud/filestore/libs/storage/tablet/tablet_actor_readdata.cpp b/cloud/filestore/libs/storage/tablet/tablet_actor_readdata.cpp index b18da2da204..050a0fd79b6 100644 --- a/cloud/filestore/libs/storage/tablet/tablet_actor_readdata.cpp +++ b/cloud/filestore/libs/storage/tablet/tablet_actor_readdata.cpp @@ -390,7 +390,8 @@ STFUNC(TReadDataActor::StateWork) //////////////////////////////////////////////////////////////////////////////// -NProto::TError ValidateRequest(const NProto::TReadDataRequest& request, ui32 blockSize) +template +NProto::TError ValidateRequest(const TReadRequest& request, ui32 blockSize) { const TByteRange range( request.GetOffset(), @@ -447,7 +448,8 @@ void TIndexTabletActor::HandleReadData( msg->Record, byteRange, alignedByteRange, - std::move(blockBuffer)); + std::move(blockBuffer), + false /* describeOnly */); } void TIndexTabletActor::HandleReadDataCompleted( @@ -463,6 +465,45 @@ void TIndexTabletActor::HandleReadDataCompleted( //////////////////////////////////////////////////////////////////////////////// +void TIndexTabletActor::HandleDescribeData( + const TEvIndexTablet::TEvDescribeDataRequest::TPtr& ev, + const TActorContext& ctx) +{ + auto validator = [&] (const NProtoPrivate::TDescribeDataRequest& request) { + return ValidateRequest(request, GetBlockSize()); + }; + + if (!AcceptRequest(ev, ctx, validator)) { + return; + } + + auto* msg = ev->Get(); + const TByteRange byteRange( + msg->Record.GetOffset(), + msg->Record.GetLength(), + GetBlockSize() + ); + + auto requestInfo = CreateRequestInfo( + ev->Sender, + ev->Cookie, + msg->CallContext); + + TByteRange alignedByteRange = byteRange.AlignedSuperRange(); + auto blockBuffer = CreateBlockBuffer(alignedByteRange); + + ExecuteTx( + ctx, + std::move(requestInfo), + msg->Record, + byteRange, + alignedByteRange, + std::move(blockBuffer), + true /* describeOnly */); +} + +//////////////////////////////////////////////////////////////////////////////// + bool TIndexTabletActor::PrepareTx_ReadData( const TActorContext& ctx, TTransactionContext& tx, @@ -584,19 +625,54 @@ void TIndexTabletActor::CompleteTx_ReadData( const TActorContext& ctx, TTxIndexTablet::TReadData& args) { - if (FAILED(args.Error.GetCode())) { - auto response = std::make_unique(args.Error); - CompleteResponse( + if (args.DescribeOnly && !HasError(args.Error)) { + auto response = + std::make_unique(); + auto& record = response->Record; + + // TODO: fill record.{BlobPieces,FreshDataRanges} + Y_UNUSED(record); + + CompleteResponse( response->Record, args.RequestInfo->CallContext, ctx); NCloud::Reply(ctx, *args.RequestInfo, std::move(response)); + + return; + } + + if (HasError(args.Error)) { + if (args.DescribeOnly) { + auto response = + std::make_unique(args.Error); + CompleteResponse( + response->Record, + args.RequestInfo->CallContext, + ctx); + + NCloud::Reply(ctx, *args.RequestInfo, std::move(response)); + } else { + auto response = + std::make_unique(args.Error); + CompleteResponse( + response->Record, + args.RequestInfo->CallContext, + ctx); + + NCloud::Reply(ctx, *args.RequestInfo, std::move(response)); + } + return; } if (!ShouldReadBlobs(args.Blocks)) { - ApplyBytes(LogTag, args.AlignedByteRange, std::move(args.Bytes), *args.Buffer); + ApplyBytes( + LogTag, + args.AlignedByteRange, + std::move(args.Bytes), + *args.Buffer); auto response = std::make_unique(); CopyFileData( diff --git a/cloud/filestore/libs/storage/tablet/tablet_actor_request.cpp b/cloud/filestore/libs/storage/tablet/tablet_actor_request.cpp index c8af79bda6d..854ac267e55 100644 --- a/cloud/filestore/libs/storage/tablet/tablet_actor_request.cpp +++ b/cloud/filestore/libs/storage/tablet/tablet_actor_request.cpp @@ -112,6 +112,7 @@ template void TIndexTabletActor::CompleteResponse( // FILESTORE_IMPL_VALIDATE FILESTORE_SERVICE(FILESTORE_GENERATE_IMPL, TEvService) +FILESTORE_GENERATE_IMPL(DescribeData, TEvIndexTablet) #undef FILESTORE_GENERATE_IMPL diff --git a/cloud/filestore/libs/storage/tablet/tablet_tx.h b/cloud/filestore/libs/storage/tablet/tablet_tx.h index a02e092fddc..e1477b8ad26 100644 --- a/cloud/filestore/libs/storage/tablet/tablet_tx.h +++ b/cloud/filestore/libs/storage/tablet/tablet_tx.h @@ -1123,6 +1123,7 @@ struct TTxIndexTablet const TByteRange OriginByteRange; const TByteRange AlignedByteRange; /*const*/ IBlockBufferPtr Buffer; + const bool DescribeOnly; ui64 CommitId = InvalidCommitId; ui64 NodeId = InvalidNodeId; @@ -1133,18 +1134,21 @@ struct TTxIndexTablet // NOTE: should persist state across tx restarts TSet MixedBlocksRanges; + template TReadData( TRequestInfoPtr requestInfo, - const NProto::TReadDataRequest& request, + const TReadRequest& request, TByteRange originByteRange, TByteRange alignedByteRange, - IBlockBufferPtr buffer) + IBlockBufferPtr buffer, + bool describeOnly) : TSessionAware(request) , RequestInfo(std::move(requestInfo)) , Handle(request.GetHandle()) , OriginByteRange(originByteRange) , AlignedByteRange(alignedByteRange) , Buffer(std::move(buffer)) + , DescribeOnly(describeOnly) , Blocks(AlignedByteRange.BlockCount()) , Bytes(AlignedByteRange.BlockCount()) { diff --git a/cloud/filestore/private/api/protos/tablet.proto b/cloud/filestore/private/api/protos/tablet.proto index 94e196e571e..06769d0e4d8 100644 --- a/cloud/filestore/private/api/protos/tablet.proto +++ b/cloud/filestore/private/api/protos/tablet.proto @@ -6,6 +6,8 @@ import "cloud/filestore/public/api/protos/fs.proto"; import "cloud/filestore/public/api/protos/headers.proto"; import "cloud/filestore/config/storage.proto"; +import "contrib/ydb/core/protos/base.proto"; + package NCloud.NFileStore.NProtoPrivate; option go_package = "a.yandex-team.ru/cloud/filestore/private/api/protos"; @@ -209,7 +211,7 @@ message TGetFileSystemConfigResponse } //////////////////////////////////////////////////////////////////////////////// -// GetStorageConfig fields request/response. +// GetStorageConfigFields request/response. message TGetStorageConfigFieldsRequest { @@ -233,7 +235,7 @@ message TGetStorageConfigFieldsResponse } //////////////////////////////////////////////////////////////////////////////// -// Change StorageConfig request/response +// ChangeStorageConfig request/response message TChangeStorageConfigRequest { @@ -258,3 +260,72 @@ message TChangeStorageConfigResponse // Result Storage config. NProto.TStorageConfig StorageConfig = 2; } + +//////////////////////////////////////////////////////////////////////////////// +// DescribeData request/response. + +message TDescribeDataRequest +{ + // Optional request headers. + NProto.THeaders Headers = 1; + + // FileSystem identifier. + string FileSystemId = 2; + + // Node. + uint64 NodeId = 3; + + // IO handle. + uint64 Handle = 4; + + // Starting offset for read. + uint64 Offset = 5; + + // Number of bytes to read. + uint64 Length = 6; +} + +// Represents actual data - for the data that is not stored in data channels as +// blobs yet. +message TFreshDataRange +{ + // This offset is relative to the beginning of the inode. + uint64 Offset = 1; + // Data bytes. + bytes Content = 2; +} + +// Represents a range of consecutive bytes inside some blob. +message TRangeInBlob +{ + // This offset is relative to the beginning of the inode. + uint64 Offset = 1; + // This offset is relative to the beginning of the blob. Measured in bytes. + uint32 BlobOffset = 2; + uint32 Length = 3; +} + +// Represents the ranges that need to be read from a single blob. +message TBlobPiece +{ + // Blob id. + NKikimrProto.TLogoBlobID BlobId = 1; + + // Group id. + uint32 BSGroupId = 2; + + // Data ranges. + repeated TRangeInBlob Ranges = 3; +} + +message TDescribeDataResponse +{ + // Optional error, set only if error happened. + NCloud.NProto.TError Error = 1; + + repeated TFreshDataRange FreshDataRanges = 2; + repeated TBlobPiece BlobPieces = 3; + + // Optional response headers. + NProto.TResponseHeaders Headers = 1000; +} diff --git a/cloud/filestore/private/api/protos/ya.make b/cloud/filestore/private/api/protos/ya.make index a2b61bacbc2..10547660d25 100644 --- a/cloud/filestore/private/api/protos/ya.make +++ b/cloud/filestore/private/api/protos/ya.make @@ -1,12 +1,14 @@ PROTO_LIBRARY(filestore-private-api-protos) -INCLUDE_TAGS(GO_PROTO) +EXCLUDE_TAGS(GO_PROTO) EXCLUDE_TAGS(JAVA_PROTO) PEERDIR( cloud/filestore/config cloud/filestore/public/api/protos cloud/storage/core/protos + + contrib/ydb/core/protos ) SRCS(