Skip to content

Commit 7881ed5

Browse files
gridnevvvitqyryq
authored andcommitted
don't remove DESC sorting from plan for sys view (ydb-platform#28937)
1 parent 84b9c47 commit 7881ed5

File tree

2 files changed

+302
-3
lines changed

2 files changed

+302
-3
lines changed

ydb/core/kqp/opt/physical/kqp_opt_phy_sort.cpp

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,13 @@ TExprBase KqpRemoveRedundantSortOverReadTable(TExprBase node, TExprContext& ctx,
9393
}
9494

9595
if (direction == ESortDirection::Reverse) {
96+
// For sys views, we need to set reverse flag even if UseSource returns false
97+
// because sys view actors can handle reverse direction
98+
bool isSysView = tableDesc.Metadata->Kind == EKikimrTableKind::SysView;
99+
if (isSysView) {
100+
return node;
101+
}
102+
96103
if (!UseSource(kqpCtx, tableDesc) && kqpCtx.IsScanQuery()) {
97104
return node;
98105
}
@@ -206,6 +213,13 @@ TExprBase KqpRemoveRedundantSortOverReadTableFSM(
206213
}
207214

208215
if (isReversed) {
216+
// For sys views, we need to set reverse flag even if UseSource returns false
217+
// because sys view actors can handle reverse direction
218+
bool isSysView = tableDesc.Metadata->Kind == EKikimrTableKind::SysView;
219+
if (isSysView) {
220+
return node;
221+
}
222+
209223
if (!UseSource(kqpCtx, tableDesc) && kqpCtx.IsScanQuery()) {
210224
return node;
211225
}

ydb/core/kqp/ut/sysview/kqp_sys_view_ut.cpp

Lines changed: 288 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -564,6 +564,291 @@ order by SessionId;)", "%Y-%m-%d %H:%M:%S %Z", sessionsSet.front().GetId().data(
564564
CompareYson(expected, StreamResultToYson(it));
565565
}
566566

