From 9f85789ad1e0c2e548b0ac0560dda8584b43be0c Mon Sep 17 00:00:00 2001 From: "zengqiang.xu" Date: Mon, 8 Aug 2022 18:52:16 +0800 Subject: [PATCH 1/3] HDFS-16724. RBF should support get the information about ancestor mount points --- .../router/NoLocationException.java | 32 ++++ .../router/RouterClientProtocol.java | 33 ++-- .../federation/router/RouterRpcServer.java | 2 +- .../TestRouterMountTableWithoutDefaultNS.java | 154 ++++++++++++++++++ 4 files changed, 207 insertions(+), 14 deletions(-) create mode 100644 hadoop-hdfs-project/hadoop-hdfs-rbf/src/main/java/org/apache/hadoop/hdfs/server/federation/router/NoLocationException.java create mode 100644 hadoop-hdfs-project/hadoop-hdfs-rbf/src/test/java/org/apache/hadoop/hdfs/server/federation/router/TestRouterMountTableWithoutDefaultNS.java diff --git a/hadoop-hdfs-project/hadoop-hdfs-rbf/src/main/java/org/apache/hadoop/hdfs/server/federation/router/NoLocationException.java b/hadoop-hdfs-project/hadoop-hdfs-rbf/src/main/java/org/apache/hadoop/hdfs/server/federation/router/NoLocationException.java new file mode 100644 index 0000000000000..580a4907d9a99 --- /dev/null +++ b/hadoop-hdfs-project/hadoop-hdfs-rbf/src/main/java/org/apache/hadoop/hdfs/server/federation/router/NoLocationException.java @@ -0,0 +1,32 @@ +/** + * 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.hdfs.server.federation.router; + +import java.io.IOException; + +/** + * Exception when no location found. + */ +public class NoLocationException extends IOException { + + private static final long serialVersionUID = 1L; + + public NoLocationException(String message) { + super(message); + } +} diff --git a/hadoop-hdfs-project/hadoop-hdfs-rbf/src/main/java/org/apache/hadoop/hdfs/server/federation/router/RouterClientProtocol.java b/hadoop-hdfs-project/hadoop-hdfs-rbf/src/main/java/org/apache/hadoop/hdfs/server/federation/router/RouterClientProtocol.java index 73445595de7ad..526f83ffdd1f4 100644 --- a/hadoop-hdfs-project/hadoop-hdfs-rbf/src/main/java/org/apache/hadoop/hdfs/server/federation/router/RouterClientProtocol.java +++ b/hadoop-hdfs-project/hadoop-hdfs-rbf/src/main/java/org/apache/hadoop/hdfs/server/federation/router/RouterClientProtocol.java @@ -935,19 +935,22 @@ public BatchedDirectoryListing getBatchedListing(String[] srcs, public HdfsFileStatus getFileInfo(String src) throws IOException { rpcServer.checkOperation(NameNode.OperationCategory.READ); - final List locations = - rpcServer.getLocationsForPath(src, false, false); - RemoteMethod method = new RemoteMethod("getFileInfo", - new Class[] {String.class}, new RemoteParam()); - HdfsFileStatus ret = null; - // If it's a directory, we check in all locations - if (rpcServer.isPathAll(src)) { - ret = getFileInfoAll(locations, method); - } else { - // Check for file information sequentially - ret = rpcClient.invokeSequential( - locations, method, HdfsFileStatus.class, null); + IOException noLocationException = null; + try { + final List locations = rpcServer.getLocationsForPath(src, false, false); + RemoteMethod method = new RemoteMethod("getFileInfo", + new Class[] {String.class}, new RemoteParam()); + + // If it's a directory, we check in all locations + if (rpcServer.isPathAll(src)) { + ret = getFileInfoAll(locations, method); + } else { + // Check for file information sequentially + ret = rpcClient.invokeSequential(locations, method, HdfsFileStatus.class, null); + } + } catch (NoLocationException | RouterResolveException e) { + noLocationException = e; } // If there is no real path, check mount points @@ -956,7 +959,7 @@ public HdfsFileStatus getFileInfo(String src) throws IOException { if (children != null && !children.isEmpty()) { Map dates = getMountPointDates(src); long date = 0; - if (dates != null && dates.containsKey(src)) { + if (dates.containsKey(src)) { date = dates.get(src); } ret = getMountPointStatus(src, children.size(), date); @@ -966,6 +969,10 @@ public HdfsFileStatus getFileInfo(String src) throws IOException { } } + if (ret == null && noLocationException != null) { + throw noLocationException; + } + return ret; } diff --git a/hadoop-hdfs-project/hadoop-hdfs-rbf/src/main/java/org/apache/hadoop/hdfs/server/federation/router/RouterRpcServer.java b/hadoop-hdfs-project/hadoop-hdfs-rbf/src/main/java/org/apache/hadoop/hdfs/server/federation/router/RouterRpcServer.java index 423e0ba8e483e..d06103535673f 100644 --- a/hadoop-hdfs-project/hadoop-hdfs-rbf/src/main/java/org/apache/hadoop/hdfs/server/federation/router/RouterRpcServer.java +++ b/hadoop-hdfs-project/hadoop-hdfs-rbf/src/main/java/org/apache/hadoop/hdfs/server/federation/router/RouterRpcServer.java @@ -1731,7 +1731,7 @@ protected List getLocationsForPath(String path, final PathLocation location = this.subclusterResolver.getDestinationForPath(path); if (location == null) { - throw new IOException("Cannot find locations for " + path + " in " + + throw new NoLocationException("Cannot find locations for " + path + " in " + this.subclusterResolver.getClass().getSimpleName()); } diff --git a/hadoop-hdfs-project/hadoop-hdfs-rbf/src/test/java/org/apache/hadoop/hdfs/server/federation/router/TestRouterMountTableWithoutDefaultNS.java b/hadoop-hdfs-project/hadoop-hdfs-rbf/src/test/java/org/apache/hadoop/hdfs/server/federation/router/TestRouterMountTableWithoutDefaultNS.java new file mode 100644 index 0000000000000..77d4b5fd71a4f --- /dev/null +++ b/hadoop-hdfs-project/hadoop-hdfs-rbf/src/test/java/org/apache/hadoop/hdfs/server/federation/router/TestRouterMountTableWithoutDefaultNS.java @@ -0,0 +1,154 @@ +/** + * 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.hdfs.server.federation.router; + +import org.apache.hadoop.conf.Configuration; +import org.apache.hadoop.hdfs.protocol.ClientProtocol; +import org.apache.hadoop.hdfs.protocol.HdfsFileStatus; +import org.apache.hadoop.hdfs.server.federation.MiniRouterDFSCluster.RouterContext; +import org.apache.hadoop.hdfs.server.federation.RouterConfigBuilder; +import org.apache.hadoop.hdfs.server.federation.StateStoreDFSCluster; +import org.apache.hadoop.hdfs.server.federation.resolver.MountTableManager; +import org.apache.hadoop.hdfs.server.federation.resolver.MountTableResolver; +import org.apache.hadoop.hdfs.server.federation.store.protocol.AddMountTableEntryRequest; +import org.apache.hadoop.hdfs.server.federation.store.protocol.AddMountTableEntryResponse; +import org.apache.hadoop.hdfs.server.federation.store.protocol.GetMountTableEntriesRequest; +import org.apache.hadoop.hdfs.server.federation.store.protocol.GetMountTableEntriesResponse; +import org.apache.hadoop.hdfs.server.federation.store.protocol.RemoveMountTableEntryRequest; +import org.apache.hadoop.hdfs.server.federation.store.records.MountTable; +import org.junit.After; +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; + +import java.io.IOException; +import java.util.Collections; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +/** + * Test a router end-to-end including the MountTable without default nameservice. + */ +public class TestRouterMountTableWithoutDefaultNS { + private static StateStoreDFSCluster cluster; + private static RouterContext routerContext; + private static MountTableResolver mountTable; + private static ClientProtocol routerProtocol; + + @BeforeClass + public static void globalSetUp() throws Exception { + // Build and start a federated cluster + cluster = new StateStoreDFSCluster(false, 2); + Configuration conf = new RouterConfigBuilder() + .stateStore() + .admin() + .rpc() + .build(); + conf.setInt(RBFConfigKeys.DFS_ROUTER_ADMIN_MAX_COMPONENT_LENGTH_KEY, 20); + conf.setBoolean(RBFConfigKeys.DFS_ROUTER_DEFAULT_NAMESERVICE_ENABLE, false); + cluster.addRouterOverrides(conf); + cluster.startCluster(); + cluster.startRouters(); + cluster.waitClusterUp(); + + // Get the end points + routerContext = cluster.getRandomRouter(); + Router router = routerContext.getRouter(); + routerProtocol = routerContext.getClient().getNamenode(); + mountTable = (MountTableResolver) router.getSubclusterResolver(); + } + + @AfterClass + public static void tearDown() { + if (cluster != null) { + cluster.stopRouter(routerContext); + cluster.shutdown(); + cluster = null; + } + } + + @After + public void clearMountTable() throws IOException { + RouterClient client = routerContext.getAdminClient(); + MountTableManager mountTableManager = client.getMountTableManager(); + GetMountTableEntriesRequest req1 = + GetMountTableEntriesRequest.newInstance("/"); + GetMountTableEntriesResponse response = + mountTableManager.getMountTableEntries(req1); + for (MountTable entry : response.getEntries()) { + RemoveMountTableEntryRequest req2 = + RemoveMountTableEntryRequest.newInstance(entry.getSourcePath()); + mountTableManager.removeMountTableEntry(req2); + } + } + + /** + * Add a mount table entry to the mount table through the admin API. + * @param entry Mount table entry to add. + * @return If it was succesfully added. + * @throws IOException Problems adding entries. + */ + private boolean addMountTable(final MountTable entry) throws IOException { + RouterClient client = routerContext.getAdminClient(); + MountTableManager mountTableManager = client.getMountTableManager(); + AddMountTableEntryRequest addRequest = + AddMountTableEntryRequest.newInstance(entry); + AddMountTableEntryResponse addResponse = + mountTableManager.addMountTableEntry(addRequest); + + // Reload the Router cache + mountTable.loadCache(true); + + return addResponse.getStatus(); + } + + /** + * Verify that RBF that disable default nameservice should support + * get information about ancestor mount points. + */ + @Test + public void testGetFileInfoWithSubMountPoint() throws IOException { + MountTable addEntry = MountTable.newInstance("/testdir/1", + Collections.singletonMap("ns0", "/testdir/1")); + assertTrue(addMountTable(addEntry)); + HdfsFileStatus finfo = routerProtocol.getFileInfo("/testdir"); + assertNotNull(finfo); + assertEquals("supergroup", finfo.getGroup()); + assertTrue(finfo.isDirectory()); + } + + /** + * Verify that RBF doesn't support get the file information + * with no location and sub mount points. + */ + @Test + public void testGetFileInfoWithoutSubMountPoint() throws IOException { + MountTable addEntry = MountTable.newInstance("/testdir/1", + Collections.singletonMap("ns0", "/testdir/1")); + assertTrue(addMountTable(addEntry)); + try { + routerProtocol.getFileInfo("/testdir2"); + fail(); + } catch (IOException ioe) { + assertTrue(ioe.getMessage().contains("Cannot find locations for /testdir2")); + } + } +} From 92090b11ca206d34488dbf7e61f9dd671197de07 Mon Sep 17 00:00:00 2001 From: "zengqiang.xu" Date: Wed, 10 Aug 2022 18:00:35 +0800 Subject: [PATCH 2/3] HDFS-16724. Modify patch based on comments --- .../router/NoLocationException.java | 7 +++++- .../router/RouterClientProtocol.java | 4 ++- .../federation/router/RouterRpcServer.java | 3 +-- .../TestRouterMountTableWithoutDefaultNS.java | 25 +++++++------------ 4 files changed, 19 insertions(+), 20 deletions(-) diff --git a/hadoop-hdfs-project/hadoop-hdfs-rbf/src/main/java/org/apache/hadoop/hdfs/server/federation/router/NoLocationException.java b/hadoop-hdfs-project/hadoop-hdfs-rbf/src/main/java/org/apache/hadoop/hdfs/server/federation/router/NoLocationException.java index 580a4907d9a99..8079248aa6c1f 100644 --- a/hadoop-hdfs-project/hadoop-hdfs-rbf/src/main/java/org/apache/hadoop/hdfs/server/federation/router/NoLocationException.java +++ b/hadoop-hdfs-project/hadoop-hdfs-rbf/src/main/java/org/apache/hadoop/hdfs/server/federation/router/NoLocationException.java @@ -20,12 +20,17 @@ import java.io.IOException; /** - * Exception when no location found. + * This exception is thrown when can not get any mount point for the input path. + * RBF cannot forward any requests for the path. */ public class NoLocationException extends IOException { private static final long serialVersionUID = 1L; + public NoLocationException(String path, String className) { + super("Cannot find locations for " + path + " in " + className); + } + public NoLocationException(String message) { super(message); } diff --git a/hadoop-hdfs-project/hadoop-hdfs-rbf/src/main/java/org/apache/hadoop/hdfs/server/federation/router/RouterClientProtocol.java b/hadoop-hdfs-project/hadoop-hdfs-rbf/src/main/java/org/apache/hadoop/hdfs/server/federation/router/RouterClientProtocol.java index 526f83ffdd1f4..a48107228f718 100644 --- a/hadoop-hdfs-project/hadoop-hdfs-rbf/src/main/java/org/apache/hadoop/hdfs/server/federation/router/RouterClientProtocol.java +++ b/hadoop-hdfs-project/hadoop-hdfs-rbf/src/main/java/org/apache/hadoop/hdfs/server/federation/router/RouterClientProtocol.java @@ -959,7 +959,7 @@ public HdfsFileStatus getFileInfo(String src) throws IOException { if (children != null && !children.isEmpty()) { Map dates = getMountPointDates(src); long date = 0; - if (dates.containsKey(src)) { + if (dates != null && dates.containsKey(src)) { date = dates.get(src); } ret = getMountPointStatus(src, children.size(), date); @@ -969,6 +969,8 @@ public HdfsFileStatus getFileInfo(String src) throws IOException { } } + // Can't find mount point for path and the path didn't contain any sub monit points, + // throw the NoLocationException to client. if (ret == null && noLocationException != null) { throw noLocationException; } diff --git a/hadoop-hdfs-project/hadoop-hdfs-rbf/src/main/java/org/apache/hadoop/hdfs/server/federation/router/RouterRpcServer.java b/hadoop-hdfs-project/hadoop-hdfs-rbf/src/main/java/org/apache/hadoop/hdfs/server/federation/router/RouterRpcServer.java index d06103535673f..25fc8ca20dc39 100644 --- a/hadoop-hdfs-project/hadoop-hdfs-rbf/src/main/java/org/apache/hadoop/hdfs/server/federation/router/RouterRpcServer.java +++ b/hadoop-hdfs-project/hadoop-hdfs-rbf/src/main/java/org/apache/hadoop/hdfs/server/federation/router/RouterRpcServer.java @@ -1731,8 +1731,7 @@ protected List getLocationsForPath(String path, final PathLocation location = this.subclusterResolver.getDestinationForPath(path); if (location == null) { - throw new NoLocationException("Cannot find locations for " + path + " in " + - this.subclusterResolver.getClass().getSimpleName()); + throw new NoLocationException(path, this.subclusterResolver.getClass().getSimpleName()); } // We may block some write operations diff --git a/hadoop-hdfs-project/hadoop-hdfs-rbf/src/test/java/org/apache/hadoop/hdfs/server/federation/router/TestRouterMountTableWithoutDefaultNS.java b/hadoop-hdfs-project/hadoop-hdfs-rbf/src/test/java/org/apache/hadoop/hdfs/server/federation/router/TestRouterMountTableWithoutDefaultNS.java index 77d4b5fd71a4f..bbf56fe06b470 100644 --- a/hadoop-hdfs-project/hadoop-hdfs-rbf/src/test/java/org/apache/hadoop/hdfs/server/federation/router/TestRouterMountTableWithoutDefaultNS.java +++ b/hadoop-hdfs-project/hadoop-hdfs-rbf/src/test/java/org/apache/hadoop/hdfs/server/federation/router/TestRouterMountTableWithoutDefaultNS.java @@ -25,12 +25,14 @@ import org.apache.hadoop.hdfs.server.federation.StateStoreDFSCluster; import org.apache.hadoop.hdfs.server.federation.resolver.MountTableManager; import org.apache.hadoop.hdfs.server.federation.resolver.MountTableResolver; +import org.apache.hadoop.hdfs.server.federation.resolver.RouterResolveException; import org.apache.hadoop.hdfs.server.federation.store.protocol.AddMountTableEntryRequest; import org.apache.hadoop.hdfs.server.federation.store.protocol.AddMountTableEntryResponse; import org.apache.hadoop.hdfs.server.federation.store.protocol.GetMountTableEntriesRequest; import org.apache.hadoop.hdfs.server.federation.store.protocol.GetMountTableEntriesResponse; import org.apache.hadoop.hdfs.server.federation.store.protocol.RemoveMountTableEntryRequest; import org.apache.hadoop.hdfs.server.federation.store.records.MountTable; +import org.apache.hadoop.test.LambdaTestUtils; import org.junit.After; import org.junit.AfterClass; import org.junit.BeforeClass; @@ -42,7 +44,6 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; /** * Test a router end-to-end including the MountTable without default nameservice. @@ -89,10 +90,8 @@ public static void tearDown() { public void clearMountTable() throws IOException { RouterClient client = routerContext.getAdminClient(); MountTableManager mountTableManager = client.getMountTableManager(); - GetMountTableEntriesRequest req1 = - GetMountTableEntriesRequest.newInstance("/"); - GetMountTableEntriesResponse response = - mountTableManager.getMountTableEntries(req1); + GetMountTableEntriesRequest req1 = GetMountTableEntriesRequest.newInstance("/"); + GetMountTableEntriesResponse response = mountTableManager.getMountTableEntries(req1); for (MountTable entry : response.getEntries()) { RemoveMountTableEntryRequest req2 = RemoveMountTableEntryRequest.newInstance(entry.getSourcePath()); @@ -109,10 +108,8 @@ public void clearMountTable() throws IOException { private boolean addMountTable(final MountTable entry) throws IOException { RouterClient client = routerContext.getAdminClient(); MountTableManager mountTableManager = client.getMountTableManager(); - AddMountTableEntryRequest addRequest = - AddMountTableEntryRequest.newInstance(entry); - AddMountTableEntryResponse addResponse = - mountTableManager.addMountTableEntry(addRequest); + AddMountTableEntryRequest addRequest = AddMountTableEntryRequest.newInstance(entry); + AddMountTableEntryResponse addResponse = mountTableManager.addMountTableEntry(addRequest); // Reload the Router cache mountTable.loadCache(true); @@ -140,15 +137,11 @@ public void testGetFileInfoWithSubMountPoint() throws IOException { * with no location and sub mount points. */ @Test - public void testGetFileInfoWithoutSubMountPoint() throws IOException { + public void testGetFileInfoWithoutSubMountPoint() throws Exception { MountTable addEntry = MountTable.newInstance("/testdir/1", Collections.singletonMap("ns0", "/testdir/1")); assertTrue(addMountTable(addEntry)); - try { - routerProtocol.getFileInfo("/testdir2"); - fail(); - } catch (IOException ioe) { - assertTrue(ioe.getMessage().contains("Cannot find locations for /testdir2")); - } + LambdaTestUtils.intercept(RouterResolveException.class, + () -> routerContext.getRouter().getRpcServer().getFileInfo("/testdir2")); } } From a0b33ae191e76347df4e4d56b85360d64ce4361c Mon Sep 17 00:00:00 2001 From: "zengqiang.xu" Date: Sat, 13 Aug 2022 00:32:31 +0800 Subject: [PATCH 3/3] HDFS-16724. Modify patch based on comments --- .../server/federation/router/NoLocationException.java | 8 ++------ .../hdfs/server/federation/router/RouterRpcServer.java | 2 +- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/hadoop-hdfs-project/hadoop-hdfs-rbf/src/main/java/org/apache/hadoop/hdfs/server/federation/router/NoLocationException.java b/hadoop-hdfs-project/hadoop-hdfs-rbf/src/main/java/org/apache/hadoop/hdfs/server/federation/router/NoLocationException.java index 8079248aa6c1f..1a010ac546cc5 100644 --- a/hadoop-hdfs-project/hadoop-hdfs-rbf/src/main/java/org/apache/hadoop/hdfs/server/federation/router/NoLocationException.java +++ b/hadoop-hdfs-project/hadoop-hdfs-rbf/src/main/java/org/apache/hadoop/hdfs/server/federation/router/NoLocationException.java @@ -27,11 +27,7 @@ public class NoLocationException extends IOException { private static final long serialVersionUID = 1L; - public NoLocationException(String path, String className) { - super("Cannot find locations for " + path + " in " + className); - } - - public NoLocationException(String message) { - super(message); + public NoLocationException(String path, Class t) { + super("Cannot find locations for " + path + " in " + t.getSimpleName()); } } diff --git a/hadoop-hdfs-project/hadoop-hdfs-rbf/src/main/java/org/apache/hadoop/hdfs/server/federation/router/RouterRpcServer.java b/hadoop-hdfs-project/hadoop-hdfs-rbf/src/main/java/org/apache/hadoop/hdfs/server/federation/router/RouterRpcServer.java index 25fc8ca20dc39..07880ac45d378 100644 --- a/hadoop-hdfs-project/hadoop-hdfs-rbf/src/main/java/org/apache/hadoop/hdfs/server/federation/router/RouterRpcServer.java +++ b/hadoop-hdfs-project/hadoop-hdfs-rbf/src/main/java/org/apache/hadoop/hdfs/server/federation/router/RouterRpcServer.java @@ -1731,7 +1731,7 @@ protected List getLocationsForPath(String path, final PathLocation location = this.subclusterResolver.getDestinationForPath(path); if (location == null) { - throw new NoLocationException(path, this.subclusterResolver.getClass().getSimpleName()); + throw new NoLocationException(path, this.subclusterResolver.getClass()); } // We may block some write operations