diff --git a/cloud/filestore/apps/client/lib/diagnose_filesystem.cpp b/cloud/filestore/apps/client/lib/diagnose_filesystem.cpp index 9735905f453..acedd84c519 100644 --- a/cloud/filestore/apps/client/lib/diagnose_filesystem.cpp +++ b/cloud/filestore/apps/client/lib/diagnose_filesystem.cpp @@ -179,5 +179,5 @@ TCommandPtr NewDiagnoseFilesystemCommand() { return std::make_shared(); } - + } // namespace NCloud::NFileStore::NClient diff --git a/cloud/filestore/tests/loadtest/max-file-count-test/canondata/result.json b/cloud/filestore/tests/loadtest/max-file-count-test/canondata/result.json new file mode 100644 index 00000000000..eb1c01ff9a8 --- /dev/null +++ b/cloud/filestore/tests/loadtest/max-file-count-test/canondata/result.json @@ -0,0 +1,5 @@ +{ + "test.test_max_file_count": { + "uri": "file://test.test_max_file_count/results.txt" + } +} diff --git a/cloud/filestore/tests/loadtest/max-file-count-test/canondata/test.test_max_file_count/results.txt b/cloud/filestore/tests/loadtest/max-file-count-test/canondata/test.test_max_file_count/results.txt new file mode 100644 index 00000000000..35eed5ae6a3 --- /dev/null +++ b/cloud/filestore/tests/loadtest/max-file-count-test/canondata/test.test_max_file_count/results.txt @@ -0,0 +1,3 @@ +/ max-file-count-loadtest: +/ max-file-count-loadtest: +/ max-file-count-loadtest: diff --git a/cloud/filestore/tests/loadtest/max-file-count-test/max-file-count.txt b/cloud/filestore/tests/loadtest/max-file-count-test/max-file-count.txt new file mode 100644 index 00000000000..009092fa46d --- /dev/null +++ b/cloud/filestore/tests/loadtest/max-file-count-test/max-file-count.txt @@ -0,0 +1,26 @@ +Tests { + LoadTest { + Name: "max-file-count" + ClientId: "max-file-count-loadtest" + CreateFileStoreRequest { + FileSystemId: "max-file-count" + FolderId: "folder" + CloudId: "cloud" + BlocksCount: 10241024 + BlockSize: 4096 + } + KeepFileStore: true + MaxNodes: 3 + RequestsCount: 30 + IODepth: 16 + DataLoadSpec { + Actions { + Action: ACTION_READ + Rate: 1 + } + ReadBytes: 4096 + WriteBytes: 4096 + MinFileCount: 3 + } + } +} diff --git a/cloud/filestore/tests/loadtest/max-file-count-test/test.py b/cloud/filestore/tests/loadtest/max-file-count-test/test.py new file mode 100644 index 00000000000..4482e42dc43 --- /dev/null +++ b/cloud/filestore/tests/loadtest/max-file-count-test/test.py @@ -0,0 +1,43 @@ +import os +import re +import yatest.common as common + +from cloud.filestore.tests.python.lib.client import FilestoreCliClient +from cloud.filestore.tests.python.lib.loadtest import run_load_test + + +def test_max_file_count(): + filesystem_id = "max-file-count" + results_path = common.output_path("results.txt") + config_path = common.source_path( + "cloud/filestore/tests/loadtest/max-file-count-test/max-file-count.txt" + ) + client = FilestoreCliClient( + common.binary_path("cloud/filestore/apps/client/filestore-client"), + os.getenv("NFS_SERVER_PORT"), + cwd=common.output_path()) + + try: + run_load_test( + filesystem_id, + config_path, + os.getenv("NFS_SERVER_PORT"), + ) + + files = client.find(filesystem_id, depth=1).decode().splitlines() + normalized_entries = [ + re.sub( + r"(?<=max-file-count-loadtest:)[0-9a-f-]+$", + "", + file.rsplit("\t", 1)[0], + ) + for file in files + ] + + with open(results_path, "w") as results: + results.write("\n".join(normalized_entries) + "\n") + + finally: + client.destroy(filesystem_id) + + return common.canonical_file(results_path, local=True) diff --git a/cloud/filestore/tests/loadtest/max-file-count-test/ya.make b/cloud/filestore/tests/loadtest/max-file-count-test/ya.make new file mode 100644 index 00000000000..3c49bd8d1b1 --- /dev/null +++ b/cloud/filestore/tests/loadtest/max-file-count-test/ya.make @@ -0,0 +1,25 @@ +PY3TEST() + +INCLUDE(${ARCADIA_ROOT}/cloud/filestore/tests/recipes/medium.inc) + +TEST_SRCS( + test.py +) + +DEPENDS( + cloud/filestore/apps/client + cloud/filestore/tools/testing/loadtest/bin +) + +PEERDIR( + cloud/filestore/tests/python/lib +) + +SET( + NFS_STORAGE_CONFIG_PATCH + cloud/filestore/tests/common_configs/nfs-storage-migration-patch.txt +) + +INCLUDE(${ARCADIA_ROOT}/cloud/filestore/tests/recipes/service-kikimr.inc) + +END() diff --git a/cloud/filestore/tools/testing/loadtest/lib/count_limiter.h b/cloud/filestore/tools/testing/loadtest/lib/count_limiter.h new file mode 100644 index 00000000000..738983c627d --- /dev/null +++ b/cloud/filestore/tools/testing/loadtest/lib/count_limiter.h @@ -0,0 +1,63 @@ +#pragma once + +#include + +#include +#include + +namespace NCloud::NFileStore::NLoadTest { + +//////////////////////////////////////////////////////////////////////////////// + +class TCountLimiter +{ +private: + const ui64 MaxCount; + std::atomic ReservedCount = 0; + + bool TryReserve() + { + if (!MaxCount) { + // MaxCount == 0 means unlimited. + return true; + } + + ui64 count = ReservedCount.load(std::memory_order_relaxed); + while (count < MaxCount){ + if (ReservedCount.compare_exchange_weak( + count, + count + 1, + std::memory_order_relaxed)) + { + return true; + } + } + return count < MaxCount; + } + +public: + explicit TCountLimiter(ui64 maxCount) + : MaxCount(maxCount) + {} + + bool TryReserveHandle() + { + return TryReserve(); + } + + bool TryReserveNode() + { + return TryReserve(); + } + + void Release() + { + if (MaxCount) { + ReservedCount.fetch_sub(1, std::memory_order_relaxed); + } + } +}; + +using TCountLimiterPtr = std::shared_ptr; + +} // namespace NCloud::NFileStore::NLoadTest diff --git a/cloud/filestore/tools/testing/loadtest/lib/request.h b/cloud/filestore/tools/testing/loadtest/lib/request.h index 5765fac69ef..507e5e25400 100644 --- a/cloud/filestore/tools/testing/loadtest/lib/request.h +++ b/cloud/filestore/tools/testing/loadtest/lib/request.h @@ -4,6 +4,7 @@ #include "shm_client.h" #include +#include #include #include @@ -72,14 +73,16 @@ IRequestGeneratorPtr CreateIndexRequestGenerator( IFileStoreServicePtr client, NClient::ISessionPtr session, TString filesystemId, - NProto::THeaders headers); + NProto::THeaders headers, + TCountLimiterPtr countLimiter); IRequestGeneratorPtr CreateDataRequestGenerator( NProto::TDataLoadSpec spec, ILoggingServicePtr logging, NClient::ISessionPtr session, TString filesystemId, - NProto::THeaders headers); + NProto::THeaders headers, + TCountLimiterPtr countLimiter); IRequestGeneratorPtr CreateReplayRequestGeneratorFs( NProto::TReplaySpec spec, @@ -98,7 +101,8 @@ IRequestGeneratorPtr CreateReplayRequestGeneratorGRPC( IRequestGeneratorPtr CreateFastShardRequestGenerator( NProto::TFastShardLoadSpec spec, ui32 maxParallelism, - ILoggingServicePtr logging); + ILoggingServicePtr logging, + TCountLimiterPtr countLimiter); IRequestGeneratorPtr CreateDatashardLikeRequestGenerator( NProto::TDatashardLikeLoadSpec spec, @@ -106,6 +110,7 @@ IRequestGeneratorPtr CreateDatashardLikeRequestGenerator( NClient::ISessionPtr session, IShmDataClientPtr dataClient, TString filesystemId, - NProto::THeaders headers); + NProto::THeaders headers, + TCountLimiterPtr countLimiter); } // namespace NCloud::NFileStore::NLoadTest diff --git a/cloud/filestore/tools/testing/loadtest/lib/request_data.cpp b/cloud/filestore/tools/testing/loadtest/lib/request_data.cpp index 85672ace397..13052956247 100644 --- a/cloud/filestore/tools/testing/loadtest/lib/request_data.cpp +++ b/cloud/filestore/tools/testing/loadtest/lib/request_data.cpp @@ -106,6 +106,7 @@ class TDataRequestGenerator final const NProto::TDataLoadSpec Spec; const TString FileSystemId; const NProto::THeaders Headers; + const TCountLimiterPtr CountLimiter; TLog Log; @@ -134,10 +135,12 @@ class TDataRequestGenerator final ILoggingServicePtr logging, ISessionPtr session, TString filesystemId, - NProto::THeaders headers) + NProto::THeaders headers, + TCountLimiterPtr countLimiter) : Spec(std::move(spec)) , FileSystemId(std::move(filesystemId)) , Headers(std::move(headers)) + , CountLimiter(std::move(countLimiter)) , Session(std::move(session)) { Log = logging->CreateLog(Headers.GetClientId()); @@ -238,6 +241,12 @@ class TDataRequestGenerator final auto started = TInstant::Now(); TGuard guard(StateLock); + if (!CountLimiter->TryReserveHandle()) { + return MakeFuture({ + NProto::ACTION_CREATE_HANDLE, + started, + MakeError(S_FALSE)}); + } auto name = GenerateNodeName(); auto request = CreateRequest(); @@ -301,6 +310,7 @@ class TDataRequestGenerator final return HandleResizeAfterCreateHandle(f, name, started); }); } catch (const TServiceError& e) { + CountLimiter->Release(); auto error = MakeError(e.GetCode(), TString{e.GetMessage()}); STORAGE_ERROR("create handle for %s has failed: %s", name.Quote().c_str(), @@ -646,14 +656,16 @@ IRequestGeneratorPtr CreateDataRequestGenerator( ILoggingServicePtr logging, ISessionPtr session, TString filesystemId, - NProto::THeaders headers) + NProto::THeaders headers, + TCountLimiterPtr countLimiter) { return std::make_shared( std::move(spec), std::move(logging), std::move(session), std::move(filesystemId), - std::move(headers)); + std::move(headers), + std::move(countLimiter)); } } // namespace NCloud::NFileStore::NLoadTest diff --git a/cloud/filestore/tools/testing/loadtest/lib/request_datashard_like.cpp b/cloud/filestore/tools/testing/loadtest/lib/request_datashard_like.cpp index 16abf5207fe..c0f7997932a 100644 --- a/cloud/filestore/tools/testing/loadtest/lib/request_datashard_like.cpp +++ b/cloud/filestore/tools/testing/loadtest/lib/request_datashard_like.cpp @@ -50,6 +50,7 @@ class TDatashardLikeRequestGenerator final const NProto::TDatashardLikeLoadSpec Spec; const TString FileSystemId; const NProto::THeaders Headers; + const TCountLimiterPtr CountLimiter; TLog Log; @@ -77,10 +78,12 @@ class TDatashardLikeRequestGenerator final ISessionPtr session, IShmDataClientPtr dataClient, TString filesystemId, - NProto::THeaders headers) + NProto::THeaders headers, + TCountLimiterPtr countLimiter) : Spec(std::move(spec)) , FileSystemId(std::move(filesystemId)) , Headers(std::move(headers)) + , CountLimiter(std::move(countLimiter)) , Session(std::move(session)) , DataClient(std::move(dataClient)) { @@ -174,6 +177,12 @@ class TDatashardLikeRequestGenerator final TFuture DoCreateNode() { auto started = TInstant::Now(); + if (!CountLimiter->TryReserveNode()) { + return MakeFuture({ + NProto::ACTION_CREATE_NODE, + started, + MakeError(S_FALSE)}); + } auto request = CreateRequest(); request->SetNodeId(RootNodeId); @@ -231,6 +240,7 @@ class TDatashardLikeRequestGenerator final [=, this](const TFuture& f) { return HandleResizeAfterCreate(f, info, started); }); } catch (const TServiceError& e) { + CountLimiter->Release(); auto error = MakeError(e.GetCode(), TString{e.GetMessage()}); STORAGE_ERROR( "create node has failed: %s", @@ -496,7 +506,8 @@ IRequestGeneratorPtr CreateDatashardLikeRequestGenerator( NClient::ISessionPtr session, IShmDataClientPtr dataClient, TString filesystemId, - NProto::THeaders headers) + NProto::THeaders headers, + TCountLimiterPtr countLimiter) { return std::make_shared( std::move(spec), @@ -504,7 +515,8 @@ IRequestGeneratorPtr CreateDatashardLikeRequestGenerator( std::move(session), std::move(dataClient), std::move(filesystemId), - std::move(headers)); + std::move(headers), + std::move(countLimiter)); } } // namespace NCloud::NFileStore::NLoadTest diff --git a/cloud/filestore/tools/testing/loadtest/lib/request_fastshard.cpp b/cloud/filestore/tools/testing/loadtest/lib/request_fastshard.cpp index ca3a18fb2f6..ef95b165869 100644 --- a/cloud/filestore/tools/testing/loadtest/lib/request_fastshard.cpp +++ b/cloud/filestore/tools/testing/loadtest/lib/request_fastshard.cpp @@ -73,6 +73,7 @@ class TFastShardRequestGenerator final const ui64 ReadBytes; const ui64 WriteBytes; const ui64 InitialFileSize; + TCountLimiterPtr CountLimiter; TClient Client; TDeque> Endpoints; @@ -88,12 +89,14 @@ class TFastShardRequestGenerator final TFastShardRequestGenerator( NProto::TFastShardLoadSpec spec, ui32 maxParallelism, - ILoggingServicePtr /*logging*/) + ILoggingServicePtr /*logging*/, + TCountLimiterPtr countLimiter) : Spec(std::move(spec)) , ShardFileSystemId(Spec.GetShardFileSystemId()) , ReadBytes(Spec.GetReadBytes() ? Spec.GetReadBytes() : DefaultIoSize) , WriteBytes(Spec.GetWriteBytes() ? Spec.GetWriteBytes() : DefaultIoSize) , InitialFileSize(Spec.GetInitialFileSize()) + , CountLimiter(std::move(countLimiter)) { Y_ENSURE(!ShardFileSystemId.empty(), "ShardFileSystemId must be set"); Y_ENSURE(maxParallelism > 0); @@ -199,6 +202,11 @@ class TFastShardRequestGenerator final // Create a file if the pool had nothing to offer. if (s->File.Handle == 0) { + if (!ptr->CountLimiter->TryReserveHandle()) { + releaseEndpoint(); + s->Promise.SetValue({s->Action, s->Started, MakeError(S_FALSE)}); + return; + } if (!ptr) { releaseEndpoint(); s->Promise.SetValue({s->Action, s->Started, @@ -220,6 +228,7 @@ class TFastShardRequestGenerator final ? resp.GetError() : resp.GetCreateHandle().GetError(); if (HasError(createErr)) { + ptr->CountLimiter->Release(); releaseEndpoint(); s->Promise.SetValue({s->Action, s->Started, createErr}); return; @@ -349,12 +358,14 @@ class TFastShardRequestGenerator final IRequestGeneratorPtr CreateFastShardRequestGenerator( NProto::TFastShardLoadSpec spec, ui32 maxParallelism, - ILoggingServicePtr logging) + ILoggingServicePtr logging, + TCountLimiterPtr countLimiter) { return std::make_shared( std::move(spec), maxParallelism, - std::move(logging)); + std::move(logging), + std::move(countLimiter)); } } // namespace NCloud::NFileStore::NLoadTest diff --git a/cloud/filestore/tools/testing/loadtest/lib/request_fastshard_stub.cpp b/cloud/filestore/tools/testing/loadtest/lib/request_fastshard_stub.cpp index ff2b71eeafc..89b3b0d9cff 100644 --- a/cloud/filestore/tools/testing/loadtest/lib/request_fastshard_stub.cpp +++ b/cloud/filestore/tools/testing/loadtest/lib/request_fastshard_stub.cpp @@ -31,11 +31,13 @@ class TFastShardRequestGenerator final IRequestGeneratorPtr CreateFastShardRequestGenerator( NProto::TFastShardLoadSpec spec, ui32 maxParallelism, - ILoggingServicePtr logging) + ILoggingServicePtr logging, + TCountLimiterPtr countLimiter) { Y_UNUSED(spec); Y_UNUSED(maxParallelism); Y_UNUSED(logging); + Y_UNUSED(countLimiter); return std::make_shared(); } diff --git a/cloud/filestore/tools/testing/loadtest/lib/request_index.cpp b/cloud/filestore/tools/testing/loadtest/lib/request_index.cpp index 65a9a6fd7d0..6ebb9a53160 100644 --- a/cloud/filestore/tools/testing/loadtest/lib/request_index.cpp +++ b/cloud/filestore/tools/testing/loadtest/lib/request_index.cpp @@ -48,6 +48,7 @@ class TIndexRequestGenerator final const NProto::TIndexLoadSpec Spec; const TString FileSystemId; const NProto::THeaders Headers; + const TCountLimiterPtr CountLimiter; const ui64 OwnerId = RandomNumber(100500u); TLog Log; @@ -76,10 +77,12 @@ class TIndexRequestGenerator final IFileStoreServicePtr client, ISessionPtr session, TString filesystemId, - NProto::THeaders headers) + NProto::THeaders headers, + TCountLimiterPtr countLimiter) : Spec(std::move(spec)) , FileSystemId(std::move(filesystemId)) , Headers(std::move(headers)) + , CountLimiter(std::move(countLimiter)) , Client(std::move(client)) , Session(std::move(session)) { @@ -148,7 +151,7 @@ class TIndexRequestGenerator final TGuard guard(StateLock); auto started = TInstant::Now(); - if (Spec.GetMaxNodes() && Nodes.size() >= Spec.GetMaxNodes()) { + if (!CountLimiter->TryReserveNode()) { return MakeFuture({ NProto::ACTION_CREATE_NODE, started, @@ -197,6 +200,7 @@ class TIndexRequestGenerator final name.c_str(), FormatError(error).c_str()); + CountLimiter->Release(); return {NProto::ACTION_CREATE_NODE, started, error}; } } @@ -777,7 +781,8 @@ IRequestGeneratorPtr CreateIndexRequestGenerator( IFileStoreServicePtr client, ISessionPtr session, TString filesystemId, - NProto::THeaders headers) + NProto::THeaders headers, + TCountLimiterPtr countLimiter) { return std::make_shared( std::move(spec), @@ -785,7 +790,8 @@ IRequestGeneratorPtr CreateIndexRequestGenerator( std::move(client), std::move(session), std::move(filesystemId), - std::move(headers)); + std::move(headers), + std::move(countLimiter)); } } // namespace NCloud::NFileStore::NLoadTest diff --git a/cloud/filestore/tools/testing/loadtest/lib/test.cpp b/cloud/filestore/tools/testing/loadtest/lib/test.cpp index 762e9f02508..0aa937900f5 100644 --- a/cloud/filestore/tools/testing/loadtest/lib/test.cpp +++ b/cloud/filestore/tools/testing/loadtest/lib/test.cpp @@ -534,7 +534,6 @@ class TLoadTest final NProto::THeaders headers; headers.SetClientId(Config.GetClientId()); headers.SetSessionId(SessionId); - switch (Config.GetSpecsCase()) { case NProto::TLoadTest::kIndexLoadSpec: RequestGenerator = CreateIndexRequestGenerator( @@ -543,7 +542,8 @@ class TLoadTest final Client, Session, FileSystemId, - headers); + headers, + std::make_shared(Config.GetMaxNodes())); break; case NProto::TLoadTest::kDataLoadSpec: RequestGenerator = CreateDataRequestGenerator( @@ -551,7 +551,8 @@ class TLoadTest final Logging, Session, FileSystemId, - headers); + headers, + std::make_shared(Config.GetMaxNodes())); break; case NProto::TLoadTest::kReplayFsSpec: RequestGenerator = CreateReplayRequestGeneratorFs( @@ -594,14 +595,16 @@ class TLoadTest final Session, ShmClient, FileSystemId, - headers); + headers, + std::make_shared(Config.GetMaxNodes())); break; } case NProto::TLoadTest::kFastShardLoadSpec: RequestGenerator = CreateFastShardRequestGenerator( Config.GetFastShardLoadSpec(), Config.GetIODepth(), - Logging); + Logging, + std::make_shared(Config.GetMaxNodes())); break; default: ythrow yexception() diff --git a/cloud/filestore/tools/testing/loadtest/protos/loadtest.proto b/cloud/filestore/tools/testing/loadtest/protos/loadtest.proto index f8547575631..31a3b3a33f7 100644 --- a/cloud/filestore/tools/testing/loadtest/protos/loadtest.proto +++ b/cloud/filestore/tools/testing/loadtest/protos/loadtest.proto @@ -37,7 +37,6 @@ message TIndexLoadSpec } repeated TAction Actions = 1; - uint64 MaxNodes = 2; } message TDataLoadSpec @@ -210,6 +209,10 @@ message TLoadTest // disables destruction of the filestore created via CreateFileStoreRequest bool KeepFileStore = 16; string ClientId = 19; + + // Maximum number of nodes created by this synthetic load test. + // Zero means unlimited. Ignored by replay specs. + uint64 MaxNodes = 22; } ////////////////////////////////////////////////////////////////////////////////