This is an automated email from the ASF dual-hosted git repository.
ivandika3 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/ozone.git
The following commit(s) were added to refs/heads/master by this push:
new d280cea6a03 HDDS-15492. Support OM follower read for gRPC client
(#10591)
d280cea6a03 is described below
commit d280cea6a03b98f569d2e4837a49050d5e0295b7
Author: Eric C. Ho <[email protected]>
AuthorDate: Tue Jun 30 11:34:02 2026 +0800
HDDS-15492. Support OM follower read for gRPC client (#10591)
---
.../ozone/om/ha/GrpcOMFailoverProxyProvider.java | 4 +
.../ozone/om/protocolPB/GrpcOmTransport.java | 176 +++++++++++++++--
.../ozone/om/protocolPB/TestS3GrpcOmTransport.java | 219 +++++++++++++++++++++
.../ozone/om/AbstractOzoneManagerHATest.java | 2 +
.../org/apache/hadoop/ozone/om/OmTestUtil.java | 8 +
...stOzoneManagerHAFollowerReadWithAllRunning.java | 71 +++++--
...OzoneManagerHAFollowerReadWithStoppedNodes.java | 1 +
.../hadoop/ozone/MiniOzoneHAClusterImpl.java | 3 +
8 files changed, 456 insertions(+), 28 deletions(-)
diff --git
a/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/ha/GrpcOMFailoverProxyProvider.java
b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/ha/GrpcOMFailoverProxyProvider.java
index 41cd4595654..471f15789f6 100644
---
a/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/ha/GrpcOMFailoverProxyProvider.java
+++
b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/ha/GrpcOMFailoverProxyProvider.java
@@ -128,6 +128,10 @@ protected synchronized boolean shouldFailover(Exception
ex) {
return super.shouldFailover(ex);
}
+ public synchronized boolean shouldFailoverForFollowerRead(Exception ex) {
+ return shouldFailover(ex);
+ }
+
@Override
public synchronized void close() throws IOException { }
diff --git
a/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/protocolPB/GrpcOmTransport.java
b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/protocolPB/GrpcOmTransport.java
index e794107cd54..21baa053e44 100644
---
a/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/protocolPB/GrpcOmTransport.java
+++
b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/om/protocolPB/GrpcOmTransport.java
@@ -51,16 +51,23 @@
import org.apache.hadoop.io.Text;
import org.apache.hadoop.io.retry.RetryPolicy;
import org.apache.hadoop.ipc_.RemoteException;
+import org.apache.hadoop.ozone.OmUtils;
import org.apache.hadoop.ozone.OzoneConfigKeys;
import org.apache.hadoop.ozone.om.exceptions.OMException;
import org.apache.hadoop.ozone.om.exceptions.OMException.ResultCodes;
import org.apache.hadoop.ozone.om.ha.GrpcOMFailoverProxyProvider;
+import org.apache.hadoop.ozone.om.ha.OMFailoverProxyProviderBase;
+import org.apache.hadoop.ozone.om.helpers.ReadConsistency;
import
org.apache.hadoop.ozone.om.protocolPB.grpc.ClientAddressClientInterceptor;
import org.apache.hadoop.ozone.om.protocolPB.grpc.GrpcClientConstants;
import
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.OMRequest;
import
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.OMResponse;
+import
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.ReadConsistencyHint;
import org.apache.hadoop.ozone.protocol.proto.OzoneManagerServiceGrpc;
import org.apache.hadoop.security.UserGroupInformation;
+import org.apache.ratis.protocol.exceptions.ReadException;
+import org.apache.ratis.protocol.exceptions.ReadIndexException;
+import org.apache.ratis.util.Preconditions;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -92,6 +99,10 @@ public class GrpcOmTransport implements OmTransport {
private RetryPolicy retryPolicy;
private final GrpcOMFailoverProxyProvider<OzoneManagerProtocolPB>
omFailoverProxyProvider;
+ private volatile boolean useFollowerRead;
+ private final ReadConsistencyHint followerReadConsistency;
+ private final ReadConsistencyHint leaderReadConsistency;
+ private int currentFollowerReadIndex = -1;
public static void setCaCerts(List<X509Certificate> x509Certificates) {
caCerts = x509Certificates;
@@ -117,6 +128,27 @@ public GrpcOmTransport(ConfigurationSource conf,
omServiceId,
OzoneManagerProtocolPB.class);
+ this.useFollowerRead = conf.getBoolean(
+ OzoneConfigKeys.OZONE_CLIENT_FOLLOWER_READ_ENABLED_KEY,
+ OzoneConfigKeys.OZONE_CLIENT_FOLLOWER_READ_ENABLED_DEFAULT);
+ String defaultFollowerReadConsistencyStr = conf.get(
+ OzoneConfigKeys.OZONE_CLIENT_FOLLOWER_READ_DEFAULT_CONSISTENCY_KEY,
+ OzoneConfigKeys.OZONE_CLIENT_FOLLOWER_READ_DEFAULT_CONSISTENCY_DEFAULT
+ );
+ ReadConsistency defaultFollowerReadConsistency =
+ ReadConsistency.valueOf(defaultFollowerReadConsistencyStr);
+ String defaultLeaderReadConsistencyStr = conf.get(
+ OzoneConfigKeys.OZONE_CLIENT_LEADER_READ_DEFAULT_CONSISTENCY_KEY,
+ OzoneConfigKeys.OZONE_CLIENT_LEADER_READ_DEFAULT_CONSISTENCY_DEFAULT);
+ ReadConsistency defaultLeaderReadConsistency =
+ ReadConsistency.valueOf(defaultLeaderReadConsistencyStr);
+
Preconditions.assertTrue(defaultFollowerReadConsistency.allowFollowerRead(),
+ "Invalid follower read consistency " + defaultFollowerReadConsistency);
+ Preconditions.assertTrue(!defaultLeaderReadConsistency.allowFollowerRead(),
+ "Invalid leader read consistency " + defaultLeaderReadConsistency);
+ this.followerReadConsistency = defaultFollowerReadConsistency.getHint();
+ this.leaderReadConsistency = defaultLeaderReadConsistency.getHint();
+
start();
}
@@ -174,7 +206,63 @@ public void start() throws IOException {
@Override
public OMResponse submitRequest(OMRequest payload) throws IOException {
- AtomicReference<OMResponse> resp = new AtomicReference<>();
+ if (useFollowerRead && OmUtils.shouldSendToFollower(payload)) {
+ return submitRequestWithFollowerRead(payload);
+ }
+ return submitRequestToLeader(addReadConsistencyHint(payload,
+ leaderReadConsistency));
+ }
+
+ private OMResponse submitRequestWithFollowerRead(OMRequest payload)
+ throws IOException {
+ OMRequest followerPayload = addReadConsistencyHint(payload,
+ followerReadConsistency);
+ int failedCount = 0;
+ for (int i = 0; useFollowerRead &&
+ i < omFailoverProxyProvider.getOMProxyMap().getNodeIds().size(); i++) {
+ String nodeId = getCurrentFollowerReadNodeId();
+ String followerHost =
omFailoverProxyProvider.getGrpcProxyAddress(nodeId);
+ try {
+ OMResponse response = submitRequestToHost(followerPayload,
followerHost);
+ LOG.debug("Invocation with cmdType {} using follower read host {} was
successful",
+ followerPayload.getCmdType(), followerHost);
+ return response;
+ } catch (StatusRuntimeException e) {
+ LOG.debug("Invocation with cmdType {} using follower read host {}
failed",
+ followerPayload.getCmdType(), followerHost, e);
+ Exception unwrapped = unwrapException(new Exception(e));
+ if (OMFailoverProxyProviderBase.getNotLeaderException(unwrapped) !=
null) {
+ LOG.debug("Encountered OMNotLeaderException from {}. Disable OM
follower read and retry OM leader directly.",
+ followerHost);
+ useFollowerRead = false;
+ break;
+ }
+ if (OMFailoverProxyProviderBase.getLeaderNotReadyException(unwrapped)
!= null) {
+ break;
+ }
+ ReadIndexException readIndexException =
+ OMFailoverProxyProviderBase.getReadIndexException(unwrapped);
+ ReadException readException =
+ OMFailoverProxyProviderBase.getReadException(unwrapped);
+ if (readIndexException != null || readException != null ||
+ omFailoverProxyProvider.shouldFailoverForFollowerRead(unwrapped)) {
+ failedCount++;
+ changeFollowerReadProxy(nodeId);
+ } else {
+ throw e;
+ }
+ }
+ }
+ if (failedCount > 0) {
+ LOG.warn("{} nodes have failed for read request with cmdType {}. Falling
back to leader.",
+ failedCount, payload.getCmdType());
+ }
+ return submitRequestToLeader(addReadConsistencyHint(payload,
+ leaderReadConsistency));
+ }
+
+ private OMResponse submitRequestToLeader(OMRequest payload)
+ throws IOException {
int requestFailoverCount = 0;
boolean tryOtherHost = true;
int expectedFailoverCount = 0;
@@ -183,14 +271,7 @@ public OMResponse submitRequest(OMRequest payload) throws
IOException {
tryOtherHost = false;
expectedFailoverCount = globalFailoverCount.get();
try {
- InetAddress inetAddress = InetAddress.getLocalHost();
- Context.current()
- .withValue(GrpcClientConstants.CLIENT_IP_ADDRESS_CTX_KEY,
- inetAddress.getHostAddress())
- .withValue(GrpcClientConstants.CLIENT_HOSTNAME_CTX_KEY,
- inetAddress.getHostName())
- .run(() -> resp.set(clients.get(host.get())
- .submitRequest(payload)));
+ return submitRequestToHost(payload, host.get());
} catch (StatusRuntimeException e) {
LOG.error("Failed to submit request", e);
if (e.getStatus().getCode() == Status.Code.UNAVAILABLE) {
@@ -208,9 +289,49 @@ public OMResponse submitRequest(OMRequest payload) throws
IOException {
}
}
}
+ throw new OMException(resultCode);
+ }
+
+ private OMResponse submitRequestToHost(OMRequest payload, String targetHost)
+ throws IOException {
+ AtomicReference<OMResponse> resp = new AtomicReference<>();
+ InetAddress inetAddress = InetAddress.getLocalHost();
+ Context.current()
+ .withValue(GrpcClientConstants.CLIENT_IP_ADDRESS_CTX_KEY,
+ inetAddress.getHostAddress())
+ .withValue(GrpcClientConstants.CLIENT_HOSTNAME_CTX_KEY,
+ inetAddress.getHostName())
+ .run(() -> resp.set(clients.get(targetHost)
+ .submitRequest(payload)));
return resp.get();
}
+ private OMRequest addReadConsistencyHint(OMRequest payload,
+ ReadConsistencyHint readConsistencyHint) {
+ if (!payload.hasReadConsistencyHint() && readConsistencyHint != null) {
+ return payload.toBuilder()
+ .setReadConsistencyHint(readConsistencyHint)
+ .build();
+ }
+ return payload;
+ }
+
+ private synchronized String getCurrentFollowerReadNodeId() {
+ if (currentFollowerReadIndex < 0) {
+ currentFollowerReadIndex = 0;
+ }
+ return new
ArrayList<>(omFailoverProxyProvider.getOMProxyMap().getNodeIds())
+ .get(currentFollowerReadIndex);
+ }
+
+ private synchronized void changeFollowerReadProxy(String currentNodeId) {
+ String currentFollowerReadNodeId = getCurrentFollowerReadNodeId();
+ if (currentFollowerReadNodeId.equals(currentNodeId)) {
+ currentFollowerReadIndex = (currentFollowerReadIndex + 1) %
+ omFailoverProxyProvider.getOMProxyMap().getNodeIds().size();
+ }
+ }
+
private Exception unwrapException(Exception ex) {
Exception grpcException = null;
try {
@@ -230,11 +351,10 @@ private Exception unwrapException(Exception ex) {
grpcException = cn.newInstance(status.getDescription());
IOException remote = null;
try {
- String cause = status.getDescription();
- int colonIndex = cause.indexOf(':');
- cause = cause.substring(colonIndex + 2);
- remote = new RemoteException(cause.substring(0, colonIndex),
- cause.substring(colonIndex + 1));
+ String description = status.getDescription();
+ int colonIndex = description.indexOf(':');
+ remote = new RemoteException(description.substring(0, colonIndex),
+ description.substring(colonIndex + 2));
grpcException.initCause(remote);
} catch (Exception e) {
LOG.error("cannot get cause for remote exception");
@@ -371,4 +491,32 @@ public void startClient(ManagedChannel testChannel) throws
IOException {
LOG.info("{}: started", CLIENT_NAME);
}
+ @VisibleForTesting
+ public void startClient(String nodeId, ManagedChannel testChannel) throws
IOException {
+ String hostaddr = omFailoverProxyProvider.getGrpcProxyAddress(nodeId);
+ clients.put(hostaddr,
+ OzoneManagerServiceGrpc
+ .newBlockingStub(testChannel));
+ LOG.info("{}: started test client for {}", CLIENT_NAME, nodeId);
+ }
+
+ @VisibleForTesting
+ public synchronized void changeFollowerReadInitialProxy(String nodeId) {
+ List<String> nodeIds = new ArrayList<>(
+ omFailoverProxyProvider.getOMProxyMap().getNodeIds());
+ for (int i = 0; i < nodeIds.size(); i++) {
+ if (nodeIds.get(i).equals(nodeId)) {
+ currentFollowerReadIndex = i;
+ return;
+ }
+ }
+ }
+
+ @VisibleForTesting
+ public void changeLeaderProxyForTest(String nodeId) throws IOException {
+ omFailoverProxyProvider.setNextOmProxy(nodeId);
+ omFailoverProxyProvider.performFailover(null);
+ host.set(omFailoverProxyProvider.getGrpcProxyAddress(nodeId));
+ }
+
}
diff --git
a/hadoop-ozone/common/src/test/java/org/apache/hadoop/ozone/om/protocolPB/TestS3GrpcOmTransport.java
b/hadoop-ozone/common/src/test/java/org/apache/hadoop/ozone/om/protocolPB/TestS3GrpcOmTransport.java
index 176d9b6d03b..3d2a5fedda3 100644
---
a/hadoop-ozone/common/src/test/java/org/apache/hadoop/ozone/om/protocolPB/TestS3GrpcOmTransport.java
+++
b/hadoop-ozone/common/src/test/java/org/apache/hadoop/ozone/om/protocolPB/TestS3GrpcOmTransport.java
@@ -18,8 +18,12 @@
package org.apache.hadoop.ozone.om.protocolPB;
import static org.apache.hadoop.ozone.ClientVersion.CURRENT_VERSION;
+import static org.apache.hadoop.ozone.om.OMConfigKeys.OZONE_OM_ADDRESS_KEY;
import static
org.apache.hadoop.ozone.om.OMConfigKeys.OZONE_OM_GRPC_MAXIMUM_RESPONSE_LENGTH;
import static
org.apache.hadoop.ozone.om.OMConfigKeys.OZONE_OM_GRPC_MAXIMUM_RESPONSE_LENGTH_DEFAULT;
+import static org.apache.hadoop.ozone.om.OMConfigKeys.OZONE_OM_GRPC_PORT_KEY;
+import static org.apache.hadoop.ozone.om.OMConfigKeys.OZONE_OM_NODES_KEY;
+import static org.apache.hadoop.ozone.om.OMConfigKeys.OZONE_OM_SERVICE_IDS_KEY;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.AdditionalAnswers.delegatesTo;
@@ -29,13 +33,19 @@
import io.grpc.ManagedChannel;
import io.grpc.inprocess.InProcessChannelBuilder;
import io.grpc.inprocess.InProcessServerBuilder;
+import io.grpc.stub.StreamObserver;
import io.grpc.testing.GrpcCleanupRule;
import java.io.IOException;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicReference;
import org.apache.hadoop.hdds.conf.OzoneConfiguration;
import org.apache.hadoop.ozone.OzoneConfigKeys;
+import org.apache.hadoop.ozone.ha.ConfUtils;
import org.apache.hadoop.ozone.om.exceptions.OMNotLeaderException;
import
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.OMRequest;
import
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.OMResponse;
+import
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.ReadConsistencyHint;
+import
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.ReadConsistencyProto;
import
org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.ServiceListRequest;
import org.apache.hadoop.ozone.protocol.proto.OzoneManagerProtocolProtos.Type;
import org.apache.hadoop.ozone.protocol.proto.OzoneManagerServiceGrpc;
@@ -272,6 +282,215 @@ public void testGrpcFailoverExceedMaxMesgLen() throws
Exception {
assertThrows(Exception.class, () -> client.submitRequest(omRequest));
}
+ @Test
+ public void testFollowerReadDoesNotFailoverFromKnownLeader() throws
Exception {
+ conf.setBoolean(OzoneConfigKeys.OZONE_CLIENT_FOLLOWER_READ_ENABLED_KEY,
true);
+ configureHaOmService("om0", "om1");
+
+ AtomicInteger leaderRequestCount = new AtomicInteger();
+ AtomicInteger followerRequestCount = new AtomicInteger();
+ AtomicReference<OMRequest> leaderRequest = new AtomicReference<>();
+
+ client = new GrpcOmTransport(conf, ugi, omServiceId);
+ client.startClient("om0", createNodeChannel("om0",
+ leaderRequestCount, leaderRequest));
+ client.startClient("om1", createNodeChannel("om1",
+ followerRequestCount, new AtomicReference<>()));
+ client.changeLeaderProxyForTest("om0");
+ client.changeFollowerReadInitialProxy("om0");
+
+ OMRequest request = OMRequest.newBuilder()
+ .setCmdType(Type.ListVolume)
+ .setVersion(CURRENT_VERSION)
+ .setClientId("test")
+ .build();
+
+ client.submitRequest(request);
+
+ assertEquals(1, leaderRequestCount.get());
+ assertEquals(0, followerRequestCount.get());
+ assertEquals(ReadConsistencyProto.LINEARIZABLE_ALLOW_FOLLOWER,
+ leaderRequest.get().getReadConsistencyHint().getReadConsistency());
+ }
+
+ @Test
+ public void testFollowerReadDoesNotRouteWriteRequestToFollower() throws
Exception {
+ conf.setBoolean(OzoneConfigKeys.OZONE_CLIENT_FOLLOWER_READ_ENABLED_KEY,
true);
+ configureHaOmService("om0", "om1");
+
+ AtomicInteger leaderRequestCount = new AtomicInteger();
+ AtomicInteger followerRequestCount = new AtomicInteger();
+ AtomicReference<OMRequest> leaderRequest = new AtomicReference<>();
+
+ client = new GrpcOmTransport(conf, ugi, omServiceId);
+ client.startClient("om0", createNodeChannel("om0",
+ leaderRequestCount, leaderRequest));
+ client.startClient("om1", createNodeChannel("om1",
+ followerRequestCount, new AtomicReference<>()));
+ client.changeLeaderProxyForTest("om0");
+ client.changeFollowerReadInitialProxy("om1");
+
+ client.submitRequest(OMRequest.newBuilder()
+ .setCmdType(Type.CreateVolume)
+ .setVersion(CURRENT_VERSION)
+ .setClientId("test")
+ .build());
+
+ assertEquals(1, leaderRequestCount.get());
+ assertEquals(0, followerRequestCount.get());
+ assertEquals(ReadConsistencyProto.DEFAULT,
+ leaderRequest.get().getReadConsistencyHint().getReadConsistency());
+ }
+
+ @Test
+ public void testFollowerReadKeepsExistingConsistencyHint() throws Exception {
+ conf.setBoolean(OzoneConfigKeys.OZONE_CLIENT_FOLLOWER_READ_ENABLED_KEY,
true);
+ configureHaOmService("om0", "om1");
+
+ AtomicInteger followerRequestCount = new AtomicInteger();
+ AtomicReference<OMRequest> followerRequest = new AtomicReference<>();
+
+ client = new GrpcOmTransport(conf, ugi, omServiceId);
+ client.startClient("om0", createNodeChannel("om0",
+ new AtomicInteger(), new AtomicReference<>()));
+ client.startClient("om1", createNodeChannel("om1",
+ followerRequestCount, followerRequest));
+ client.changeLeaderProxyForTest("om0");
+ client.changeFollowerReadInitialProxy("om1");
+
+ client.submitRequest(OMRequest.newBuilder()
+ .setCmdType(Type.ListVolume)
+ .setVersion(CURRENT_VERSION)
+ .setClientId("test")
+ .setReadConsistencyHint(ReadConsistencyHint.newBuilder()
+ .setReadConsistency(ReadConsistencyProto.LOCAL_LEASE)
+ .build())
+ .build());
+
+ assertEquals(1, followerRequestCount.get());
+ assertEquals(ReadConsistencyProto.LOCAL_LEASE,
+ followerRequest.get().getReadConsistencyHint().getReadConsistency());
+ }
+
+ @Test
+ public void testFollowerReadFallsBackToLeaderOnNotLeaderException() throws
Exception {
+ conf.setBoolean(OzoneConfigKeys.OZONE_CLIENT_FOLLOWER_READ_ENABLED_KEY,
true);
+ configureHaOmService("om0", "om1");
+
+ AtomicInteger leaderRequestCount = new AtomicInteger();
+ AtomicInteger followerRequestCount = new AtomicInteger();
+ AtomicReference<OMRequest> leaderRequest = new AtomicReference<>();
+
+ client = new GrpcOmTransport(conf, ugi, omServiceId);
+ client.startClient("om0", createNodeChannel("om0",
+ leaderRequestCount, leaderRequest));
+ client.startClient("om1",
createNotLeaderNodeChannel(followerRequestCount));
+ client.changeLeaderProxyForTest("om0");
+ client.changeFollowerReadInitialProxy("om1");
+
+ client.submitRequest(OMRequest.newBuilder()
+ .setCmdType(Type.ListVolume)
+ .setVersion(CURRENT_VERSION)
+ .setClientId("test")
+ .build());
+
+ assertEquals(1, followerRequestCount.get());
+ assertEquals(1, leaderRequestCount.get());
+ assertEquals(ReadConsistencyProto.DEFAULT,
+ leaderRequest.get().getReadConsistencyHint().getReadConsistency());
+ }
+
+ @Test
+ public void testFollowerReadRejectsInvalidFollowerReadConsistency() {
+ conf.setBoolean(OzoneConfigKeys.OZONE_CLIENT_FOLLOWER_READ_ENABLED_KEY,
true);
+
conf.set(OzoneConfigKeys.OZONE_CLIENT_FOLLOWER_READ_DEFAULT_CONSISTENCY_KEY,
"DEFAULT");
+ configureHaOmService("om0", "om1");
+
+ assertThrows(IllegalStateException.class,
+ () -> new GrpcOmTransport(conf, ugi, omServiceId));
+ }
+
+ @Test
+ public void testFollowerReadRejectsInvalidLeaderReadConsistency() {
+ conf.setBoolean(OzoneConfigKeys.OZONE_CLIENT_FOLLOWER_READ_ENABLED_KEY,
true);
+ conf.set(OzoneConfigKeys.OZONE_CLIENT_LEADER_READ_DEFAULT_CONSISTENCY_KEY,
+ "LINEARIZABLE_ALLOW_FOLLOWER");
+ configureHaOmService("om0", "om1");
+
+ assertThrows(IllegalStateException.class,
+ () -> new GrpcOmTransport(conf, ugi, omServiceId));
+ }
+
+ private void configureHaOmService(String... nodeIds) {
+ omServiceId = "om-service-test";
+ conf.set(OZONE_OM_SERVICE_IDS_KEY, omServiceId);
+ conf.set(ConfUtils.addKeySuffixes(OZONE_OM_NODES_KEY, omServiceId),
+ String.join(",", nodeIds));
+ for (int i = 0; i < nodeIds.length; i++) {
+ conf.set(ConfUtils.addKeySuffixes(OZONE_OM_ADDRESS_KEY, omServiceId,
+ nodeIds[i]), "localhost");
+ conf.setInt(ConfUtils.addKeySuffixes(OZONE_OM_GRPC_PORT_KEY, omServiceId,
+ nodeIds[i]), 19880 + i);
+ }
+ }
+
+ private ManagedChannel createNodeChannel(String nodeId,
+ AtomicInteger requestCount, AtomicReference<OMRequest> lastRequest)
+ throws IOException {
+ String nodeServerName = InProcessServerBuilder.generateName();
+ grpcCleanup.register(InProcessServerBuilder
+ .forName(nodeServerName)
+ .directExecutor()
+ .addService(new OzoneManagerServiceGrpc.OzoneManagerServiceImplBase() {
+ @Override
+ public void submitRequest(OMRequest request,
+ StreamObserver<OMResponse> responseObserver) {
+ requestCount.incrementAndGet();
+ lastRequest.set(request);
+ responseObserver.onNext(OMResponse.newBuilder()
+ .setSuccess(true)
+ .setStatus(org.apache.hadoop.ozone.protocol
+ .proto.OzoneManagerProtocolProtos.Status.OK)
+ .setLeaderOMNodeId(nodeId)
+ .setCmdType(request.getCmdType())
+ .build());
+ responseObserver.onCompleted();
+ }
+ })
+ .build()
+ .start());
+ return grpcCleanup.register(
+
InProcessChannelBuilder.forName(nodeServerName).directExecutor().build());
+ }
+
+ private ManagedChannel createNotLeaderNodeChannel(AtomicInteger requestCount)
+ throws IOException {
+ String nodeServerName = InProcessServerBuilder.generateName();
+ grpcCleanup.register(InProcessServerBuilder
+ .forName(nodeServerName)
+ .directExecutor()
+ .addService(new OzoneManagerServiceGrpc.OzoneManagerServiceImplBase() {
+ @Override
+ public void submitRequest(OMRequest request,
+ StreamObserver<OMResponse> responseObserver) {
+ requestCount.incrementAndGet();
+ try {
+ throw createNotLeaderException();
+ } catch (Throwable e) {
+ IOException ex = new IOException(e.getCause());
+ responseObserver.onError(io.grpc.Status
+ .INTERNAL
+ .withDescription(ex.getMessage())
+ .asRuntimeException());
+ }
+ }
+ })
+ .build()
+ .start());
+ return grpcCleanup.register(
+
InProcessChannelBuilder.forName(nodeServerName).directExecutor().build());
+ }
+
private static OMRequest arbitraryOmRequest() {
ServiceListRequest req = ServiceListRequest.newBuilder().build();
return OMRequest.newBuilder()
diff --git
a/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/om/AbstractOzoneManagerHATest.java
b/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/om/AbstractOzoneManagerHATest.java
index 8152607b39e..65fc84f7a54 100644
---
a/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/om/AbstractOzoneManagerHATest.java
+++
b/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/om/AbstractOzoneManagerHATest.java
@@ -29,6 +29,7 @@
import static org.apache.hadoop.ozone.OzoneConsts.OZONE_URI_DELIMITER;
import static
org.apache.hadoop.ozone.om.OMConfigKeys.OZONE_DEFAULT_BUCKET_LAYOUT;
import static
org.apache.hadoop.ozone.om.OMConfigKeys.OZONE_KEY_DELETING_LIMIT_PER_TASK;
+import static
org.apache.hadoop.ozone.om.OMConfigKeys.OZONE_OM_S3_GPRC_SERVER_ENABLED;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
@@ -158,6 +159,7 @@ protected static void initCluster(boolean
followerReadEnabled) throws Exception
// Enable the OM follower read.
omHAConfig.setReadOption("LINEARIZABLE");
omHAConfig.setReadLeaderLeaseEnabled(true);
+ conf.setBoolean(OZONE_OM_S3_GPRC_SERVER_ENABLED, true);
}
conf.setFromObject(omHAConfig);
diff --git
a/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/om/OmTestUtil.java
b/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/om/OmTestUtil.java
index 1f66b38c309..0d8c3cdf24d 100644
---
a/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/om/OmTestUtil.java
+++
b/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/om/OmTestUtil.java
@@ -20,6 +20,7 @@
import org.apache.hadoop.ozone.client.ObjectStore;
import org.apache.hadoop.ozone.om.ha.HadoopRpcOMFailoverProxyProvider;
import
org.apache.hadoop.ozone.om.ha.HadoopRpcOMFollowerReadFailoverProxyProvider;
+import org.apache.hadoop.ozone.om.protocolPB.GrpcOmTransport;
import org.apache.hadoop.ozone.om.protocolPB.Hadoop3OmTransport;
import
org.apache.hadoop.ozone.om.protocolPB.OzoneManagerProtocolClientSideTranslatorPB;
import org.apache.hadoop.ozone.om.protocolPB.OzoneManagerProtocolPB;
@@ -45,6 +46,13 @@ static HadoopRpcOMFollowerReadFailoverProxyProvider
getFollowerReadFailoverProxy
return transport.getOmFollowerReadFailoverProxyProvider();
}
+ static GrpcOmTransport getGrpcOmTransport(ObjectStore store) {
+ OzoneManagerProtocolClientSideTranslatorPB ozoneManagerClient =
+ (OzoneManagerProtocolClientSideTranslatorPB)
store.getClientProxy().getOzoneManagerClient();
+
+ return (GrpcOmTransport) ozoneManagerClient.getTransport();
+ }
+
static String getCurrentOmProxyNodeId(ObjectStore store) {
return getFailoverProxyProvider(store).getCurrentProxyOMNodeId();
}
diff --git
a/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/om/TestOzoneManagerHAFollowerReadWithAllRunning.java
b/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/om/TestOzoneManagerHAFollowerReadWithAllRunning.java
index 9262da093a4..5dd6a6fecb6 100644
---
a/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/om/TestOzoneManagerHAFollowerReadWithAllRunning.java
+++
b/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/om/TestOzoneManagerHAFollowerReadWithAllRunning.java
@@ -21,6 +21,7 @@
import static
org.apache.hadoop.ozone.OzoneConfigKeys.OZONE_CLIENT_FOLLOWER_READ_DEFAULT_CONSISTENCY_KEY;
import static
org.apache.hadoop.ozone.OzoneConfigKeys.OZONE_CLIENT_FOLLOWER_READ_ENABLED_KEY;
import static
org.apache.hadoop.ozone.OzoneConfigKeys.OZONE_CLIENT_LEADER_READ_DEFAULT_CONSISTENCY_KEY;
+import static org.apache.hadoop.ozone.om.OMConfigKeys.OZONE_OM_TRANSPORT_CLASS;
import static
org.apache.hadoop.ozone.om.exceptions.OMException.ResultCodes.DIRECTORY_NOT_FOUND;
import static
org.apache.hadoop.ozone.om.exceptions.OMException.ResultCodes.FILE_ALREADY_EXISTS;
import static
org.apache.hadoop.ozone.om.exceptions.OMException.ResultCodes.NOT_A_FILE;
@@ -40,6 +41,7 @@
import java.time.Instant;
import java.util.ArrayList;
import java.util.List;
+import java.util.stream.Stream;
import org.apache.commons.lang3.RandomStringUtils;
import org.apache.hadoop.hdds.conf.OzoneConfiguration;
import org.apache.hadoop.hdds.protocol.StorageType;
@@ -59,7 +61,11 @@
import org.apache.hadoop.ozone.om.ha.HadoopRpcOMFailoverProxyProvider;
import
org.apache.hadoop.ozone.om.ha.HadoopRpcOMFollowerReadFailoverProxyProvider;
import org.apache.hadoop.ozone.om.ha.OMProxyInfo;
+import org.apache.hadoop.ozone.om.protocolPB.GrpcOmTransport;
+import org.apache.hadoop.ozone.om.protocolPB.GrpcOmTransportFactory;
+import org.apache.hadoop.ozone.om.protocolPB.Hadoop3OmTransportFactory;
import org.apache.hadoop.ozone.om.protocolPB.OmTransport;
+import org.apache.hadoop.ozone.om.protocolPB.OmTransportFactory;
import
org.apache.hadoop.ozone.om.protocolPB.OzoneManagerProtocolClientSideTranslatorPB;
import org.apache.hadoop.ozone.om.protocolPB.OzoneManagerProtocolPB;
import org.apache.hadoop.ozone.om.ratis.OzoneManagerRatisServer;
@@ -73,6 +79,8 @@
import
org.apache.hadoop.ozone.protocolPB.OzoneManagerProtocolServerSideTranslatorPB;
import org.apache.ozone.test.tag.Flaky;
import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.MethodSource;
/**
* Ozone Manager HA follower read tests where all OMs are running throughout
all tests.
@@ -105,29 +113,63 @@ void testOMFollowerReadProxyProviderInitialization() {
}
}
- @Test
- void testFollowerReadTargetsFollower() throws Exception {
- ObjectStore objectStore = getObjectStore();
- HadoopRpcOMFollowerReadFailoverProxyProvider
followerReadFailoverProxyProvider =
- OmTestUtil.getFollowerReadFailoverProxyProvider(objectStore);
+ private static Stream<Class<? extends OmTransportFactory>>
followerReadTransportClasses() {
+ return Stream.<Class<? extends OmTransportFactory>>of(
+ Hadoop3OmTransportFactory.class,
+ GrpcOmTransportFactory.class);
+ }
+ @ParameterizedTest
+ @MethodSource("followerReadTransportClasses")
+ void testFollowerReadTargetsFollower(Class<? extends OmTransportFactory>
omTransportClass) throws Exception {
+ OzoneConfiguration clientConf = new OzoneConfiguration(getConf());
+ clientConf.setBoolean(OZONE_CLIENT_FOLLOWER_READ_ENABLED_KEY, true);
+ clientConf.set(OZONE_CLIENT_FOLLOWER_READ_DEFAULT_CONSISTENCY_KEY,
"LOCAL_LEASE");
+ clientConf.set(OZONE_OM_TRANSPORT_CLASS, omTransportClass.getName());
String leaderOMNodeId = getCluster().getOMLeader().getOMNodeId();
- String followerOMNodeId = null;
+ OzoneManager followerOM = null;
for (OzoneManager om : getCluster().getOzoneManagersList()) {
if (!om.getOMNodeId().equals(leaderOMNodeId)) {
- followerOMNodeId = om.getOMNodeId();
+ followerOM = om;
break;
}
}
- assertNotNull(followerOMNodeId);
+ assertNotNull(followerOM);
-
followerReadFailoverProxyProvider.changeInitialProxyForTest(followerOMNodeId);
- objectStore.getClientProxy().listVolumes(null, null, 10);
+ OzoneClient ozoneClient = null;
+ try {
+ ozoneClient = OzoneClientFactory.getRpcClient(getOmServiceId(),
clientConf);
+ ObjectStore objectStore = ozoneClient.getObjectStore();
+ changeFollowerReadInitialProxy(objectStore, omTransportClass,
leaderOMNodeId, followerOM.getOMNodeId());
+ long previousLocalLeaseSuccess =
followerOM.getMetrics().getNumFollowerReadLocalLeaseSuccess();
+
+ objectStore.listVolumes("");
- OMProxyInfo<OzoneManagerProtocolPB> lastProxy =
- (OMProxyInfo<OzoneManagerProtocolPB>)
followerReadFailoverProxyProvider.getLastProxy();
- assertNotNull(lastProxy);
- assertEquals(followerOMNodeId, lastProxy.getNodeId());
+ long currentLocalLeaseSuccess =
followerOM.getMetrics().getNumFollowerReadLocalLeaseSuccess();
+
assertThat(currentLocalLeaseSuccess).isGreaterThan(previousLocalLeaseSuccess);
+ } finally {
+ IOUtils.closeQuietly(ozoneClient);
+ }
+ }
+
+ private void changeFollowerReadInitialProxy(ObjectStore objectStore,
+ Class<? extends OmTransportFactory> omTransportClass, String
leaderOMNodeId, String followerOMNodeId)
+ throws Exception {
+ if (Hadoop3OmTransportFactory.class.equals(omTransportClass)) {
+ HadoopRpcOMFollowerReadFailoverProxyProvider
followerReadFailoverProxyProvider =
+ OmTestUtil.getFollowerReadFailoverProxyProvider(objectStore);
+
followerReadFailoverProxyProvider.changeInitialProxyForTest(followerOMNodeId);
+ return;
+ }
+
+ if (GrpcOmTransportFactory.class.equals(omTransportClass)) {
+ GrpcOmTransport grpcOmTransport =
OmTestUtil.getGrpcOmTransport(objectStore);
+ grpcOmTransport.changeLeaderProxyForTest(leaderOMNodeId);
+ grpcOmTransport.changeFollowerReadInitialProxy(followerOMNodeId);
+ return;
+ }
+
+ throw new IllegalArgumentException("Unsupported OM transport class " +
omTransportClass);
}
/**
@@ -571,4 +613,5 @@ void testClientWithLocalLeaseEnabled() throws Exception {
IOUtils.closeQuietly(ozoneClient);
}
}
+
}
diff --git
a/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/om/TestOzoneManagerHAFollowerReadWithStoppedNodes.java
b/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/om/TestOzoneManagerHAFollowerReadWithStoppedNodes.java
index 878bfad603b..e26e5328d7a 100644
---
a/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/om/TestOzoneManagerHAFollowerReadWithStoppedNodes.java
+++
b/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/om/TestOzoneManagerHAFollowerReadWithStoppedNodes.java
@@ -312,6 +312,7 @@ void testIncrementalWaitTimeWithSameNodeFailover() throws
Exception {
}
@Test
+ @Order(Integer.MAX_VALUE)
void testOMRetryProxy() {
int maxFailoverAttempts = getOzoneClientFailoverMaxAttempts();
// Stop all the OMs.
diff --git
a/hadoop-ozone/mini-cluster/src/main/java/org/apache/hadoop/ozone/MiniOzoneHAClusterImpl.java
b/hadoop-ozone/mini-cluster/src/main/java/org/apache/hadoop/ozone/MiniOzoneHAClusterImpl.java
index 48fd95b075e..0a553853bd9 100644
---
a/hadoop-ozone/mini-cluster/src/main/java/org/apache/hadoop/ozone/MiniOzoneHAClusterImpl.java
+++
b/hadoop-ozone/mini-cluster/src/main/java/org/apache/hadoop/ozone/MiniOzoneHAClusterImpl.java
@@ -376,12 +376,15 @@ private static void configureOMPorts(ConfigurationTarget
conf,
OMConfigKeys.OZONE_OM_HTTP_ADDRESS_KEY, omServiceId, omNodeId);
String omHttpsAddrKey = ConfUtils.addKeySuffixes(
OMConfigKeys.OZONE_OM_HTTPS_ADDRESS_KEY, omServiceId, omNodeId);
+ String omGrpcPortKey = ConfUtils.addKeySuffixes(
+ OMConfigKeys.OZONE_OM_GRPC_PORT_KEY, omServiceId, omNodeId);
String omRatisPortKey = ConfUtils.addKeySuffixes(
OMConfigKeys.OZONE_OM_RATIS_PORT_KEY, omServiceId, omNodeId);
conf.set(omAddrKey, localhostWithFreePort());
conf.set(omHttpAddrKey, localhostWithFreePort());
conf.set(omHttpsAddrKey, localhostWithFreePort());
+ conf.setInt(omGrpcPortKey, getFreePort());
conf.setInt(omRatisPortKey, getFreePort());
}
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]