diff --git a/build/conf/opensource.conf b/build/conf/opensource.conf index 2b3c783799..a442d37156 100644 --- a/build/conf/opensource.conf +++ b/build/conf/opensource.conf @@ -22,10 +22,10 @@ when ($OPENSOURCE == "yes") { SO_OUTPUTS=yes UDF_NO_PROBE=yes USE_ASMLIB=no - USE_DYNAMIC_LIBFUSE=yes USE_MKL=no VALIDATE_DATA=no when ($FORCE_STATIC_LINKING != "yes") { + USE_DYNAMIC_LIBFUSE=yes _USE_AIO=dynamic _USE_ICONV=dynamic _USE_IDN=dynamic diff --git a/cloud/filestore/libs/diagnostics/profile_log_events.cpp b/cloud/filestore/libs/diagnostics/profile_log_events.cpp index 9784a45a0c..00d8f3261b 100644 --- a/cloud/filestore/libs/diagnostics/profile_log_events.cpp +++ b/cloud/filestore/libs/diagnostics/profile_log_events.cpp @@ -418,6 +418,27 @@ void InitProfileLogRequestInfo( nodeInfo->SetNodeName(request.GetCheckpointId()); } +template <> +void InitProfileLogRequestInfo( + NProto::TProfileLogRequestInfo& profileLogRequest, + const NProto::TFsyncRequest& request) +{ + auto* nodeInfo = profileLogRequest.MutableNodeInfo(); + nodeInfo->SetNodeId(request.GetNodeId()); + nodeInfo->SetHandle(request.GetHandle()); + nodeInfo->SetFlags(request.GetDataSync()); +} + +template <> +void InitProfileLogRequestInfo( + NProto::TProfileLogRequestInfo& profileLogRequest, + const NProto::TFsyncDirRequest& request) +{ + auto* nodeInfo = profileLogRequest.MutableNodeInfo(); + nodeInfo->SetNodeId(request.GetNodeId()); + nodeInfo->SetFlags(request.GetDataSync()); +} + //////////////////////////////////////////////////////////////////////////////// #define IMPLEMENT_DEFAULT_METHOD(name, ns) \ @@ -474,6 +495,8 @@ void InitProfileLogRequestInfo( IMPLEMENT_DEFAULT_METHOD(DescribeData, NProtoPrivate) IMPLEMENT_DEFAULT_METHOD(GenerateBlobIds, NProtoPrivate) IMPLEMENT_DEFAULT_METHOD(AddData, NProtoPrivate) + IMPLEMENT_DEFAULT_METHOD(Fsync, NProto) + IMPLEMENT_DEFAULT_METHOD(FsyncDir, NProto) #undef IMPLEMENT_DEFAULT_METHOD diff --git a/cloud/filestore/libs/diagnostics/profile_log_events.h b/cloud/filestore/libs/diagnostics/profile_log_events.h index c5b7c1e1a2..63f5bb5830 100644 --- a/cloud/filestore/libs/diagnostics/profile_log_events.h +++ b/cloud/filestore/libs/diagnostics/profile_log_events.h @@ -30,6 +30,7 @@ namespace NFuse { #define FILESTORE_FUSE_REQUESTS(xxx, ...) \ xxx(Flush, __VA_ARGS__) \ xxx(Fsync, __VA_ARGS__) \ + xxx(FsyncDir, __VA_ARGS__) \ // FILESTORE_FUSE_REQUESTS #define FILESTORE_MATERIALIZE_REQUEST(name, ...) name, diff --git a/cloud/filestore/libs/service/auth_scheme.cpp b/cloud/filestore/libs/service/auth_scheme.cpp index 9195c3ad3d..59730f7fe3 100644 --- a/cloud/filestore/libs/service/auth_scheme.cpp +++ b/cloud/filestore/libs/service/auth_scheme.cpp @@ -47,6 +47,8 @@ TPermissionList GetRequestPermissions(EFileStoreRequest requestType) case EFileStoreRequest::GenerateBlobIds: case EFileStoreRequest::WriteBlob: case EFileStoreRequest::AddData: + case EFileStoreRequest::Fsync: + case EFileStoreRequest::FsyncDir: return CreatePermissionList({EPermission::Write}); case EFileStoreRequest::AddClusterNode: diff --git a/cloud/filestore/libs/service/request.h b/cloud/filestore/libs/service/request.h index 5c8a235622..ae73837285 100644 --- a/cloud/filestore/libs/service/request.h +++ b/cloud/filestore/libs/service/request.h @@ -70,6 +70,9 @@ namespace NCloud::NFileStore { xxx(ReadData, __VA_ARGS__) \ xxx(WriteData, __VA_ARGS__) \ xxx(AllocateData, __VA_ARGS__) \ + \ + xxx(Fsync, __VA_ARGS__) \ + xxx(FsyncDir, __VA_ARGS__) \ // FILESTORE_DATA_METHODS #define FILESTORE_DATA_SERVICE(xxx, ...) \ diff --git a/cloud/filestore/libs/service_local/fs.h b/cloud/filestore/libs/service_local/fs.h index 9ac30ff86f..bc03886aed 100644 --- a/cloud/filestore/libs/service_local/fs.h +++ b/cloud/filestore/libs/service_local/fs.h @@ -58,6 +58,9 @@ namespace NCloud::NFileStore { xxx(TestLock, __VA_ARGS__) \ \ xxx(AllocateData, __VA_ARGS__) \ + \ + xxx(Fsync, __VA_ARGS__) \ + xxx(FsyncDir, __VA_ARGS__) \ // FILESTORE_DATA_METHODS_LOCAL_SYNC #define FILESTORE_DATA_METHODS_LOCAL_ASYNC(xxx, ...) \ diff --git a/cloud/filestore/libs/service_local/fs_data.cpp b/cloud/filestore/libs/service_local/fs_data.cpp index e42757cfb3..8351918ef6 100644 --- a/cloud/filestore/libs/service_local/fs_data.cpp +++ b/cloud/filestore/libs/service_local/fs_data.cpp @@ -112,21 +112,14 @@ TFuture TLocalFileSystem::WriteDataAsync( TErrorResponse(ErrorInvalidHandle(request.GetHandle()))); } - const FHANDLE fd = *handle; auto b = std::move(*request.MutableBuffer()); TArrayRef data(b.begin(), b.vend()); auto promise = NewPromise(); FileIOService->AsyncWrite(*handle, request.GetOffset(), data).Subscribe( - [b = std::move(b), promise, fd] (const TFuture& f) mutable { + [b = std::move(b), promise] (const TFuture& f) mutable { NProto::TWriteDataResponse response; try { f.GetValue(); - TFileHandle h(fd); - const bool flushed = h.Flush(); - h.Release(); - if (!flushed) { - throw yexception() << "failed to flush " << fd; - } } catch (...) { *response.MutableError() = MakeError(E_IO, CurrentExceptionMessage()); @@ -161,4 +154,45 @@ NProto::TAllocateDataResponse TLocalFileSystem::AllocateData( return {}; } +NProto::TFsyncResponse TLocalFileSystem::Fsync( + const NProto::TFsyncRequest& request) +{ + STORAGE_TRACE("Fsync " << DumpMessage(request)); + + auto session = GetSession(request); + auto* handle = session->LookupHandle(request.GetHandle()); + if (!handle || !handle->IsOpen()) { + return TErrorResponse(ErrorInvalidHandle(request.GetHandle())); + } + + const bool flushed = + request.GetDataSync() ? handle->FlushData() : handle->Flush(); + if (!flushed) { + return TErrorResponse(E_IO, "flush failed"); + } + + return {}; +} + +NProto::TFsyncDirResponse TLocalFileSystem::FsyncDir( + const NProto::TFsyncDirRequest& request) +{ + STORAGE_TRACE("FsyncDir " << DumpMessage(request)); + + auto session = GetSession(request); + auto node = session->LookupNode(request.GetNodeId()); + if (!node) { + return TErrorResponse(ErrorInvalidTarget(request.GetNodeId())); + } + + auto handle = node->OpenHandle(O_RDONLY|O_DIRECTORY); + const bool flushed = + request.GetDataSync() ? handle.FlushData() : handle.Flush(); + if (!flushed) { + return TErrorResponse(E_IO, "flush failed"); + } + + return {}; +} + } // namespace NCloud::NFileStore diff --git a/cloud/filestore/libs/service_local/service_ut.cpp b/cloud/filestore/libs/service_local/service_ut.cpp index 45a1a33b4d..ce6ee4594c 100644 --- a/cloud/filestore/libs/service_local/service_ut.cpp +++ b/cloud/filestore/libs/service_local/service_ut.cpp @@ -636,6 +636,23 @@ struct TTestBootstrap return request; } + auto CreateFsyncRequest(ui64 node, ui64 handle, bool dataSync) + { + auto request = CreateRequest(); + request->SetNodeId(node); + request->SetHandle(handle); + request->SetDataSync(dataSync); + return request; + } + + auto CreateFsyncDirRequest(ui64 node, bool dataSync) + { + auto request = CreateRequest(); + request->SetNodeId(node); + request->SetDataSync(dataSync); + return request; + } + #define FILESTORE_DECLARE_METHOD(name, ns) \ template \ NProto::T##name##Response name(Args&&... args) \ @@ -1580,6 +1597,23 @@ Y_UNIT_TEST_SUITE(LocalFileStore) UNIT_ASSERT_VALUES_EQUAL(names2[0], "d"); } } + + Y_UNIT_TEST(ShouldFsyncFileAndDir) + { + TTestBootstrap bootstrap("fs"); + + auto fileNodeId = CreateFile(bootstrap, RootNodeId, "file1"); + auto dirNodeId = CreateDirectory(bootstrap, RootNodeId, "dir1"); + + auto fileHandle = + bootstrap.CreateHandle(fileNodeId, "", TCreateHandleArgs::RDWR) + .GetHandle(); + + for (auto dataSync: {true, false}) { + bootstrap.Fsync(fileNodeId, fileHandle, dataSync); + bootstrap.FsyncDir(dirNodeId, dataSync); + } + } }; } // namespace NCloud::NFileStore diff --git a/cloud/filestore/libs/storage/api/components.h b/cloud/filestore/libs/storage/api/components.h index d8de97ce3f..fb93e03c18 100644 --- a/cloud/filestore/libs/storage/api/components.h +++ b/cloud/filestore/libs/storage/api/components.h @@ -18,12 +18,13 @@ namespace NCloud::NFileStore::NStorage { #define FILESTORE_ACTORS(xxx) \ xxx(SCHEMESHARD) \ xxx(SERVICE) \ - xxx(SERVICE_WORKER) \ + xxx(SERVICE_PART2) \ xxx(SERVICE_PROXY) \ xxx(TABLET) \ xxx(TABLET_WORKER) \ xxx(TABLET_PROXY) \ xxx(SS_PROXY) \ + xxx(SERVICE_WORKER) \ // FILESTORE_ACTORS #define FILESTORE_COMPONENTS(xxx) \ @@ -80,6 +81,8 @@ struct TFileStoreEvents END }; + static_assert(SERVICE_END + 1 == SERVICE_PART2_START, + "SERVICE_PART2 should follow SERVICE since SERVICE has more than 100 events"); static_assert(END < EventSpaceEnd(NKikimr::TKikimrEvents::ES_FILESTORE), "END expected to be < EventSpaceEnd(NKikimr::TKikimrEvents::ES_FILESTORE)"); }; diff --git a/cloud/filestore/libs/storage/api/service.h b/cloud/filestore/libs/storage/api/service.h index ab387a104c..866b266a57 100644 --- a/cloud/filestore/libs/storage/api/service.h +++ b/cloud/filestore/libs/storage/api/service.h @@ -266,11 +266,17 @@ struct TEvService EvExecuteActionRequest = EvBegin + 95, EvExecuteActionResponse, + EvFsyncRequest = EvBegin + 97, + EvFsyncResponse, + + EvFsyncDirRequest = EvBegin + 99, + EvFsyncDirResponse, + EvEnd }; - static_assert(EvEnd < (int)TFileStoreEvents::SERVICE_END, - "EvEnd expected to be < TFileStoreEvents::SERVICE_END"); + static_assert(EvEnd < (int)TFileStoreEvents::SERVICE_PART2_END, + "EvEnd expected to be < TFileStoreEvents::SERVICE_PART2_END"); FILESTORE_SERVICE(FILESTORE_DECLARE_PROTO_EVENTS, NProto) diff --git a/cloud/filestore/libs/storage/service/service_actor_fsync.cpp b/cloud/filestore/libs/storage/service/service_actor_fsync.cpp new file mode 100644 index 0000000000..86dab1567a --- /dev/null +++ b/cloud/filestore/libs/storage/service/service_actor_fsync.cpp @@ -0,0 +1,27 @@ +#include "service_actor.h" + +namespace NCloud::NFileStore::NStorage { + +using namespace NActors; + +using namespace NKikimr; + +//////////////////////////////////////////////////////////////////////////////// + +void TStorageServiceActor::HandleFsync( + const TEvService::TEvFsyncRequest::TPtr& ev, + const TActorContext& ctx) +{ + auto response = std::make_unique(); + NCloud::Reply(ctx, *ev, std::move(response)); +} + +void TStorageServiceActor::HandleFsyncDir( + const TEvService::TEvFsyncDirRequest::TPtr& ev, + const TActorContext& ctx) +{ + auto response = std::make_unique(); + NCloud::Reply(ctx, *ev, std::move(response)); +} + +} // 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 c4dcea1bbf..0749ad30b9 100644 --- a/cloud/filestore/libs/storage/service/service_ut.cpp +++ b/cloud/filestore/libs/storage/service/service_ut.cpp @@ -5809,6 +5809,41 @@ Y_UNIT_TEST_SUITE(TStorageServiceTest) UNIT_ASSERT_VALUES_EQUAL(lastCompactionMapRangeId, 29); } + + Y_UNIT_TEST(ShouldFsyncFileAndDir) + { + TTestEnv env; + env.CreateSubDomain("nfs"); + + ui32 nodeIdx = env.CreateNode("nfs"); + + TServiceClient service(env.GetRuntime(), nodeIdx); + service.CreateFileStore("test", 1'000); + + auto headers = service.InitSession("test", "client"); + + ui64 fileNodeId = + service + .CreateNode(headers, TCreateNodeArgs::File(RootNodeId, "file")) + ->Record.GetNode() + .GetId(); + + ui64 fileHandle = + service + .CreateHandle(headers, "test", fileNodeId, "", TCreateHandleArgs::RDWR) + ->Record.GetHandle(); + + ui64 dirNodeId = + service + .CreateNode(headers, TCreateNodeArgs::Directory(RootNodeId, "dir")) + ->Record.GetNode() + .GetId(); + + for (auto datasync: {true, false}) { + service.Fsync(headers, "test", fileNodeId, fileHandle, datasync); + service.FsyncDir(headers, "test", dirNodeId, datasync); + } + } } } // namespace NCloud::NFileStore::NStorage diff --git a/cloud/filestore/libs/storage/service/ya.make b/cloud/filestore/libs/storage/service/ya.make index 27ca295533..366b6e2ba6 100644 --- a/cloud/filestore/libs/storage/service/ya.make +++ b/cloud/filestore/libs/storage/service/ya.make @@ -25,6 +25,7 @@ SRCS( service_actor_destroyfs.cpp service_actor_destroysession.cpp service_actor_forward.cpp + service_actor_fsync.cpp service_actor_getfsinfo.cpp service_actor_getnodeattr.cpp service_actor_getsessionevents.cpp diff --git a/cloud/filestore/libs/storage/testlib/service_client.h b/cloud/filestore/libs/storage/testlib/service_client.h index 05ea2244e4..3d06bcdbe3 100644 --- a/cloud/filestore/libs/storage/testlib/service_client.h +++ b/cloud/filestore/libs/storage/testlib/service_client.h @@ -444,6 +444,36 @@ class TServiceClient return request; } + std::unique_ptr CreateFsyncRequest( + const THeaders& headers, + const TString& fileSystemId, + const ui64 nodeId, + const ui64 handle, + bool dataSync) + { + auto request = std::make_unique(); + headers.Fill(request->Record); + request->Record.SetFileSystemId(fileSystemId); + request->Record.SetNodeId(nodeId); + request->Record.SetHandle(handle); + request->Record.SetDataSync(dataSync); + return request; + } + + std::unique_ptr CreateFsyncDirRequest( + const THeaders& headers, + const TString& fileSystemId, + const ui64 nodeId, + bool dataSync) + { + auto request = std::make_unique(); + headers.Fill(request->Record); + request->Record.SetFileSystemId(fileSystemId); + request->Record.SetNodeId(nodeId); + request->Record.SetDataSync(dataSync); + return request; + } + std::unique_ptr CreateSetNodeXAttrRequest( const THeaders& headers, const TString& fileSystemId, diff --git a/cloud/filestore/libs/vfs_fuse/fs_impl.h b/cloud/filestore/libs/vfs_fuse/fs_impl.h index d5bdf9621d..89d5056380 100644 --- a/cloud/filestore/libs/vfs_fuse/fs_impl.h +++ b/cloud/filestore/libs/vfs_fuse/fs_impl.h @@ -364,6 +364,12 @@ class TFileSystem final fuse_req_t req, fuse_ino_t ino); + bool ValidateDirectoryHandle( + TCallContext& callContext, + fuse_req_t req, + fuse_ino_t ino, + uint64_t fh); + bool UpdateNodesCache( const NProto::TNodeAttr& attrs, fuse_entry_param& entry); diff --git a/cloud/filestore/libs/vfs_fuse/fs_impl_data.cpp b/cloud/filestore/libs/vfs_fuse/fs_impl_data.cpp index 9362af0cec..65e9b77430 100644 --- a/cloud/filestore/libs/vfs_fuse/fs_impl_data.cpp +++ b/cloud/filestore/libs/vfs_fuse/fs_impl_data.cpp @@ -7,6 +7,7 @@ namespace NCloud::NFileStore::NFuse { using namespace NCloud::NFileStore::NVFS; +using namespace NThreading; namespace { @@ -444,7 +445,8 @@ void TFileSystem::FSync( TNodeId{fi ? ino : InvalidNodeId}, THandle{fi ? fi->fh : InvalidHandle}); - auto callback = [=, ptr = weak_from_this(), requestInfo = std::move(requestInfo)] + std::function&)> + callback = [=, ptr = weak_from_this(), requestInfo = std::move(requestInfo)] (const auto& future) mutable { auto self = ptr.lock(); if (!self) { @@ -465,6 +467,34 @@ void TFileSystem::FSync( } }; + if (fi) { + callback = [ptr = weak_from_this(), + callContext, + ino, + datasync, + fh = fi->fh, + callback = std::move(callback)](const auto& future) mutable + { + auto self = ptr.lock(); + if (!self) { + return; + } + + if (HasError(future.GetValue())) { + callback(future); + return; + } + + auto request = StartRequest(ino); + request->SetHandle(fh); + request->SetDataSync(datasync); + self->Session->Fsync(callContext, std::move(request)) + .Apply([](const auto& future) + { return future.GetValue().GetError(); }) + .Subscribe(std::move(callback)); + }; + } + if (fi) { if (datasync) { FSyncQueue.WaitForDataRequests(reqId, TNodeId {ino}, THandle {fi->fh}) @@ -491,21 +521,30 @@ void TFileSystem::FSyncDir( int datasync, fuse_file_info* fi) { + Y_ABORT_UNLESS(fi); + STORAGE_DEBUG("FSyncDir #" << ino << " @" << fi->fh); if (!ValidateNodeId(*callContext, req, ino)) { return; } + if (!ValidateDirectoryHandle(*callContext, req, ino, fi->fh)) { + return; + } + const auto reqId = callContext->RequestId; NProto::TProfileLogRequestInfo requestInfo; - InitProfileLogRequestInfo(requestInfo, EFileStoreFuseRequest::Fsync, Now()); + InitProfileLogRequestInfo( + requestInfo, + EFileStoreFuseRequest::FsyncDir, + Now()); InitNodeInfo( requestInfo, datasync, - TNodeId{fi ? ino : InvalidNodeId}, - THandle{fi ? fi->fh : InvalidHandle}); + TNodeId{ino}, + THandle{fi->fh}); auto callback = [=, ptr = weak_from_this(), requestInfo = std::move(requestInfo)] (const auto& future) mutable { @@ -528,12 +567,37 @@ void TFileSystem::FSyncDir( } }; - if (datasync) { - FSyncQueue.WaitForDataRequests(reqId) + auto waitCallback = + [ptr = weak_from_this(), + callContext, + ino, + datasync, + callback = std::move(callback)](const auto& future) mutable + { + auto self = ptr.lock(); + if (!self) { + return; + } + + if (HasError(future.GetValue())) { + callback(future); + return; + } + + auto request = StartRequest(ino); + request->SetDataSync(datasync); + self->Session->FsyncDir(callContext, std::move(request)) + .Apply([](const auto& future) + { return future.GetValue().GetError(); }) .Subscribe(std::move(callback)); + }; + + if (datasync) { + FSyncQueue.WaitForDataRequests(reqId).Subscribe( + std::move(waitCallback)); } else { - FSyncQueue.WaitForRequests(reqId) - .Subscribe(std::move(callback)); + FSyncQueue.WaitForRequests(reqId).Subscribe( + std::move(waitCallback)); } } diff --git a/cloud/filestore/libs/vfs_fuse/fs_impl_list.cpp b/cloud/filestore/libs/vfs_fuse/fs_impl_list.cpp index 1695af0fea..7e49f0a6ef 100644 --- a/cloud/filestore/libs/vfs_fuse/fs_impl_list.cpp +++ b/cloud/filestore/libs/vfs_fuse/fs_impl_list.cpp @@ -398,4 +398,35 @@ void TFileSystem::ReleaseDir( ReplyError(*callContext, {}, req, 0); } +bool TFileSystem::ValidateDirectoryHandle( + TCallContext& callContext, + fuse_req_t req, + fuse_ino_t ino, + uint64_t fh) +{ + std::shared_ptr handle; + with_lock (CacheLock) { + auto it = DirectoryHandles.find(fh); + if (it == DirectoryHandles.end()) { + ReplyError( + callContext, + ErrorInvalidHandle(fh), + req, + EBADF); + return false; + } + + handle = it->second; + } + + Y_ABORT_UNLESS(handle); + + if (!CheckDirectoryHandle(req, ino, *handle, Log, __func__)) { + ReplyError(callContext, ErrorInvalidHandle(fh), req, EBADF); + return false; + } + + return true; +} + } // namespace NCloud::NFileStore::NFuse diff --git a/cloud/filestore/libs/vfs_fuse/fs_ut.cpp b/cloud/filestore/libs/vfs_fuse/fs_ut.cpp index 91429c9d68..625557d1da 100644 --- a/cloud/filestore/libs/vfs_fuse/fs_ut.cpp +++ b/cloud/filestore/libs/vfs_fuse/fs_ut.cpp @@ -267,6 +267,20 @@ Y_UNIT_TEST_SUITE(TFileSystemTest) return MakeFuture(result); }; + int fsyncCalledWithDataSync = 0; + int fsyncCalledWithoutDataSync = 0; + bootstrap.Service->FsyncHandler = [&] (auto callContext, auto request) { + UNIT_ASSERT_VALUES_EQUAL(FileSystemId, callContext->FileSystemId); + + if (request->GetDataSync()) { + fsyncCalledWithDataSync++; + } else { + fsyncCalledWithoutDataSync++; + } + + return MakeFuture(NProto::TFsyncResponse()); + }; + bootstrap.Start(); auto handle = bootstrap.Fuse->SendRequest("/file1", RootNodeId); @@ -275,6 +289,18 @@ Y_UNIT_TEST_SUITE(TFileSystemTest) auto write = bootstrap.Fuse->SendRequest( nodeId, handleId, 0, CreateBuffer(4096, 'a')); UNIT_ASSERT_NO_EXCEPTION(write.GetValue(WaitTimeout)); + + auto fsync = bootstrap.Fuse->SendRequest( + nodeId, handleId, false /* no data sync */); + UNIT_ASSERT_NO_EXCEPTION(fsync.GetValue(WaitTimeout)); + UNIT_ASSERT( + fsyncCalledWithoutDataSync == 1 && fsyncCalledWithDataSync == 0); + + fsync = bootstrap.Fuse->SendRequest( + nodeId, handleId, true /* data sync */); + UNIT_ASSERT_NO_EXCEPTION(fsync.GetValue(WaitTimeout)); + UNIT_ASSERT( + fsyncCalledWithoutDataSync == 1 && fsyncCalledWithDataSync == 1); } Y_UNIT_TEST(ShouldPassSessionId) @@ -500,6 +526,20 @@ Y_UNIT_TEST_SUITE(TFileSystemTest) return MakeFuture(result); }; + int fsyncDirCalledWithDataSync = 0; + int fsyncDirCalledWithoutDataSync = 0; + bootstrap.Service->FsyncDirHandler = [&] (auto callContext, auto request) { + UNIT_ASSERT_VALUES_EQUAL(FileSystemId, callContext->FileSystemId); + + if (request->GetDataSync()) { + fsyncDirCalledWithDataSync++; + } else { + fsyncDirCalledWithoutDataSync++; + } + + return MakeFuture(NProto::TFsyncDirResponse()); + }; + bootstrap.Start(); const ui64 nodeId = 123; @@ -519,6 +559,20 @@ Y_UNIT_TEST_SUITE(TFileSystemTest) size = read.GetValue(); UNIT_ASSERT_VALUES_EQUAL(size, 0); + auto fsyncdir = bootstrap.Fuse->SendRequest( + nodeId, handleId, false /* no data sync */); + UNIT_ASSERT_NO_EXCEPTION(fsyncdir.GetValue(WaitTimeout)); + UNIT_ASSERT( + fsyncDirCalledWithoutDataSync == 1 && + fsyncDirCalledWithDataSync == 0); + + fsyncdir = bootstrap.Fuse->SendRequest( + nodeId, handleId, true /* data sync */); + UNIT_ASSERT_NO_EXCEPTION(fsyncdir.GetValue(WaitTimeout)); + UNIT_ASSERT( + fsyncDirCalledWithoutDataSync == 1 && + fsyncDirCalledWithDataSync == 1); + auto close = bootstrap.Fuse->SendRequest(nodeId, handleId); UNIT_ASSERT_NO_EXCEPTION(close.GetValue(WaitTimeout)); } diff --git a/cloud/filestore/libs/vhost/request.h b/cloud/filestore/libs/vhost/request.h index 6592d7e52b..d8373e0417 100644 --- a/cloud/filestore/libs/vhost/request.h +++ b/cloud/filestore/libs/vhost/request.h @@ -424,4 +424,30 @@ struct TReleaseRequest } }; +//////////////////////////////////////////////////////////////////////////////// + +struct TFsyncRequest + : public TRequestBase +{ + TFsyncRequest(ui64 nodeId, ui64 fh, bool datasync) + { + In->Header.opcode = FUSE_FSYNC; + In->Header.nodeid = nodeId; + In->Body.fh = fh; + In->Body.fsync_flags = datasync; + } +}; + +struct TFsyncDirRequest + : public TRequestBase +{ + TFsyncDirRequest(ui64 nodeId, ui64 fh, bool datasync) + { + In->Header.opcode = FUSE_FSYNCDIR; + In->Header.nodeid = nodeId; + In->Body.fh = fh; + In->Body.fsync_flags = datasync; + } +}; + } // namespace NCloud::NFileStore::NVhost diff --git a/cloud/filestore/public/api/grpc/service.proto b/cloud/filestore/public/api/grpc/service.proto index 7b7fe2ab5d..924f930834 100644 --- a/cloud/filestore/public/api/grpc/service.proto +++ b/cloud/filestore/public/api/grpc/service.proto @@ -346,6 +346,20 @@ service TFileStoreService }; } + rpc Fsync(TFsyncRequest) returns (TFsyncResponse) { + option (google.api.http) = { + post: "/fsync" + body: "*" + }; + } + + rpc FsyncDir(TFsyncDirRequest) returns (TFsyncDirResponse) { + option (google.api.http) = { + post: "/fsync_dir" + body: "*" + }; + } + // // Locking operations. // diff --git a/cloud/filestore/public/api/protos/data.proto b/cloud/filestore/public/api/protos/data.proto index 2d1d731330..a9d5fc31e7 100644 --- a/cloud/filestore/public/api/protos/data.proto +++ b/cloud/filestore/public/api/protos/data.proto @@ -259,3 +259,60 @@ message TTruncateDataResponse // Optional response headers. TResponseHeaders Headers = 1000; } + +//////////////////////////////////////////////////////////////////////////////// +// Fsync request/response + +message TFsyncRequest +{ + // Optional request headers. + THeaders Headers = 1; + + // FileSystem identifier. + string FileSystemId = 2; + + // Node. + uint64 NodeId = 3; + + // IO handle. + uint64 Handle = 4; + + // If true only the user data should be flushed, not the meta data. + bool DataSync = 5; +} + +message TFsyncResponse +{ + // Optional error, set only if error happened. + NCloud.NProto.TError Error = 1; + + // Optional response headers. + TResponseHeaders Headers = 1000; +} + +//////////////////////////////////////////////////////////////////////////////// +// FsyncDir request/response + +message TFsyncDirRequest +{ + // Optional request headers. + THeaders Headers = 1; + + // FileSystem identifier. + string FileSystemId = 2; + + // Node. + uint64 NodeId = 3; + + // If true only the user data should be flushed, not the meta data. + bool DataSync = 4; +} + +message TFsyncDirResponse +{ + // Optional error, set only if error happened. + NCloud.NProto.TError Error = 1; + + // Optional response headers. + TResponseHeaders Headers = 1000; +} diff --git a/cloud/filestore/tools/analytics/libs/event-log/dump_ut.cpp b/cloud/filestore/tools/analytics/libs/event-log/dump_ut.cpp index 1556b89273..4150891ff5 100644 --- a/cloud/filestore/tools/analytics/libs/event-log/dump_ut.cpp +++ b/cloud/filestore/tools/analytics/libs/event-log/dump_ut.cpp @@ -94,7 +94,7 @@ Y_UNIT_TEST_SUITE(TDumpTest) { const auto requests = GetRequestTypes(); - UNIT_ASSERT_VALUES_EQUAL(70, requests.size()); + UNIT_ASSERT_VALUES_EQUAL(73, requests.size()); ui32 index = 0; #define TEST_REQUEST_TYPE(id, name) \ @@ -151,20 +151,23 @@ Y_UNIT_TEST_SUITE(TDumpTest) TEST_REQUEST_TYPE(43, ReadData); TEST_REQUEST_TYPE(44, WriteData); TEST_REQUEST_TYPE(45, AllocateData); - TEST_REQUEST_TYPE(46, GetSessionEventsStream); - TEST_REQUEST_TYPE(47, StartEndpoint); - TEST_REQUEST_TYPE(48, StopEndpoint); - TEST_REQUEST_TYPE(49, ListEndpoints); - TEST_REQUEST_TYPE(50, KickEndpoint); - TEST_REQUEST_TYPE(51, DescribeData); - TEST_REQUEST_TYPE(52, GenerateBlobIds); - TEST_REQUEST_TYPE(53, AddData); - TEST_REQUEST_TYPE(54, ReadBlob); - TEST_REQUEST_TYPE(55, WriteBlob); + TEST_REQUEST_TYPE(46, Fsync); + TEST_REQUEST_TYPE(47, FsyncDir); + TEST_REQUEST_TYPE(48, GetSessionEventsStream); + TEST_REQUEST_TYPE(49, StartEndpoint); + TEST_REQUEST_TYPE(50, StopEndpoint); + TEST_REQUEST_TYPE(51, ListEndpoints); + TEST_REQUEST_TYPE(52, KickEndpoint); + TEST_REQUEST_TYPE(53, DescribeData); + TEST_REQUEST_TYPE(54, GenerateBlobIds); + TEST_REQUEST_TYPE(55, AddData); + TEST_REQUEST_TYPE(56, ReadBlob); + TEST_REQUEST_TYPE(57, WriteBlob); // Fuse TEST_REQUEST_TYPE(1001, Flush); TEST_REQUEST_TYPE(1002, Fsync); + TEST_REQUEST_TYPE(1003, FsyncDir); // Tablet TEST_REQUEST_TYPE(10001, Flush); diff --git a/cloud/filestore/tools/analytics/libs/event-log/request_printer.cpp b/cloud/filestore/tools/analytics/libs/event-log/request_printer.cpp index 5cb5ed95b3..1a44e96b9d 100644 --- a/cloud/filestore/tools/analytics/libs/event-log/request_printer.cpp +++ b/cloud/filestore/tools/analytics/libs/event-log/request_printer.cpp @@ -567,6 +567,7 @@ IRequestPrinterPtr CreateRequestPrinter(ui32 requestType) switch (static_cast(requestType)) { case NFuse::EFileStoreFuseRequest::Flush: case NFuse::EFileStoreFuseRequest::Fsync: + case NFuse::EFileStoreFuseRequest::FsyncDir: return std::make_shared(); default: break;