diff --git a/ydb/core/protos/sys_view.proto b/ydb/core/protos/sys_view.proto index 0103eabc9b51..00700d0a2f30 100644 --- a/ydb/core/protos/sys_view.proto +++ b/ydb/core/protos/sys_view.proto @@ -47,6 +47,10 @@ message TPartitionStats { optional uint64 ByKeyFilterSize = 21; optional uint32 FollowerId = 22; + + optional uint64 LocksAcquired = 23; + optional uint64 LocksWholeShard = 24; + optional uint64 LocksBroken = 25; } message TPartitionStatsResult { @@ -133,8 +137,10 @@ enum EStatsType { METRICS_ONE_HOUR = 8; TOP_REQUEST_UNITS_ONE_MINUTE = 9; TOP_REQUEST_UNITS_ONE_HOUR = 10; - TOP_PARTITIONS_ONE_MINUTE = 11; - TOP_PARTITIONS_ONE_HOUR = 12; + TOP_PARTITIONS_BY_CPU_ONE_MINUTE = 11; + TOP_PARTITIONS_BY_CPU_ONE_HOUR = 12; + TOP_PARTITIONS_BY_TLI_ONE_MINUTE = 13; + TOP_PARTITIONS_BY_TLI_ONE_HOUR = 14; } message TEvGetQueryStats { @@ -608,6 +614,9 @@ message TTopPartitionsInfo { optional uint64 IndexSize = 8; optional uint32 InFlightTxCount = 9; optional uint32 FollowerId = 10; + optional uint64 LocksAcquired = 11; + optional uint64 LocksWholeShard = 12; + optional uint64 LocksBroken = 13; } message TTopPartitionsEntry { @@ -634,6 +643,7 @@ message TEvGetTopPartitionsResponse { // partitions stats collector -> SVP message TEvSendTopPartitions { - repeated TTopPartitionsInfo Partitions = 1; + repeated TTopPartitionsInfo PartitionsByCpu = 1; + repeated TTopPartitionsInfo PartitionsByTli = 3; optional uint64 TimeUs = 2; } diff --git a/ydb/core/sys_view/common/schema.cpp b/ydb/core/sys_view/common/schema.cpp index 9e84249c6908..1d449bbcad73 100644 --- a/ydb/core/sys_view/common/schema.cpp +++ b/ydb/core/sys_view/common/schema.cpp @@ -291,8 +291,10 @@ class TSystemViewResolver : public ISystemViewResolver { RegisterColumnTableSystemView(TablePrimaryIndexGranuleStatsName); RegisterColumnTableSystemView(TablePrimaryIndexOptimizerStatsName); - RegisterSystemView(TopPartitions1MinuteName); - RegisterSystemView(TopPartitions1HourName); + RegisterSystemView(TopPartitionsByCpu1MinuteName); + RegisterSystemView(TopPartitionsByCpu1HourName); + RegisterSystemView(TopPartitionsByTli1MinuteName); + RegisterSystemView(TopPartitionsByTli1HourName); RegisterPgTablesSystemViews(); diff --git a/ydb/core/sys_view/common/schema.h b/ydb/core/sys_view/common/schema.h index 9bdc436f59a2..e133fc5f1366 100644 --- a/ydb/core/sys_view/common/schema.h +++ b/ydb/core/sys_view/common/schema.h @@ -43,8 +43,11 @@ constexpr TStringBuf TablePrimaryIndexPortionStatsName = "primary_index_portion_ constexpr TStringBuf TablePrimaryIndexGranuleStatsName = "primary_index_granule_stats"; constexpr TStringBuf TablePrimaryIndexOptimizerStatsName = "primary_index_optimizer_stats"; -constexpr TStringBuf TopPartitions1MinuteName = "top_partitions_one_minute"; -constexpr TStringBuf TopPartitions1HourName = "top_partitions_one_hour"; +constexpr TStringBuf TopPartitionsByCpu1MinuteName = "top_partitions_one_minute"; +constexpr TStringBuf TopPartitionsByCpu1HourName = "top_partitions_one_hour"; + +constexpr TStringBuf TopPartitionsByTli1MinuteName = "top_partitions_by_tli_one_minute"; +constexpr TStringBuf TopPartitionsByTli1HourName = "top_partitions_by_tli_one_hour"; constexpr TStringBuf PgTablesName = "pg_tables"; constexpr TStringBuf InformationSchemaTablesName = "tables"; @@ -93,6 +96,9 @@ struct Schema : NIceDb::Schema { struct LastTtlRowsProcessed : Column<25, NScheme::NTypeIds::Uint64> {}; struct LastTtlRowsErased : Column<26, NScheme::NTypeIds::Uint64> {}; struct FollowerId : Column<27, NScheme::NTypeIds::Uint32> {}; + struct LocksAcquired : Column<28, NScheme::NTypeIds::Uint64> {}; + struct LocksWholeShard : Column<29, NScheme::NTypeIds::Uint64> {}; + struct LocksBroken : Column<30, NScheme::NTypeIds::Uint64> {}; using TKey = TableKey; using TColumns = TableColumns< @@ -122,7 +128,11 @@ struct Schema : NIceDb::Schema { LastTtlRunTime, LastTtlRowsProcessed, LastTtlRowsErased, - FollowerId>; + FollowerId, + LocksAcquired, + LocksWholeShard, + LocksBroken + >; }; struct Nodes : Table<2> { @@ -757,6 +767,36 @@ struct Schema : NIceDb::Schema { QueryCpuLimitPercentPerNode, QueryMemoryLimitPercentPerNode>; }; + + struct TopPartitionsTli : Table<23> { + struct IntervalEnd : Column<1, NScheme::NTypeIds::Timestamp> {}; + struct Rank : Column<2, NScheme::NTypeIds::Uint32> {}; + struct TabletId : Column<3, NScheme::NTypeIds::Uint64> {}; + struct Path : Column<4, NScheme::NTypeIds::Utf8> {}; + struct LocksAcquired : Column<5, NScheme::NTypeIds::Uint64> {}; + struct LocksWholeShard : Column<6, NScheme::NTypeIds::Uint64> {}; + struct LocksBroken : Column<7, NScheme::NTypeIds::Uint64> {}; + struct NodeId : Column<8, NScheme::NTypeIds::Uint32> {}; + struct DataSize : Column<9, NScheme::NTypeIds::Uint64> {}; + struct RowCount : Column<10, NScheme::NTypeIds::Uint64> {}; + struct IndexSize : Column<11, NScheme::NTypeIds::Uint64> {}; + struct FollowerId : Column<12, NScheme::NTypeIds::Uint32> {}; + + using TKey = TableKey; + using TColumns = TableColumns< + IntervalEnd, + Rank, + TabletId, + Path, + LocksAcquired, + LocksWholeShard, + LocksBroken, + NodeId, + DataSize, + RowCount, + IndexSize, + FollowerId>; + }; }; bool MaybeSystemViewPath(const TVector& path); diff --git a/ydb/core/sys_view/partition_stats/partition_stats.cpp b/ydb/core/sys_view/partition_stats/partition_stats.cpp index ad353bd30932..a60cb9b8f060 100644 --- a/ydb/core/sys_view/partition_stats/partition_stats.cpp +++ b/ydb/core/sys_view/partition_stats/partition_stats.cpp @@ -32,7 +32,8 @@ class TPartitionStatsCollector : public TActorBootstrappedUsePartitionStatsCollectorForTests) { - OverloadedPartitionBound = 0.0; + OverloadedByCpuPartitionBound = 0.0; + OverloadedByTliPartitionBound = 0; ProcessOverloadedInterval = TDuration::Seconds(1); } @@ -88,7 +89,7 @@ class TPartitionStatsCollector : public TActorBootstrapped newPartitions; - std::set overloaded; + std::set overloadedByCpu, overloadedByTli; for (auto shardIdx : ev->Get()->ShardIndices) { auto old = oldPartitions.find(shardIdx); @@ -96,17 +97,24 @@ class TPartitionStatsCollector : public TActorBootstrappedsecond; for (const auto& followerStat: old->second.FollowerStats) { - if (IsPartitionOverloaded(followerStat.second)) - overloaded.insert({shardIdx, followerStat.first}); + if (IsPartitionOverloadedByCpu(followerStat.second)) + overloadedByCpu.insert({shardIdx, followerStat.first}); + if (IsPartitionOverloadedByTli(followerStat.second)) + overloadedByTli.insert({shardIdx, followerStat.first}); } } } - if (!overloaded.empty()) { - tables.Overloaded[pathId].swap(overloaded); + if (!overloadedByCpu.empty()) { + tables.OverloadedByCpu[pathId].swap(overloadedByCpu); } else { - tables.Overloaded.erase(pathId); + tables.OverloadedByCpu.erase(pathId); } + if (!overloadedByTli.empty()) { + tables.OverloadedByTli[pathId].swap(overloadedByTli); + } else { + tables.OverloadedByTli.erase(pathId); + } oldPartitions.swap(newPartitions); table.ShardIndices.swap(ev->Get()->ShardIndices); @@ -125,7 +133,8 @@ class TPartitionStatsCollector : public TActorBootstrappedsecond.erase(overloadedFollower); if (overloadedFound->second.empty()) { - tables.Overloaded.erase(pathId); + tables.OverloadedByCpu.erase(pathId); } } } + if (IsPartitionOverloadedByTli(newStats)) { + tables.OverloadedByTli[pathId].insert(overloadedFollower); + } else { + auto overloadedFound = tables.OverloadedByTli.find(pathId); + if (overloadedFound != tables.OverloadedByTli.end()) { + overloadedFound->second.erase(overloadedFollower); + if (overloadedFound->second.empty()) { + tables.OverloadedByTli.erase(pathId); + } + } + } if (followerStats.HasTtlStats()) { newStats.MutableTtlStats()->Swap(followerStats.MutableTtlStats()); @@ -396,37 +416,54 @@ class TPartitionStatsCollector : public TActorBootstrappedsecond; - struct TPartition { + struct TPartitionByCpu { TPathId PathId; TShardIdx ShardIdx; ui32 FollowerId; double CPUCores; }; - std::vector sorted; + std::vector sortedByCpu; - for (const auto& [pathId, overloadedFollowers] : domainTables.Overloaded) { - for (const TOverloadedFollower& overloadedFollower : overloadedFollowers) { + struct TPartitionByTli { + TPathId PathId; + TShardIdx ShardIdx; + ui32 FollowerId; + ui64 LocksBroken; + }; + std::vector sortedByTli; + + for (const auto& [pathId, overloadedFollowers] : domainTables.OverloadedByCpu) { + for (const TFollowerStats& overloadedFollower : overloadedFollowers) { const auto& table = domainTables.Stats[pathId]; const auto& partition = table.Partitions.at(overloadedFollower.ShardIdx).FollowerStats.at(overloadedFollower.FollowerId); - sorted.emplace_back(TPartition{pathId, overloadedFollower.ShardIdx, overloadedFollower.FollowerId, partition.GetCPUCores()}); + sortedByCpu.emplace_back(TPartitionByCpu{pathId, overloadedFollower.ShardIdx, overloadedFollower.FollowerId, partition.GetCPUCores()}); } } + for (const auto& [pathId, overloadedFollowers] : domainTables.OverloadedByTli) { + for (const TFollowerStats& overloadedFollower : overloadedFollowers) { + const auto& table = domainTables.Stats[pathId]; + const auto& partition = table.Partitions.at(overloadedFollower.ShardIdx).FollowerStats.at(overloadedFollower.FollowerId); + sortedByTli.emplace_back(TPartitionByTli{pathId, overloadedFollower.ShardIdx, overloadedFollower.FollowerId, partition.GetLocksBroken()}); + } + } - std::sort(sorted.begin(), sorted.end(), + std::sort(sortedByCpu.begin(), sortedByCpu.end(), [] (const auto& l, const auto& r) { return l.CPUCores > r.CPUCores; }); + std::sort(sortedByTli.begin(), sortedByTli.end(), + [] (const auto& l, const auto& r) { return l.LocksBroken > r.LocksBroken; }); auto now = TActivationContext::Now(); auto nowUs = now.MicroSeconds(); size_t count = 0; auto sendEvent = MakeHolder(); - for (const auto& entry : sorted) { + for (const auto& entry : sortedByCpu) { const auto& table = domainTables.Stats[entry.PathId]; const auto& followerStats = table.Partitions.at(entry.ShardIdx).FollowerStats; const auto& partition = followerStats.at(entry.FollowerId); const auto& leaderPartition = followerStats.at(0); - auto* result = sendEvent->Record.AddPartitions(); + auto* result = sendEvent->Record.AddPartitionsByCpu(); result->SetTabletId(partition.GetTabletId()); result->SetPath(table.Path); result->SetPeakTimeUs(nowUs); @@ -442,11 +479,34 @@ class TPartitionStatsCollector : public TActorBootstrappedRecord.AddPartitionsByTli(); + result->SetTabletId(partition.GetTabletId()); + result->SetPath(table.Path); + result->SetLocksAcquired(partition.GetLocksAcquired()); + result->SetLocksWholeShard(partition.GetLocksWholeShard()); + result->SetLocksBroken(partition.GetLocksBroken()); + result->SetNodeId(partition.GetNodeId()); + result->SetDataSize(leaderPartition.GetDataSize()); + result->SetRowCount(leaderPartition.GetRowCount()); + result->SetIndexSize(leaderPartition.GetIndexSize()); + result->SetFollowerId(partition.GetFollowerId()); + + if (++count == TOP_PARTITIONS_COUNT) { + break; + } + } sendEvent->Record.SetTimeUs(nowUs); SVLOG_D("NSysView::TPartitionStatsCollector: TEvProcessOverloaded " - << "top size# " << sorted.size() + << ", top size by CPU # " << sortedByCpu.size() + << ", top size by TLI # " << sortedByTli.size() << ", time# " << now); Send(MakePipePerNodeCacheID(false), @@ -467,8 +527,11 @@ class TPartitionStatsCollector : public TActorBootstrapped= OverloadedPartitionBound; + bool IsPartitionOverloadedByCpu(const NKikimrSysView::TPartitionStats& stats) const { + return stats.GetCPUCores() >= OverloadedByCpuPartitionBound; + } + bool IsPartitionOverloadedByTli(const NKikimrSysView::TPartitionStats& stats) const { + return stats.GetLocksBroken() >= OverloadedByTliPartitionBound; } private: @@ -478,7 +541,8 @@ class TPartitionStatsCollector : public TActorBootstrapped Stats; - std::unordered_map> Overloaded; + std::unordered_map> OverloadedByCpu; + std::unordered_map> OverloadedByTli; }; std::unordered_map DomainTables; @@ -714,6 +779,15 @@ class TPartitionStatsScan : public TScanActorBase { insert({TSchema::FollowerId::ColumnId, [] (const TPartitionStatsResult&, const TPartitionStats&, const TPartitionStats& stats) { return TCell::Make(stats.GetFollowerId()); }}); + insert({TSchema::LocksAcquired::ColumnId, [] (const TPartitionStatsResult&, const TPartitionStats&, const TPartitionStats& stats) { + return TCell::Make(stats.GetLocksAcquired()); + }}); + insert({TSchema::LocksWholeShard::ColumnId, [] (const TPartitionStatsResult&, const TPartitionStats&, const TPartitionStats& stats) { + return TCell::Make(stats.GetLocksWholeShard()); + }}); + insert({TSchema::LocksBroken::ColumnId, [] (const TPartitionStatsResult&, const TPartitionStats&, const TPartitionStats& stats) { + return TCell::Make(stats.GetLocksBroken()); + }}); } }; static TExtractorsMap extractors; diff --git a/ydb/core/sys_view/partition_stats/top_partitions.cpp b/ydb/core/sys_view/partition_stats/top_partitions.cpp index b1705a9c3e65..8d9a96d3f6e9 100644 --- a/ydb/core/sys_view/partition_stats/top_partitions.cpp +++ b/ydb/core/sys_view/partition_stats/top_partitions.cpp @@ -17,13 +17,13 @@ void SetField<1>(NKikimrSysView::TTopPartitionsKey& key, ui32 value) { key.SetRank(value); } -struct TTopPartitionsExtractorMap : +struct TTopPartitionsByCpuExtractorMap : public std::unordered_map> { using S = Schema::TopPartitions; using E = NKikimrSysView::TTopPartitionsEntry; - TTopPartitionsExtractorMap() { + TTopPartitionsByCpuExtractorMap() { insert({S::IntervalEnd::ColumnId, [] (const E& entry) { return TCell::Make(entry.GetKey().GetIntervalEndUs()); }}); @@ -64,31 +64,101 @@ struct TTopPartitionsExtractorMap : } }; -THolder CreateTopPartitionsScan(const NActors::TActorId& ownerId, ui32 scanId, const TTableId& tableId, +struct TTopPartitionsByTliExtractorMap : + public std::unordered_map> +{ + using S = Schema::TopPartitionsTli; + using E = NKikimrSysView::TTopPartitionsEntry; + + TTopPartitionsByTliExtractorMap() { + insert({S::IntervalEnd::ColumnId, [] (const E& entry) { + return TCell::Make(entry.GetKey().GetIntervalEndUs()); + }}); + insert({S::Rank::ColumnId, [] (const E& entry) { + return TCell::Make(entry.GetKey().GetRank()); + }}); + insert({S::TabletId::ColumnId, [] (const E& entry) { + return TCell::Make(entry.GetInfo().GetTabletId()); + }}); + insert({S::Path::ColumnId, [] (const E& entry) { + const auto& text = entry.GetInfo().GetPath(); + return TCell(text.data(), text.size()); + }}); + insert({S::LocksAcquired::ColumnId, [] (const E& entry) { + return TCell::Make(entry.GetInfo().GetLocksAcquired()); + }}); + insert({S::LocksWholeShard::ColumnId, [] (const E& entry) { + return TCell::Make(entry.GetInfo().GetLocksWholeShard()); + }}); + insert({S::LocksBroken::ColumnId, [] (const E& entry) { + return TCell::Make(entry.GetInfo().GetLocksBroken()); + }}); + insert({S::NodeId::ColumnId, [] (const E& entry) { + return TCell::Make(entry.GetInfo().GetNodeId()); + }}); + insert({S::DataSize::ColumnId, [] (const E& entry) { + return TCell::Make(entry.GetInfo().GetDataSize()); + }}); + insert({S::RowCount::ColumnId, [] (const E& entry) { + return TCell::Make(entry.GetInfo().GetRowCount()); + }}); + insert({S::IndexSize::ColumnId, [] (const E& entry) { + return TCell::Make(entry.GetInfo().GetIndexSize()); + }}); + insert({S::FollowerId::ColumnId, [] (const E& entry) { + return TCell::Make(entry.GetInfo().GetFollowerId()); + }}); + } +}; + +THolder CreateTopPartitionsByCpuScan(const NActors::TActorId& ownerId, ui32 scanId, const TTableId& tableId, const TTableRange& tableRange, const TArrayRef& columns) { - using TTopPartitionsScan = TProcessorScan< + using TTopPartitionsByCpuScan = TProcessorScan< NKikimrSysView::TTopPartitionsEntry, NKikimrSysView::TEvGetTopPartitionsRequest, NKikimrSysView::TEvGetTopPartitionsResponse, TEvSysView::TEvGetTopPartitionsRequest, TEvSysView::TEvGetTopPartitionsResponse, - TTopPartitionsExtractorMap, + TTopPartitionsByCpuExtractorMap, ui64, ui32 >; - auto viewName = tableId.SysViewInfo; + static const std::map nameToStatus = { + {TopPartitionsByCpu1MinuteName, NKikimrSysView::TOP_PARTITIONS_BY_CPU_ONE_MINUTE}, + {TopPartitionsByCpu1HourName, NKikimrSysView::TOP_PARTITIONS_BY_CPU_ONE_HOUR}, + }; - if (viewName == TopPartitions1MinuteName) { - return MakeHolder(ownerId, scanId, tableId, tableRange, columns, - NKikimrSysView::TOP_PARTITIONS_ONE_MINUTE); + auto statusIter = nameToStatus.find(tableId.SysViewInfo.ConstRef()); + Y_ABORT_UNLESS(statusIter != nameToStatus.end()); - } else if (viewName == TopPartitions1HourName) { - return MakeHolder(ownerId, scanId, tableId, tableRange, columns, - NKikimrSysView::TOP_PARTITIONS_ONE_HOUR); - } - return {}; + return MakeHolder(ownerId, scanId, tableId, tableRange, columns, statusIter->second); +} + +THolder CreateTopPartitionsByTliScan(const NActors::TActorId& ownerId, ui32 scanId, const TTableId& tableId, + const TTableRange& tableRange, const TArrayRef& columns) +{ + using TTopPartitionsByTliScan = TProcessorScan< + NKikimrSysView::TTopPartitionsEntry, + NKikimrSysView::TEvGetTopPartitionsRequest, + NKikimrSysView::TEvGetTopPartitionsResponse, + TEvSysView::TEvGetTopPartitionsRequest, + TEvSysView::TEvGetTopPartitionsResponse, + TTopPartitionsByTliExtractorMap, + ui64, + ui32 + >; + + static const std::map nameToStatus = { + {TopPartitionsByTli1MinuteName, NKikimrSysView::TOP_PARTITIONS_BY_TLI_ONE_MINUTE}, + {TopPartitionsByTli1HourName, NKikimrSysView::TOP_PARTITIONS_BY_TLI_ONE_HOUR}, + }; + + auto statusIter = nameToStatus.find(tableId.SysViewInfo.ConstRef()); + Y_ABORT_UNLESS(statusIter != nameToStatus.end()); + + return MakeHolder(ownerId, scanId, tableId, tableRange, columns, statusIter->second); } } // NKikimr::NSysView diff --git a/ydb/core/sys_view/partition_stats/top_partitions.h b/ydb/core/sys_view/partition_stats/top_partitions.h index 44cdfb8f7b9d..16a082c16476 100644 --- a/ydb/core/sys_view/partition_stats/top_partitions.h +++ b/ydb/core/sys_view/partition_stats/top_partitions.h @@ -7,8 +7,11 @@ namespace NKikimr::NSysView { -THolder CreateTopPartitionsScan(const NActors::TActorId& ownerId, ui32 scanId, +THolder CreateTopPartitionsByCpuScan(const NActors::TActorId& ownerId, ui32 scanId, const TTableId& tableId, const TTableRange& tableRange, const TArrayRef& columns); +THolder CreateTopPartitionsByTliScan(const NActors::TActorId& ownerId, ui32 scanId, + const TTableId& tableId, const TTableRange& tableRange, + const TArrayRef& columns); } // NKikimr::NSysView diff --git a/ydb/core/sys_view/processor/processor_impl.cpp b/ydb/core/sys_view/processor/processor_impl.cpp index d7615d0f1d58..798598b6d721 100644 --- a/ydb/core/sys_view/processor/processor_impl.cpp +++ b/ydb/core/sys_view/processor/processor_impl.cpp @@ -186,12 +186,16 @@ void TSysViewProcessor::PersistPartitionResults(NIceDb::TNiceDb& db) { auto intervalEnd = IntervalEnd + TotalInterval; PersistPartitionTopResults( - db, PartitionTopMinute, TopPartitionsOneMinute, intervalEnd); + db, PartitionTopByCpuMinute, TopPartitionsByCpuOneMinute, intervalEnd); + PersistPartitionTopResults( + db, PartitionTopByTliMinute, TopPartitionsByTliOneMinute, intervalEnd); auto hourEnd = EndOfHourInterval(intervalEnd); PersistPartitionTopResults( - db, PartitionTopHour, TopPartitionsOneHour, hourEnd); + db, PartitionTopByCpuHour, TopPartitionsByCpuOneHour, hourEnd); + PersistPartitionTopResults( + db, PartitionTopByTliHour, TopPartitionsByTliOneHour, hourEnd); } void TSysViewProcessor::ScheduleAggregate() { @@ -296,7 +300,8 @@ void TSysViewProcessor::Reset(NIceDb::TNiceDb& db, const TActorContext& ctx) { clearQueryTop(NKikimrSysView::TOP_CPU_TIME_ONE_MINUTE, ByCpuTimeMinute); clearQueryTop(NKikimrSysView::TOP_REQUEST_UNITS_ONE_MINUTE, ByRequestUnitsMinute); - clearPartitionTop(NKikimrSysView::TOP_PARTITIONS_ONE_MINUTE, PartitionTopMinute); + clearPartitionTop(NKikimrSysView::TOP_PARTITIONS_BY_CPU_ONE_MINUTE, PartitionTopByCpuMinute); + clearPartitionTop(NKikimrSysView::TOP_PARTITIONS_BY_TLI_ONE_MINUTE, PartitionTopByTliMinute); CurrentStage = COLLECT; PersistStage(db); @@ -321,7 +326,8 @@ void TSysViewProcessor::Reset(NIceDb::TNiceDb& db, const TActorContext& ctx) { } if (partitionOldHourEnd != partitionNewHourEnd) { - clearPartitionTop(NKikimrSysView::TOP_PARTITIONS_ONE_HOUR, PartitionTopHour); + clearPartitionTop(NKikimrSysView::TOP_PARTITIONS_BY_CPU_ONE_HOUR, PartitionTopByCpuHour); + clearPartitionTop(NKikimrSysView::TOP_PARTITIONS_BY_TLI_ONE_HOUR, PartitionTopByTliHour); } SVLOG_D("[" << TabletID() << "] Reset: interval end# " << IntervalEnd); @@ -341,8 +347,10 @@ void TSysViewProcessor::Reset(NIceDb::TNiceDb& db, const TActorContext& ctx) { CutHistory(db, TopByRequestUnitsOneMinute, minuteHistorySize); CutHistory(db, TopByRequestUnitsOneHour, hourHistorySize); - CutHistory(db, TopPartitionsOneMinute, minuteHistorySize); - CutHistory(db, TopPartitionsOneHour, hourHistorySize); + CutHistory(db, TopPartitionsByCpuOneMinute, minuteHistorySize); + CutHistory(db, TopPartitionsByCpuOneHour, hourHistorySize); + CutHistory(db, TopPartitionsByTliOneMinute, minuteHistorySize); + CutHistory(db, TopPartitionsByTliOneHour, hourHistorySize); } void TSysViewProcessor::SendRequests() { @@ -506,11 +514,17 @@ void TSysViewProcessor::Reply(typename TRequest::TPtr& ev) { TMap* entries = nullptr; if constexpr (std::is_same::value) { switch (record.GetType()) { - case NKikimrSysView::TOP_PARTITIONS_ONE_MINUTE: - entries = &TopPartitionsOneMinute; + case NKikimrSysView::TOP_PARTITIONS_BY_CPU_ONE_MINUTE: + entries = &TopPartitionsByCpuOneMinute; break; - case NKikimrSysView::TOP_PARTITIONS_ONE_HOUR: - entries = &TopPartitionsOneHour; + case NKikimrSysView::TOP_PARTITIONS_BY_CPU_ONE_HOUR: + entries = &TopPartitionsByCpuOneHour; + break; + case NKikimrSysView::TOP_PARTITIONS_BY_TLI_ONE_MINUTE: + entries = &TopPartitionsByTliOneMinute; + break; + case NKikimrSysView::TOP_PARTITIONS_BY_TLI_ONE_HOUR: + entries = &TopPartitionsByTliOneHour; break; default: SVLOG_CRIT("[" << TabletID() << "] unexpected stats type: " << (size_t)record.GetType()); @@ -767,10 +781,14 @@ bool TSysViewProcessor::OnRenderAppHtmlPage(NMon::TEvRemoteHttpInfo::TPtr ev, << " Count: " << TopByRequestUnitsOneMinute.size() << Endl << Endl; str << "TopByRequestUnitsOneHour" << Endl << " Count: " << TopByRequestUnitsOneHour.size() << Endl << Endl; - str << "TopPartitionsOneMinute" << Endl - << " Count: " << TopPartitionsOneMinute.size() << Endl << Endl; - str << "TopPartitionsOneHour" << Endl - << " Count: " << TopPartitionsOneHour.size() << Endl << Endl; + str << "TopPartitionsByCpuOneMinute" << Endl + << " Count: " << TopPartitionsByCpuOneMinute.size() << Endl << Endl; + str << "TopPartitionsByCpuOneHour" << Endl + << " Count: " << TopPartitionsByCpuOneHour.size() << Endl << Endl; + str << "TopPartitionsByTliOneMinute" << Endl + << " Count: " << TopPartitionsByTliOneMinute.size() << Endl << Endl; + str << "TopPartitionsByTliOneHour" << Endl + << " Count: " << TopPartitionsByTliOneHour.size() << Endl << Endl; } } } diff --git a/ydb/core/sys_view/processor/processor_impl.h b/ydb/core/sys_view/processor/processor_impl.h index be57156b58ea..a7b6e5ae2c2f 100644 --- a/ydb/core/sys_view/processor/processor_impl.h +++ b/ydb/core/sys_view/processor/processor_impl.h @@ -336,12 +336,16 @@ class TSysViewProcessor : public TActor, public NTabletFlatEx TResultStatsMap TopByRequestUnitsOneHour; // IntervalPartitionTops - TPartitionTop PartitionTopMinute; - TPartitionTop PartitionTopHour; + TPartitionTop PartitionTopByCpuMinute; + TPartitionTop PartitionTopByCpuHour; + TPartitionTop PartitionTopByTliMinute; + TPartitionTop PartitionTopByTliHour; // TopPartitions... - TResultPartitionsMap TopPartitionsOneMinute; - TResultPartitionsMap TopPartitionsOneHour; + TResultPartitionsMap TopPartitionsByCpuOneMinute; + TResultPartitionsMap TopPartitionsByCpuOneHour; + TResultPartitionsMap TopPartitionsByTliOneMinute; + TResultPartitionsMap TopPartitionsByTliOneHour; // limited queue of user requests static constexpr size_t PendingRequestsLimit = 5; diff --git a/ydb/core/sys_view/processor/schema.h b/ydb/core/sys_view/processor/schema.h index bb9ff08a500e..939ed1a7f1ba 100644 --- a/ydb/core/sys_view/processor/schema.h +++ b/ydb/core/sys_view/processor/schema.h @@ -119,6 +119,8 @@ struct TProcessorSchema : NIceDb::Schema { RESULT_PARTITION_TABLE(TopPartitionsOneMinute, 17) RESULT_PARTITION_TABLE(TopPartitionsOneHour, 18) + RESULT_PARTITION_TABLE(TopPartitionsByTliOneMinute, 20) + RESULT_PARTITION_TABLE(TopPartitionsByTliOneHour, 21) #undef RESULT_PARTITION_TABLE @@ -141,7 +143,9 @@ struct TProcessorSchema : NIceDb::Schema { IntervalPartitionTops, IntervalPartitionFollowerTops, TopPartitionsOneMinute, - TopPartitionsOneHour + TopPartitionsOneHour, + TopPartitionsByTliOneMinute, + TopPartitionsByTliOneHour >; using TSettings = SchemaSettings< diff --git a/ydb/core/sys_view/processor/tx_init.cpp b/ydb/core/sys_view/processor/tx_init.cpp index 98a78e1b6904..48e727f01187 100644 --- a/ydb/core/sys_view/processor/tx_init.cpp +++ b/ydb/core/sys_view/processor/tx_init.cpp @@ -101,11 +101,17 @@ struct TSysViewProcessor::TTxInit : public TTxBase { Y_PROTOBUF_SUPPRESS_NODISCARD partition->ParseFromString(data); switch ((NKikimrSysView::EStatsType)type) { - case NKikimrSysView::TOP_PARTITIONS_ONE_MINUTE: - Self->PartitionTopMinute.emplace_back(std::move(partition)); + case NKikimrSysView::TOP_PARTITIONS_BY_CPU_ONE_MINUTE: + Self->PartitionTopByCpuMinute.emplace_back(std::move(partition)); break; - case NKikimrSysView::TOP_PARTITIONS_ONE_HOUR: - Self->PartitionTopHour.emplace_back(std::move(partition)); + case NKikimrSysView::TOP_PARTITIONS_BY_CPU_ONE_HOUR: + Self->PartitionTopByCpuHour.emplace_back(std::move(partition)); + break; + case NKikimrSysView::TOP_PARTITIONS_BY_TLI_ONE_MINUTE: + Self->PartitionTopByTliMinute.emplace_back(std::move(partition)); + break; + case NKikimrSysView::TOP_PARTITIONS_BY_TLI_ONE_HOUR: + Self->PartitionTopByTliHour.emplace_back(std::move(partition)); break; default: SVLOG_CRIT("[" << Self->TabletID() << "] ignoring unexpected partition stats type: " << type); @@ -428,29 +434,46 @@ struct TSysViewProcessor::TTxInit : public TTxBase { // IntervalPartitionTops { - Self->PartitionTopMinute.clear(); - Self->PartitionTopMinute.reserve(TOP_PARTITIONS_COUNT); - Self->PartitionTopHour.clear(); - Self->PartitionTopHour.reserve(TOP_PARTITIONS_COUNT); + Self->PartitionTopByCpuMinute.clear(); + Self->PartitionTopByCpuMinute.reserve(TOP_PARTITIONS_COUNT); + Self->PartitionTopByCpuHour.clear(); + Self->PartitionTopByCpuHour.reserve(TOP_PARTITIONS_COUNT); + + Self->PartitionTopByTliMinute.clear(); + Self->PartitionTopByTliMinute.reserve(TOP_PARTITIONS_COUNT); + Self->PartitionTopByTliHour.clear(); + Self->PartitionTopByTliHour.reserve(TOP_PARTITIONS_COUNT); if (!LoadIntervalPartitionTops(db)) return false; if (!LoadIntervalPartitionTops(db)) return false; - auto compare = [] (const auto& l, const auto& r) { + auto compareByCpu = [] (const auto& l, const auto& r) { return l->GetCPUCores() == r->GetCPUCores() ? l->GetTabletId() < r->GetTabletId() : l->GetCPUCores() > r->GetCPUCores(); }; - std::sort(Self->PartitionTopMinute.begin(), Self->PartitionTopMinute.end(), compare); - std::sort(Self->PartitionTopHour.begin(), Self->PartitionTopHour.end(), compare); + std::sort(Self->PartitionTopByCpuMinute.begin(), Self->PartitionTopByCpuMinute.end(), compareByCpu); + std::sort(Self->PartitionTopByCpuHour.begin(), Self->PartitionTopByCpuHour.end(), compareByCpu); + + auto compareByTli = [] (const auto& l, const auto& r) { + return l->GetLocksBroken() == r->GetLocksBroken() ? + l->GetTabletId() < r->GetTabletId() : l->GetLocksBroken() > r->GetLocksBroken(); + }; + + std::sort(Self->PartitionTopByTliMinute.begin(), Self->PartitionTopByTliMinute.end(), compareByTli); + std::sort(Self->PartitionTopByTliHour.begin(), Self->PartitionTopByTliHour.end(), compareByTli); } // TopPartitions... - if (!LoadPartitionResults(db, Self->TopPartitionsOneMinute)) + if (!LoadPartitionResults(db, Self->TopPartitionsByCpuOneMinute)) + return false; + if (!LoadPartitionResults(db, Self->TopPartitionsByCpuOneHour)) + return false; + if (!LoadPartitionResults(db, Self->TopPartitionsByTliOneMinute)) return false; - if (!LoadPartitionResults(db, Self->TopPartitionsOneHour)) + if (!LoadPartitionResults(db, Self->TopPartitionsByTliOneHour)) return false; auto deadline = Self->IntervalEnd + Self->TotalInterval; diff --git a/ydb/core/sys_view/processor/tx_top_partitions.cpp b/ydb/core/sys_view/processor/tx_top_partitions.cpp index 42a0d89f4aa9..b871b7d45bd0 100644 --- a/ydb/core/sys_view/processor/tx_top_partitions.cpp +++ b/ydb/core/sys_view/processor/tx_top_partitions.cpp @@ -17,14 +17,23 @@ struct TSysViewProcessor::TTxTopPartitions : public TTxBase { { using TPartitionTopKey = std::pair; + const bool isTopByCpu = statsType == NKikimrSysView::TOP_PARTITIONS_BY_CPU_ONE_MINUTE || statsType == NKikimrSysView::TOP_PARTITIONS_BY_CPU_ONE_HOUR; + TPartitionTop result; result.reserve(TOP_PARTITIONS_COUNT); std::unordered_set seen; size_t index = 0; auto topIt = top.begin(); + auto getPartition = [&] () { + return isTopByCpu ? Record.GetPartitionsByCpu(index) : Record.GetPartitionsByTli(index); + }; + auto getPartitionSize = [&] () { + return isTopByCpu ? Record.PartitionsByCpuSize() : Record.PartitionsByTliSize(); + }; + auto copyNewPartition = [&] () { - const auto& newPartition = Record.GetPartitions(index); + const auto& newPartition = getPartition(); const ui64 tabletId = newPartition.GetTabletId(); const ui32 followerId = newPartition.GetFollowerId(); @@ -49,10 +58,10 @@ struct TSysViewProcessor::TTxTopPartitions : public TTxBase { while (result.size() < TOP_PARTITIONS_COUNT) { if (topIt == top.end()) { - if (index == Record.PartitionsSize()) { + if (index == getPartitionSize()) { break; } - const auto& partition = Record.GetPartitions(index); + const auto& partition = getPartition(); const ui64 tabletId = partition.GetTabletId(); const ui32 followerId = partition.GetFollowerId(); if (seen.contains({tabletId, followerId})) { @@ -67,19 +76,23 @@ struct TSysViewProcessor::TTxTopPartitions : public TTxBase { ++topIt; continue; } - if (index == Record.PartitionsSize()) { + if (index == getPartitionSize()) { result.emplace_back(std::move(*topIt++)); seen.insert({topTabletId, topFollowerId}); continue; } - const auto& newPartition = Record.GetPartitions(index); + const auto& newPartition = getPartition(); const ui64 tabletId = newPartition.GetTabletId(); const ui32 followerId = newPartition.GetFollowerId(); if (seen.contains({tabletId, followerId})) { ++index; continue; } - if ((*topIt)->GetCPUCores() >= newPartition.GetCPUCores()) { + const bool isOverloadedByCpu = (statsType == NKikimrSysView::TOP_PARTITIONS_BY_CPU_ONE_MINUTE || statsType == NKikimrSysView::TOP_PARTITIONS_BY_CPU_ONE_HOUR) + && (*topIt)->GetCPUCores() >= newPartition.GetCPUCores(); + const bool isOverloadedByTli = (statsType == NKikimrSysView::TOP_PARTITIONS_BY_TLI_ONE_MINUTE || statsType == NKikimrSysView::TOP_PARTITIONS_BY_TLI_ONE_HOUR) + && (*topIt)->GetLocksBroken() >= newPartition.GetLocksBroken(); + if (isOverloadedByCpu || isOverloadedByTli) { result.emplace_back(std::move(*topIt++)); seen.insert({topTabletId, topFollowerId}); } else { @@ -107,11 +120,15 @@ struct TSysViewProcessor::TTxTopPartitions : public TTxBase { bool Execute(TTransactionContext& txc, const TActorContext&) override { SVLOG_D("[" << Self->TabletID() << "] TTxTopPartitions::Execute: " - << "partition count# " << Record.PartitionsSize()); + << ", partition by CPU count# " << Record.PartitionsByCpuSize() + << ", partition by TLI count# " << Record.PartitionsByTliSize() + ); NIceDb::TNiceDb db(txc.DB); - ProcessTop(db, NKikimrSysView::TOP_PARTITIONS_ONE_MINUTE, Self->PartitionTopMinute); - ProcessTop(db, NKikimrSysView::TOP_PARTITIONS_ONE_HOUR, Self->PartitionTopHour); + ProcessTop(db, NKikimrSysView::TOP_PARTITIONS_BY_CPU_ONE_MINUTE, Self->PartitionTopByCpuMinute); + ProcessTop(db, NKikimrSysView::TOP_PARTITIONS_BY_CPU_ONE_HOUR, Self->PartitionTopByCpuHour); + ProcessTop(db, NKikimrSysView::TOP_PARTITIONS_BY_TLI_ONE_MINUTE, Self->PartitionTopByTliMinute); + ProcessTop(db, NKikimrSysView::TOP_PARTITIONS_BY_TLI_ONE_HOUR, Self->PartitionTopByTliHour); return true; } diff --git a/ydb/core/sys_view/scan.cpp b/ydb/core/sys_view/scan.cpp index 2f7292dba1e1..011665741989 100644 --- a/ydb/core/sys_view/scan.cpp +++ b/ydb/core/sys_view/scan.cpp @@ -243,12 +243,18 @@ THolder CreateSystemViewScan( return CreateQueryMetricsScan(ownerId, scanId, tableId, tableRange, columns); } - if (tableId.SysViewInfo == TopPartitions1MinuteName || - tableId.SysViewInfo == TopPartitions1HourName) + if (tableId.SysViewInfo == TopPartitionsByCpu1MinuteName || + tableId.SysViewInfo == TopPartitionsByCpu1HourName) { - return CreateTopPartitionsScan(ownerId, scanId, tableId, tableRange, columns); + return CreateTopPartitionsByCpuScan(ownerId, scanId, tableId, tableRange, columns); } + if (tableId.SysViewInfo == TopPartitionsByTli1MinuteName || + tableId.SysViewInfo == TopPartitionsByTli1HourName) + { + return CreateTopPartitionsByTliScan(ownerId, scanId, tableId, tableRange, columns); + } + if (tableId.SysViewInfo == PgTablesName) { return CreatePgTablesScan(ownerId, scanId, tableId, tablePath, tableRange, columns); } diff --git a/ydb/core/sys_view/ut_kqp.cpp b/ydb/core/sys_view/ut_kqp.cpp index 5e39d9abef4d..2dedc8b4c486 100644 --- a/ydb/core/sys_view/ut_kqp.cpp +++ b/ydb/core/sys_view/ut_kqp.cpp @@ -122,6 +122,54 @@ void CreateRootTable(TTestEnv& env, ui64 partitionCount = 1, bool fillTable = fa } } +void BreakLock(TSession& session, const TString& tableName) { + std::optional tx1; + + { // tx0: write test data + auto result = session.ExecuteDataQuery(TStringBuilder() << + "UPSERT INTO `" << tableName << "` (Key, Value) VALUES (55u, \"Fifty five\")", + TTxControl::BeginTx().CommitTx()).ExtractValueSync(); + UNIT_ASSERT_VALUES_EQUAL_C(result.GetStatus(), EStatus::SUCCESS, result.GetIssues().ToString()); + } + + { // tx0: read all data + auto result = session.ExecuteDataQuery(TStringBuilder() << + "SELECT * FROM `" << tableName << "`", + TTxControl::BeginTx().CommitTx()).ExtractValueSync(); + UNIT_ASSERT_VALUES_EQUAL_C(result.GetStatus(), EStatus::SUCCESS, result.GetIssues().ToString()); + } + + while (!tx1) { + // tx1: start reading + auto result = session.ExecuteDataQuery(TStringBuilder() << + "SELECT * FROM `" << tableName << "` WHERE Key = 55u", + TTxControl::BeginTx()).ExtractValueSync(); + UNIT_ASSERT_VALUES_EQUAL_C(result.GetStatus(), EStatus::SUCCESS, result.GetIssues().ToString()); + TString yson = FormatResultSetYson(result.GetResultSet(0)); + if (yson == "[]") { + continue; + } + + NKqp::CompareYson(R"([ + [[55u];["Fifty five"]]; + ])", yson); + tx1 = result.GetTransaction(); + UNIT_ASSERT(tx1); + } + + { // tx2: write + commit + auto result = session.ExecuteDataQuery(TStringBuilder() << + "UPSERT INTO `" << tableName << "` (Key, Value) VALUES (55u, \"NewValue1\")", + TTxControl::BeginTx().CommitTx()).ExtractValueSync(); + UNIT_ASSERT_VALUES_EQUAL_C(result.GetStatus(), EStatus::SUCCESS, result.GetIssues().ToString()); + } + + { // tx1: try to commit + auto result = tx1->Commit().ExtractValueSync(); + UNIT_ASSERT_VALUES_EQUAL_C(result.GetStatus(), EStatus::SUCCESS, result.GetIssues().ToString()); + } +} + void SetupAuthEnvironment(TTestEnv& env) { env.GetServer().GetRuntime()->SetLogPriority(NKikimrServices::FLAT_TX_SCHEMESHARD, NLog::PRI_DEBUG); env.GetServer().GetRuntime()->SetLogPriority(NKikimrServices::SYSTEM_VIEWS, NLog::PRI_TRACE); @@ -170,6 +218,47 @@ void CheckEmpty(TScanQueryPartIterator& it) { NKqp::CompareYson(expected, NKqp::StreamResultToYson(it)); } +size_t GetRowCount(TTableClient& client, const TString& tableName, const TString& condition = {}) { + TStringBuilder query; + query << "SELECT * FROM `" << tableName << "`"; + if (!condition.empty()) + query << " WHERE " << condition; + auto it = client.StreamExecuteScanQuery(query).GetValueSync(); + UNIT_ASSERT_C(it.IsSuccess(), it.GetIssues().ToString()); + auto ysonString = NKqp::StreamResultToYson(it); + auto node = NYT::NodeFromYsonString(ysonString, ::NYson::EYsonType::Node); + UNIT_ASSERT(node.IsList()); + return node.AsList().size(); +} + +ui64 GetIntervalEnd(TTableClient& client, const TString& name) { + TStringBuilder query; + query << "SELECT MAX(IntervalEnd) FROM `" << name << "`"; + auto it = client.StreamExecuteScanQuery(query).GetValueSync(); + UNIT_ASSERT_C(it.IsSuccess(), it.GetIssues().ToString()); + auto ysonString = NKqp::StreamResultToYson(it); + auto node = NYT::NodeFromYsonString(ysonString, ::NYson::EYsonType::Node); + UNIT_ASSERT(node.IsList()); + UNIT_ASSERT(node.AsList().size() == 1); + auto row = node.AsList()[0]; + UNIT_ASSERT(row.IsList()); + UNIT_ASSERT(row.AsList().size() == 1); + auto value = row.AsList()[0]; + UNIT_ASSERT(value.IsList()); + UNIT_ASSERT(value.AsList().size() == 1); + return value.AsList()[0].AsUint64(); +} + +void WaitForStats(TTableClient& client, const TString& tableName, const TString& condition = {}) { + size_t rowCount = 0; + for (size_t iter = 0; iter < 30; ++iter) { + if (rowCount = GetRowCount(client, tableName, condition)) + break; + Sleep(TDuration::Seconds(5)); + } + UNIT_ASSERT_GE(rowCount, 0); +} + class TShowCreateTableChecker { public: @@ -1556,6 +1645,36 @@ WITH ( check.Uint64(1u); // LastTtlRowsErased } + Y_UNIT_TEST(PartitionStatsLocksFields) { + NDataShard::gDbStatsReportInterval = TDuration::Seconds(0); + + TTestEnv env; + CreateRootTable(env, /* partitionCount */ 1, /* fillTable */ true); + + TTableClient client(env.GetDriver()); + auto session = client.CreateSession().GetValueSync().GetSession(); + + BreakLock(session, "/Root/Table0"); + + WaitForStats(client, "/Root/.sys/partition_stats", "LocksBroken != 0"); + + auto it = client.StreamExecuteScanQuery(R"( + SELECT + LocksAcquired, + LocksWholeShard, + LocksBroken + FROM `/Root/.sys/partition_stats`; + )").GetValueSync(); + + UNIT_ASSERT_C(it.IsSuccess(), it.GetIssues().ToString()); + auto ysonString = NKqp::StreamResultToYson(it); + TYsonFieldChecker check(ysonString, 3); + + check.Uint64(1); // LocksAcquired + check.Uint64(0); // LocksWholeShard + check.Uint64(1); // LocksBroken + } + Y_UNIT_TEST(PartitionStatsFields) { NDataShard::gDbStatsReportInterval = TDuration::Seconds(0); @@ -1611,13 +1730,16 @@ WITH ( TxRejectedByOutOfStorage, TxRejectedByOverload, FollowerId, + LocksAcquired, + LocksWholeShard, + LocksBroken, UpdateTime FROM `/Root/.sys/partition_stats`; )").GetValueSync(); UNIT_ASSERT_C(it.IsSuccess(), it.GetIssues().ToString()); auto ysonString = NKqp::StreamResultToYson(it); - TYsonFieldChecker check(ysonString, 24); + TYsonFieldChecker check(ysonString, 27); check.Uint64GreaterOrEquals(nowUs); // AccessTime check.DoubleGreaterOrEquals(0.0); // CPUCores @@ -1642,6 +1764,9 @@ WITH ( check.Uint64(0u); // TxRejectedByOutOfStorage check.Uint64(0u); // TxRejectedByOverload check.Uint64(0u); // FollowerId + check.Uint64(0u); // LocksAcquired + check.Uint64(0u); // LocksWholeShard + check.Uint64(0u); // LocksBroken check.Uint64GreaterOrEquals(nowUs); // UpdateTime } @@ -2031,38 +2156,7 @@ WITH ( } } - size_t GetRowCount(TTableClient& client, const TString& tableName, const TString& condition = {}) { - TStringBuilder query; - query << "SELECT * FROM `" << tableName << "`"; - if (!condition.empty()) - query << " WHERE " << condition; - auto it = client.StreamExecuteScanQuery(query).GetValueSync(); - UNIT_ASSERT_C(it.IsSuccess(), it.GetIssues().ToString()); - auto ysonString = NKqp::StreamResultToYson(it); - auto node = NYT::NodeFromYsonString(ysonString, ::NYson::EYsonType::Node); - UNIT_ASSERT(node.IsList()); - return node.AsList().size(); - } - - ui64 GetIntervalEnd(TTableClient& client, const TString& name) { - TStringBuilder query; - query << "SELECT MAX(IntervalEnd) FROM `" << name << "`"; - auto it = client.StreamExecuteScanQuery(query).GetValueSync(); - UNIT_ASSERT_C(it.IsSuccess(), it.GetIssues().ToString()); - auto ysonString = NKqp::StreamResultToYson(it); - auto node = NYT::NodeFromYsonString(ysonString, ::NYson::EYsonType::Node); - UNIT_ASSERT(node.IsList()); - UNIT_ASSERT(node.AsList().size() == 1); - auto row = node.AsList()[0]; - UNIT_ASSERT(row.IsList()); - UNIT_ASSERT(row.AsList().size() == 1); - auto value = row.AsList()[0]; - UNIT_ASSERT(value.IsList()); - UNIT_ASSERT(value.AsList().size() == 1); - return value.AsList()[0].AsUint64(); - } - - Y_UNIT_TEST(TopPartitionsFields) { + Y_UNIT_TEST(TopPartitionsByCpuFields) { NDataShard::gDbStatsReportInterval = TDuration::Seconds(0); auto nowUs = TInstant::Now().MicroSeconds(); @@ -2114,7 +2208,7 @@ WITH ( check.Uint64(0); // InFlightTxCount } - Y_UNIT_TEST(TopPartitionsTables) { + Y_UNIT_TEST(TopPartitionsByCpuTables) { NDataShard::gDbStatsReportInterval = TDuration::Seconds(0); constexpr ui64 partitionCount = 5; @@ -2144,7 +2238,7 @@ WITH ( check("/Root/Tenant1/.sys/top_partitions_one_hour"); } - Y_UNIT_TEST(TopPartitionsRanges) { + Y_UNIT_TEST(TopPartitionsByCpuRanges) { NDataShard::gDbStatsReportInterval = TDuration::Seconds(0); constexpr ui64 partitionCount = 5; @@ -2225,7 +2319,7 @@ WITH ( } } - Y_UNIT_TEST(TopPartitionsFollowers) { + Y_UNIT_TEST(TopPartitionsByCpuFollowers) { NDataShard::gDbStatsReportInterval = TDuration::Seconds(0); auto nowUs = TInstant::Now().MicroSeconds(); @@ -2400,6 +2494,58 @@ WITH ( } } + Y_UNIT_TEST(TopPartitionsByTliFields) { + NDataShard::gDbStatsReportInterval = TDuration::Seconds(0); + + TTestEnv env(1, 4, {.EnableSVP = true}); + CreateTenantsAndTables(env); + + TTableClient client(env.GetDriver()); + auto session = client.CreateSession().GetValueSync().GetSession(); + + const TString tableName = "/Root/Tenant1/Table1"; + const TString viewName = "/Root/Tenant1/.sys/top_partitions_by_tli_one_minute"; + + BreakLock(session, tableName); + + WaitForStats(client, viewName, "LocksAcquired != 0"); + + ui64 intervalEnd = GetIntervalEnd(client, viewName); + + TStringBuilder query; + query << R"( + SELECT + IntervalEnd, + Rank, + TabletId, + Path, + LocksAcquired, + LocksWholeShard, + LocksBroken, + NodeId, + DataSize, + RowCount, + IndexSize)" + << " FROM `" << viewName << "`" + << " WHERE IntervalEnd = CAST(" << intervalEnd << "ul as Timestamp)" + << " AND Path=\"" << tableName << "\""; + auto it = client.StreamExecuteScanQuery(query).GetValueSync(); + UNIT_ASSERT_C(it.IsSuccess(), it.GetIssues().ToString()); + auto ysonString = NKqp::StreamResultToYson(it); + TYsonFieldChecker check(ysonString, 11); + check.Uint64(intervalEnd); // IntervalEnd + check.Uint64(1); // Rank + check.Uint64Greater(0); // TabletId + check.String(tableName); // Path + check.Uint64GreaterOrEquals(1); // LocksAcquired + check.Uint64(0); // LocksWholeShard + check.Uint64GreaterOrEquals(1); // LocksBroken + check.Uint64Greater(0); // NodeId + check.Uint64Greater(0); // DataSize + check.Uint64(4); // RowCount + check.Uint64(0); // IndexSize + } + Y_UNIT_TEST(Describe) { TTestEnv env; CreateRootTable(env); @@ -2419,7 +2565,7 @@ WITH ( const auto& columns = table.GetTableColumns(); const auto& keyColumns = table.GetPrimaryKeyColumns(); - UNIT_ASSERT_VALUES_EQUAL(columns.size(), 27); + UNIT_ASSERT_VALUES_EQUAL(columns.size(), 30); UNIT_ASSERT_STRINGS_EQUAL(columns[0].Name, "OwnerId"); UNIT_ASSERT_STRINGS_EQUAL(FormatType(columns[0].Type), "Uint64?"); @@ -2580,8 +2726,7 @@ WITH ( UNIT_ASSERT_VALUES_EQUAL(entry.Type, ESchemeEntryType::Directory); auto children = result.GetChildren(); - - UNIT_ASSERT_VALUES_EQUAL(children.size(), 31); + UNIT_ASSERT_VALUES_EQUAL(children.size(), 33); THashSet names; for (const auto& child : children) { @@ -2600,7 +2745,7 @@ WITH ( auto children = result.GetChildren(); - UNIT_ASSERT_VALUES_EQUAL(children.size(), 25); + UNIT_ASSERT_VALUES_EQUAL(children.size(), 27); THashSet names; for (const auto& child : children) { diff --git a/ydb/core/tx/schemeshard/schemeshard__init.cpp b/ydb/core/tx/schemeshard/schemeshard__init.cpp index 5370236d7a27..852e72db95c7 100644 --- a/ydb/core/tx/schemeshard/schemeshard__init.cpp +++ b/ydb/core/tx/schemeshard/schemeshard__init.cpp @@ -2395,6 +2395,10 @@ struct TSchemeShard::TTxInit : public TTransactionBase { stats.FullCompactionTs = rowSet.GetValueOrDefault(); stats.MemDataSize = rowSet.GetValueOrDefault(); + stats.LocksAcquired = rowSet.GetValueOrDefault(); + stats.LocksWholeShard = rowSet.GetValueOrDefault(); + stats.LocksBroken = rowSet.GetValueOrDefault(); + tableInfo->UpdateShardStats(shardIdx, stats); // note that we don't update shard metrics here, because we will always update diff --git a/ydb/core/tx/schemeshard/schemeshard__table_stats.cpp b/ydb/core/tx/schemeshard/schemeshard__table_stats.cpp index 3c665d18edc5..71d616f75bfa 100644 --- a/ydb/core/tx/schemeshard/schemeshard__table_stats.cpp +++ b/ydb/core/tx/schemeshard/schemeshard__table_stats.cpp @@ -84,6 +84,9 @@ auto TSchemeShard::BuildStatsForCollector(TPathId pathId, TShardIdx shardIdx, TT sysStats.SetPlannedTxCompleted(stats.PlannedTxCompleted); sysStats.SetTxRejectedByOverload(stats.TxRejectedByOverload); sysStats.SetTxRejectedBySpace(stats.TxRejectedBySpace); + sysStats.SetLocksAcquired(stats.LocksAcquired); + sysStats.SetLocksWholeShard(stats.LocksWholeShard); + sysStats.SetLocksBroken(stats.LocksBroken); if (nodeId) { sysStats.SetNodeId(*nodeId); @@ -199,6 +202,10 @@ TPartitionStats TTxStoreTableStats::PrepareStats(const TActorContext& ctx, newStats.RangeReads = tableStats.GetRangeReads(); newStats.RangeReadRows = tableStats.GetRangeReadRows(); + newStats.LocksAcquired = tableStats.GetLocksAcquired(); + newStats.LocksWholeShard = tableStats.GetLocksWholeShard(); + newStats.LocksBroken = tableStats.GetLocksBroken(); + TInstant now = AppData(ctx)->TimeProvider->Now(); newStats.SetCurrentRawCpuUsage(tabletMetrics.GetCPU(), now); newStats.Memory = tabletMetrics.GetMemory(); diff --git a/ydb/core/tx/schemeshard/schemeshard_impl.cpp b/ydb/core/tx/schemeshard/schemeshard_impl.cpp index bcfa20d6a5a2..394e0e1f207c 100644 --- a/ydb/core/tx/schemeshard/schemeshard_impl.cpp +++ b/ydb/core/tx/schemeshard/schemeshard_impl.cpp @@ -2601,7 +2601,11 @@ void TSchemeShard::PersistTablePartitionStats(NIceDb::TNiceDb& db, const TPathId NIceDb::TUpdate(stats.SearchHeight), NIceDb::TUpdate(stats.FullCompactionTs), - NIceDb::TUpdate(stats.MemDataSize) + NIceDb::TUpdate(stats.MemDataSize), + + NIceDb::TUpdate(stats.LocksAcquired), + NIceDb::TUpdate(stats.LocksWholeShard), + NIceDb::TUpdate(stats.LocksBroken) ); if (!stats.StoragePoolsStats.empty()) { diff --git a/ydb/core/tx/schemeshard/schemeshard_info_types.cpp b/ydb/core/tx/schemeshard/schemeshard_info_types.cpp index 22170422176a..41cefe686171 100644 --- a/ydb/core/tx/schemeshard/schemeshard_info_types.cpp +++ b/ydb/core/tx/schemeshard/schemeshard_info_types.cpp @@ -1681,6 +1681,10 @@ void TTableInfo::SetPartitioning(TVector&& newPartitioning) { newAggregatedStats.RangeReads = Stats.Aggregated.RangeReads; newAggregatedStats.RangeReadRows = Stats.Aggregated.RangeReadRows; + newAggregatedStats.LocksAcquired = Stats.Aggregated.LocksAcquired; + newAggregatedStats.LocksWholeShard = Stats.Aggregated.LocksWholeShard; + newAggregatedStats.LocksBroken = Stats.Aggregated.LocksBroken; + if (SplitOpsInFlight.empty()) { ExpectedPartitionCount = newPartitioning.size(); } @@ -1785,6 +1789,10 @@ void TTableAggregatedStats::UpdateShardStats(TShardIdx datashardIdx, const TPart Aggregated.ReadIops += (newStats.ReadIops - oldStats.ReadIops); Aggregated.WriteIops += (newStats.WriteIops - oldStats.WriteIops); + Aggregated.LocksAcquired += (newStats.LocksAcquired - oldStats.LocksAcquired); + Aggregated.LocksWholeShard += (newStats.LocksWholeShard - oldStats.LocksWholeShard); + Aggregated.LocksBroken += (newStats.LocksBroken - oldStats.LocksBroken); + auto topUsage = oldStats.TopUsage.Update(newStats.TopUsage); oldStats = newStats; oldStats.TopUsage = std::move(topUsage); diff --git a/ydb/core/tx/schemeshard/schemeshard_info_types.h b/ydb/core/tx/schemeshard/schemeshard_info_types.h index c29ea5711e3b..c9f7d53bdbc4 100644 --- a/ydb/core/tx/schemeshard/schemeshard_info_types.h +++ b/ydb/core/tx/schemeshard/schemeshard_info_types.h @@ -272,6 +272,10 @@ struct TPartitionStats { ui64 MemDataSize = 0; ui32 ShardState = NKikimrTxDataShard::Unknown; + ui64 LocksAcquired = 0; + ui64 LocksWholeShard = 0; + ui64 LocksBroken = 0; + // True when PartOwners has parts from other tablets bool HasBorrowedData = false; diff --git a/ydb/core/tx/schemeshard/schemeshard_path_describer.cpp b/ydb/core/tx/schemeshard/schemeshard_path_describer.cpp index 242106546be2..c8f736880fbc 100644 --- a/ydb/core/tx/schemeshard/schemeshard_path_describer.cpp +++ b/ydb/core/tx/schemeshard/schemeshard_path_describer.cpp @@ -42,6 +42,10 @@ static void FillTableStats(NKikimrTableStats::TTableStats* stats, const TPartiti stats->SetRangeReads(tableStats.RangeReads); stats->SetRangeReadRows(tableStats.RangeReadRows); + stats->SetLocksAcquired(tableStats.LocksAcquired); + stats->SetLocksWholeShard(tableStats.LocksWholeShard); + stats->SetLocksBroken(tableStats.LocksBroken); + stats->SetPartCount(tableStats.PartCount); stats->SetHasSchemaChanges(tableStats.HasSchemaChanges); diff --git a/ydb/core/tx/schemeshard/schemeshard_schema.h b/ydb/core/tx/schemeshard/schemeshard_schema.h index 06ea54fa3caa..034d1243be8c 100644 --- a/ydb/core/tx/schemeshard/schemeshard_schema.h +++ b/ydb/core/tx/schemeshard/schemeshard_schema.h @@ -391,6 +391,10 @@ struct Schema : NIceDb::Schema { struct ByKeyFilterSize : Column<34, NScheme::NTypeIds::Uint64> {}; + struct LocksAcquired : Column<35, NScheme::NTypeIds::Uint64> {}; + struct LocksWholeShard : Column<36, NScheme::NTypeIds::Uint64> {}; + struct LocksBroken : Column<37, NScheme::NTypeIds::Uint64> {}; + using TKey = TableKey; using TColumns = TableColumns< TableOwnerId, @@ -426,7 +430,10 @@ struct Schema : NIceDb::Schema { FullCompactionTs, MemDataSize, StoragePoolsStats, - ByKeyFilterSize + ByKeyFilterSize, + LocksAcquired, + LocksWholeShard, + LocksBroken >; }; diff --git a/ydb/tests/functional/scheme_tests/canondata/tablet_scheme_tests.TestTabletSchemes.test_tablet_schemes_flat_schemeshard_/flat_schemeshard.schema b/ydb/tests/functional/scheme_tests/canondata/tablet_scheme_tests.TestTabletSchemes.test_tablet_schemes_flat_schemeshard_/flat_schemeshard.schema index dc0018f5edb8..3aaca60e8ed8 100644 --- a/ydb/tests/functional/scheme_tests/canondata/tablet_scheme_tests.TestTabletSchemes.test_tablet_schemes_flat_schemeshard_/flat_schemeshard.schema +++ b/ydb/tests/functional/scheme_tests/canondata/tablet_scheme_tests.TestTabletSchemes.test_tablet_schemes_flat_schemeshard_/flat_schemeshard.schema @@ -7358,6 +7358,21 @@ "ColumnId": 34, "ColumnName": "ByKeyFilterSize", "ColumnType": "Uint64" + }, + { + "ColumnId": 35, + "ColumnName": "LocksAcquired", + "ColumnType": "Uint64" + }, + { + "ColumnId": 36, + "ColumnName": "LocksWholeShard", + "ColumnType": "Uint64" + }, + { + "ColumnId": 37, + "ColumnName": "LocksBroken", + "ColumnType": "Uint64" } ], "ColumnsDropped": [], @@ -7397,7 +7412,10 @@ 31, 32, 33, - 34 + 34, + 35, + 36, + 37 ], "RoomID": 0, "Codec": 0,