diff --git a/cloud/filestore/config/storage.proto b/cloud/filestore/config/storage.proto index 1bfd511ccea..94999f28e07 100644 --- a/cloud/filestore/config/storage.proto +++ b/cloud/filestore/config/storage.proto @@ -305,4 +305,8 @@ message TStorageConfig // Enables usage of GetNodeAttrBatch requests instead of GetNodeAttr when // appropriate. optional bool GetNodeAttrBatchEnabled = 358; + + // Blob compression experiment params. + optional uint32 BlobCompressionRate = 364; + optional string BlobCompressionCodec = 365; } diff --git a/cloud/filestore/libs/storage/core/config.cpp b/cloud/filestore/libs/storage/core/config.cpp index 3a0c7a534d3..7d070935839 100644 --- a/cloud/filestore/libs/storage/core/config.cpp +++ b/cloud/filestore/libs/storage/core/config.cpp @@ -165,6 +165,8 @@ namespace { xxx(MultiTabletForwardingEnabled, bool, false )\ xxx(GetNodeAttrBatchEnabled, bool, false )\ xxx(AllowFileStoreForceDestroy, bool, false )\ + xxx(BlobCompressionRate, ui32, 0 )\ + xxx(BlobCompressionCodec, TString, "lz4" )\ // 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 196aaa3072a..fcfc4fcfc55 100644 --- a/cloud/filestore/libs/storage/core/config.h +++ b/cloud/filestore/libs/storage/core/config.h @@ -217,6 +217,9 @@ class TStorageConfig void DumpHtml(IOutputStream& out) const; void DumpOverridesHtml(IOutputStream& out) const; + ui32 GetBlobCompressionRate() const; + TString GetBlobCompressionCodec() const; + const NProto::TStorageConfig& GetStorageConfigProto() const; }; diff --git a/cloud/filestore/libs/storage/tablet/tablet_actor.cpp b/cloud/filestore/libs/storage/tablet/tablet_actor.cpp index af9bb2a3dba..dee4ed0156e 100644 --- a/cloud/filestore/libs/storage/tablet/tablet_actor.cpp +++ b/cloud/filestore/libs/storage/tablet/tablet_actor.cpp @@ -55,6 +55,7 @@ TIndexTabletActor::TIndexTabletActor( ) , Config(std::move(config)) , UseNoneCompactionPolicy(useNoneCompactionPolicy) + , BlobCodec(NBlockCodecs::Codec(Config->GetBlobCompressionCodec())) { UpdateLogTag(); } diff --git a/cloud/filestore/libs/storage/tablet/tablet_actor.h b/cloud/filestore/libs/storage/tablet/tablet_actor.h index 2a307aadb23..42776e7c46a 100644 --- a/cloud/filestore/libs/storage/tablet/tablet_actor.h +++ b/cloud/filestore/libs/storage/tablet/tablet_actor.h @@ -40,6 +40,14 @@ #include +namespace NBlockCodecs { + +//////////////////////////////////////////////////////////////////////////////// + +struct ICodec; + +} // namespace NBlockCodecs + namespace NCloud::NFileStore::NStorage { //////////////////////////////////////////////////////////////////////////////// @@ -126,6 +134,9 @@ class TIndexTabletActor final std::atomic IdleTime{0}; TBusyIdleTimeCalculatorAtomics BusyIdleCalc; + std::atomic UncompressedBytesWritten{0}; + std::atomic CompressedBytesWritten{0}; + NMetrics::TDefaultWindowCalculator MaxUsedQuota{0}; using TLatHistogram = NMetrics::THistogram; @@ -244,6 +255,8 @@ class TIndexTabletActor final ui32 BackpressureErrorCount = 0; + const NBlockCodecs::ICodec* BlobCodec; + public: TIndexTabletActor( const NActors::TActorId& owner, diff --git a/cloud/filestore/libs/storage/tablet/tablet_actor_counters.cpp b/cloud/filestore/libs/storage/tablet/tablet_actor_counters.cpp index a61ba40008c..6d469fbf1f5 100644 --- a/cloud/filestore/libs/storage/tablet/tablet_actor_counters.cpp +++ b/cloud/filestore/libs/storage/tablet/tablet_actor_counters.cpp @@ -316,6 +316,13 @@ void TIndexTabletActor::TMetrics::Register( FsRegistry, {CreateLabel("request", "WriteData"), CreateLabel("histogram", "ThrottlerDelay")}); + REGISTER_AGGREGATABLE_SUM( + UncompressedBytesWritten, + EMetricType::MT_DERIVATIVE); + REGISTER_AGGREGATABLE_SUM( + CompressedBytesWritten, + EMetricType::MT_DERIVATIVE); + #define REGISTER_REQUEST(name) \ REGISTER_AGGREGATABLE_SUM( \ name.Count, \ diff --git a/cloud/filestore/libs/storage/tablet/tablet_actor_writeblob.cpp b/cloud/filestore/libs/storage/tablet/tablet_actor_writeblob.cpp index 20f9e6464b3..3e2d677882b 100644 --- a/cloud/filestore/libs/storage/tablet/tablet_actor_writeblob.cpp +++ b/cloud/filestore/libs/storage/tablet/tablet_actor_writeblob.cpp @@ -296,6 +296,22 @@ void TIndexTabletActor::HandleWriteBlob( return; } + const auto compRate = Config->GetBlobCompressionRate(); + if (BlobCodec && compRate && blob.BlobId.GetHash() % compRate == 0) { + TString out; + out.ReserveAndResize( + BlobCodec->MaxCompressedLength(blob.BlobContent)); + const size_t sz = BlobCodec->Compress( + blob.BlobContent, + out.begin()); + Metrics.UncompressedBytesWritten.fetch_add( + blob.BlobContent.Size(), + std::memory_order_relaxed); + Metrics.CompressedBytesWritten.fetch_add( + sz, + std::memory_order_relaxed); + } + auto blobId = MakeBlobId(TabletID(), blob.BlobId); auto proxy = Info()->BSProxyIDForChannel( diff --git a/cloud/filestore/libs/storage/tablet/tablet_ut_counters.cpp b/cloud/filestore/libs/storage/tablet/tablet_ut_counters.cpp index 92e6e5bb0d4..508bfb9d761 100644 --- a/cloud/filestore/libs/storage/tablet/tablet_ut_counters.cpp +++ b/cloud/filestore/libs/storage/tablet/tablet_ut_counters.cpp @@ -685,6 +685,89 @@ Y_UNIT_TEST_SUITE(TIndexTabletTest_Counters) UNIT_ASSERT_DOUBLES_EQUAL(sz, (network * reportInterval), sz / 100); } + + Y_UNIT_TEST(ShouldReportCompressionMetrics) + { + NProto::TStorageConfig storageConfig; + storageConfig.SetBlobCompressionRate(1); + storageConfig.SetWriteBlobThreshold(1); + + TTestEnv env({}, std::move(storageConfig)); + auto registry = env.GetRegistry(); + + env.CreateSubDomain("nfs"); + ui32 nodeIdx = env.CreateNode("nfs"); + ui64 tabletId = env.BootIndexTablet(nodeIdx); + + TIndexTabletClient tablet(env.GetRuntime(), nodeIdx, tabletId); + tablet.InitSession("client", "session"); + const auto nodeId = + CreateNode(tablet, TCreateNodeArgs::File(RootNodeId, "test")); + const auto handle = CreateHandle(tablet, nodeId); + + tablet.WriteData(handle, 0, 4_KB, 'a'); + + TTestRegistryVisitor visitor; + registry->Visit(TInstant::Zero(), visitor); + visitor.ValidateExpectedCounters({ + { + { + {"sensor", "UncompressedBytesWritten"}, + {"filesystem", "test"} + }, + 4_KB // expected + }, + { + { + {"sensor", "CompressedBytesWritten"}, + {"filesystem", "test"} + }, + 34 // expected + }, + }); + } + + Y_UNIT_TEST(ShouldNotReportCompressionMetricsForAllBlobs) + { + NProto::TStorageConfig storageConfig; + storageConfig.SetBlobCompressionRate(2); + storageConfig.SetWriteBlobThreshold(1); + + TTestEnv env({}, std::move(storageConfig)); + auto registry = env.GetRegistry(); + + env.CreateSubDomain("nfs"); + ui32 nodeIdx = env.CreateNode("nfs"); + ui64 tabletId = env.BootIndexTablet(nodeIdx); + + TIndexTabletClient tablet(env.GetRuntime(), nodeIdx, tabletId); + tablet.InitSession("client", "session"); + const auto nodeId = + CreateNode(tablet, TCreateNodeArgs::File(RootNodeId, "test")); + const auto handle = CreateHandle(tablet, nodeId); + + for (int i = 0; i < 10; i++) + tablet.WriteData(handle, 0, 4_KB, 'a'); + + TTestRegistryVisitor visitor; + registry->Visit(TInstant::Zero(), visitor); + visitor.ValidateExpectedCountersWithPredicate({ + { + { + {"sensor", "UncompressedBytesWritten"}, + {"filesystem", "test"} + }, + [](i64 val) { return val > 0 && val < 40960; } // expected + }, + { + { + {"sensor", "CompressedBytesWritten"}, + {"filesystem", "test"}, + }, + [](i64 val) { return val > 0 && val < 370; } // expected + }, + }); + } } } // namespace NCloud::NFileStore::NStorage diff --git a/cloud/filestore/libs/storage/testlib/test_env.cpp b/cloud/filestore/libs/storage/testlib/test_env.cpp index cce3f4254cc..a59ca0e8d81 100644 --- a/cloud/filestore/libs/storage/testlib/test_env.cpp +++ b/cloud/filestore/libs/storage/testlib/test_env.cpp @@ -824,6 +824,25 @@ void TTestRegistryVisitor::ValidateExpectedCounters( } } +void TTestRegistryVisitor::ValidateExpectedCountersWithPredicate( + const TVector< + std::pair, std::function> + >& expectedCounters) +{ + for (const auto& [labels, predicate]: expectedCounters) { + const auto labelsStr = LabelsToString(labels); + + int matchingCountersCount = 0; + for (const auto& entry: MetricsEntries) { + if (entry.Matches(labels)) { + ++matchingCountersCount; + UNIT_ASSERT(predicate(entry.Value)); + } + } + UNIT_ASSERT_VALUES_EQUAL_C(matchingCountersCount, 1, labelsStr); + } +} + void TTestRegistryVisitor::ValidateExpectedHistogram( const TVector, i64>>& expectedCounters, bool checkEqual) diff --git a/cloud/filestore/libs/storage/testlib/test_env.h b/cloud/filestore/libs/storage/testlib/test_env.h index cf904b8932d..e4842057a76 100644 --- a/cloud/filestore/libs/storage/testlib/test_env.h +++ b/cloud/filestore/libs/storage/testlib/test_env.h @@ -24,6 +24,8 @@ #include +#include + namespace NCloud::NFileStore::NStorage { //////////////////////////////////////////////////////////////////////////////// @@ -204,6 +206,10 @@ class TTestRegistryVisitor const TVector& GetEntries() const; void ValidateExpectedCounters( const TVector, i64>>& expectedCounters); + void ValidateExpectedCountersWithPredicate( + const TVector< + std::pair, std::function> + >& expectedCounters); void ValidateExpectedHistogram( const TVector, i64>>& expectedCounters, bool checkEqual);