567+
Y_UNIT_TEST(NodesOrderByDesc) {
568+
// Test to reproduce issue #12585: ORDER BY DESC doesn't work for sys views
569+
// The sys view actors ignore the direction flag and don't guarantee order
570+
TKikimrRunner kikimr("", KikimrDefaultUtDomainRoot, 5);
571+
auto client = kikimr.GetQueryClient();
572+
auto session = client.GetSession().GetValueSync().GetSession();
573+
574+
ui32 offset = kikimr.GetTestServer().GetRuntime()->GetNodeId(0);
575+
576+
auto result = session.ExecuteQuery(R"(--!syntax_v1
577+
SELECT NodeId, Host
578+
FROM `/Root/.sys/nodes`
579+
ORDER BY NodeId DESC
580+
)", NYdb::NQuery::TTxControl::NoTx()).GetValueSync();
581+
582+
UNIT_ASSERT_C(result.IsSuccess(), result.GetIssues().ToString());
583+
584+
// Collect all results
585+
TVector<ui32> nodeIds;
586+
auto resultSet = result.GetResultSet(0);
587+
NYdb::TResultSetParser parser(resultSet);
588+
while (parser.TryNextRow()) {
589+
auto nodeId = parser.ColumnParser("NodeId").GetOptionalUint32().value();
590+
nodeIds.push_back(nodeId);
591+
}
592+
593+
// Verify we got all 5 nodes
594+
UNIT_ASSERT_VALUES_EQUAL(nodeIds.size(), 5);
595+
596+
// Verify results are in descending order (this should fail if the bug exists)
597+
// According to issue #12585, sys view actors ignore the direction flag
598+
// and don't guarantee order, so this assertion should fail
599+
for (size_t i = 1; i < nodeIds.size(); ++i) {
600+
UNIT_ASSERT_C(nodeIds[i - 1] >= nodeIds[i],
601+
TStringBuilder() << "Results not in descending order: "
602+
<< "nodeIds[" << (i - 1) << "] = " << nodeIds[i - 1]
603+
<< " < nodeIds[" << i << "] = " << nodeIds[i]
604+
<< ". ORDER BY DESC is being ignored by sys view actors.");
605+
}
606+
607+
// Verify exact expected order: offset+4, offset+3, offset+2, offset+1, offset
608+
UNIT_ASSERT_VALUES_EQUAL(nodeIds[0], offset + 4);
609+
UNIT_ASSERT_VALUES_EQUAL(nodeIds[1], offset + 3);
610+
UNIT_ASSERT_VALUES_EQUAL(nodeIds[2], offset + 2);
611+
UNIT_ASSERT_VALUES_EQUAL(nodeIds[3], offset + 1);
612+
UNIT_ASSERT_VALUES_EQUAL(nodeIds[4], offset);
613+
}
614+
615+
Y_UNIT_TEST(PartitionStatsOrderByDesc) {
616+
// Test ORDER BY DESC for partition_stats sys view
617+
// Primary key: OwnerId, PathId, PartIdx, FollowerId
618+
TKikimrRunner kikimr;
619+
auto client = kikimr.GetQueryClient();
620+
auto session = client.GetSession().GetValueSync().GetSession();
621+
622+
auto result = session.ExecuteQuery(R"(--!syntax_v1
623+
SELECT OwnerId, PathId, PartIdx, FollowerId, Path
624+
FROM `/Root/.sys/partition_stats`
625+
ORDER BY OwnerId DESC, PathId DESC, PartIdx DESC, FollowerId DESC
626+
)", NYdb::NQuery::TTxControl::NoTx()).GetValueSync();
627+
628+
UNIT_ASSERT_C(result.IsSuccess(), result.GetIssues().ToString());
629+
630+
// Collect all results
631+
struct TPartitionKey {
632+
ui64 OwnerId;
633+
ui64 PathId;
634+
ui64 PartIdx;
635+
ui32 FollowerId;
636+
};
637+
TVector<TPartitionKey> partitionKeys;
638+
auto resultSet = result.GetResultSet(0);
639+
NYdb::TResultSetParser parser(resultSet);
640+
while (parser.TryNextRow()) {
641+
auto ownerId = parser.ColumnParser("OwnerId").GetOptionalUint64().value();
642+
auto pathId = parser.ColumnParser("PathId").GetOptionalUint64().value();
643+
auto partIdx = parser.ColumnParser("PartIdx").GetOptionalUint64().value();
644+
auto followerId = parser.ColumnParser("FollowerId").GetOptionalUint32().value();
645+
partitionKeys.push_back({ownerId, pathId, partIdx, followerId});
646+
}
647+
648+
// Verify we got some results
649+
UNIT_ASSERT_C(partitionKeys.size() > 0, "Expected at least one partition");
650+
651+
// Verify results are in descending order by OwnerId, PathId, PartIdx, FollowerId
652+
for (size_t i = 1; i < partitionKeys.size(); ++i) {
653+
const auto& prev = partitionKeys[i - 1];
654+
const auto& curr = partitionKeys[i];
655+
656+
auto prevKey = std::tie(prev.OwnerId, prev.PathId, prev.PartIdx, prev.FollowerId);
657+
auto currKey = std::tie(curr.OwnerId, curr.PathId, curr.PartIdx, curr.FollowerId);
658+
659+
UNIT_ASSERT_C(prevKey >= currKey,
660+
TStringBuilder() << "Results not in descending order: "
661+
<< "partitionKeys[" << (i - 1) << "] = (" << prev.OwnerId << ", " << prev.PathId << ", " << prev.PartIdx << ", " << prev.FollowerId << ")"
662+
<< " < partitionKeys[" << i << "] = (" << curr.OwnerId << ", " << curr.PathId << ", " << curr.PartIdx << ", " << curr.FollowerId << ")"
663+
<< ". ORDER BY DESC is being ignored by sys view actors.");
664+
}
665+
}
666+
667+
Y_UNIT_TEST(QuerySessionsOrderByDesc) {
668+
// Test ORDER BY DESC for query_sessions sys view
669+
NKikimrConfig::TFeatureFlags featureFlags;
670+
featureFlags.SetEnableRealSystemViewPaths(true);
671+
TKikimrSettings settings;
672+
settings.SetWithSampleTables(false);
673+
settings.SetFeatureFlags(featureFlags);
674+
settings.SetAuthToken("root@builtin");
675+
TKikimrRunner kikimr(settings);
676+
677+
auto client = kikimr.GetQueryClient();
678+
auto session = client.GetSession().GetValueSync().GetSession();
679+
680+
// Create some sessions to have data
681+
const size_t sessionsCount = 5;
682+
std::vector<NYdb::NQuery::TSession> sessionsSet;
683+
for(ui32 i = 0; i < sessionsCount; i++) {
684+
sessionsSet.emplace_back(std::move(client.GetSession().GetValueSync().GetSession()));
685+
}
686+
687+
// Wait a bit for sessions to be registered
688+
::Sleep(TDuration::MilliSeconds(100));
689+
690+
auto result = session.ExecuteQuery(R"(--!syntax_v1
691+
SELECT SessionId, State, NodeId
692+
FROM `/Root/.sys/query_sessions`
693+
ORDER BY SessionId DESC
694+
)", NYdb::NQuery::TTxControl::NoTx()).GetValueSync();
695+
696+
UNIT_ASSERT_C(result.IsSuccess(), result.GetIssues().ToString());
697+
698+
// Collect all results
699+
TVector<std::string> sessionIds;
700+
auto resultSet = result.GetResultSet(0);
701+
NYdb::TResultSetParser parser(resultSet);
702+
while (parser.TryNextRow()) {
703+
auto sessionId = parser.ColumnParser("SessionId").GetOptionalUtf8().value();
704+
sessionIds.push_back(sessionId);
705+
}
706+
707+
// Verify we got some results
708+
UNIT_ASSERT_C(sessionIds.size() > 0, "Expected at least one session");
709+
710+
// Verify results are in descending order (lexicographically)
711+
for (size_t i = 1; i < sessionIds.size(); ++i) {
712+
UNIT_ASSERT_C(sessionIds[i - 1] >= sessionIds[i],
713+
TStringBuilder() << "Results not in descending order: "
714+
<< "sessionIds[" << (i - 1) << "] = \"" << sessionIds[i - 1] << "\""
715+
<< " < sessionIds[" << i << "] = \"" << sessionIds[i] << "\""
716+
<< ". ORDER BY DESC is being ignored by sys view actors.");
717+
}
718+
}
719+
720+
Y_UNIT_TEST(CompileCacheQueriesOrderByDesc) {
721+
// Test ORDER BY DESC for compile_cache_queries sys view
722+
// Primary key: NodeId, QueryId
723+
auto serverSettings = TKikimrSettings().SetKqpSettings({ NKikimrKqp::TKqpSetting() });
724+
serverSettings.AppConfig.MutableTableServiceConfig()->SetEnableImplicitQueryParameterTypes(true);
725+
TKikimrRunner kikimr(serverSettings);
726+
kikimr.GetTestServer().GetRuntime()->GetAppData().FeatureFlags.SetEnableCompileCacheView(true);
727+
auto client = kikimr.GetQueryClient();
728+
auto session = client.GetSession().GetValueSync().GetSession();
729+
730+
// Execute some queries to populate compile cache
731+
auto tableClient = kikimr.GetTableClient();
732+
for (ui32 i = 0; i < 3; ++i) {
733+
auto tableSession = tableClient.CreateSession().GetValueSync().GetSession();
734+
auto paramsBuilder = TParamsBuilder();
735+
paramsBuilder.AddParam("$k").Uint64(i).Build();
736+
auto executedResult = tableSession.ExecuteDataQuery(
737+
R"(DECLARE $k AS Uint64;
738+
SELECT COUNT(*) FROM `/Root/EightShard` WHERE Key = $k;)",
739+
TTxControl::BeginTx().CommitTx(),
740+
paramsBuilder.Build()
741+
).GetValueSync();
742+
UNIT_ASSERT_C(executedResult.IsSuccess(), executedResult.GetIssues().ToString());
743+
}
744+
745+
// Wait a bit for compile cache to be updated
746+
::Sleep(TDuration::MilliSeconds(100));
747+
748+
auto result = session.ExecuteQuery(R"(--!syntax_v1
749+
SELECT NodeId, QueryId, Query, CompilationDuration
750+
FROM `/Root/.sys/compile_cache_queries`
751+
ORDER BY NodeId DESC, QueryId DESC
752+
)", NYdb::NQuery::TTxControl::NoTx()).GetValueSync();
753+
754+
UNIT_ASSERT_C(result.IsSuccess(), result.GetIssues().ToString());
755+
756+
// Collect all results
757+
struct TCompileCacheKey {
758+
ui32 NodeId;
759+
std::string QueryId;
760+
};
761+
TVector<TCompileCacheKey> compileCacheKeys;
762+
auto resultSet = result.GetResultSet(0);
763+
NYdb::TResultSetParser parser(resultSet);
764+
while (parser.TryNextRow()) {
765+
auto nodeId = parser.ColumnParser("NodeId").GetOptionalUint32().value();
766+
auto queryId = parser.ColumnParser("QueryId").GetOptionalUtf8().value();
767+
compileCacheKeys.push_back({nodeId, queryId});
768+
}
769+
770+
// Verify we got some results
771+
if (compileCacheKeys.size() > 0) {
772+
// Verify results are in descending order by NodeId, then QueryId
773+
for (size_t i = 1; i < compileCacheKeys.size(); ++i) {
774+
const auto& prev = compileCacheKeys[i - 1];
775+
const auto& curr = compileCacheKeys[i];
776+
777+
auto prevKey = std::tie(prev.NodeId, prev.QueryId);
778+
auto currKey = std::tie(curr.NodeId, curr.QueryId);
779+
780+
UNIT_ASSERT_C(prevKey >= currKey,
781+
TStringBuilder() << "Results not in descending order: "
782+
<< "compileCacheKeys[" << (i - 1) << "] = (" << prev.NodeId << ", \"" << prev.QueryId << "\")"
783+
<< " < compileCacheKeys[" << i << "] = (" << curr.NodeId << ", \"" << curr.QueryId << "\")"
784+
<< ". ORDER BY DESC is being ignored by sys view actors.");
785+
}
786+
}
787+
}
788+
789+
Y_UNIT_TEST(TopQueriesOrderByDesc) {
790+
// Test ORDER BY DESC for top_queries sys views
791+
TKikimrRunner kikimr("", KikimrDefaultUtDomainRoot, 3);
792+
auto client = kikimr.GetQueryClient();
793+
auto session = client.GetSession().GetValueSync().GetSession();
794+
795+
// Execute some queries to populate stats
796+
auto tableClient = kikimr.GetTableClient();
797+
auto tableSession = tableClient.CreateSession().GetValueSync().GetSession();
798+
{
799+
auto result = tableSession.ExecuteDataQuery(Q_(R"(
800+
SELECT * FROM `/Root/TwoShard`
801+
)"), TTxControl::BeginTx().CommitTx()).GetValueSync();
802+
UNIT_ASSERT_VALUES_EQUAL(result.GetStatus(), EStatus::SUCCESS);
803+
}
804+
{
805+
auto result = tableSession.ExecuteDataQuery(Q_(R"(
806+
SELECT * FROM `/Root/EightShard`
807+
)"), TTxControl::BeginTx().CommitTx()).GetValueSync();
808+
UNIT_ASSERT_VALUES_EQUAL(result.GetStatus(), EStatus::SUCCESS);
809+
}
810+
811+
// Wait a bit for stats to be collected
812+
::Sleep(TDuration::MilliSeconds(500));
813+
814+
// Test top_queries_by_read_bytes_one_minute
815+
auto result = session.ExecuteQuery(R"(--!syntax_v1
816+
SELECT IntervalEnd, Rank, ReadBytes
817+
FROM `/Root/.sys/top_queries_by_read_bytes_one_minute`
818+
ORDER BY IntervalEnd DESC, Rank DESC
819+
)", NYdb::NQuery::TTxControl::NoTx()).GetValueSync();
820+
821+
UNIT_ASSERT_C(result.IsSuccess(), result.GetIssues().ToString());
822+
823+
// Collect all results
824+
TVector<std::pair<TInstant, ui32>> intervalEndRank;
825+
auto resultSet = result.GetResultSet(0);
826+
NYdb::TResultSetParser parser(resultSet);
827+
while (parser.TryNextRow()) {
828+
auto intervalEnd = parser.ColumnParser("IntervalEnd").GetOptionalTimestamp().value();
829+
auto rank = parser.ColumnParser("Rank").GetOptionalUint32().value();
830+
intervalEndRank.push_back({intervalEnd, rank});
831+
}
832+
833+
// Verify we got some results (may be empty if no stats collected yet)
834+
if (intervalEndRank.size() > 0) {
835+
// Verify results are in descending order by IntervalEnd, then Rank
836+
for (size_t i = 1; i < intervalEndRank.size(); ++i) {
837+
const auto& prev = intervalEndRank[i - 1];
838+
const auto& curr = intervalEndRank[i];
839+
840+
auto prevKey = std::tie(prev.first, prev.second);
841+
auto currKey = std::tie(curr.first, curr.second);
842+
843+
UNIT_ASSERT_C(prevKey >= currKey,
844+
TStringBuilder() << "Results not in descending order: "
845+
<< "intervalEndRank[" << (i - 1) << "] = (" << prev.first.ToString() << ", " << prev.second << ")"
846+
<< " < intervalEndRank[" << i << "] = (" << curr.first.ToString() << ", " << curr.second << ")"
847+
<< ". ORDER BY DESC is being ignored by sys view actors.");
848+
}
849+
}
850+
}
851+
567852
Y_UNIT_TEST(QueryStatsSimple) {
568853
auto checkTable = [&] (const TStringBuf tableName) {
569854
TKikimrRunner kikimr("", KikimrDefaultUtDomainRoot, 3);
@@ -920,15 +1205,15 @@ order by SessionId;)", "%Y-%m-%d %H:%M:%S %Z", sessionsSet.front().GetId().data(
9201205
);
9211206
)").ExtractValueSync();
9221207
UNIT_ASSERT_VALUES_EQUAL_C(createResult.GetStatus(), EStatus::SUCCESS, createResult.GetIssues());
923-
1208+
9241209
auto insertResult = session.ExecuteDataQuery(R"(
9251210
INSERT INTO TestTable (Key, Value) VALUES
9261211
(42, "val"), (NULL, "val1");
9271212
)", TTxControl::BeginTx().CommitTx()).GetValueSync();
9281213
UNIT_ASSERT_C(insertResult.IsSuccess(), insertResult.GetIssues().ToString());
9291214
}
930-
931-
1215+
1216+
9321217
auto session = db.CreateSession().GetValueSync().GetSession();
9331218
TString query = R"(
9341219
SELECT * FROM TestTable WHERE Key in [42, NULL];

0 commit comments

Comments
 (0)