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

[Filestore] avoid fsync on every write for local service #1990

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
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
2 changes: 1 addition & 1 deletion build/conf/opensource.conf
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
23 changes: 23 additions & 0 deletions cloud/filestore/libs/diagnostics/profile_log_events.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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) \
Expand Down Expand Up @@ -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

Expand Down
1 change: 1 addition & 0 deletions cloud/filestore/libs/diagnostics/profile_log_events.h
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
2 changes: 2 additions & 0 deletions cloud/filestore/libs/service/auth_scheme.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
3 changes: 3 additions & 0 deletions cloud/filestore/libs/service/request.h
Original file line number Diff line number Diff line change
Expand Up @@ -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, ...) \
Expand Down
3 changes: 3 additions & 0 deletions cloud/filestore/libs/service_local/fs.h
Original file line number Diff line number Diff line change
Expand Up @@ -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, ...) \
Expand Down
50 changes: 42 additions & 8 deletions cloud/filestore/libs/service_local/fs_data.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -112,21 +112,14 @@ TFuture<NProto::TWriteDataResponse> TLocalFileSystem::WriteDataAsync(
TErrorResponse(ErrorInvalidHandle(request.GetHandle())));
}

const FHANDLE fd = *handle;
auto b = std::move(*request.MutableBuffer());
TArrayRef<char> data(b.begin(), b.vend());
auto promise = NewPromise<NProto::TWriteDataResponse>();
FileIOService->AsyncWrite(*handle, request.GetOffset(), data).Subscribe(
[b = std::move(b), promise, fd] (const TFuture<ui32>& f) mutable {
[b = std::move(b), promise] (const TFuture<ui32>& 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());
Expand Down Expand Up @@ -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
34 changes: 34 additions & 0 deletions cloud/filestore/libs/service_local/service_ut.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -636,6 +636,23 @@ struct TTestBootstrap
return request;
}

auto CreateFsyncRequest(ui64 node, ui64 handle, bool dataSync)
{
auto request = CreateRequest<NProto::TFsyncRequest>();
request->SetNodeId(node);
request->SetHandle(handle);
request->SetDataSync(dataSync);
return request;
}

auto CreateFsyncDirRequest(ui64 node, bool dataSync)
{
auto request = CreateRequest<NProto::TFsyncDirRequest>();
request->SetNodeId(node);
request->SetDataSync(dataSync);
return request;
}

#define FILESTORE_DECLARE_METHOD(name, ns) \
template <typename... Args> \
NProto::T##name##Response name(Args&&... args) \
Expand Down Expand Up @@ -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
5 changes: 4 additions & 1 deletion cloud/filestore/libs/storage/api/components.h
Original file line number Diff line number Diff line change
Expand Up @@ -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) \
Expand Down Expand Up @@ -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)");
};
Expand Down
10 changes: 8 additions & 2 deletions cloud/filestore/libs/storage/api/service.h
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down
27 changes: 27 additions & 0 deletions cloud/filestore/libs/storage/service/service_actor_fsync.cpp
Original file line number Diff line number Diff line change
@@ -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<TEvService::TEvFsyncResponse>();
NCloud::Reply(ctx, *ev, std::move(response));
}

void TStorageServiceActor::HandleFsyncDir(
const TEvService::TEvFsyncDirRequest::TPtr& ev,
const TActorContext& ctx)
{
auto response = std::make_unique<TEvService::TEvFsyncDirResponse>();
NCloud::Reply(ctx, *ev, std::move(response));
}

} // namespace NCloud::NFileStore::NStorage
35 changes: 35 additions & 0 deletions cloud/filestore/libs/storage/service/service_ut.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
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 @@ -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
Expand Down
30 changes: 30 additions & 0 deletions cloud/filestore/libs/storage/testlib/service_client.h
Original file line number Diff line number Diff line change
Expand Up @@ -444,6 +444,36 @@ class TServiceClient
return request;
}

std::unique_ptr<TEvService::TEvFsyncRequest> CreateFsyncRequest(
const THeaders& headers,
const TString& fileSystemId,
const ui64 nodeId,
const ui64 handle,
bool dataSync)
{
auto request = std::make_unique<TEvService::TEvFsyncRequest>();
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<TEvService::TEvFsyncDirRequest> CreateFsyncDirRequest(
const THeaders& headers,
const TString& fileSystemId,
const ui64 nodeId,
bool dataSync)
{
auto request = std::make_unique<TEvService::TEvFsyncDirRequest>();
headers.Fill(request->Record);
request->Record.SetFileSystemId(fileSystemId);
request->Record.SetNodeId(nodeId);
request->Record.SetDataSync(dataSync);
return request;
}

std::unique_ptr<TEvService::TEvSetNodeXAttrRequest> CreateSetNodeXAttrRequest(
const THeaders& headers,
const TString& fileSystemId,
Expand Down
6 changes: 6 additions & 0 deletions cloud/filestore/libs/vfs_fuse/fs_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
Loading