diff --git a/ydb/core/blobstorage/dsproxy/ut_fat/dsproxy_ut.cpp b/ydb/core/blobstorage/dsproxy/ut_fat/dsproxy_ut.cpp index c4363e00e275..c8e0d01a41b9 100644 --- a/ydb/core/blobstorage/dsproxy/ut_fat/dsproxy_ut.cpp +++ b/ydb/core/blobstorage/dsproxy/ut_fat/dsproxy_ut.cpp @@ -4252,6 +4252,7 @@ class TBlobStorageProxyTest: public TTestBase { vDiskConfig->GCOnlySynced = false; vDiskConfig->HullCompLevelRateThreshold = 0.1; vDiskConfig->SkeletonFrontQueueBackpressureCheckMsgId = false; + vDiskConfig->UseCostTracker = false; IActor* vDisk = CreateVDisk(vDiskConfig, bsInfo, counters); TActorSetupCmd vDiskSetup(vDisk, TMailboxType::Revolving, 0); diff --git a/ydb/core/blobstorage/nodewarden/node_warden_impl.cpp b/ydb/core/blobstorage/nodewarden/node_warden_impl.cpp index 066cbcb3ca9c..c2ce68a64322 100644 --- a/ydb/core/blobstorage/nodewarden/node_warden_impl.cpp +++ b/ydb/core/blobstorage/nodewarden/node_warden_impl.cpp @@ -178,6 +178,19 @@ void TNodeWarden::Bootstrap() { icb->RegisterSharedControl(EnableSyncLogChunkCompressionSSD, "VDiskControls.EnableSyncLogChunkCompressionSSD"); icb->RegisterSharedControl(MaxSyncLogChunksInFlightHDD, "VDiskControls.MaxSyncLogChunksInFlightHDD"); icb->RegisterSharedControl(MaxSyncLogChunksInFlightSSD, "VDiskControls.MaxSyncLogChunksInFlightSSD"); + + icb->RegisterSharedControl(CostMetricsParametersByMedia[NPDisk::DEVICE_TYPE_ROT].BurstThresholdNs, + "VDiskControls.BurstThresholdNsHDD"); + icb->RegisterSharedControl(CostMetricsParametersByMedia[NPDisk::DEVICE_TYPE_SSD].BurstThresholdNs, + "VDiskControls.BurstThresholdNsSSD"); + icb->RegisterSharedControl(CostMetricsParametersByMedia[NPDisk::DEVICE_TYPE_NVME].BurstThresholdNs, + "VDiskControls.BurstThresholdNsNVME"); + icb->RegisterSharedControl(CostMetricsParametersByMedia[NPDisk::DEVICE_TYPE_ROT].DiskTimeAvailableScale, + "VDiskControls.DiskTimeAvailableScaleHDD"); + icb->RegisterSharedControl(CostMetricsParametersByMedia[NPDisk::DEVICE_TYPE_SSD].DiskTimeAvailableScale, + "VDiskControls.DiskTimeAvailableScaleSSD"); + icb->RegisterSharedControl(CostMetricsParametersByMedia[NPDisk::DEVICE_TYPE_NVME].DiskTimeAvailableScale, + "VDiskControls.DiskTimeAvailableScaleNVME"); } // start replication broker diff --git a/ydb/core/blobstorage/nodewarden/node_warden_impl.h b/ydb/core/blobstorage/nodewarden/node_warden_impl.h index b0dc88d2febf..30c3e5c464b8 100644 --- a/ydb/core/blobstorage/nodewarden/node_warden_impl.h +++ b/ydb/core/blobstorage/nodewarden/node_warden_impl.h @@ -142,6 +142,8 @@ namespace NKikimr::NStorage { TReplQuoter::TPtr ReplNodeRequestQuoter; TReplQuoter::TPtr ReplNodeResponseQuoter; + TCostMetricsParametersByMedia CostMetricsParametersByMedia; + public: struct TGroupRecord; @@ -159,6 +161,11 @@ namespace NKikimr::NStorage { , EnableSyncLogChunkCompressionSSD(0, 0, 1) , MaxSyncLogChunksInFlightHDD(10, 1, 1024) , MaxSyncLogChunksInFlightSSD(10, 1, 1024) + , CostMetricsParametersByMedia({ + TCostMetricsParameters{200}, + TCostMetricsParameters{50}, + TCostMetricsParameters{32}, + }) { Y_ABORT_UNLESS(Cfg->BlobStorageConfig.GetServiceSet().AvailabilityDomainsSize() <= 1); AvailDomainId = 1; diff --git a/ydb/core/blobstorage/nodewarden/node_warden_vdisk.cpp b/ydb/core/blobstorage/nodewarden/node_warden_vdisk.cpp index 47ebb271344f..dbd0da73a8e3 100644 --- a/ydb/core/blobstorage/nodewarden/node_warden_vdisk.cpp +++ b/ydb/core/blobstorage/nodewarden/node_warden_vdisk.cpp @@ -184,20 +184,9 @@ namespace NKikimr::NStorage { vdiskConfig->MaxSyncLogChunksInFlight = MaxSyncLogChunksInFlightSSD; } - vdiskConfig->FeatureFlags = Cfg->FeatureFlags; + vdiskConfig->CostMetricsParametersByMedia = CostMetricsParametersByMedia; - if (Cfg->BlobStorageConfig.HasCostMetricsSettings()) { - for (auto type : Cfg->BlobStorageConfig.GetCostMetricsSettings().GetVDiskTypes()) { - if (type.HasPDiskType() && deviceType == PDiskTypeToPDiskType(type.GetPDiskType())) { - if (type.HasBurstThresholdNs()) { - vdiskConfig->BurstThresholdNs = type.GetBurstThresholdNs(); - } - if (type.HasDiskTimeAvailableScale()) { - vdiskConfig->DiskTimeAvailableScale = type.GetDiskTimeAvailableScale(); - } - } - } - } + vdiskConfig->FeatureFlags = Cfg->FeatureFlags; if (StorageConfig.HasBlobStorageConfig() && StorageConfig.GetBlobStorageConfig().HasVDiskPerformanceSettings()) { for (auto &type : StorageConfig.GetBlobStorageConfig().GetVDiskPerformanceSettings().GetVDiskTypes()) { diff --git a/ydb/core/blobstorage/pdisk/blobstorage_pdisk.h b/ydb/core/blobstorage/pdisk/blobstorage_pdisk.h index 40048de251dc..7cdc40c002c8 100644 --- a/ydb/core/blobstorage/pdisk/blobstorage_pdisk.h +++ b/ydb/core/blobstorage/pdisk/blobstorage_pdisk.h @@ -173,7 +173,7 @@ struct TEvYardInitResult : public TEventLocal ownedChunks, - const TString &errorReason) + EDeviceType trueMediaType, const TString &errorReason) : Status(status) , StatusFlags(statusFlags) , PDiskParams(new TPDiskParams( @@ -196,8 +196,8 @@ struct TEvYardInitResult : public TEventLocal(), error.Str())); + GetStatusFlags(OwnerSystem, evYardInit.OwnerGroupType), TVector(), + Cfg->RetrieveDeviceType(), error.Str())); Mon.YardInit.CountResponse(); } @@ -1790,7 +1791,8 @@ bool TPDisk::YardInitForKnownVDisk(TYardInit &evYardInit, TOwner owner) { DriveModel.SeekTimeNs() / 1000ull, DriveModel.Speed(TDriveModel::OP_TYPE_READ), DriveModel.Speed(TDriveModel::OP_TYPE_WRITE), readBlockSize, writeBlockSize, DriveModel.BulkWriteBlockSize(), GetUserAccessibleChunkSize(), GetChunkAppendBlockSize(), owner, - ownerRound, GetStatusFlags(OwnerSystem, evYardInit.OwnerGroupType), ownedChunks, nullptr)); + ownerRound, GetStatusFlags(OwnerSystem, evYardInit.OwnerGroupType), ownedChunks, + Cfg->RetrieveDeviceType(), nullptr)); GetStartingPoints(owner, result->StartingPoints); ownerData.VDiskId = vDiskId; ownerData.CutLogId = evYardInit.CutLogId; @@ -1941,7 +1943,7 @@ void TPDisk::YardInitFinish(TYardInit &evYardInit) { DriveModel.Speed(TDriveModel::OP_TYPE_WRITE), readBlockSize, writeBlockSize, DriveModel.BulkWriteBlockSize(), GetUserAccessibleChunkSize(), GetChunkAppendBlockSize(), owner, ownerRound, GetStatusFlags(OwnerSystem, evYardInit.OwnerGroupType) | ui32(NKikimrBlobStorage::StatusNewOwner), TVector(), - nullptr)); + Cfg->RetrieveDeviceType(), nullptr)); GetStartingPoints(result->PDiskParams->Owner, result->StartingPoints); WriteSysLogRestorePoint(new TCompletionEventSender( this, evYardInit.Sender, result.Release(), Mon.YardInit.Results), evYardInit.ReqId, {}); diff --git a/ydb/core/blobstorage/pdisk/blobstorage_pdisk_params.cpp b/ydb/core/blobstorage/pdisk/blobstorage_pdisk_params.cpp index b8564ea0305e..4e7078f9a8d9 100644 --- a/ydb/core/blobstorage/pdisk/blobstorage_pdisk_params.cpp +++ b/ydb/core/blobstorage/pdisk/blobstorage_pdisk_params.cpp @@ -11,7 +11,7 @@ namespace NKikimr { //////////////////////////////////////////////////////////////////////////// TPDiskParams::TPDiskParams(NPDisk::TOwner owner, ui64 ownerRound, ui32 chunkSize, ui32 appendBlockSize, ui64 seekTimeUs, ui64 readSpeedBps, ui64 writeSpeedBps, ui64 readBlockSize, - ui64 writeBlockSize, ui64 bulkWriteBlockSize) + ui64 writeBlockSize, ui64 bulkWriteBlockSize, NPDisk::EDeviceType trueMediaType) : Owner(owner) , OwnerRound(ownerRound) , ChunkSize(chunkSize) @@ -25,6 +25,7 @@ namespace NKikimr { , BulkWriteBlockSize(bulkWriteBlockSize) , PrefetchSizeBytes(CalculatePrefetchSizeBytes(seekTimeUs, readSpeedBps)) , GlueRequestDistanceBytes(CalculateGlueRequestDistanceBytes(seekTimeUs, readSpeedBps)) + , TrueMediaType(trueMediaType) { Y_DEBUG_ABORT_UNLESS(AppendBlockSize <= ChunkSize); } diff --git a/ydb/core/blobstorage/pdisk/blobstorage_pdisk_params.h b/ydb/core/blobstorage/pdisk/blobstorage_pdisk_params.h index 7e5017d87916..175bd3ec0ddd 100644 --- a/ydb/core/blobstorage/pdisk/blobstorage_pdisk_params.h +++ b/ydb/core/blobstorage/pdisk/blobstorage_pdisk_params.h @@ -27,13 +27,15 @@ namespace NKikimr { const ui64 PrefetchSizeBytes; // Pdisk is expected to stream data of this size at 83% of max speed. const ui64 GlueRequestDistanceBytes; // It is faster to read unneeded data of this size than to seek over it. + const NPDisk::EDeviceType TrueMediaType; + static ui32 CalculateRecommendedReadSize(ui64 seekTimeUs, ui64 readSpeedBps, ui64 appendBlockSize); static ui64 CalculatePrefetchSizeBytes(ui64 seekTimeUs, ui64 readSpeedBps); static ui64 CalculateGlueRequestDistanceBytes(ui64 seekTimeUs, ui64 readSpeedBps); TPDiskParams(NPDisk::TOwner owner, ui64 ownerRound, ui32 chunkSize, ui32 appendBlockSize, ui64 seekTimeUs, ui64 readSpeedBps, ui64 writeSpeedBps, ui64 readBlockSize, - ui64 writeBlockSize, ui64 bulkWriteBlockSize); + ui64 writeBlockSize, ui64 bulkWriteBlockSize, NPDisk::EDeviceType trueMediaType); void OutputHtml(IOutputStream &str) const; TString ToString() const; }; diff --git a/ydb/core/blobstorage/pdisk/mock/pdisk_mock.cpp b/ydb/core/blobstorage/pdisk/mock/pdisk_mock.cpp index 4b5275627ec0..e81a537f158d 100644 --- a/ydb/core/blobstorage/pdisk/mock/pdisk_mock.cpp +++ b/ydb/core/blobstorage/pdisk/mock/pdisk_mock.cpp @@ -427,7 +427,7 @@ class TPDiskMockActor : public TActorBootstrapped { const ui64 bulkWriteBlockSize = 65536; res = std::make_unique(NKikimrProto::OK, seekTimeUs, readSpeedBps, writeSpeedBps, readBlockSize, writeBlockSize, bulkWriteBlockSize, Impl.ChunkSize, Impl.AppendBlockSize, ownerId, - owner->OwnerRound, GetStatusFlags(), std::move(ownedChunks), TString()); + owner->OwnerRound, GetStatusFlags(), std::move(ownedChunks), NPDisk::DEVICE_TYPE_NVME, TString()); res->StartingPoints = owner->StartingPoints; } else { res = std::make_unique(NKikimrProto::INVALID_ROUND, "invalid owner round"); diff --git a/ydb/core/blobstorage/ut_blobstorage/lib/env.h b/ydb/core/blobstorage/ut_blobstorage/lib/env.h index 98bf955f42bd..319b722f614c 100644 --- a/ydb/core/blobstorage/ut_blobstorage/lib/env.h +++ b/ydb/core/blobstorage/ut_blobstorage/lib/env.h @@ -23,6 +23,10 @@ struct TEnvironmentSetup { std::set CommencedReplication; std::unordered_map Cache; + using TIcbControlKey = std::pair; // { nodeId, name } + + std::unordered_map IcbControls; + struct TSettings { const ui32 NodeCount = 9; const bool VDiskReplPausedAtStart = false; @@ -39,7 +43,7 @@ struct TEnvironmentSetup { const bool SuppressCompatibilityCheck = false; const TFeatureFlags FeatureFlags; const NPDisk::EDeviceType DiskType = NPDisk::EDeviceType::DEVICE_TYPE_NVME; - const ui32 BurstThresholdNs = 0; + const ui64 BurstThresholdNs = 0; const ui32 MinHugeBlobInBytes = 0; const float DiskTimeAvailableScale = 1; const bool UseFakeConfigDispatcher = false; @@ -374,14 +378,24 @@ struct TEnvironmentSetup { } config->FeatureFlags = Settings.FeatureFlags; - { - auto* type = config->BlobStorageConfig.MutableCostMetricsSettings()->AddVDiskTypes(); - type->SetPDiskType(NKikimrBlobStorage::EPDiskType::ROT); - if (Settings.BurstThresholdNs) { - type->SetBurstThresholdNs(Settings.BurstThresholdNs); - } - type->SetDiskTimeAvailableScale(Settings.DiskTimeAvailableScale); + TAppData* appData = Runtime->GetNode(nodeId)->AppData.get(); + +#define ADD_ICB_CONTROL(controlName, defaultVal, minVal, maxVal, currentValue) { \ + TControlWrapper control(defaultVal, minVal, maxVal); \ + appData->Icb->RegisterSharedControl(control, controlName); \ + control = currentValue; \ + IcbControls.insert({{nodeId, controlName}, std::move(control)}); \ + } + + if (Settings.BurstThresholdNs) { + ADD_ICB_CONTROL("VDiskControls.BurstThresholdNsHDD", 200'000'000, 1, 1'000'000'000'000, Settings.BurstThresholdNs); + ADD_ICB_CONTROL("VDiskControls.BurstThresholdNsSSD", 50'000'000, 1, 1'000'000'000'000, Settings.BurstThresholdNs); + ADD_ICB_CONTROL("VDiskControls.BurstThresholdNsNVME", 32'000'000, 1, 1'000'000'000'000, Settings.BurstThresholdNs); } + ADD_ICB_CONTROL("VDiskControls.DiskTimeAvailableScaleHDD", 1'000, 1, 1'000'000, std::round(Settings.DiskTimeAvailableScale * 1'000)); + ADD_ICB_CONTROL("VDiskControls.DiskTimeAvailableScaleSSD", 1'000, 1, 1'000'000, std::round(Settings.DiskTimeAvailableScale * 1'000)); + ADD_ICB_CONTROL("VDiskControls.DiskTimeAvailableScaleNVME", 1'000, 1, 1'000'000, std::round(Settings.DiskTimeAvailableScale * 1'000)); +#undef ADD_ICB_CONTROL { auto* type = config->BlobStorageConfig.MutableVDiskPerformanceSettings()->AddVDiskTypes(); @@ -699,7 +713,7 @@ struct TEnvironmentSetup { }); } - void PutBlob(const ui32 groupId, const TLogoBlobID& blobId, const TString& part) { + void PutBlob(const ui32 groupId, const TLogoBlobID& blobId, const TString& part) { TActorId edge = Runtime->AllocateEdgeActor(Settings.ControllerNodeId); Runtime->WrapInActorContext(edge, [&] { SendToBSProxy(edge, groupId, new TEvBlobStorage::TEvPut(blobId, part, TInstant::Max(), @@ -924,4 +938,19 @@ struct TEnvironmentSetup { } return ctr; }; + + void SetIcbControl(ui32 nodeId, TString controlName, ui64 value) { + if (nodeId == 0) { + for (nodeId = 1; nodeId <= Settings.NodeCount; ++nodeId) { + auto it = IcbControls.find({nodeId, controlName}); + Y_ABORT_UNLESS(it != IcbControls.end()); + it->second = value; + } + } else { + auto it = IcbControls.find({nodeId, controlName}); + Y_ABORT_UNLESS(it != IcbControls.end()); + it->second = value; + } + } + }; diff --git a/ydb/core/blobstorage/ut_blobstorage/monitoring.cpp b/ydb/core/blobstorage/ut_blobstorage/monitoring.cpp index 21b08edb82e1..1461253ac2e9 100644 --- a/ydb/core/blobstorage/ut_blobstorage/monitoring.cpp +++ b/ydb/core/blobstorage/ut_blobstorage/monitoring.cpp @@ -254,21 +254,22 @@ Y_UNIT_TEST_SUITE(BurstDetection) { } void TestDiskTimeAvailableScaling() { - auto measure = [](float scale) { - TBlobStorageGroupInfo::TTopology topology(TBlobStorageGroupType::ErasureNone, 1, 1, 1, true); - std::unique_ptr env; - ui32 groupSize; - TBlobStorageGroupType groupType; - ui32 groupId; - std::vector pdiskLayout; - SetupEnv(topology, env, groupSize, groupType, groupId, pdiskLayout, 0, scale); - - return env->AggregateVDiskCounters(env->StoragePoolName, groupSize, groupSize, groupId, pdiskLayout, - "advancedCost", "DiskTimeAvailable"); - }; + TBlobStorageGroupInfo::TTopology topology(TBlobStorageGroupType::ErasureNone, 1, 1, 1, true); + std::unique_ptr env; + ui32 groupSize; + TBlobStorageGroupType groupType; + ui32 groupId; + std::vector pdiskLayout; + SetupEnv(topology, env, groupSize, groupType, groupId, pdiskLayout, 0, 1); + + i64 test1 = env->AggregateVDiskCounters(env->StoragePoolName, groupSize, groupSize, groupId, pdiskLayout, + "advancedCost", "DiskTimeAvailable"); + + env->SetIcbControl(0, "VDiskControls.DiskTimeAvailableScaleNVME", 2'000); + env->Sim(TDuration::Minutes(5)); - i64 test1 = measure(1); - i64 test2 = measure(2); + i64 test2 = env->AggregateVDiskCounters(env->StoragePoolName, groupSize, groupSize, groupId, pdiskLayout, + "advancedCost", "DiskTimeAvailable"); i64 delta = test1 * 2 - test2; diff --git a/ydb/core/blobstorage/ut_group/main.cpp b/ydb/core/blobstorage/ut_group/main.cpp index 81b1266c5bdc..a7e9e12a6702 100644 --- a/ydb/core/blobstorage/ut_group/main.cpp +++ b/ydb/core/blobstorage/ut_group/main.cpp @@ -417,6 +417,7 @@ class TTestEnv { TString()); auto vdiskConfig = AllVDiskKinds->MakeVDiskConfig(baseInfo); vdiskConfig->EnableVDiskCooldownTimeout = true; + vdiskConfig->UseCostTracker = false; auto counters = Counters->GetSubgroup("node", ToString(disk.NodeId))->GetSubgroup("vdisk", disk.VDiskId.ToString()); const TActorId& actorId = runtime.Register(CreateVDisk(vdiskConfig, Info, counters), TActorId(), 0, std::nullopt, disk.NodeId); runtime.RegisterService(disk.VDiskActorId, actorId); diff --git a/ydb/core/blobstorage/ut_mirror3of4/main.cpp b/ydb/core/blobstorage/ut_mirror3of4/main.cpp index b8f950d70ffe..b7fc225d63d0 100644 --- a/ydb/core/blobstorage/ut_mirror3of4/main.cpp +++ b/ydb/core/blobstorage/ut_mirror3of4/main.cpp @@ -273,6 +273,7 @@ class TTestEnv { "" ); const auto vcfg = MakeIntrusive(baseInfo); + vcfg->UseCostTracker = false; auto vdiskCounters = Counters->GetSubgroup("vdisk", ToString(i)); runtime.RegisterService(info.GetActorId(i), runtime.Register(CreateVDisk(vcfg, Info, vdiskCounters), TActorId(), 0, std::nullopt, pdisk.NodeId)); diff --git a/ydb/core/blobstorage/ut_vdisk/lib/prepare.cpp b/ydb/core/blobstorage/ut_vdisk/lib/prepare.cpp index e4887900c8f5..cdc0412bfb95 100644 --- a/ydb/core/blobstorage/ut_vdisk/lib/prepare.cpp +++ b/ydb/core/blobstorage/ut_vdisk/lib/prepare.cpp @@ -256,6 +256,7 @@ bool TDefaultVDiskSetup::SetUp(TAllVDisks::TVDiskInstance &vdisk, TAllPDisks *pd modifier(vdisk.Cfg.Get()); } vdisk.Cfg->RunRepl = runRepl; + vdisk.Cfg->UseCostTracker = false; return true; } diff --git a/ydb/core/blobstorage/ut_vdisk2/env.h b/ydb/core/blobstorage/ut_vdisk2/env.h index e15da631531e..2cce86504ff2 100644 --- a/ydb/core/blobstorage/ut_vdisk2/env.h +++ b/ydb/core/blobstorage/ut_vdisk2/env.h @@ -174,6 +174,7 @@ namespace NKikimr { NPDisk::DEVICE_TYPE_SSD, VSlotId, NKikimrBlobStorage::TVDiskKind::Default, 1, "static"); VDiskConfig = AllVDiskKinds->MakeVDiskConfig(baseInfo); + VDiskConfig->UseCostTracker = false; // create and register actor std::unique_ptr vdisk(NKikimr::CreateVDisk(VDiskConfig, Info, Counters->GetSubgroup("subsystem", "vdisk"))); diff --git a/ydb/core/blobstorage/vdisk/common/blobstorage_cost_tracker.cpp b/ydb/core/blobstorage/vdisk/common/blobstorage_cost_tracker.cpp index 17ad430340eb..da11c7ab2994 100644 --- a/ydb/core/blobstorage/vdisk/common/blobstorage_cost_tracker.cpp +++ b/ydb/core/blobstorage/vdisk/common/blobstorage_cost_tracker.cpp @@ -42,15 +42,16 @@ class TBsCostModelMirror3of4 : public TBsCostModelBase { }; TBsCostTracker::TBsCostTracker(const TBlobStorageGroupType& groupType, NPDisk::EDeviceType diskType, - const TIntrusivePtr<::NMonitoring::TDynamicCounters>& counters, ui64 burstThresholdNs, - float diskTimeAvailableScale) + const TIntrusivePtr<::NMonitoring::TDynamicCounters>& counters, + const TCostMetricsParameters& costMetricsParameters) : GroupType(groupType) , CostCounters(counters->GetSubgroup("subsystem", "advancedCost")) , MonGroup(std::make_shared(CostCounters)) - , BucketCapacity(burstThresholdNs * diskTimeAvailableScale) , Bucket(&DiskTimeAvailable, &BucketCapacity, nullptr, nullptr, nullptr, nullptr, true) - , DiskTimeAvailableScale(diskTimeAvailableScale) + , BurstThresholdNs(costMetricsParameters.BurstThresholdNs) + , DiskTimeAvailableScale(costMetricsParameters.DiskTimeAvailableScale) { + AtomicSet(BucketCapacity, GetDiskTimeAvailableScale() * BurstThresholdNs); BurstDetector.Initialize(CostCounters, "BurstDetector"); switch (GroupType.GetErasure()) { case TBlobStorageGroupType::ErasureMirror3dc: diff --git a/ydb/core/blobstorage/vdisk/common/blobstorage_cost_tracker.h b/ydb/core/blobstorage/vdisk/common/blobstorage_cost_tracker.h index 81c61ef5a454..c47a1e31fe9b 100644 --- a/ydb/core/blobstorage/vdisk/common/blobstorage_cost_tracker.h +++ b/ydb/core/blobstorage/vdisk/common/blobstorage_cost_tracker.h @@ -5,6 +5,7 @@ #include "vdisk_events.h" #include "vdisk_handle_class.h" #include "vdisk_mongroups.h" +#include "vdisk_performance_params.h" #include #include @@ -314,18 +315,20 @@ class TBsCostTracker { TIntrusivePtr<::NMonitoring::TDynamicCounters> CostCounters; std::shared_ptr MonGroup; - TAtomic BucketCapacity; // 10^9 nsec + TAtomic BucketCapacity = 1'000'000'000; // 10^9 nsec TAtomic DiskTimeAvailable = 1'000'000'000; TBucketQuoter> Bucket; TLight BurstDetector; std::atomic SeqnoBurstDetector = 0; static constexpr ui32 ConcurrentHugeRequestsAllowed = 3; - float DiskTimeAvailableScale = 1; + + TMemorizableControlWrapper BurstThresholdNs; + TMemorizableControlWrapper DiskTimeAvailableScale; public: TBsCostTracker(const TBlobStorageGroupType& groupType, NPDisk::EDeviceType diskType, - const TIntrusivePtr<::NMonitoring::TDynamicCounters>& counters, ui64 burstThresholdNs, - float diskTimeAvailableScale); + const TIntrusivePtr<::NMonitoring::TDynamicCounters>& counters, + const TCostMetricsParameters& costMetricsParameters); template ui64 GetCost(const TEv& ev) const { @@ -346,12 +349,14 @@ class TBsCostTracker { } void CountRequest(ui64 cost) { + AtomicSet(BucketCapacity, GetDiskTimeAvailableScale() * BurstThresholdNs.Update(TAppData::TimeProvider->Now())); Bucket.UseAndFill(cost); BurstDetector.Set(!Bucket.IsAvail(), SeqnoBurstDetector.fetch_add(1)); } - void SetTimeAvailable(ui32 diskTimeAvailableNSec) { - ui64 diskTimeAvailable = diskTimeAvailableNSec * DiskTimeAvailableScale; + void SetTimeAvailable(ui64 diskTimeAvailableNSec) { + ui64 diskTimeAvailable = diskTimeAvailableNSec * GetDiskTimeAvailableScale(); + AtomicSet(DiskTimeAvailable, diskTimeAvailable); MonGroup->DiskTimeAvailableCtr() = diskTimeAvailable; } @@ -405,6 +410,11 @@ class TBsCostTracker { void CountPDiskResponse() { BurstDetector.Set(!Bucket.IsAvail(), SeqnoBurstDetector.fetch_add(1)); } + +private: + float GetDiskTimeAvailableScale() { + return 0.001 * DiskTimeAvailableScale.Update(TAppData::TimeProvider->Now()); + } }; } // NKikimr diff --git a/ydb/core/blobstorage/vdisk/common/vdisk_config.cpp b/ydb/core/blobstorage/vdisk/common/vdisk_config.cpp index 8a0f784f6c48..d9fa9ee01442 100644 --- a/ydb/core/blobstorage/vdisk/common/vdisk_config.cpp +++ b/ydb/core/blobstorage/vdisk/common/vdisk_config.cpp @@ -1,5 +1,4 @@ #include "vdisk_config.h" -#include "vdisk_performance_params.h" #include #include @@ -124,8 +123,6 @@ namespace NKikimr { BarrierValidation = true; // switch by default on debug builds #endif - BurstThresholdNs = NPDisk::DevicePerformance.at(baseInfo.DeviceType).BurstThresholdNs; - DiskTimeAvailableScale = 1; } void TVDiskConfig::SetupHugeBytes() { diff --git a/ydb/core/blobstorage/vdisk/common/vdisk_config.h b/ydb/core/blobstorage/vdisk/common/vdisk_config.h index 35a9ec4e681d..31e30788eeb1 100644 --- a/ydb/core/blobstorage/vdisk/common/vdisk_config.h +++ b/ydb/core/blobstorage/vdisk/common/vdisk_config.h @@ -1,5 +1,8 @@ #pragma once #include "defs.h" + +#include "vdisk_performance_params.h" + #include #include #include @@ -217,8 +220,8 @@ namespace NKikimr { TControlWrapper EnableVPatch = true; ///////////// COST METRICS SETTINGS //////////////// - ui64 BurstThresholdNs = 1'000'000'000; - float DiskTimeAvailableScale = 1; + bool UseCostTracker = true; + TCostMetricsParametersByMedia CostMetricsParametersByMedia; ///////////// FEATURE FLAGS //////////////////////// NKikimrConfig::TFeatureFlags FeatureFlags; diff --git a/ydb/core/blobstorage/vdisk/common/vdisk_context.cpp b/ydb/core/blobstorage/vdisk/common/vdisk_context.cpp index 0bea06ba05c8..b5c6af515f1a 100644 --- a/ydb/core/blobstorage/vdisk/common/vdisk_context.cpp +++ b/ydb/core/blobstorage/vdisk/common/vdisk_context.cpp @@ -30,9 +30,7 @@ namespace NKikimr { TReplQuoter::TPtr replPDiskReadQuoter, TReplQuoter::TPtr replPDiskWriteQuoter, TReplQuoter::TPtr replNodeRequestQuoter, - TReplQuoter::TPtr replNodeResponseQuoter, - ui64 burstThresholdNs, - float diskTimeAvailableScale) + TReplQuoter::TPtr replNodeResponseQuoter) : TBSProxyContext(vdiskCounters->GetSubgroup("subsystem", "memhull")) , VDiskActorId(vdiskActorId) , Top(std::move(top)) @@ -59,8 +57,7 @@ namespace NKikimr { , ReplPDiskWriteQuoter(std::move(replPDiskWriteQuoter)) , ReplNodeRequestQuoter(std::move(replNodeRequestQuoter)) , ReplNodeResponseQuoter(std::move(replNodeResponseQuoter)) - , CostTracker(std::make_shared(Top->GType, type, vdiskCounters, burstThresholdNs, - diskTimeAvailableScale)) + , CostTracker() , OOSMonGroup(std::make_shared(VDiskCounters, "subsystem", "oos")) , OutOfSpaceState(Top->GetTotalVDisksNum(), Top->GetOrderNumber(ShortSelfVDisk)) , CostMonGroup(vdiskCounters, "subsystem", "cost") diff --git a/ydb/core/blobstorage/vdisk/common/vdisk_context.h b/ydb/core/blobstorage/vdisk/common/vdisk_context.h index 0eb1219300ed..c6ceeaa0a5c6 100644 --- a/ydb/core/blobstorage/vdisk/common/vdisk_context.h +++ b/ydb/core/blobstorage/vdisk/common/vdisk_context.h @@ -68,7 +68,7 @@ namespace NKikimr { TString LocalRecoveryErrorStr; std::unique_ptr CostModel; - std::shared_ptr CostTracker; + std::unique_ptr CostTracker; // oos logging std::atomic CurrentOOSStatusFlag = NKikimrBlobStorage::StatusIsValid; @@ -104,9 +104,7 @@ namespace NKikimr { TReplQuoter::TPtr replPDiskReadQuoter = nullptr, TReplQuoter::TPtr replPDiskWriteQuoter = nullptr, TReplQuoter::TPtr replNodeRequestQuoter = nullptr, - TReplQuoter::TPtr replNodeResponseQuoter = nullptr, - ui64 burstThresholdNs = 1'000'000'000, - float diskTimeAvailableScale = 1); + TReplQuoter::TPtr replNodeResponseQuoter = nullptr); // The function checks response from PDisk. Normally, it's OK. // Other alternatives are: 1) shutdown; 2) FAIL @@ -181,7 +179,9 @@ namespace NKikimr { if (CostModel) { CostMonGroup.DefragCostNs() += CostModel->GetCost(ev); } - CostTracker->CountDefragRequest(ev); + if (CostTracker) { + CostTracker->CountDefragRequest(ev); + } } template @@ -189,7 +189,9 @@ namespace NKikimr { if (CostModel) { CostMonGroup.ScrubCostNs() += CostModel->GetCost(ev); } - CostTracker->CountScrubRequest(ev); + if (CostTracker) { + CostTracker->CountScrubRequest(ev); + } } template @@ -197,12 +199,14 @@ namespace NKikimr { if (CostModel) { CostMonGroup.CompactionCostNs() += CostModel->GetCost(ev); } - CostTracker->CountCompactionRequest(ev); + if (CostTracker) { + CostTracker->CountCompactionRequest(ev); + } } void UpdateCostModel(std::unique_ptr&& newCostModel) { CostModel = std::move(newCostModel); - if (CostModel) { + if (CostModel && CostTracker) { CostTracker->UpdateCostModel(*CostModel); } } diff --git a/ydb/core/blobstorage/vdisk/common/vdisk_performance_params.cpp b/ydb/core/blobstorage/vdisk/common/vdisk_performance_params.cpp index f859097d6414..be20d86ef81b 100644 --- a/ydb/core/blobstorage/vdisk/common/vdisk_performance_params.cpp +++ b/ydb/core/blobstorage/vdisk/common/vdisk_performance_params.cpp @@ -17,4 +17,9 @@ extern const std::unordered_map VD } }, }; +TCostMetricsParameters::TCostMetricsParameters(ui64 defaultBurstThresholdMs) + : BurstThresholdNs(defaultBurstThresholdMs * 1'000'000, 1, 1'000'000'000'000) + , DiskTimeAvailableScale(1'000, 1, 1'000'000) +{} + } // namespace NKikimr diff --git a/ydb/core/blobstorage/vdisk/common/vdisk_performance_params.h b/ydb/core/blobstorage/vdisk/common/vdisk_performance_params.h index 6042b4fccffd..90a009270625 100644 --- a/ydb/core/blobstorage/vdisk/common/vdisk_performance_params.h +++ b/ydb/core/blobstorage/vdisk/common/vdisk_performance_params.h @@ -5,6 +5,7 @@ #include "defs.h" #include +#include namespace NKikimr { @@ -14,4 +15,12 @@ struct TVDiskPerformanceParams { extern const std::unordered_map VDiskPerformance; +struct TCostMetricsParameters { + TCostMetricsParameters(ui64 defaultBurstThresholdMs = 100); + TControlWrapper BurstThresholdNs; + TControlWrapper DiskTimeAvailableScale; +}; + +using TCostMetricsParametersByMedia = TStackVec; + } // namespace NKikimr diff --git a/ydb/core/blobstorage/vdisk/hullop/blobstorage_hullcompact.h b/ydb/core/blobstorage/vdisk/hullop/blobstorage_hullcompact.h index 7485638d890b..c9e7af000394 100644 --- a/ydb/core/blobstorage/vdisk/hullop/blobstorage_hullcompact.h +++ b/ydb/core/blobstorage/vdisk/hullop/blobstorage_hullcompact.h @@ -167,7 +167,9 @@ namespace NKikimr { // the same logic for every yard response: apply response and restart main cycle void HandleYardResponse(NPDisk::TEvChunkReadResult::TPtr& ev, const TActorContext &ctx) { --PendingResponses; - HullCtx->VCtx->CostTracker->CountPDiskResponse(); + if (HullCtx->VCtx->CostTracker) { + HullCtx->VCtx->CostTracker->CountPDiskResponse(); + } if (ev->Get()->Status != NKikimrProto::CORRUPTED) { CHECK_PDISK_RESPONSE(HullCtx->VCtx, ev, ctx); } @@ -200,7 +202,9 @@ namespace NKikimr { void HandleYardResponse(NPDisk::TEvChunkWriteResult::TPtr& ev, const TActorContext &ctx) { --PendingResponses; - HullCtx->VCtx->CostTracker->CountPDiskResponse(); + if (HullCtx->VCtx->CostTracker) { + HullCtx->VCtx->CostTracker->CountPDiskResponse(); + } CHECK_PDISK_RESPONSE(HullCtx->VCtx, ev, ctx); if (FinalizeIfAborting(ctx)) { return; diff --git a/ydb/core/blobstorage/vdisk/localrecovery/localrecovery_public.cpp b/ydb/core/blobstorage/vdisk/localrecovery/localrecovery_public.cpp index e0260d99201c..ab40800fd481 100644 --- a/ydb/core/blobstorage/vdisk/localrecovery/localrecovery_public.cpp +++ b/ydb/core/blobstorage/vdisk/localrecovery/localrecovery_public.cpp @@ -501,6 +501,21 @@ namespace NKikimr { LocRecCtx->HullDbRecovery = std::make_shared(hullCtx); LocRecCtx->HullCtx = hullCtx; + if (Config->UseCostTracker) { + NPDisk::EDeviceType trueMediaType = LocRecCtx->PDiskCtx->Dsk->TrueMediaType; + if (trueMediaType == NPDisk::DEVICE_TYPE_UNKNOWN) { + // Unable to resolve type from PDisk's properties, using type from VDisk config + trueMediaType = Config->BaseInfo.DeviceType; + } + if (trueMediaType != NPDisk::DEVICE_TYPE_UNKNOWN) { + LocRecCtx->HullCtx->VCtx->CostTracker.reset(new TBsCostTracker( + LocRecCtx->HullCtx->VCtx->Top->GType, trueMediaType, + LocRecCtx->HullCtx->VCtx->VDiskCounters, + Config->CostMetricsParametersByMedia[trueMediaType] + )); + } + } + // store reported owned chunks LocRecCtx->ReportedOwnedChunks = std::move(m->OwnedChunks); diff --git a/ydb/core/blobstorage/vdisk/repl/blobstorage_hullreplwritesst_ut.cpp b/ydb/core/blobstorage/vdisk/repl/blobstorage_hullreplwritesst_ut.cpp index b1eeba13a829..e4c184ed8551 100644 --- a/ydb/core/blobstorage/vdisk/repl/blobstorage_hullreplwritesst_ut.cpp +++ b/ydb/core/blobstorage/vdisk/repl/blobstorage_hullreplwritesst_ut.cpp @@ -15,7 +15,8 @@ std::shared_ptr CreateReplCtx(TVector& vdisks, const TIntrus auto vctx = MakeIntrusive(TActorId(), info->PickTopology(), counters, TVDiskID(0, 1, 0, 0, 0), nullptr, NPDisk::DEVICE_TYPE_UNKNOWN); auto hugeBlobCtx = std::make_shared(nullptr, true); - auto dsk = MakeIntrusive(ui8(1), 1u, 128u << 20, 4096u, 0u, 1000000000u, 1000000000u, 65536u, 65536u, 65536u); + auto dsk = MakeIntrusive(ui8(1), 1u, 128u << 20, 4096u, 0u, 1000000000u, 1000000000u, 65536u, 65536u, 65536u, + NPDisk::DEVICE_TYPE_UNKNOWN); auto pdiskCtx = std::make_shared(dsk, TActorId(), TString()); auto replCtx = std::make_shared( vctx, diff --git a/ydb/core/blobstorage/vdisk/scrub/scrub_actor_pdisk.cpp b/ydb/core/blobstorage/vdisk/scrub/scrub_actor_pdisk.cpp index e477ce788fc8..22e93924194c 100644 --- a/ydb/core/blobstorage/vdisk/scrub/scrub_actor_pdisk.cpp +++ b/ydb/core/blobstorage/vdisk/scrub/scrub_actor_pdisk.cpp @@ -11,7 +11,9 @@ namespace NKikimr { Send(ScrubCtx->PDiskCtx->PDiskId, msg.release()); CurrentState = TStringBuilder() << "reading data from " << part.ToString(); auto res = WaitForPDiskEvent(); - ScrubCtx->VCtx->CostTracker->CountPDiskResponse(); + if (ScrubCtx->VCtx->CostTracker) { + ScrubCtx->VCtx->CostTracker->CountPDiskResponse(); + } auto *m = res->Get(); Y_VERIFY_S(m->Status == NKikimrProto::OK || m->Status == NKikimrProto::CORRUPTED, "Status# " << NKikimrProto::EReplyStatus_Name(m->Status)); @@ -42,7 +44,9 @@ namespace NKikimr { Send(ScrubCtx->PDiskCtx->PDiskId, msg.release()); CurrentState = TStringBuilder() << "writing index to " << part.ToString(); auto res = WaitForPDiskEvent(); - ScrubCtx->VCtx->CostTracker->CountPDiskResponse(); + if (ScrubCtx->VCtx->CostTracker) { + ScrubCtx->VCtx->CostTracker->CountPDiskResponse(); + } Y_ABORT_UNLESS(res->Get()->Status == NKikimrProto::OK); // FIXME: good logic } diff --git a/ydb/core/blobstorage/vdisk/skeleton/blobstorage_skeleton.cpp b/ydb/core/blobstorage/vdisk/skeleton/blobstorage_skeleton.cpp index 20ba724ce1b1..acf14fc95018 100644 --- a/ydb/core/blobstorage/vdisk/skeleton/blobstorage_skeleton.cpp +++ b/ydb/core/blobstorage/vdisk/skeleton/blobstorage_skeleton.cpp @@ -2201,13 +2201,21 @@ namespace NKikimr { TABLED() {str << "VDiskIncarnationGuid";} TABLED() {str << Db->GetVDiskIncarnationGuid(true);} } - TABLER() { - TABLED() {str << "BurstThresholdNs";} - TABLED() {str << Config->BurstThresholdNs;} - } - TABLER() { - TABLED() {str << "DiskTimeAvailableScale";} - TABLED() {str << Config->DiskTimeAvailableScale;} + + if (PDiskCtx && PDiskCtx->Dsk) { + NPDisk::EDeviceType trueMedia = PDiskCtx->Dsk->TrueMediaType; + TABLER() { + TABLED() {str << "TrueMediaType";} + TABLED() {str << NPDisk::DeviceTypeStr(trueMedia, true); } + } + TABLER() { + TABLED() {str << "BurstThresholdNs";} + TABLED() {str << (i64)Config->CostMetricsParametersByMedia[trueMedia].BurstThresholdNs;} + } + TABLER() { + TABLED() {str << "DiskTimeAvailableScale";} + TABLED() {str << 0.001 * Config->CostMetricsParametersByMedia[trueMedia].DiskTimeAvailableScale;} + } } } } diff --git a/ydb/core/blobstorage/vdisk/skeleton/blobstorage_skeletonfront.cpp b/ydb/core/blobstorage/vdisk/skeleton/blobstorage_skeletonfront.cpp index 43f91582f9ed..2ad10639072b 100644 --- a/ydb/core/blobstorage/vdisk/skeleton/blobstorage_skeletonfront.cpp +++ b/ydb/core/blobstorage/vdisk/skeleton/blobstorage_skeletonfront.cpp @@ -724,7 +724,7 @@ namespace NKikimr { VCtx = MakeIntrusive(ctx.SelfID, GInfo->PickTopology(), VDiskCounters, SelfVDiskId, ctx.ExecutorThread.ActorSystem, baseInfo.DeviceType, baseInfo.DonorMode, baseInfo.ReplPDiskReadQuoter, baseInfo.ReplPDiskWriteQuoter, baseInfo.ReplNodeRequestQuoter, - baseInfo.ReplNodeResponseQuoter, Config->BurstThresholdNs, Config->DiskTimeAvailableScale); + baseInfo.ReplNodeResponseQuoter); // create IntQueues IntQueueAsyncGets = std::make_unique( @@ -1208,7 +1208,7 @@ namespace NKikimr { void HandleRequestWithQoS(const TActorContext &ctx, TAutoPtr> &ev, const char *msgName, ui64 cost, TIntQueueClass &intQueue) { CheckEvent(ev, msgName); - const ui64 advancedCost = VCtx->CostTracker->GetCost(*ev->Get()); + const ui64 advancedCost = VCtx->CostTracker ? VCtx->CostTracker->GetCost(*ev->Get()) : 0; const ui32 recByteSize = ev->Get()->GetCachedByteSize(); auto &record = ev->Get()->Record; auto &msgQoS = *record.MutableMsgQoS(); @@ -1259,10 +1259,14 @@ namespace NKikimr { } else { if (clientId.GetType() == NBackpressure::EQueueClientType::DSProxy) { CostGroup.SkeletonFrontUserCostNs() += cost; - VCtx->CostTracker->CountUserCost(advancedCost); + if (VCtx->CostTracker) { + VCtx->CostTracker->CountUserCost(advancedCost); + } } else { CostGroup.SkeletonFrontInternalCostNs() += cost; - VCtx->CostTracker->CountInternalCost(advancedCost); + if (VCtx->CostTracker) { + VCtx->CostTracker->CountInternalCost(advancedCost); + } } } } @@ -1609,7 +1613,9 @@ namespace NKikimr { extQueue.Completed(ctx, msgCtx, event); TIntQueueClass &intQueue = GetIntQueue(msgCtx.IntQueueId); intQueue.Completed(ctx, msgCtx, *this); - VCtx->CostTracker->CountPDiskResponse(); + if (VCtx->CostTracker) { + VCtx->CostTracker->CountPDiskResponse(); + } if (!ev->Get()->DoNotResend) { TActivationContext::Send(event.release()); } diff --git a/ydb/core/blobstorage/vdisk/skeleton/skeleton_oos_tracker.cpp b/ydb/core/blobstorage/vdisk/skeleton/skeleton_oos_tracker.cpp index 5ecaabc3b673..175f45fa8d56 100644 --- a/ydb/core/blobstorage/vdisk/skeleton/skeleton_oos_tracker.cpp +++ b/ydb/core/blobstorage/vdisk/skeleton/skeleton_oos_tracker.cpp @@ -96,7 +96,9 @@ namespace NKikimr { if (msg->NumSlots > 0) { ui32 timeAvailable = 1'000'000'000 / msg->NumSlots; CostGroup.DiskTimeAvailableNs() = timeAvailable; - VCtx->CostTracker->SetTimeAvailable(timeAvailable); + if (VCtx->CostTracker) { + VCtx->CostTracker->SetTimeAvailable(timeAvailable); + } } Become(&TThis::WaitFunc); diff --git a/ydb/core/protos/config.proto b/ydb/core/protos/config.proto index 26b53fd0be56..217878dd062e 100644 --- a/ydb/core/protos/config.proto +++ b/ydb/core/protos/config.proto @@ -289,16 +289,6 @@ message TBlobStorageConfig { optional TAutoconfigSettings AutoconfigSettings = 6; - message TCostMetricsConfig { - optional NKikimrBlobStorage.EPDiskType PDiskType = 1; - optional uint64 BurstThresholdNs = 2; - optional float DiskTimeAvailableScale = 3; - } - - message TCostMetricsSettings { - repeated TCostMetricsConfig VDiskTypes = 1; - }; - message TVDiskPerformanceConfig { optional NKikimrBlobStorage.EPDiskType PDiskType = 1; optional uint32 MinHugeBlobSizeInBytes = 4; @@ -308,7 +298,7 @@ message TBlobStorageConfig { repeated TVDiskPerformanceConfig VDiskTypes = 1; }; - optional TCostMetricsSettings CostMetricsSettings = 7; + reserved 7; // TCostMetricsSettings, moved to ICB optional TVDiskPerformanceSettings VDiskPerformanceSettings = 8; } @@ -1262,6 +1252,37 @@ message TImmediateControlsConfig { MinValue: 1, MaxValue: 1024, DefaultValue: 10 }]; + + optional uint64 BurstThresholdNsHDD = 6 [(ControlOptions) = { + Description: "Minumum operation queue size that is considered a burst, setting for HDD", + MinValue: 1, + MaxValue: 1000000000000, + DefaultValue: 200000000 }]; + optional uint64 BurstThresholdNsSSD = 7 [(ControlOptions) = { + Description: "Minumum operation queue size that is considered a burst, setting for SSD", + MinValue: 1, + MaxValue: 1000000000000, + DefaultValue: 50000000 }]; + optional uint64 BurstThresholdNsNVME = 8 [(ControlOptions) = { + Description: "Minumum operation queue size that is considered a burst, setting for NVME", + MinValue: 1, + MaxValue: 1000000000000, + DefaultValue: 32000000 }]; + optional uint64 DiskTimeAvailableScaleHDD = 9 [(ControlOptions) = { + Description: "Scale coefficient DiskTimeAvailableScale metric, this parameter will be converted to float and divided by 1'000, setting for HDD", + MinValue: 1, + MaxValue: 1000000, + DefaultValue: 1000 }]; + optional uint64 DiskTimeAvailableScaleSSD = 10 [(ControlOptions) = { + Description: "Scale coefficient for DiskTimeAvailableScale metric, this parameter will be converted to float and divided by 1'000, setting for SSD", + MinValue: 1, + MaxValue: 1000000, + DefaultValue: 1000 }]; + optional uint64 DiskTimeAvailableScaleNVME = 11 [(ControlOptions) = { + Description: "Scale coefficient DiskTimeAvailableScale metric, this parameter will be converted to float and divided by 1'000, setting for NVME", + MinValue: 1, + MaxValue: 1000000, + DefaultValue: 1000 }]; } message TTabletControls {