Aleksei Ieshin created HDDS-17555:
-------------------------------------

             Summary: Topology-aware read in OM ignores racks: pipeline 
datanodes are not mapped onto OM's cluster map before sorting
                 Key: HDDS-17555
                 URL: https://issues.apache.org/jira/browse/HDDS-17555
             Project: Apache Ozone
          Issue Type: Bug
          Components: OM
    Affects Versions: 2.2.1, 2.2.0, 2.1.0, 2.0.0
            Reporter: Aleksei Ieshin


h2. Problem

On the read path, OM sorts each block's datanodes by distance to the client 
({{{}KeyManagerImpl.sortDatanodes(String, List<OmKeyInfo>){}}} → 
{{sortDatanodes(nodes, clientMachine)}} → 
{{{}NetworkTopology.sortByDistanceCost{}}}). The nodes come straight from 
{{{}pipeline.getNodes(){}}}. Those pipelines reach OM from SCM over RPC, so 
each {{DatanodeDetails}} is built by {{{}DatanodeDetails.getFromProtoBuf{}}}. 
It carries {{networkLocation}} and {{{}level{}}}, but {*}no parent{*}: it isn't 
linked into OM's cluster map.

{{NetworkTopologyImpl.getDistanceCost(client, node)}} walks 
{{node.getAncestor(level - 1)}} to the root. For an unlinked node that returns 
{{{}null{}}}, so the method returns {{{}Integer.MAX_VALUE{}}}. Every pipeline 
node therefore gets the same cost unless it _equals_ the client. The sort 
reduces to "client's own node first (when the client is a datanode in the 
pipeline), then random". Rack locality is never applied to reads.

HDDS-15059 (2.3.0) describes exactly this in a code comment for the 
streaming-write path ("The nodes come from SCM over RPC, so they are 
deserialized DatanodeDetails with no parent/level: the topology treats them as 
unknown (distance Integer.MAX_VALUE) and the order comes out random").

In the HDDS-15059 review ([PR 
#10633|https://github.com/apache/ozone/pull/10633]) the read path was 
explicitly left untouched.
h2. Reproduction

A unit test in {{hadoop-hdds/framework}} 
({{{}org.apache.hadoop.hdds.scm.net{}}}) on 2.2.1:
 # Build a {{NetworkTopologyImpl}} with ROOT/RACK/LEAF schemas and 6 datanodes 
on {{{}/r1{}}}, {{{}/r2{}}}, {{{}/r3{}}}, two per rack.
 # Make "pipeline" copies the way OM receives them: 
{{{}DatanodeDetails.getFromProtoBuf(dn.getProtoBufMessage()){}}}.
 # Use a non-datanode client on {{{}/r1{}}}, built the way 
{{KeyManagerImpl.getOtherNode}} does it: {{{}new NodeImpl(host, "/r1", rack, 
rack.getLevel() + 1, NODE_COST_DEFAULT){}}}.

||Measure||Topology-linked nodes||Deserialized (as on OM's read path)||
|{{level}} / {{parent}}|3 / {{{}/r1{}}}...|3 / {{null}}|
|{{getDistanceCost(client, node)}}|2 same rack, 4 other rack|{{2147483647}} for 
all|
|Both {{/r1}} nodes sorted first, 1000 sorts|1000|76 (random: 2/6 × 1/5 ≈ 6.7%)|
|Client = datanode dn0 on {{{}/r1{}}}: other {{/r1}} node sorted 
second|n/a|179/1000 (random ≈ 20%)|

Test source attached: {{{}TestReadPathTopologySort.java{}}}.
h2. Impact
 * {{ozone.network.topology.aware.read=true}} (the default) gives no rack 
locality for RATIS reads. Only same-host preference survives.
 * It still costs the full sort on every read. Each node's {{getDistanceCost}} 
takes {{{}NetworkTopologyImpl{}}}'s global read lock before returning 
{{{}MAX_VALUE{}}}. Under heavy read load that lock is a CPU hot spot (see 
HDDS-17553).
 * EC reads aren't affected by the ordering ({{{}ECBlockInputStream{}}} selects 
nodes by replica index from {{{}getNodes(){}}}).

h2. Proposed fix

Give the read path real rack distances for pipeline nodes that arrive from SCM 
without a parent, {*}without adding per-node lock acquisitions{*}:
 * Preferred: compute the sort cost from the node's 
{{{}networkLocation{}}}/{{{}level{}}} (lock-free, like HDFS 
{{{}NetworkTopology.getWeightUsingNetworkLocation{}}}, using the schema's 
per-level costs), so unlinked nodes need no tree lookup.

Independent of the lock-contention fix (HDDS-17553). Unlinked nodes take the 
path branch before any lock, so this change alone also removes the per-node 
{{netlock}} acquisition from OM's read sort. Both touch 
{{{}sortByDistanceCost{}}}, so whichever lands second needs a small rebase.



--
This message was sent by Atlassian Jira
(v10.3.26#10030026)

---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to