Skip to content

Commit f1d5440

Browse files
authored
don't remove DESC sorting from plan for sys view (#28937) (#28957)
2 parents d498ade + 237cff9 commit f1d5440

File tree

2 files changed

+230
-0
lines changed

2 files changed

+230
-0
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: 216 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -546,6 +546,222 @@ order by SessionId;)", "%Y-%m-%d %H:%M:%S %Z", sessionsSet.front().GetId().data(
546546
CompareYson(expected, StreamResultToYson(it));
547547
}
548548

549+
Y_UNIT_TEST(NodesOrderByDesc) {
550+
// Test to reproduce issue #12585: ORDER BY DESC doesn't work for sys views
551+
// The sys view actors ignore the direction flag and don't guarantee order
552+
TKikimrRunner kikimr("", KikimrDefaultUtDomainRoot, 5);
553+
auto client = kikimr.GetQueryClient();
554+
auto session = client.GetSession().GetValueSync().GetSession();
555+
556+
ui32 offset = kikimr.GetTestServer().GetRuntime()->GetNodeId(0);
557+
558+
auto result = session.ExecuteQuery(R"(--!syntax_v1
559+
SELECT NodeId, Host
560+
FROM `/Root/.sys/nodes`
561+
ORDER BY NodeId DESC
562+
)", NYdb::NQuery::TTxControl::NoTx()).GetValueSync();
563+
564+
UNIT_ASSERT_C(result.IsSuccess(), result.GetIssues().ToString());
565+
566+
// Collect all results
567+
TVector<ui32> nodeIds;
568+
auto resultSet = result.GetResultSet(0);
569+
NYdb::TResultSetParser parser(resultSet);
570+
while (parser.TryNextRow()) {
571+
auto nodeId = parser.ColumnParser("NodeId").GetOptionalUint32().value();
572+
nodeIds.push_back(nodeId);
573+
}
574+
575+
// Verify we got all 5 nodes
576+
UNIT_ASSERT_VALUES_EQUAL(nodeIds.size(), 5);
577+
578+
// Verify results are in descending order (this should fail if the bug exists)
579+
// According to issue #12585, sys view actors ignore the direction flag
580+
// and don't guarantee order, so this assertion should fail
581+
for (size_t i = 1; i < nodeIds.size(); ++i) {
582+
UNIT_ASSERT_C(nodeIds[i - 1] >= nodeIds[i],
583+
TStringBuilder() << "Results not in descending order: "
584+
<< "nodeIds[" << (i - 1) << "] = " << nodeIds[i - 1]
585+
<< " < nodeIds[" << i << "] = " << nodeIds[i]
586+
<< ". ORDER BY DESC is being ignored by sys view actors.");
587+
}
588+
589+
// Verify exact expected order: offset+4, offset+3, offset+2, offset+1, offset
590+
UNIT_ASSERT_VALUES_EQUAL(nodeIds[0], offset + 4);
591+
UNIT_ASSERT_VALUES_EQUAL(nodeIds[1], offset + 3);
592+
UNIT_ASSERT_VALUES_EQUAL(nodeIds[2], offset + 2);
593+
UNIT_ASSERT_VALUES_EQUAL(nodeIds[3], offset + 1);
594+
UNIT_ASSERT_VALUES_EQUAL(nodeIds[4], offset);
595+
}
596+
597+
Y_UNIT_TEST(PartitionStatsOrderByDesc) {
598+
// Test ORDER BY DESC for partition_stats sys view
599+
// Primary key: OwnerId, PathId, PartIdx, FollowerId
600+
TKikimrRunner kikimr;
601+
auto client = kikimr.GetQueryClient();
602+
auto session = client.GetSession().GetValueSync().GetSession();
603+
604+
auto result = session.ExecuteQuery(R"(--!syntax_v1
605+
SELECT OwnerId, PathId, PartIdx, FollowerId, Path
606+
FROM `/Root/.sys/partition_stats`
607+
ORDER BY OwnerId DESC, PathId DESC, PartIdx DESC, FollowerId DESC
608+
)", NYdb::NQuery::TTxControl::NoTx()).GetValueSync();
609+
610+
UNIT_ASSERT_C(result.IsSuccess(), result.GetIssues().ToString());
611+
612+
// Collect all results
613+
struct TPartitionKey {
614+
ui64 OwnerId;
615+
ui64 PathId;
616+
ui64 PartIdx;
617+
ui32 FollowerId;
618+
};
619+
TVector<TPartitionKey> partitionKeys;
620+
auto resultSet = result.GetResultSet(0);
621+
NYdb::TResultSetParser parser(resultSet);
622+
while (parser.TryNextRow()) {
623+
auto ownerId = parser.ColumnParser("OwnerId").GetOptionalUint64().value();
624+
auto pathId = parser.ColumnParser("PathId").GetOptionalUint64().value();
625+
auto partIdx = parser.ColumnParser("PartIdx").GetOptionalUint64().value();
626+
auto followerId = parser.ColumnParser("FollowerId").GetOptionalUint32().value();
627+
partitionKeys.push_back({ownerId, pathId, partIdx, followerId});
628+
}
629+
630+
// Verify we got some results
631+
UNIT_ASSERT_C(partitionKeys.size() > 0, "Expected at least one partition");
632+
633+
// Verify results are in descending order by OwnerId, PathId, PartIdx, FollowerId
634+
for (size_t i = 1; i < partitionKeys.size(); ++i) {
635+
const auto& prev = partitionKeys[i - 1];
636+
const auto& curr = partitionKeys[i];
637+
638+
auto prevKey = std::tie(prev.OwnerId, prev.PathId, prev.PartIdx, prev.FollowerId);
639+
auto currKey = std::tie(curr.OwnerId, curr.PathId, curr.PartIdx, curr.FollowerId);
640+
641+
UNIT_ASSERT_C(prevKey >= currKey,
642+
TStringBuilder() << "Results not in descending order: "
643+
<< "partitionKeys[" << (i - 1) << "] = (" << prev.OwnerId << ", " << prev.PathId << ", " << prev.PartIdx << ", " << prev.FollowerId << ")"
644+
<< " < partitionKeys[" << i << "] = (" << curr.OwnerId << ", " << curr.PathId << ", " << curr.PartIdx << ", " << curr.FollowerId << ")"
645+
<< ". ORDER BY DESC is being ignored by sys view actors.");
646+
}
647+
}
648+
649+
Y_UNIT_TEST(QuerySessionsOrderByDesc) {
650+
// Test ORDER BY DESC for query_sessions sys view
651+
NKikimrConfig::TFeatureFlags featureFlags;
652+
featureFlags.SetEnableRealSystemViewPaths(true);
653+
TKikimrSettings settings;
654+
settings.SetWithSampleTables(false);
655+
settings.SetFeatureFlags(featureFlags);
656+
settings.SetAuthToken("root@builtin");
657+
TKikimrRunner kikimr(settings);
658+
659+
auto client = kikimr.GetQueryClient();
660+
auto session = client.GetSession().GetValueSync().GetSession();
661+
662+
// Create some sessions to have data
663+
const size_t sessionsCount = 5;
664+
std::vector<NYdb::NQuery::TSession> sessionsSet;
665+
for(ui32 i = 0; i < sessionsCount; i++) {
666+
sessionsSet.emplace_back(std::move(client.GetSession().GetValueSync().GetSession()));
667+
}
668+
669+
// Wait a bit for sessions to be registered
670+
::Sleep(TDuration::MilliSeconds(100));
671+
672+
auto result = session.ExecuteQuery(R"(--!syntax_v1
673+
SELECT SessionId, State, NodeId
674+
FROM `/Root/.sys/query_sessions`
675+
ORDER BY SessionId DESC
676+
)", NYdb::NQuery::TTxControl::NoTx()).GetValueSync();
677+
678+
UNIT_ASSERT_C(result.IsSuccess(), result.GetIssues().ToString());
679+
680+
// Collect all results
681+
TVector<std::string> sessionIds;
682+
auto resultSet = result.GetResultSet(0);
683+
NYdb::TResultSetParser parser(resultSet);
684+
while (parser.TryNextRow()) {
685+
auto sessionId = parser.ColumnParser("SessionId").GetOptionalUtf8().value();
686+
sessionIds.push_back(sessionId);
687+
}
688+
689+
// Verify we got some results
690+
UNIT_ASSERT_C(sessionIds.size() > 0, "Expected at least one session");
691+
692+
// Verify results are in descending order (lexicographically)
693+
for (size_t i = 1; i < sessionIds.size(); ++i) {
694+
UNIT_ASSERT_C(sessionIds[i - 1] >= sessionIds[i],
695+
TStringBuilder() << "Results not in descending order: "
696+
<< "sessionIds[" << (i - 1) << "] = \"" << sessionIds[i - 1] << "\""
697+
<< " < sessionIds[" << i << "] = \"" << sessionIds[i] << "\""
698+
<< ". ORDER BY DESC is being ignored by sys view actors.");
699+
}
700+
}
701+
702+
Y_UNIT_TEST(TopQueriesOrderByDesc) {
703+
// Test ORDER BY DESC for top_queries sys views
704+
TKikimrRunner kikimr("", KikimrDefaultUtDomainRoot, 3);
705+
auto client = kikimr.GetQueryClient();
706+
auto session = client.GetSession().GetValueSync().GetSession();
707+
708+
// Execute some queries to populate stats
709+
auto tableClient = kikimr.GetTableClient();
710+
auto tableSession = tableClient.CreateSession().GetValueSync().GetSession();
711+
{
712+
auto result = tableSession.ExecuteDataQuery(Q_(R"(
713+
SELECT * FROM `/Root/TwoShard`
714+
)"), TTxControl::BeginTx().CommitTx()).GetValueSync();
715+
UNIT_ASSERT_VALUES_EQUAL(result.GetStatus(), EStatus::SUCCESS);
716+
}
717+
{
718+
auto result = tableSession.ExecuteDataQuery(Q_(R"(
719+
SELECT * FROM `/Root/EightShard`
720+
)"), TTxControl::BeginTx().CommitTx()).GetValueSync();
721+
UNIT_ASSERT_VALUES_EQUAL(result.GetStatus(), EStatus::SUCCESS);
722+
}
723+
724+
// Wait a bit for stats to be collected
725+
::Sleep(TDuration::MilliSeconds(500));
726+
727+
// Test top_queries_by_read_bytes_one_minute
728+
auto result = session.ExecuteQuery(R"(--!syntax_v1
729+
SELECT IntervalEnd, Rank, ReadBytes
730+
FROM `/Root/.sys/top_queries_by_read_bytes_one_minute`
731+
ORDER BY IntervalEnd DESC, Rank DESC
732+
)", NYdb::NQuery::TTxControl::NoTx()).GetValueSync();
733+
734+
UNIT_ASSERT_C(result.IsSuccess(), result.GetIssues().ToString());
735+
736+
// Collect all results
737+
TVector<std::pair<TInstant, ui32>> intervalEndRank;
738+
auto resultSet = result.GetResultSet(0);
739+
NYdb::TResultSetParser parser(resultSet);
740+
while (parser.TryNextRow()) {
741+
auto intervalEnd = parser.ColumnParser("IntervalEnd").GetOptionalTimestamp().value();
742+
auto rank = parser.ColumnParser("Rank").GetOptionalUint32().value();
743+
intervalEndRank.push_back({intervalEnd, rank});
744+
}
745+
746+
// Verify we got some results (may be empty if no stats collected yet)
747+
if (intervalEndRank.size() > 0) {
748+
// Verify results are in descending order by IntervalEnd, then Rank
749+
for (size_t i = 1; i < intervalEndRank.size(); ++i) {
750+
const auto& prev = intervalEndRank[i - 1];
751+
const auto& curr = intervalEndRank[i];
752+
753+
auto prevKey = std::tie(prev.first, prev.second);
754+
auto currKey = std::tie(curr.first, curr.second);
755+
756+
UNIT_ASSERT_C(prevKey >= currKey,
757+
TStringBuilder() << "Results not in descending order: "
758+
<< "intervalEndRank[" << (i - 1) << "] = (" << prev.first.ToString() << ", " << prev.second << ")"
759+
<< " < intervalEndRank[" << i << "] = (" << curr.first.ToString() << ", " << curr.second << ")"
760+
<< ". ORDER BY DESC is being ignored by sys view actors.");
761+
}
762+
}
763+
}
764+
549765
Y_UNIT_TEST(QueryStatsSimple) {
550766
auto checkTable = [&] (const TStringBuf tableName) {
551767
TKikimrRunner kikimr("", KikimrDefaultUtDomainRoot, 3);

0 commit comments

Comments
 (0)