diff --git a/ydb/public/lib/ydb_cli/commands/benchmark_utils.cpp b/ydb/public/lib/ydb_cli/commands/benchmark_utils.cpp index add5cea55718..a6a24bf165fb 100644 --- a/ydb/public/lib/ydb_cli/commands/benchmark_utils.cpp +++ b/ydb/public/lib/ydb_cli/commands/benchmark_utils.cpp @@ -9,6 +9,7 @@ #include #include #include +#include #include #include @@ -160,9 +161,12 @@ class IQueryResultScanner { YDB_READONLY_DEF(TString, QueryPlan); YDB_READONLY_DEF(TString, PlanAst); YDB_ACCESSOR_DEF(TString, DeadlineName); - + TQueryBenchmarkResult::TRawResults RawResults; public: virtual ~IQueryResultScanner() = default; + TQueryBenchmarkResult::TRawResults&& ExtractRawResults() { + return std::move(RawResults); + } virtual void OnStart(const TVector& columns) = 0; virtual void OnBeforeRow() = 0; virtual void OnAfterRow() = 0; @@ -208,11 +212,11 @@ class IQueryResultScanner { } if (streamPart.HasResultSet()) { - auto result = streamPart.ExtractResultSet(); - auto columns = result.GetColumnsMeta(); + RawResults.emplace_back(streamPart.ExtractResultSet()); + auto columns = RawResults.back().GetColumnsMeta(); OnStart(columns); - NYdb::TResultSetParser parser(result); + NYdb::TResultSetParser parser(RawResults.back()); while (parser.TryNextRow()) { OnBeforeRow(); for (ui32 i = 0; i < columns.size(); ++i) { @@ -262,37 +266,6 @@ class TQueryResultScannerComposite: public IQueryResultScanner { } }; -class TYSONResultScanner: public IQueryResultScanner { -private: - TStringStream ResultString; - mutable std::unique_ptr Writer; -public: - TYSONResultScanner() { - } - TString GetResult() const { - Writer.reset(); - return ResultString.Str(); - } - virtual void OnStart(const TVector& /*columns*/) override { - Writer = std::make_unique(&ResultString, NYson::EYsonFormat::Text, ::NYson::EYsonType::Node, true); - Writer->OnBeginList(); - } - virtual void OnBeforeRow() override { - Writer->OnListItem(); - Writer->OnBeginList(); - } - virtual void OnAfterRow() override { - Writer->OnEndList(); - } - virtual void OnRowItem(const NYdb::TColumn& /*c*/, const NYdb::TValue& value) override { - Writer->OnListItem(); - FormatValueYson(value, *Writer); - } - virtual void OnFinish() override { - Writer->OnEndList(); - } -}; - class TCSVResultScanner: public IQueryResultScanner, public TQueryResultInfo { public: TCSVResultScanner() { @@ -328,26 +301,24 @@ TQueryBenchmarkResult ExecuteImpl(const TString& query, NTable::TTableClient& cl TStreamExecScanQuerySettings settings; settings.CollectQueryStats(ECollectQueryStatsMode::Full); settings.Explain(explainOnly); - if (auto error = SetTimeoutSettings(settings, deadline)) { + if (const auto error = SetTimeoutSettings(settings, deadline)) { return *error; } auto it = client.StreamExecuteScanQuery(query, settings).GetValueSync(); - if (auto error = ResultByStatus(it, deadline.Name)) { + if (const auto error = ResultByStatus(it, deadline.Name)) { return *error; } - std::shared_ptr scannerYson = std::make_shared(); std::shared_ptr scannerCSV = std::make_shared(); TQueryResultScannerComposite composite; composite.SetDeadlineName(deadline.Name); - composite.AddScanner(scannerYson); composite.AddScanner(scannerCSV); if (!composite.Scan(it)) { return TQueryBenchmarkResult::Error( composite.GetErrorInfo(), composite.GetQueryPlan(), composite.GetPlanAst()); } else { return TQueryBenchmarkResult::Result( - scannerYson->GetResult(), + composite.ExtractRawResults(), *scannerCSV, composite.GetServerTiming(), composite.GetQueryPlan(), @@ -379,18 +350,16 @@ TQueryBenchmarkResult ExecuteImpl(const TString& query, NQuery::TQueryClient& cl return *error; } - std::shared_ptr scannerYson = std::make_shared(); std::shared_ptr scannerCSV = std::make_shared(); TQueryResultScannerComposite composite; composite.SetDeadlineName(deadline.Name); - composite.AddScanner(scannerYson); composite.AddScanner(scannerCSV); if (!composite.Scan(it)) { return TQueryBenchmarkResult::Error( composite.GetErrorInfo(), composite.GetQueryPlan(), composite.GetPlanAst()); } else { return TQueryBenchmarkResult::Result( - scannerYson->GetResult(), + composite.ExtractRawResults(), *scannerCSV, composite.GetServerTiming(), composite.GetQueryPlan(), @@ -629,6 +598,26 @@ bool CompareValue(const NYdb::TValue& v, TStringBuf vExpected) { } } +TQueryResultInfo::TColumnsRemap TQueryResultInfo::GetColumnsRemap() const { + TColumnsRemap result; + ui32 idx = 0; + for (auto&& i : Columns) { + result.emplace(i.Name, idx++); + } + return result; +} + +TString TQueryResultInfo::CalcHash() const { + MD5 hasher; + for (const auto& row: Result) { + for (const auto& v: row) { + hasher.Update(FormatValueYson(v, NYson::EYsonFormat::Binary)); + } + } + char buf[25]; + return hasher.End_b64(buf); +} + bool TQueryResultInfo::IsExpected(std::string_view expected) const { if (expected.empty()) { return true; @@ -647,7 +636,7 @@ bool TQueryResultInfo::IsExpected(std::string_view expected) const { std::vector columnIndexes; { - const std::map columns = GetColumnsRemap(); + const auto columns = GetColumnsRemap(); auto copy = expectedLines.front(); NCsvFormat::CsvSplitter splitter(copy); while (true) { diff --git a/ydb/public/lib/ydb_cli/commands/benchmark_utils.h b/ydb/public/lib/ydb_cli/commands/benchmark_utils.h index 90ff3a199153..d1feaa2258c0 100644 --- a/ydb/public/lib/ydb_cli/commands/benchmark_utils.h +++ b/ydb/public/lib/ydb_cli/commands/benchmark_utils.h @@ -31,49 +31,42 @@ struct TTestInfo { }; class TQueryResultInfo { + YDB_READONLY_PROTECT_DEF(std::vector>, Result); + YDB_READONLY_PROTECT_DEF(TVector, Columns); protected: - std::vector> Result; - TVector Columns; -public: - std::map GetColumnsRemap() const { - std::map result; - ui32 idx = 0; - for (auto&& i : Columns) { - result.emplace(i.Name, idx++); - } - return result; - } + using TColumnsRemap = std::map; + TColumnsRemap GetColumnsRemap() const; - const std::vector>& GetResult() const { - return Result; - } - const TVector& GetColumns() const { - return Columns; - } +public: bool IsExpected(std::string_view expected) const; + TString CalcHash() const; }; class TQueryBenchmarkResult { +public: + using TRawResults = TVector; + private: YDB_READONLY_DEF(TString, ErrorInfo); - YDB_READONLY_DEF(TString, YSONResult); + YDB_READONLY_DEF(TRawResults, RawResults); YDB_READONLY_DEF(TQueryResultInfo, QueryResult); YDB_READONLY_DEF(TDuration, ServerTiming); YDB_READONLY_DEF(TString, QueryPlan); YDB_READONLY_DEF(TString, PlanAst); TQueryBenchmarkResult() = default; public: - static TQueryBenchmarkResult Result(const TString& yson, const TQueryResultInfo& queryResult, + static TQueryBenchmarkResult Result(TRawResults&& rawResults, const TQueryResultInfo& queryResult, const TDuration& serverTiming, const TString& queryPlan, const TString& planAst) { TQueryBenchmarkResult result; - result.YSONResult = yson; + result.RawResults = std::move(rawResults); result.QueryResult = queryResult; result.ServerTiming = serverTiming; result.QueryPlan = queryPlan; result.PlanAst = planAst; return result; } + static TQueryBenchmarkResult Error(const TString& error, const TString& queryPlan, const TString& planAst) { TQueryBenchmarkResult result; result.ErrorInfo = error; @@ -81,6 +74,7 @@ class TQueryBenchmarkResult { result.PlanAst = planAst; return result; } + operator bool() const { return !ErrorInfo; } diff --git a/ydb/public/lib/ydb_cli/commands/ydb_benchmark.cpp b/ydb/public/lib/ydb_cli/commands/ydb_benchmark.cpp index b98e98bf0df3..73df3341a871 100644 --- a/ydb/public/lib/ydb_cli/commands/ydb_benchmark.cpp +++ b/ydb/public/lib/ydb_cli/commands/ydb_benchmark.cpp @@ -366,17 +366,20 @@ bool TWorkloadCommandBenchmark::RunBench(TClient& client, NYdbWorkload::IWorkloa serverTimings.emplace_back(res.GetServerTiming()); ++successIteration; if (successIteration == 1) { - outFStream << queryN << ": " << Endl - << res.GetYSONResult() << Endl << Endl; + outFStream << queryN << ": " << Endl; + PrintResult(res, outFStream); } - if ((!prevResult || *prevResult != res.GetYSONResult()) && !res.GetQueryResult().IsExpected(qInfo.ExpectedResult)) { + const auto resHash = res.GetQueryResult().CalcHash(); + if ((!prevResult || *prevResult != resHash) && !res.GetQueryResult().IsExpected(qInfo.ExpectedResult)) { outFStream << queryN << ":" << Endl << "Query text:" << Endl << query << Endl << Endl << "UNEXPECTED DIFF: " << Endl - << "RESULT: " << Endl << res.GetYSONResult() << Endl + << "RESULT: " << Endl; + PrintResult(res, outFStream); + outFStream << Endl << "EXPECTATION: " << Endl << qInfo.ExpectedResult << Endl; - prevResult = res.GetYSONResult(); + prevResult = resHash; ++diffsCount; } } else { @@ -456,6 +459,15 @@ bool TWorkloadCommandBenchmark::RunBench(TClient& client, NYdbWorkload::IWorkloa return !someFailQueries; } +void TWorkloadCommandBenchmark::PrintResult(const BenchmarkUtils::TQueryBenchmarkResult& res, IOutputStream& out) const { + TResultSetPrinter printer(EDataFormat::Pretty, []() { return false; }, out, 120); + for(const auto& r: res.GetRawResults()) { + printer.Print(r); + printer.Reset(); + } + out << Endl << Endl; +} + void TWorkloadCommandBenchmark::SavePlans(const BenchmarkUtils::TQueryBenchmarkResult& res, ui32 queryNum, const TStringBuf name) const { if (!PlanFileName) { return; diff --git a/ydb/public/lib/ydb_cli/commands/ydb_benchmark.h b/ydb/public/lib/ydb_cli/commands/ydb_benchmark.h index c0fa5995eea1..f33193301460 100644 --- a/ydb/public/lib/ydb_cli/commands/ydb_benchmark.h +++ b/ydb/public/lib/ydb_cli/commands/ydb_benchmark.h @@ -22,6 +22,7 @@ class TWorkloadCommandBenchmark final: public TWorkloadCommandBase { template bool RunBench(TClient& client, NYdbWorkload::IWorkloadQueryGenerator& workloadGen); void SavePlans(const BenchmarkUtils::TQueryBenchmarkResult& res, ui32 queryNum, const TStringBuf name) const; + void PrintResult(const BenchmarkUtils::TQueryBenchmarkResult& res, IOutputStream& out) const; BenchmarkUtils::TQueryBenchmarkDeadline GetDeadline() const; private: diff --git a/ydb/public/lib/ydb_cli/common/format.cpp b/ydb/public/lib/ydb_cli/common/format.cpp index b42f273d5863..0cce98574d93 100644 --- a/ydb/public/lib/ydb_cli/common/format.cpp +++ b/ydb/public/lib/ydb_cli/common/format.cpp @@ -650,10 +650,12 @@ TString TQueryPlanPrinter::JsonToString(const NJson::TJsonValue& jsonValue) { return jsonValue.GetStringRobust(); } -TResultSetPrinter::TResultSetPrinter(EDataFormat format, std::function isInterrupted) +TResultSetPrinter::TResultSetPrinter(EDataFormat format, std::function isInterrupted, IOutputStream& output, size_t maxWidth) : Format(format) , IsInterrupted(isInterrupted) , ParquetPrinter(std::make_unique("")) + , Output(output) + , MaxWidth(maxWidth) {} TResultSetPrinter::~TResultSetPrinter() { @@ -677,13 +679,13 @@ void TResultSetPrinter::Print(const TResultSet& resultSet) { PrintJsonArray(resultSet, EBinaryStringEncoding::Unicode); break; case EDataFormat::JsonUnicode: - FormatResultSetJson(resultSet, &Cout, EBinaryStringEncoding::Unicode); + FormatResultSetJson(resultSet, &Output, EBinaryStringEncoding::Unicode); break; case EDataFormat::JsonBase64Array: PrintJsonArray(resultSet, EBinaryStringEncoding::Base64); break; case EDataFormat::JsonBase64: - FormatResultSetJson(resultSet, &Cout, EBinaryStringEncoding::Base64); + FormatResultSetJson(resultSet, &Output, EBinaryStringEncoding::Base64); break; case EDataFormat::Csv: PrintCsv(resultSet, ","); @@ -714,7 +716,7 @@ void TResultSetPrinter::BeginResultSet() { switch (Format) { case EDataFormat::JsonUnicodeArray: case EDataFormat::JsonBase64Array: - Cout << '['; + Output << '['; break; default: break; @@ -725,7 +727,7 @@ void TResultSetPrinter::EndResultSet() { switch (Format) { case EDataFormat::JsonUnicodeArray: case EDataFormat::JsonBase64Array: - Cout << ']' << Endl; + Output << ']' << Endl; break; case EDataFormat::Parquet: ParquetPrinter->Reset(); @@ -739,7 +741,7 @@ void TResultSetPrinter::EndLineBeforeNextResult() { switch (Format) { case EDataFormat::JsonUnicodeArray: case EDataFormat::JsonBase64Array: - Cout << ',' << Endl; + Output << ',' << Endl; break; default: break; @@ -755,6 +757,7 @@ void TResultSetPrinter::PrintPretty(const TResultSet& resultSet) { } TPrettyTableConfig tableConfig; + tableConfig.MaxWidth(MaxWidth); if (!FirstPart) { tableConfig.WithoutHeader(); } @@ -767,7 +770,7 @@ void TResultSetPrinter::PrintPretty(const TResultSet& resultSet) { } } - Cout << table; + Output << table; } void TResultSetPrinter::PrintJsonArray(const TResultSet& resultSet, EBinaryStringEncoding encoding) { @@ -782,7 +785,7 @@ void TResultSetPrinter::PrintJsonArray(const TResultSet& resultSet, EBinaryStrin if (firstRow) { firstRow = false; } - NJsonWriter::TBuf writer(NJsonWriter::HEM_UNSAFE, &Cout); + NJsonWriter::TBuf writer(NJsonWriter::HEM_UNSAFE, &Output); FormatResultRowJson(parser, columns, writer, encoding); } } @@ -792,12 +795,12 @@ void TResultSetPrinter::PrintCsv(const TResultSet& resultSet, const char* delim) TResultSetParser parser(resultSet); while (parser.TryNextRow()) { for (ui32 i = 0; i < columns.size(); ++i) { - Cout << FormatValueJson(parser.GetValue(i), EBinaryStringEncoding::Unicode); + Output << FormatValueJson(parser.GetValue(i), EBinaryStringEncoding::Unicode); if (i < columns.size() - 1) { - Cout << delim; + Output << delim; } } - Cout << Endl; + Output << Endl; } } diff --git a/ydb/public/lib/ydb_cli/common/format.h b/ydb/public/lib/ydb_cli/common/format.h index 20cd4bd67b05..c577572e2897 100644 --- a/ydb/public/lib/ydb_cli/common/format.h +++ b/ydb/public/lib/ydb_cli/common/format.h @@ -104,7 +104,7 @@ class TCommandWithMessagingFormat { class TResultSetPrinter { public: - TResultSetPrinter(EDataFormat format, std::function isInterrupted = []() { return false; }); + TResultSetPrinter(EDataFormat format, std::function isInterrupted = []() { return false; }, IOutputStream& output = Cout, size_t maxWidth = 0); ~TResultSetPrinter(); void Print(const TResultSet& resultSet); @@ -126,6 +126,8 @@ class TResultSetPrinter { EDataFormat Format; std::function IsInterrupted; std::unique_ptr ParquetPrinter; + IOutputStream& Output; + size_t MaxWidth; }; class TQueryPlanPrinter { diff --git a/ydb/public/lib/ydb_cli/common/pretty_table.cpp b/ydb/public/lib/ydb_cli/common/pretty_table.cpp index d577194db6ff..428b3563351c 100644 --- a/ydb/public/lib/ydb_cli/common/pretty_table.cpp +++ b/ydb/public/lib/ydb_cli/common/pretty_table.cpp @@ -83,8 +83,8 @@ class TColumnLinesPrinter { return !allColumnsPrinted; } - void Print() { - NColorizer::TColors colors = NColorizer::AutoColors(Cout); + void Print() { + NColorizer::TColors colors = NColorizer::AutoColors(Output_); Output_ << colors.Default(); Output_ << "│ ";