github-actions[bot] commented on code in PR #67518:
URL: https://github.com/apache/doris/pull/67518#discussion_r3963656513
##########
fe/fe-core/src/main/java/org/apache/doris/nereids/processor/post/materialize/LazyMaterializeTopN.java:
##########
@@ -159,6 +162,36 @@ private Plan doComputeTopN(PhysicalTopN<? extends Plan>
topN, CascadesContext ct
BiMap<Relation, SlotReference> relationToRowId =
HashBiMap.create(relationToLazySlotMap.size());
HashSet<SlotReference> rowIdSet = new HashSet<>();
StatementContext threadStatementContext =
StatementScopeIdGenerator.getStatementContext();
+ // Rowids of remote doris tables are generated by the remote cluster's
backends, and
+ // the second phase fetch goes to those remote backends directly (see
+ // MaterializationNode nodes info and the cross cluster multiget rpc).
Backend ids of
+ // clusters are independently allocated; any id collision (remote vs
local, or remote
+ // vs remote across catalogs) would silently route the fetch to a
wrong backend, so
+ // skip the rewrite and fall back to normal execution. Resolving the
remote table may
+ // issue metadata rpcs (arrow flight mode), degrade on failure too.
+ List<RemoteOlapTable> remoteTables = new ArrayList<>();
+ for (Relation relation : relationToLazySlotMap.keySet()) {
+ if (!(relation instanceof CatalogRelation)) {
+ continue;
+ }
+ TableIf relationTable = ((CatalogRelation) relation).getTable();
+ try {
+ RemoteOlapTable remoteTable =
RemoteDorisExternalCatalog.getRemoteOlapTable(relationTable);
Review Comment:
[P1] This Arrow Flight branch is unreachable. `BindRelation` represents
`use_arrow_flight=true` as `PhysicalFileScan(RemoteDorisExternalTable)`, but
`MaterializeProbeVisitor.checkRelationTableSupportedType` admits only the exact
`OlapTable.class` or a capable `PluginDrivenExternalTable`; this wrapper is
neither. Thus every candidate slot stays eager and `doComputeTopN` returns
before `relationToLazySlotMap` reaches this code. Virtual mode works through
the separate `PhysicalOlapScan` visitor, but Arrow mode is still never lazily
materialized. Please admit the wrapper by resolving its `RemoteOlapTable`
during probing and apply the same AGG_KEYS, light-schema-change, and
sequence-map safety gates, with an EXPLAIN shape assertion.
##########
fe/fe-core/src/main/java/org/apache/doris/datasource/doris/RemoteDorisExternalCatalog.java:
##########
@@ -168,6 +173,42 @@ public boolean useArrowFlight() {
"true"));
}
+ /**
+ * Returns the remote olap table behind the given table, or null if the
table does not
+ * belong to a remote doris cluster. Covers both access modes: the virtual
cluster mode
+ * binds a RemoteOlapTable directly, and the arrow flight mode binds a
+ * RemoteDorisExternalTable wrapping one.
+ */
+ public static RemoteOlapTable getRemoteOlapTable(TableIf table) {
+ if (table instanceof RemoteOlapTable) {
+ return (RemoteOlapTable) table;
+ }
+ if (table instanceof RemoteDorisExternalTable) {
+ return (RemoteOlapTable) ((RemoteDorisExternalTable)
table).getOlapTable();
+ }
+ return null;
+ }
+
+ /**
+ * Whether any remote backend id collides with a local backend id, or two
remote tables
+ * (e.g. from different remote catalogs) collide with each other. Backend
ids of clusters
+ * are independently allocated; on collision the second phase fetch cannot
distinguish the
+ * id spaces and would route rows to a wrong backend, so topn lazy
materialization must
+ * be skipped.
+ */
+ public static boolean
hasRemoteBackendIdConflict(Collection<RemoteOlapTable> remoteTables) {
+ Set<Long> localBackendIds = new
HashSet<>(Env.getCurrentSystemInfo().getAllBackendIds());
+ Set<Long> seenRemoteBackendIds = new HashSet<>();
+ for (RemoteOlapTable remoteTable : remoteTables) {
+ for (Long backendId :
remoteTable.getAllBackendsByAllCluster().keySet()) {
+ if (localBackendIds.contains(backendId) ||
!seenRemoteBackendIds.add(backendId)) {
Review Comment:
[P2] This treats ids as conflicting even when the address map for this plan
would be unambiguous. A self-join or two tables from one remote catalog
contributes the same catalog-wide map twice, so the second relation fails here
despite mapping every id to the identical endpoint (and the translator
explicitly merges that case). The local set is also overbroad:
`getAllBackendIds()` includes dead BEs and other compute/resource groups, while
`MaterializationNode` includes only alive, query-available BEs from the
selected compute group. Please compare the effective id-to-endpoint mappings
used by `nodes_info`, accepting exact duplicates and rejecting only genuinely
ambiguous endpoints; cover both same-catalog joins and irrelevant-local-BE
collisions.
##########
gensrc/proto/internal_service.proto:
##########
@@ -854,6 +854,10 @@ message PMultiGetRequestV2 {
optional bool gc_id_map = 4;
optional uint64 wg_id = 5;
optional bool file_cache_remote_only_on_miss = 6;
+ // cluster id of the sender cluster. Used by the receiver to detect
cross-cluster
+ // requests (e.g. remote doris catalog topn lazy materialization) and fall
back to
+ // a local workload group instead of failing.
+ optional int32 cluster_id = 7;
Review Comment:
[P1] This field does not reliably prove that the request is local. An old
sender omits it and an old receiver ignores it, so either mixed-version
direction still resolves the sender's numeric `wg_id`; even two new clusters
can have equal `cluster_id` values because the value is user-configurable or a
random 31-bit integer, not a globally coordinated identity. In every case
`WorkloadGroupMgr::get_group` prefers an exact local id before falling back to
`normal`, so a same-numbered unrelated group can reject or throttle the fetch.
Please mark remoteness at the plan/request boundary or use a non-colliding
identity/capability scheme, with both mixed-version directions and equal-id
all-new clusters tested.
##########
regression-test/suites/external_table_p0/remote_doris/test_remote_doris_topn_lazy_materialization.groovy:
##########
@@ -0,0 +1,131 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+// Regression test for issue apache/doris#63526: TopN (ORDER BY ... LIMIT)
over a remote
+// doris catalog used to fail with "MaterializationSinkOperatorX failed to
find rpc_struct"
+// (arrow flight mode) or "Miss matched return row loc count" (virtual cluster
mode), because
+// the rowids of remote tables encode the remote cluster's backend ids while
the second phase
+// fetch address book only contained local backends.
+// This test runs TopN queries over both catalog modes and compares the
results with querying
+// the local table directly.
+suite("test_remote_doris_topn_lazy_materialization",
"p0,external,doris,external_docker,external_docker_doris") {
+ String remote_doris_host =
context.config.otherConfigs.get("extArrowFlightSqlHost")
+ String remote_doris_user =
context.config.otherConfigs.get("extArrowFlightSqlUser")
+ String remote_doris_psw =
context.config.otherConfigs.get("extArrowFlightSqlPassword")
+
+ def showres = sql "show frontends";
Review Comment:
[P1] This case points both catalogs back at the suite's own FE (the table is
created on the current connection and every port comes from its `SHOW
FRONTENDS`). `getBackendMeta` therefore returns the same backend ids as
`Env.getCurrentSystemInfo()`, so the new conflict check returns the original
TopN before `PhysicalLazyMaterialize` is built. The row comparison can pass
without exercising remote `nodes_info`, `cluster_id`, or phase-2 fetch. Please
run this against a genuinely separate cluster with non-colliding ids and assert
the shape contains `PhysicalLazyMaterialize` for both modes; keep a separate
assertion for the collision fallback if desired.
##########
fe/fe-core/src/main/java/org/apache/doris/planner/MaterializationNode.java:
##########
@@ -157,6 +169,11 @@ public void initNodeInfo() {
for (Backend backend :
policy.getCandidateBackends(computeGroup.getBackendList())) {
nodesInfo.addToNodes(new TNodeInfo(backend.getId(), 0,
backend.getHost(), backend.getBrpcPort()));
}
+ // remote doris catalog backends; id conflicts are rejected before the
plan rewrite
+ // (LazyMaterializeTopN), so no check here.
+ for (Backend backend : remoteBackends) {
Review Comment:
[P1] Please avoid adding/contacting every remote backend unconditionally.
`getBackendMeta` includes dead BEs, this loop bypasses the availability policy
used above, and the BE eagerly resolves every advertised host and sends each
batch to every map entry. An unresolvable hostname fails `init_multi_requests`
even when that BE owns no surviving row; a transport failure for a zero-row
request is ignored only after `counter.wait()` with the full execution timeout.
This is especially harmful when a fragment in one cluster sees private
endpoints from both clusters. Filter the remote list and/or create/send RPCs
only for backend ids present in the row-id batch.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]