Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
import org.apache.hadoop.hdds.HddsUtils;
import org.apache.hadoop.hdds.conf.ConfigurationException;
import org.apache.hadoop.hdds.conf.ConfigurationSource;
import org.apache.hadoop.hdds.scm.net.HostAndPort;
import org.apache.hadoop.ozone.ha.ConfUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
Expand All @@ -60,10 +61,10 @@ public class SCMNodeInfo {
private static final Logger LOG = LoggerFactory.getLogger(SCMNodeInfo.class);
private final String serviceId;
private final String nodeId;
private final String blockClientAddress;
private final String scmClientAddress;
private final String scmSecurityAddress;
private final String scmDatanodeAddress;
private final HostAndPort blockClientAddress;
private final HostAndPort scmClientAddress;
private final HostAndPort scmSecurityAddress;
private final HostAndPort scmDatanodeAddress;

/**
* Build SCM Node information from configuration.
Expand Down Expand Up @@ -177,8 +178,8 @@ public static List<SCMNodeInfo> buildNodeInfo(ConfigurationSource conf) {

}

public static String buildAddress(String address, int port) {
return address + ':' + port;
private static HostAndPort buildAddress(String address, int port) {
return new HostAndPort(address, port);
}

public static int getPort(ConfigurationSource conf,
Expand Down Expand Up @@ -209,8 +210,8 @@ public static int getPort(ConfigurationSource conf,
* @param scmDatanodeAddress
*/
public SCMNodeInfo(String serviceId, String nodeId,
String blockClientAddress, String scmClientAddress,
String scmSecurityAddress, String scmDatanodeAddress) {
HostAndPort blockClientAddress, HostAndPort scmClientAddress,
HostAndPort scmSecurityAddress, HostAndPort scmDatanodeAddress) {
this.serviceId = serviceId;
this.nodeId = nodeId;
this.blockClientAddress = blockClientAddress;
Expand All @@ -228,18 +229,22 @@ public String getNodeId() {
}

public String getBlockClientAddress() {
return blockClientAddress;
return blockClientAddress.getHostAndPortString();

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
return blockClientAddress.getHostAndPortString();
return blockClientAddress == null ? null : blockClientAddress.getHostAndPortString();

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These HostAndPort variables in SCMNodeInfo are never null. Let's add Objects.requireNonNull in the constructor instead.

}

public String getScmClientAddress() {
return scmClientAddress;
return scmClientAddress.getHostAndPortString();

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
return scmClientAddress.getHostAndPortString();
return scmClientAddress == null ? null : scmClientAddress.getHostAndPortString();

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For comments along the same line, it is better to suggest one change and mention that the suggestion applies to other parts of the code. This makes the review less verbose, plus others do not need to respond to each similar comment.

}

public String getScmSecurityAddress() {
return scmSecurityAddress;
return scmSecurityAddress.getHostAndPortString();

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
return scmSecurityAddress.getHostAndPortString();
return scmSecurityAddress == null ? null : scmSecurityAddress.getHostAndPortString();

}

public String getScmDatanodeAddress() {
public HostAndPort getScmDatanodeHostPortAddress() {
return scmDatanodeAddress;
}

public String getScmDatanodeAddress() {
return scmDatanodeAddress.getHostAndPortString();

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
return scmDatanodeAddress.getHostAndPortString();
return scmDatanodeAddress == null ? null : scmDatanodeAddress.getHostAndPortString();

}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
/*
* 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.
*/

package org.apache.hadoop.hdds.scm.net;

import java.net.InetSocketAddress;
import org.apache.hadoop.net.NetUtils;

/**
* A class for host and port.
* It also has an address which can be updated from time to time.
*/
public class HostAndPort {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor (naming): this collides with Guava's com.google.common.net.HostAndPort, which is already imported in this same module at HddsUtils.java:36. It also lives in scm.net, which package-info scopes to network topology (NetworkTopology/Node/…) and which already has its own NetUtils — so the import org.apache.hadoop.net.NetUtils here shadows the same-package type. A more specific name/home (e.g. ScmEndpoint) would avoid both. Cheap while the type is new.

— Claude Code (AI-assisted review)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can replace Guava's HostAndPort with this. (Can be done in follow-up if considered out-of-scope.)

private final String host;
private final int port;
private final String hostAndPortString;
private final int hash;
/** The address can be updated from time to time. */
private InetSocketAddress address;

private HostAndPort(String host, int port, InetSocketAddress address) {
this.host = host;
this.port = port;
this.hostAndPortString = host + ":" + port;
this.hash = host.hashCode() ^ Integer.hashCode(port);
this.address = address != null ? address : NetUtils.createSocketAddr(hostAndPortString);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The constructor resolves eagerly. For the block/client/security addresses in SCMNodeInfo the resolved value is never read — those getters return only getHostAndPortString(), and the string consumers re-resolve it (e.g. HddsUtils.getScmAddressForClients and StorageContainerManager both call NetUtils.createSocketAddr on it again) — so each ends up resolved twice and this result is discarded. Resolving lazily inside getAddress() would drop the duplicate lookup and also fits the "address can be updated from time to time" intent.

— Claude Code (AI-assisted review)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use InetSocketAddress.createUnresolved(host, port) directly instead of Hadoop's NetUtils.createSocketAddr. The latter parses the string, into host and port (so we can save this processing), and handles more complex cases that we do not need here.

@szetszwo szetszwo Jun 26, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The constructor resolves eagerly ...

Use InetSocketAddress.createUnresolved(host, port) directly ...

Let's change the address resolution logic in HDDS-15533. This PR just do refactoring.

}

public HostAndPort(String host, int port) {
this(host, port, null);
}

public HostAndPort(InetSocketAddress address) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

equals/hashCode key on host, but this ctor derives host from address.getHostName() while the other uses the raw configured string. Those can differ (IP literal vs configured name), so two instances for the same node could be unequal and miss in the endpoint maps. It looks like this ctor is test-only today, so it's latent — but since stable host-string identity is the point of the change, might be worth dropping it or documenting that callers must use one consistent spelling.

— Claude Code (AI-assisted review)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is only used in ContainerTestUtils. Let me change the code there.

this(address.getHostName(), address.getPort(), address);
}

public String getHostName() {
return host;
}

public int getPort() {
return port;
}

public String getHostAndPortString() {
return hostAndPortString;
}

public synchronized InetSocketAddress getAddress() {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why is this synchronized?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is for HDDS-15533, which will update it. Actually, let's remove it here and add it there.

return address;
}

@Override
public int hashCode() {
return hash;
}

@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
} else if (!(obj instanceof HostAndPort)) {
return false;
}
final HostAndPort that = (HostAndPort) obj;
// address must not be compared
return this.hash == that.hash
&& this.port == that.port
&& this.host.equals(that.host);
}

@Override
public String toString() {
final InetSocketAddress a = getAddress();
final Object resolved = a != null && a.getAddress() != null ? a.getAddress() : "<unresolved>";
return hostAndPortString + "/" + resolved;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,6 @@
import com.google.common.collect.Sets;
import java.io.File;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
Expand Down Expand Up @@ -71,6 +70,7 @@
import org.apache.hadoop.hdds.protocol.DiskBalancerProtocol;
import org.apache.hadoop.hdds.protocol.SecretKeyProtocol;
import org.apache.hadoop.hdds.protocolPB.SCMSecurityProtocolClientSideTranslatorPB;
import org.apache.hadoop.hdds.scm.net.HostAndPort;
import org.apache.hadoop.hdds.security.SecurityConfig;
import org.apache.hadoop.hdds.security.symmetric.DefaultSecretKeyClient;
import org.apache.hadoop.hdds.security.symmetric.SecretKeyClient;
Expand Down Expand Up @@ -764,12 +764,12 @@ private String reconfigScmNodes(String value) {
LOG.info("Reconfiguring SCM nodes for service ID {} with new SCM nodes {} and remove SCM nodes {}",
scmServiceId, scmNodesIdsToAdd, scmNodesIdsToRemove);

Collection<Pair<String, InetSocketAddress>> scmToAdd = HddsServerUtil.getSCMAddressForDatanodes(
final Collection<Pair<String, HostAndPort>> scmToAdd = HddsServerUtil.getSCMAddressForDatanodes(
getConf(), scmServiceId, scmNodesIdsToAdd);
if (scmToAdd == null) {
throw new IllegalStateException("Reconfiguration failed to get SCM address to add due to wrong configuration");
}
Collection<Pair<String, InetSocketAddress>> scmToRemove = HddsServerUtil.getSCMAddressForDatanodes(
final Collection<Pair<String, HostAndPort>> scmToRemove = HddsServerUtil.getSCMAddressForDatanodes(
getConf(), scmServiceId, scmNodesIdsToRemove);
if (scmToRemove == null) {
throw new IllegalArgumentException(
Expand All @@ -790,10 +790,10 @@ private String reconfigScmNodes(String value) {
}

// Add the new SCM servers
for (Pair<String, InetSocketAddress> pair : scmToAdd) {
for (Pair<String, HostAndPort> pair : scmToAdd) {
String scmNodeId = pair.getLeft();
InetSocketAddress scmAddress = pair.getRight();
if (scmAddress.isUnresolved()) {
final HostAndPort scmAddress = pair.getRight();
if (scmAddress.getAddress().isUnresolved()) {
LOG.warn("Reconfiguration failed to add SCM address {} for SCM service {} since it can't " +
"be resolved, skipping", scmAddress, scmServiceId);
continue;
Expand All @@ -809,9 +809,9 @@ private String reconfigScmNodes(String value) {
}

// Remove the old SCM server
for (Pair<String, InetSocketAddress> pair : scmToRemove) {
for (Pair<String, HostAndPort> pair : scmToRemove) {
String scmNodeId = pair.getLeft();
InetSocketAddress scmAddress = pair.getRight();
final HostAndPort scmAddress = pair.getRight();
try {
connectionManager.removeSCMServer(scmAddress);
context.removeEndpoint(scmAddress);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,11 @@

import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.CaseFormat;
import java.net.InetSocketAddress;
import java.util.HashMap;
import java.util.Map;
import org.apache.commons.text.WordUtils;
import org.apache.hadoop.hdds.protocol.proto.StorageContainerDatanodeProtocolProtos.SCMCommandProto;
import org.apache.hadoop.hdds.scm.net.HostAndPort;
import org.apache.hadoop.hdfs.util.EnumCounters;
import org.apache.hadoop.metrics2.MetricsCollector;
import org.apache.hadoop.metrics2.MetricsInfo;
Expand Down Expand Up @@ -68,9 +68,9 @@ public final class DatanodeQueueMetrics implements MetricsSource {

private Map<SCMCommandProto.Type, MetricsInfo> stateContextCommandQueueMap;
private Map<SCMCommandProto.Type, MetricsInfo> commandDispatcherQueueMap;
private Map<InetSocketAddress, MetricsInfo> incrementalReportsQueueMap;
private Map<InetSocketAddress, MetricsInfo> containerActionQueueMap;
private Map<InetSocketAddress, MetricsInfo> pipelineActionQueueMap;
private Map<HostAndPort, MetricsInfo> incrementalReportsQueueMap;
private Map<HostAndPort, MetricsInfo> containerActionQueueMap;
private Map<HostAndPort, MetricsInfo> pipelineActionQueueMap;

public DatanodeQueueMetrics(DatanodeStateMachine datanodeStateMachine) {
this.registry = new MetricsRegistry(METRICS_SOURCE_NAME);
Expand Down Expand Up @@ -132,19 +132,19 @@ public void getMetrics(MetricsCollector collector, boolean b) {
tmpEnum.get(entry.getKey()));
}

for (Map.Entry<InetSocketAddress, MetricsInfo> entry:
for (Map.Entry<HostAndPort, MetricsInfo> entry:
incrementalReportsQueueMap.entrySet()) {
builder.addGauge(entry.getValue(),
datanodeStateMachine.getContext()
.getIncrementalReportQueueSize().getOrDefault(entry.getKey(), 0));
}
for (Map.Entry<InetSocketAddress, MetricsInfo> entry:
for (Map.Entry<HostAndPort, MetricsInfo> entry:
containerActionQueueMap.entrySet()) {
builder.addGauge(entry.getValue(),
datanodeStateMachine.getContext()
.getContainerActionQueueSize().getOrDefault(entry.getKey(), 0));
}
for (Map.Entry<InetSocketAddress, MetricsInfo> entry:
for (Map.Entry<HostAndPort, MetricsInfo> entry:
pipelineActionQueueMap.entrySet()) {
builder.addGauge(entry.getValue(),
datanodeStateMachine.getContext().getPipelineActionQueueSize()
Expand All @@ -157,7 +157,7 @@ public static synchronized void unRegister() {
DefaultMetricsSystem.instance().unregisterSource(METRICS_SOURCE_NAME);
}

public void addEndpoint(InetSocketAddress endpoint) {
public void addEndpoint(HostAndPort endpoint) {
incrementalReportsQueueMap.computeIfAbsent(endpoint,
k -> getMetricsInfo(INCREMENTAL_REPORT_QUEUE_PREFIX,
CaseFormat.UPPER_UNDERSCORE
Expand All @@ -172,7 +172,7 @@ public void addEndpoint(InetSocketAddress endpoint) {
.to(CaseFormat.UPPER_CAMEL, k.getHostName())));
}

public void removeEndpoint(InetSocketAddress endpoint) {
public void removeEndpoint(HostAndPort endpoint) {
incrementalReportsQueueMap.remove(endpoint);
containerActionQueueMap.remove(endpoint);
pipelineActionQueueMap.remove(endpoint);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@

import com.google.common.util.concurrent.ThreadFactoryBuilder;
import java.io.Closeable;
import java.net.InetSocketAddress;
import java.time.ZonedDateTime;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
Expand All @@ -31,6 +30,7 @@
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import org.apache.hadoop.hdds.conf.ConfigurationSource;
import org.apache.hadoop.hdds.scm.net.HostAndPort;
import org.apache.hadoop.ozone.protocol.VersionResponse;
import org.apache.hadoop.ozone.protocolPB.ReconDatanodeProtocolPB;
import org.apache.hadoop.ozone.protocolPB.StorageContainerDatanodeProtocolClientSideTranslatorPB;
Expand All @@ -46,7 +46,7 @@ public class EndpointStateMachine
LOG = LoggerFactory.getLogger(EndpointStateMachine.class);
private final StorageContainerDatanodeProtocolClientSideTranslatorPB endPoint;
private final AtomicLong missedCount;
private final InetSocketAddress address;
private final HostAndPort hostAndPort;
private final Lock lock;
private final ConfigurationSource conf;
private EndPointStates state = EndPointStates.FIRST;
Expand All @@ -64,18 +64,17 @@ public class EndpointStateMachine
*
* @param endPoint - RPC endPoint.
*/
public EndpointStateMachine(InetSocketAddress address,
public EndpointStateMachine(HostAndPort hostAndPort,
StorageContainerDatanodeProtocolClientSideTranslatorPB endPoint,
ConfigurationSource conf, String threadNamePrefix) {
this.endPoint = endPoint;
this.missedCount = new AtomicLong(0);
this.address = address;
this.hostAndPort = hostAndPort;
lock = new ReentrantLock();
this.conf = conf;
executorService = Executors.newSingleThreadExecutor(
new ThreadFactoryBuilder()
.setNameFormat(threadNamePrefix + "EndpointStateMachineTaskThread-"
+ this.address + "-%d ")
.setNameFormat(threadNamePrefix + "EndpointStateMachineTaskThread-" + hostAndPort + "-%d ")
.build());
}

Expand Down Expand Up @@ -180,7 +179,7 @@ public long getMissedCount() {

@Override
public String getAddressString() {
return getAddress().toString();
return getAddress().getHostAndPortString();

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

getAddressString() is part of EndpointStateMachineMBean, so this shifts the JMX value from host/ip:port to host:port (drops the resolved IP). Intentional? Flagging only in case any monitoring parses it.

— Claude Code (AI-assisted review)

}

public void zeroMissedCount() {
Expand All @@ -192,8 +191,8 @@ public void zeroMissedCount() {
*
* @return - EndPoint.
*/
public InetSocketAddress getAddress() {
return this.address;
public HostAndPort getAddress() {
return hostAndPort;
}

/**
Expand All @@ -213,7 +212,7 @@ public InetSocketAddress getAddress() {
*/
@Override
public String toString() {
return address.toString();
return hostAndPort.toString();
}

/**
Expand All @@ -236,14 +235,8 @@ public void logIfNeeded(Exception ex) {
long missedDurationSeconds = TimeUnit.MILLISECONDS.toSeconds(
this.getMissedCount() * getScmHeartbeatInterval(this.conf)
);
LOG.warn(
"Unable to communicate to {} server at {}:{} for past {} seconds.",
serverName,
address.getAddress(),
address.getPort(),
missedDurationSeconds,
ex
);
LOG.warn("Unable to communicate to {} server at {} past {} seconds.",
serverName, hostAndPort, missedDurationSeconds, ex);
}

if (LOG.isTraceEnabled()) {
Expand Down
Loading