From be9aaff3d88c50a97d1d4d3c08d18d5f4f4ef858 Mon Sep 17 00:00:00 2001 From: Hor911 Date: Fri, 13 Sep 2024 14:15:43 +0300 Subject: [PATCH] Use better palette by default + ability to override colors with CSS vars (#9202) --- ydb/public/lib/ydb_cli/common/plan2svg.cpp | 518 +++++++++++---------- ydb/public/lib/ydb_cli/common/plan2svg.h | 8 +- 2 files changed, 285 insertions(+), 241 deletions(-) diff --git a/ydb/public/lib/ydb_cli/common/plan2svg.cpp b/ydb/public/lib/ydb_cli/common/plan2svg.cpp index 17ce29ab45bb..9ae2591b4aad 100644 --- a/ydb/public/lib/ydb_cli/common/plan2svg.cpp +++ b/ydb/public/lib/ydb_cli/common/plan2svg.cpp @@ -13,6 +13,145 @@ constexpr ui32 INTERNAL_WIDTH = 16; constexpr ui32 INTERNAL_TEXT_HEIGHT = 8; constexpr ui32 TIME_SERIES_RANGES = 32; +TString FormatDurationMs(ui64 durationMs) { + TStringBuilder builder; + + if (durationMs && durationMs < 100) { + builder << durationMs << "ms"; + } else { + auto seconds = durationMs / 1'000; + if (seconds >= 60) { + auto minutes = seconds / 60; + if (minutes >= 60) { + auto hours = minutes / 60; + builder << hours << 'h'; + if (hours < 24) { + auto minutes60 = minutes % 60; + builder << ' '; + if (minutes60 < 10) { + builder << '0'; + } + builder << minutes60 << 'm'; + } + } else { + auto seconds60 = seconds % 60; + builder << minutes << "m "; + if (seconds60 < 10) { + builder << '0'; + } + builder << seconds60 << 's'; + } + } else { + auto hundredths = (durationMs % 1'000) / 10; + builder << seconds << '.'; + if (hundredths < 10) { + builder << '0'; + } + builder << hundredths << 's'; + } + } + + return builder; +} + +TString FormatDurationUs(ui64 durationUs) { + if (durationUs && durationUs < 1000) { + return TStringBuilder() << durationUs << "us"; + } + + return FormatDurationMs(durationUs / 1000); +} + +TString FormatUsage(ui64 usec) { + return FormatDurationUs(usec); +} + +TString FormatIntegerValue(ui64 i, ui32 scale = 1000, const TString& suffix = "") { + if (i < scale) { + return Sprintf("%lu%s", i, suffix.c_str()); + } + for (auto c : "KMGTP") { + auto pcs = (i % scale) * 100 / scale; + i /= scale; + if (i < scale || c == 'P') { + return Sprintf("%lu.%.2lu%c%s", i, pcs, c, suffix.c_str()); + } + } + return ""; +} + +TString FormatBytes(ui64 bytes) { + return FormatIntegerValue(bytes, 1024, "B"); +} + +TString FormatTimeMs(ui64 time, bool shortFormat) { + if (shortFormat) { + time /= 10; + return Sprintf("%lu.%.2lu", time / 100, time % 100); + } else { + time /= 1000; + return Sprintf("%lu:%.2lu", time / 60, time % 60); + } +} + +TString FormatTimeMs(ui64 time) { + return FormatTimeMs(time, time < 60000); +} + +TString FormatTimeAgg(const TAggregation& agg, bool shortFormat) { + TStringBuilder result; + result << FormatTimeMs(agg.Min, shortFormat) << " | " << FormatTimeMs(agg.Avg, shortFormat) << " | " << FormatTimeMs(agg.Max, shortFormat); + return result; +} + +TString FormatMCpu(ui64 mCpu) { + mCpu /= 10; + return Sprintf("%lu.%.2lu", mCpu / 100, mCpu % 100); +} + +TString GetEstimation(const NJson::TJsonValue& node) { + TStringBuilder ebuilder; + auto* eCostNode = node.GetValueByPath("E-SelfCost"); + if (!eCostNode) { + eCostNode = node.GetValueByPath("E-Cost"); + } + if (eCostNode) { + auto costString = eCostNode->GetStringSafe(); + if (costString != "No estimate") { + ebuilder << "Est:"; + double cost; + if (TryFromString(costString, cost)) { + if (cost >= 1e+18) { + ebuilder << Sprintf(" %.2e", cost); + } else { + ebuilder << ' ' << FormatIntegerValue(static_cast(cost)); + } + } + if (auto* eRowsNode = node.GetValueByPath("E-Rows")) { + double rows; + if (TryFromString(eRowsNode->GetStringSafe(), rows)) { + if (rows >= 1e+18) { + ebuilder << Sprintf(" Rows: %.2e", rows); + } else { + ebuilder << " Rows: " << FormatIntegerValue(static_cast(rows)); + } + } + } + if (auto* eSizeNode = node.GetValueByPath("E-Size")) { + double size; + if (TryFromString(eSizeNode->GetStringSafe(), size)) { + if (size >= 1e+18) { + ebuilder << Sprintf(" Size: %.2e", size); + } else { + ebuilder << " Size: " << FormatBytes(static_cast(size)); + } + } + } + } + } + return ebuilder; +} + bool TAggregation::Load(const NJson::TJsonValue& node) { if (auto* countNode = node.GetValueByPath("Count")) { Count = countNode->GetIntegerSafe(); @@ -181,7 +320,7 @@ void TPlan::Load(const NJson::TJsonValue& node) { ythrow yexception() << "Unexpected plan node type [" << planNodeType << "]"; } else { Stages.push_back(std::make_shared(nodeType)); - LoadStage(Stages.back(), plan); + LoadStage(Stages.back(), plan, 0); } } } @@ -223,10 +362,34 @@ void TPlan::ResolveCteRefs() { } } } + if (cteRef.second->FromStage->StatsNode) { + if (auto* outputNode = cteRef.second->FromStage->StatsNode->GetValueByPath("Output")) { + for (const auto& subNode : outputNode->GetArray()) { + if (auto* nameNode = subNode.GetValueByPath("Name")) { + if (ToString(cteRef.second->StagePlanNodeId) == nameNode->GetStringSafe()) { + if (auto* popNode = subNode.GetValueByPath("Pop")) { + if (auto* bytesNode = popNode->GetValueByPath("Bytes")) { + cteRef.second->CteOutputBytes = std::make_shared(OutputBytes, + *bytesNode, + popNode->GetValueByPath("FirstMessageMs"), + popNode->GetValueByPath("LastMessageMs"), + popNode->GetValueByPath("WaitTimeUs.History") + ); + MaxTime = std::max(MaxTime, cteRef.second->CteOutputBytes->MaxTime); + } + if (auto* rowsNode = popNode->GetValueByPath("Rows")) { + cteRef.second->CteOutputRows = std::make_shared(OutputRows, *rowsNode); + } + } + } + } + } + } + } } } -void TPlan::LoadStage(std::shared_ptr stage, const NJson::TJsonValue& node) { +void TPlan::LoadStage(std::shared_ptr stage, const NJson::TJsonValue& node, ui32 parentPlanNodeId) { if (auto* planNodeIdNode = node.GetValueByPath("PlanNodeId")) { stage->PlanNodeId = planNodeIdNode->GetIntegerSafe(); @@ -284,21 +447,10 @@ void TPlan::LoadStage(std::shared_ptr stage, const NJson::TJsonValue& no } } stage->Info.push_back(builder); - { - TStringBuilder ebuilder; - if (auto* eCostNode = subNode.GetValueByPath("E-Cost")) { - ebuilder << "E-Cost: " << eCostNode->GetStringSafe() << " "; - } - if (auto* eRowsNode = subNode.GetValueByPath("E-Rows")) { - ebuilder << "E-Rows: " << eRowsNode->GetStringSafe() << " "; - } - if (auto* eSizeNode = subNode.GetValueByPath("E-Size")) { - ebuilder << "E-Size: " << eSizeNode->GetStringSafe(); - } - TString estring = ebuilder; - if (estring) { - stage->Info.push_back(estring); - } + + auto est = GetEstimation(subNode); + if (est) { + stage->Info.push_back(est); } } } @@ -306,7 +458,7 @@ void TPlan::LoadStage(std::shared_ptr stage, const NJson::TJsonValue& no stage->StatsNode = node.GetValueByPath("Stats"); - const NJson::TJsonValue* inputNode; + const NJson::TJsonValue* inputNode = nullptr; if (stage->StatsNode) { @@ -394,18 +546,24 @@ void TPlan::LoadStage(std::shared_ptr stage, const NJson::TJsonValue& no } if (auto* outputNode = stage->StatsNode->GetValueByPath("Output")) { - if (auto* popNode = (*outputNode)[0].GetValueByPath("Pop")) { - if (auto* bytesNode = popNode->GetValueByPath("Bytes")) { - stage->OutputBytes = std::make_shared(OutputBytes, - *bytesNode, - popNode->GetValueByPath("FirstMessageMs"), - popNode->GetValueByPath("LastMessageMs"), - popNode->GetValueByPath("WaitTimeUs.History") - ); - MaxTime = std::max(MaxTime, stage->OutputBytes->MaxTime); - } - if (auto* rowsNode = popNode->GetValueByPath("Rows")) { - stage->OutputRows = std::make_shared(OutputRows, *rowsNode); + for (const auto& subNode : outputNode->GetArray()) { + if (auto* nameNode = subNode.GetValueByPath("Name")) { + if (ToString(parentPlanNodeId) == nameNode->GetStringSafe()) { + if (auto* popNode = subNode.GetValueByPath("Pop")) { + if (auto* bytesNode = popNode->GetValueByPath("Bytes")) { + stage->OutputBytes = std::make_shared(OutputBytes, + *bytesNode, + popNode->GetValueByPath("FirstMessageMs"), + popNode->GetValueByPath("LastMessageMs"), + popNode->GetValueByPath("WaitTimeUs.History") + ); + MaxTime = std::max(MaxTime, stage->OutputBytes->MaxTime); + } + if (auto* rowsNode = popNode->GetValueByPath("Rows")) { + stage->OutputRows = std::make_shared(OutputRows, *rowsNode); + } + } + } } } } @@ -437,7 +595,7 @@ void TPlan::LoadStage(std::shared_ptr stage, const NJson::TJsonValue& no ythrow yexception() << "Unexpected plan node type [" << planNodeType << "]"; } } - auto connection = std::make_shared(subNodeType); + auto connection = std::make_shared(subNodeType, stage->PlanNodeId); stage->Connections.push_back(connection); if (keyColumnsNode) { for (auto& keyColumn : keyColumnsNode->GetArray()) { @@ -473,11 +631,11 @@ void TPlan::LoadStage(std::shared_ptr stage, const NJson::TJsonValue& no Stages.push_back(std::make_shared(nodeType)); connection->FromStage = Stages.back(); - LoadStage(Stages.back(), plan); + LoadStage(Stages.back(), plan, stage->PlanNodeId); } } else if (auto* cteNameNode = plan.GetValueByPath("CTE Name")) { auto cteName = "CTE " + cteNameNode->GetStringSafe(); - auto connection = std::make_shared(subNodeType); + auto connection = std::make_shared(subNodeType, stage->PlanNodeId); connection->CteConnection = true; stage->Connections.push_back(connection); if (keyColumnsNode) { @@ -519,10 +677,10 @@ void TPlan::LoadStage(std::shared_ptr stage, const NJson::TJsonValue& no } } else { - stage->Connections.push_back(std::make_shared("Implicit")); + stage->Connections.push_back(std::make_shared("Implicit", stage->PlanNodeId)); Stages.push_back(std::make_shared(subNodeType)); stage->Connections.back()->FromStage = Stages.back(); - LoadStage(Stages.back(), plan); + LoadStage(Stages.back(), plan, stage->PlanNodeId); } } else { ythrow yexception() << "Unexpected plan node type [" << planNodeType << "]"; @@ -558,21 +716,10 @@ void TPlan::LoadSource(std::shared_ptr source, const NJson::TJsonValue& } builder << ")"; source->Info.push_back(builder); - { - TStringBuilder ebuilder; - if (auto* eCostNode = subNode.GetValueByPath("E-Cost")) { - ebuilder << "E-Cost: " << eCostNode->GetStringSafe() << " "; - } - if (auto* eRowsNode = subNode.GetValueByPath("E-Rows")) { - ebuilder << "E-Rows: " << eRowsNode->GetStringSafe() << " "; - } - if (auto* eSizeNode = subNode.GetValueByPath("E-Size")) { - ebuilder << "E-Size: " << eSizeNode->GetStringSafe(); - } - TString estring = ebuilder; - if (estring) { - source->Info.push_back(estring); - } + + auto est = GetEstimation(subNode); + if (est) { + source->Info.push_back(est); } break; } @@ -598,10 +745,9 @@ void TPlan::MarkStageIndent(ui32 indent, ui32& offsetY, std::shared_ptr if (c->CteConnection) { c->CteIndentX = indent; c->CteOffsetY = offsetY; - offsetY += INTERNAL_HEIGHT + INTERNAL_GAP_Y * 2; + offsetY += GAP_Y + INTERNAL_HEIGHT + INTERNAL_GAP_Y * 2; stage->IndentY = std::max(stage->IndentY, offsetY); - stage->CteHeight += INTERNAL_HEIGHT + INTERNAL_GAP_Y * 2; - offsetY += GAP_Y; + stage->CteHeight += GAP_Y + INTERNAL_HEIGHT + INTERNAL_GAP_Y * 2; } else { MarkStageIndent(indent, offsetY, c->FromStage); stage->IndentY = std::max(stage->IndentY, c->FromStage->IndentY); @@ -614,134 +760,6 @@ void TPlan::MarkLayout() { MarkStageIndent(0, offsetY, Stages.front()); } -TString FormatDurationMs(ui64 durationMs) { - TStringBuilder builder; - - if (durationMs && durationMs < 100) { - builder << durationMs << "ms"; - } else { - auto seconds = durationMs / 1'000; - if (seconds >= 60) { - auto minutes = seconds / 60; - if (minutes >= 60) { - auto hours = minutes / 60; - builder << hours << 'h'; - if (hours < 24) { - auto minutes60 = minutes % 60; - builder << ' '; - if (minutes60 < 10) { - builder << '0'; - } - builder << minutes60 << 'm'; - } - } else { - auto seconds60 = seconds % 60; - builder << minutes << "m "; - if (seconds60 < 10) { - builder << '0'; - } - builder << seconds60 << 's'; - } - } else { - auto hundredths = (durationMs % 1'000) / 10; - builder << seconds << '.'; - if (hundredths < 10) { - builder << '0'; - } - builder << hundredths << 's'; - } - } - - return builder; -} - -TString FormatDurationUs(ui64 durationUs) { - if (durationUs && durationUs < 1000) { - return TStringBuilder() << durationUs << "us"; - } - - return FormatDurationMs(durationUs / 1000); -} - -TString FormatUsage(ui64 usec) { - return FormatDurationUs(usec); -} - -TString FormatBytes(ui64 bytes) { - ui64 pcs = 0; - if (bytes < 1024) { - return Sprintf("%luB", bytes); - } - pcs = (bytes % 1024) * 100 / 1024; - bytes /= 1024; - if (bytes < 1024) { - return Sprintf("%lu.%.2luKB", bytes, pcs); - } - pcs = (bytes % 1024) * 100 / 1024; - bytes /= 1024; - if (bytes < 1024) { - return Sprintf("%lu.%.2luMB", bytes, pcs); - } - pcs = (bytes % 1024) * 100 / 1024; - bytes /= 1024; - if (bytes < 1024) { - return Sprintf("%lu.%.2luGB", bytes, pcs); - } - pcs = (bytes % 1024) * 100 / 1024; - bytes /= 1024; - return Sprintf("%lu.%.2luTB", bytes, pcs); -} - -TString FormatRows(ui64 rows) { - ui64 pcs = 0; - if (rows < 1024) { - return Sprintf("%lu", rows); - } - pcs = (rows % 1024) * 100 / 1024; - rows /= 1024; - if (rows < 1024) { - return Sprintf("%lu.%.2luK", rows, pcs); - } - pcs = (rows % 1024) * 100 / 1024; - rows /= 1024; - if (rows < 1024) { - return Sprintf("%lu.%.2luM", rows, pcs); - } - pcs = (rows % 1024) * 100 / 1024; - rows /= 1024; - if (rows < 1024) { - return Sprintf("%lu.%.2luG", rows, pcs); - } - pcs = (rows % 1024) * 100 / 1024; - rows /= 1024; - return Sprintf("%lu.%.2luT", rows, pcs); -} - -TString FormatTimeMs(ui64 time, bool shortFormat) { - if (shortFormat) { - time /= 10; - return Sprintf("%lu.%.2lu", time / 100, time % 100); - } else { - time /= 1000; - return Sprintf("%lu:%.2lu", time / 60, time % 60); - } -} - -TString FormatTimeMs(ui64 time) { - return FormatTimeMs(time, time < 60000); -} - -TString FormatTimeAgg(const TAggregation& agg, bool shortFormat) { - TStringBuilder result; - result << FormatTimeMs(agg.Min, shortFormat) << " | " << FormatTimeMs(agg.Avg, shortFormat) << " | " << FormatTimeMs(agg.Max, shortFormat); - return result; -} - -TString FormatMCpu(ui64 mCpu) { - mCpu /= 10; - return Sprintf("%lu.%.2lu", mCpu / 100, mCpu % 100); -} - void TPlan::PrintTimeline(TStringBuilder& background, TStringBuilder& canvas, const TString& title, TAggregation& firstMessage, TAggregation& lastMessage, ui32 x, ui32 y, ui32 w, ui32 h, const TString& color) { auto firstMin = firstMessage.Min * w / MaxTime; @@ -902,7 +920,7 @@ void TPlan::PrintSvg(ui64 maxTime, ui32& offsetY, TStringBuilder& background, TS for (auto& s : Stages) { background << "" << Endl; auto x = Config.HeaderWidth + GAP_X; background @@ -969,8 +987,8 @@ void TPlan::PrintSvg(ui64 maxTime, ui32& offsetY, TStringBuilder& background, TS if (s->OutputRows && s->OutputRows->Details.Sum) { tooltip << ", Rows \u2211" - << FormatRows(s->OutputRows->Details.Sum) << ", " << FormatRows(s->OutputRows->Details.Min) << " | " - << FormatRows(s->OutputRows->Details.Avg) << " | " << FormatRows(s->OutputRows->Details.Max) + << FormatIntegerValue(s->OutputRows->Details.Sum) << ", " << FormatIntegerValue(s->OutputRows->Details.Min) << " | " + << FormatIntegerValue(s->OutputRows->Details.Avg) << " | " << FormatIntegerValue(s->OutputRows->Details.Max) << ", Width " << FormatBytes(s->OutputBytes->Details.Sum / s->OutputRows->Details.Sum); } PrintStageSummary(background, canvas, y0, s->OutputBytes, Config.Palette.OutputMedium, Config.Palette.OutputLight, textSum, tooltip); @@ -994,7 +1012,7 @@ void TPlan::PrintSvg(ui64 maxTime, ui32& offsetY, TStringBuilder& background, TS if (s->OutputBytes->Details.Count != taskCount) { canvas - << "" << s->OutputBytes->Details.Count << "" << Endl; } @@ -1004,7 +1022,7 @@ void TPlan::PrintSvg(ui64 maxTime, ui32& offsetY, TStringBuilder& background, TS if (d) { title << " " << FormatBytes(s->OutputBytes->Details.Sum * 1000 / d) << "/s"; if (s->OutputRows) { - title << ", Rows " << FormatRows(s->OutputRows->Details.Sum * 1000 / d) << "/s"; + title << ", Rows " << FormatIntegerValue(s->OutputRows->Details.Sum * 1000 / d) << "/s"; } } PrintTimeline(background, canvas, title, s->OutputBytes->FirstMessage, s->OutputBytes->LastMessage, px, y0, pw, INTERNAL_HEIGHT, Config.Palette.OutputMedium); @@ -1050,7 +1068,7 @@ void TPlan::PrintSvg(ui64 maxTime, ui32& offsetY, TStringBuilder& background, TS if (s->MaxMemoryUsage->Details.Count != taskCount) { canvas - << "" << s->MaxMemoryUsage->Details.Count << "" << Endl; } @@ -1087,7 +1105,7 @@ void TPlan::PrintSvg(ui64 maxTime, ui32& offsetY, TStringBuilder& background, TS } if (s->SpillingComputeTime && !s->SpillingComputeTime->History.Deriv.empty()) { - PrintDeriv(canvas, s->SpillingComputeTime->History, px, y0, pw, INTERNAL_HEIGHT, "Spilling Compute", Config.Palette.SpillingTimeDark); + PrintDeriv(canvas, s->SpillingComputeTime->History, px, y0, pw, INTERNAL_HEIGHT, "Spilling Compute", Config.Palette.SpillingTimeMedium); } } @@ -1102,7 +1120,7 @@ void TPlan::PrintSvg(ui64 maxTime, ui32& offsetY, TStringBuilder& background, TS auto xx = x; background << "" << Endl; xx = Config.HeaderWidth + GAP_X; background @@ -1129,31 +1147,52 @@ void TPlan::PrintSvg(ui64 maxTime, ui32& offsetY, TStringBuilder& background, TS << "" << s << "" << Endl; - if (c->FromStage->OutputBytes) { - auto d = c->FromStage->OutputBytes->MaxTime - c->FromStage->OutputBytes->MinTime; + if (c->CteOutputBytes) { + auto textSum = FormatBytes(c->CteOutputBytes->Details.Sum); + TStringBuilder tooltip; + tooltip + << "Output " + << c->CteOutputBytes->Details.Sum * 100 / c->CteOutputBytes->Summary->Value << "%, \u2211" + << textSum << ", " << FormatBytes(c->CteOutputBytes->Details.Min) << " | " + << FormatBytes(c->CteOutputBytes->Details.Avg) << " | " << FormatBytes(c->CteOutputBytes->Details.Max); + if (c->CteOutputRows && c->CteOutputRows->Details.Sum) { + tooltip + << ", Rows \u2211" + << FormatIntegerValue(c->CteOutputRows->Details.Sum) << ", " << FormatIntegerValue(c->CteOutputRows->Details.Min) << " | " + << FormatIntegerValue(c->CteOutputRows->Details.Avg) << " | " << FormatIntegerValue(c->CteOutputRows->Details.Max) + << ", Width " << FormatBytes(c->CteOutputBytes->Details.Sum / c->CteOutputRows->Details.Sum); + } + PrintStageSummary(background, canvas, y + INTERNAL_GAP_Y, c->CteOutputBytes, Config.Palette.OutputMedium, Config.Palette.OutputLight, textSum, tooltip); + + canvas + << "" << c->CteOutputBytes->Details.Count << "" << Endl; + + auto d = c->CteOutputBytes->MaxTime - c->CteOutputBytes->MinTime; TStringBuilder title; title << "Output"; if (d) { - title << " " << FormatBytes(c->FromStage->OutputBytes->Details.Sum * 1000 / d) << "/s"; - if (c->FromStage->OutputRows) { - title << ", Rows " << FormatRows(c->FromStage->OutputRows->Details.Sum * 1000 / d) << "/s"; + title << " " << FormatBytes(c->CteOutputBytes->Details.Sum * 1000 / d) << "/s"; + if (c->CteOutputRows) { + title << ", Rows " << FormatIntegerValue(c->CteOutputRows->Details.Sum * 1000 / d) << "/s"; } } - PrintTimeline(background, canvas, title, c->FromStage->OutputBytes->FirstMessage, c->FromStage->OutputBytes->LastMessage, px, y + INTERNAL_GAP_Y, pw, INTERNAL_HEIGHT, Config.Palette.OutputMedium); + PrintTimeline(background, canvas, title, c->CteOutputBytes->FirstMessage, c->CteOutputBytes->LastMessage, px, y + INTERNAL_GAP_Y, pw, INTERNAL_HEIGHT, Config.Palette.OutputMedium); - if (!c->FromStage->OutputBytes->WaitTime.Deriv.empty()) { - PrintWaitTime(background, c->FromStage->OutputBytes, px, y + INTERNAL_GAP_Y, pw, INTERNAL_HEIGHT, Config.Palette.OutputLight); + if (!c->CteOutputBytes->WaitTime.Deriv.empty()) { + PrintWaitTime(background, c->CteOutputBytes, px, y + INTERNAL_GAP_Y, pw, INTERNAL_HEIGHT, Config.Palette.OutputLight); } - if (!c->FromStage->OutputBytes->History.Deriv.empty()) { - PrintDeriv(canvas, c->FromStage->OutputBytes->History, px, y + INTERNAL_GAP_Y, pw, INTERNAL_HEIGHT, "", Config.Palette.OutputDark); + if (!c->CteOutputBytes->History.Deriv.empty()) { + PrintDeriv(canvas, c->CteOutputBytes->History, px, y + INTERNAL_GAP_Y, pw, INTERNAL_HEIGHT, "", Config.Palette.OutputDark); } } } TString mark; - if (c->NodeType == "HashShuffle") mark = "\u2B82"; - else if (c->NodeType == "Merge") mark = "M"; + if (c->NodeType == "HashShuffle") mark = "H"; + else if (c->NodeType == "Merge") mark = "Me"; + else if (c->NodeType == "Map") mark = "Ma"; else if (c->NodeType == "UnionAll") mark = "U"; else if (c->NodeType == "Broadcast") mark = "B"; else mark = "?"; @@ -1203,15 +1242,15 @@ void TPlan::PrintSvg(ui64 maxTime, ui32& offsetY, TStringBuilder& background, TS if (c->InputRows && c->InputRows->Details.Sum) { tooltip << ", Rows \u2211" - << FormatRows(c->InputRows->Details.Sum) << ", " << FormatRows(c->InputRows->Details.Min) << " | " - << FormatRows(c->InputRows->Details.Avg) << " | " << FormatRows(c->InputRows->Details.Max) + << FormatIntegerValue(c->InputRows->Details.Sum) << ", " << FormatIntegerValue(c->InputRows->Details.Min) << " | " + << FormatIntegerValue(c->InputRows->Details.Avg) << " | " << FormatIntegerValue(c->InputRows->Details.Max) << ", Width " << FormatBytes(c->InputBytes->Details.Sum / c->InputRows->Details.Sum); } PrintStageSummary(background, canvas, y0, c->InputBytes, Config.Palette.InputMedium, Config.Palette.InputLight, textSum, tooltip); if (c->InputBytes->Details.Count != taskCount) { canvas - << "" << c->InputBytes->Details.Count << "" << Endl; } @@ -1221,7 +1260,7 @@ void TPlan::PrintSvg(ui64 maxTime, ui32& offsetY, TStringBuilder& background, TS if (d) { title << " " << FormatBytes(c->InputBytes->Details.Sum * 1000 / d) << "/s"; if (c->InputRows) { - title << ", Rows " << FormatRows(c->InputRows->Details.Sum * 1000 / d) << "/s"; + title << ", Rows " << FormatIntegerValue(c->InputRows->Details.Sum * 1000 / d) << "/s"; } } PrintTimeline(background, canvas, title, c->InputBytes->FirstMessage, c->InputBytes->LastMessage, px, y0, pw, INTERNAL_HEIGHT, Config.Palette.InputMedium); @@ -1249,15 +1288,15 @@ void TPlan::PrintSvg(ui64 maxTime, ui32& offsetY, TStringBuilder& background, TS if (s->Source->IngressRows && s->Source->IngressRows->Details.Sum) { tooltip << ", Rows \u2211" - << FormatRows(s->Source->IngressRows->Details.Sum) << ", " << FormatRows(s->Source->IngressRows->Details.Min) << " | " - << FormatRows(s->Source->IngressRows->Details.Avg) << " | " << FormatRows(s->Source->IngressRows->Details.Max) + << FormatIntegerValue(s->Source->IngressRows->Details.Sum) << ", " << FormatIntegerValue(s->Source->IngressRows->Details.Min) << " | " + << FormatIntegerValue(s->Source->IngressRows->Details.Avg) << " | " << FormatIntegerValue(s->Source->IngressRows->Details.Max) << ", Width " << FormatBytes(s->Source->IngressBytes->Details.Sum / s->Source->IngressRows->Details.Sum); } PrintStageSummary(background, canvas, y0, s->Source->IngressBytes, Config.Palette.IngressMedium, Config.Palette.IngressLight, textSum, tooltip); if (s->Source->IngressBytes->Details.Count != taskCount) { canvas - << "" << s->Source->IngressBytes->Details.Count << "" << Endl; } @@ -1267,7 +1306,7 @@ void TPlan::PrintSvg(ui64 maxTime, ui32& offsetY, TStringBuilder& background, TS if (d) { title << " " << FormatBytes(s->Source->IngressBytes->Details.Sum * 1000 / d) << "/s"; if (s->Source->IngressRows) { - title << ", Rows " << FormatRows(s->Source->IngressRows->Details.Sum / d) << "/s"; + title << ", Rows " << FormatIntegerValue(s->Source->IngressRows->Details.Sum / d) << "/s"; } } PrintTimeline(background, canvas, title, s->Source->IngressBytes->FirstMessage, s->Source->IngressBytes->LastMessage, px, y0, pw, INTERNAL_HEIGHT, Config.Palette.IngressMedium); @@ -1288,35 +1327,36 @@ void TPlan::PrintSvg(ui64 maxTime, ui32& offsetY, TStringBuilder& background, TS } TColorPalette::TColorPalette() { - StageDark = "#B0C4DE"; - StageLight = "#E6E6FA"; - StageText = "#000000"; - StageGrid = "#4682B4"; - IngressDark = "#556B2F"; - IngressMedium = "#6B8E23"; - IngressLight = "#9ACD32"; - InputDark = "#006400"; - InputMedium = "#2E8B57"; - InputLight = "#3CB371"; - OutputDark = "#0000FF"; - OutputMedium = "#1E90FF"; - OutputLight = "#00BFFF"; - MemMedium = "#483D8B"; - MemLight = "#6A5ACD"; - CpuMedium = "#FF0000"; - CpuLight = "#FF7777"; - ConnectionFill= "#FFD700"; - ConnectionLine= "#FF0000"; - ConnectionText= "#FF0000"; - MinMaxLine = "#FFFF00"; - TextLight = "#FFFFFF"; - TextSummary = "#000000"; - SpillingBytesDark = "#34495E"; - SpillingBytesMedium = "#5D6D7E"; - SpillingBytesLight = "#85929E"; - SpillingTimeDark = "#C0392B"; - SpillingTimeMedium = "#CD6155"; - SpillingTimeLight = "#D98880"; + StageDark = "var(--stage-dark, #F2F2F2)"; + StageLight = "var(--stage-dark, #D9D9D9"; + StageText = "var(--stage-text, #262626)"; + StageTextHighlight = "var(--stage-texthl, #EA0703)"; + StageGrid = "var(--stage-grid, #B2B2B2"; + IngressDark = "var(--ingress-dark, #574F38)"; + IngressMedium = "var(--ingress-medium, #82723C)"; + IngressLight = "var(--ingress-light, #C0A645)"; + InputDark = "var(--input-dark, #315B34)"; + InputMedium = "var(--input-medium, #379A33)"; + InputLight = "var(--input-light, #3AC936)"; + OutputDark = "var(--output-dark, #3F5799)"; + OutputMedium = "var(--output-medium, #4E79EB)"; + OutputLight = "var(--output-light, #86A8FF)"; + MemMedium = "var(--mem-medium, #543B70)"; + MemLight = "var(--mem-light, #854EBD)"; + CpuMedium = "var(--cpu-medium, #EA0703)"; + CpuLight = "var(--cpu-light, #FF6866)"; + ConnectionFill= "var(--conn-fill, #BFBFBF)"; + ConnectionLine= "var(--conn-line, #BFBFBF)"; + ConnectionText= "var(--conn-text, #393939)"; + MinMaxLine = "var(--minmax-line, #FFDB4D)"; + TextLight = "var(--text-light, #FFFFFF)"; + TextSummary = "var(--text-summary, #262626)"; + SpillingBytesDark = "var(--spill-dark, #406B61)"; + SpillingBytesMedium = "var(--spill-medium, #599587)"; + SpillingBytesLight = "var(--spill-light, #72C0AE)"; + SpillingTimeDark = "var(--spill-dark, #406B61)"; + SpillingTimeMedium = "var(--spill-medium, #599587)"; + SpillingTimeLight = "var(--spill-light, #72C0AE)"; } TPlanViewConfig::TPlanViewConfig() { @@ -1399,7 +1439,7 @@ TString TPlanVisualizer::PrintSvg() { canvas << "Ingress " - << FormatBytes(p.IngressBytes->Value) << ", Rows " << FormatRows(p.IngressRows->Value); + << FormatBytes(p.IngressBytes->Value) << ", Rows " << FormatIntegerValue(p.IngressRows->Value); if (p.IngressRows->Value) { canvas << ", Width " << p.IngressBytes->Value / p.IngressRows->Value << "B"; diff --git a/ydb/public/lib/ydb_cli/common/plan2svg.h b/ydb/public/lib/ydb_cli/common/plan2svg.h index 8f16058d6871..4575872ebd84 100644 --- a/ydb/public/lib/ydb_cli/common/plan2svg.h +++ b/ydb/public/lib/ydb_cli/common/plan2svg.h @@ -81,7 +81,7 @@ class TSingleMetric { class TConnection { public: - TConnection(const TString& nodeType) : NodeType(nodeType) { + TConnection(const TString& nodeType, ui32 stagePlanNodeId) : NodeType(nodeType), StagePlanNodeId(stagePlanNodeId) { } TString NodeType; @@ -92,7 +92,10 @@ class TConnection { bool CteConnection = false; ui32 CteIndentX = 0; ui32 CteOffsetY = 0; + std::shared_ptr<TSingleMetric> CteOutputBytes; + std::shared_ptr<TSingleMetric> CteOutputRows; const NJson::TJsonValue* StatsNode = nullptr; + const ui32 StagePlanNodeId; }; class TSource { @@ -143,6 +146,7 @@ struct TColorPalette { TString StageDark; TString StageLight; TString StageText; + TString StageTextHighlight; TString StageGrid; TString IngressDark; TString IngressMedium; @@ -199,7 +203,7 @@ class TPlan { } void Load(const NJson::TJsonValue& node); - void LoadStage(std::shared_ptr<TStage> stage, const NJson::TJsonValue& node); + void LoadStage(std::shared_ptr<TStage> stage, const NJson::TJsonValue& node, ui32 parentPlanNodeId); void LoadSource(std::shared_ptr<TSource> source, const NJson::TJsonValue& node); void MarkStageIndent(ui32 indentX, ui32& offsetY, std::shared_ptr<TStage> stage); void MarkLayout();