Skip to content

Commit 1dd03cc

Browse files
abhishekdas99shvachko
authored andcommitted
HADOOP-17028. ViewFS should initialize mounted target filesystems lazily. Contributed by Abhishek Das (#2260)
1 parent ea90c51 commit 1dd03cc

File tree

11 files changed

+379
-72
lines changed

11 files changed

+379
-72
lines changed

hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/fs/viewfs/InodeTree.java

Lines changed: 53 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
*/
1818
package org.apache.hadoop.fs.viewfs;
1919

20+
import java.util.function.Function;
2021
import org.apache.hadoop.thirdparty.com.google.common.base.Preconditions;
2122
import java.io.FileNotFoundException;
2223
import java.io.IOException;
@@ -257,7 +258,10 @@ enum LinkType {
257258
*/
258259
static class INodeLink<T> extends INode<T> {
259260
final URI[] targetDirLinkList;
260-
final T targetFileSystem; // file system object created from the link.
261+
private T targetFileSystem; // file system object created from the link.
262+
// Function to initialize file system. Only applicable for simple links
263+
private Function<URI, T> fileSystemInitMethod;
264+
private final Object lock = new Object();
261265

262266
/**
263267
* Construct a mergeLink or nfly.
@@ -273,11 +277,13 @@ static class INodeLink<T> extends INode<T> {
273277
* Construct a simple link (i.e. not a mergeLink).
274278
*/
275279
INodeLink(final String pathToNode, final UserGroupInformation aUgi,
276-
final T targetFs, final URI aTargetDirLink) {
280+
Function<URI, T> createFileSystemMethod,
281+
final URI aTargetDirLink) {
277282
super(pathToNode, aUgi);
278-
targetFileSystem = targetFs;
283+
targetFileSystem = null;
279284
targetDirLinkList = new URI[1];
280285
targetDirLinkList[0] = aTargetDirLink;
286+
this.fileSystemInitMethod = createFileSystemMethod;
281287
}
282288

283289
/**
@@ -298,7 +304,30 @@ boolean isInternalDir() {
298304
return false;
299305
}
300306

301-
public T getTargetFileSystem() {
307+
/**
308+
* Get the instance of FileSystem to use, creating one if needed.
309+
* @return An Initialized instance of T
310+
* @throws IOException
311+
*/
312+
public T getTargetFileSystem() throws IOException {
313+
if (targetFileSystem != null) {
314+
return targetFileSystem;
315+
}
316+
// For non NFLY and MERGE links, we initialize the FileSystem when the
317+
// corresponding mount path is accessed.
318+
if (targetDirLinkList.length == 1) {
319+
synchronized (lock) {
320+
if (targetFileSystem != null) {
321+
return targetFileSystem;
322+
}
323+
targetFileSystem = fileSystemInitMethod.apply(targetDirLinkList[0]);
324+
if (targetFileSystem == null) {
325+
throw new IOException(
326+
"Could not initialize target File System for URI : " +
327+
targetDirLinkList[0]);
328+
}
329+
}
330+
}
302331
return targetFileSystem;
303332
}
304333
}
@@ -359,7 +388,7 @@ private void createLink(final String src, final String target,
359388
switch (linkType) {
360389
case SINGLE:
361390
newLink = new INodeLink<T>(fullPath, aUgi,
362-
getTargetFileSystem(new URI(target)), new URI(target));
391+
initAndGetTargetFs(), new URI(target));
363392
break;
364393
case SINGLE_FALLBACK:
365394
case MERGE_SLASH:
@@ -385,8 +414,7 @@ private void createLink(final String src, final String target,
385414
* 3 abstract methods.
386415
* @throws IOException
387416
*/
388-
protected abstract T getTargetFileSystem(URI uri)
389-
throws UnsupportedFileSystemException, URISyntaxException, IOException;
417+
protected abstract Function<URI, T> initAndGetTargetFs();
390418

391419
protected abstract T getTargetFileSystem(INodeDir<T> dir)
392420
throws URISyntaxException, IOException;
@@ -589,7 +617,7 @@ protected InodeTree(final Configuration config, final String viewName,
589617
if (isMergeSlashConfigured) {
590618
Preconditions.checkNotNull(mergeSlashTarget);
591619
root = new INodeLink<T>(mountTableName, ugi,
592-
getTargetFileSystem(new URI(mergeSlashTarget)),
620+
initAndGetTargetFs(),
593621
new URI(mergeSlashTarget));
594622
mountPoints.add(new MountPoint<T>("/", (INodeLink<T>) root));
595623
rootFallbackLink = null;
@@ -608,8 +636,7 @@ protected InodeTree(final Configuration config, final String viewName,
608636
+ "not allowed.");
609637
}
610638
fallbackLink = new INodeLink<T>(mountTableName, ugi,
611-
getTargetFileSystem(new URI(le.getTarget())),
612-
new URI(le.getTarget()));
639+
initAndGetTargetFs(), new URI(le.getTarget()));
613640
continue;
614641
case REGEX:
615642
addRegexMountEntry(le);
@@ -633,9 +660,8 @@ protected InodeTree(final Configuration config, final String viewName,
633660
FileSystem.LOG
634661
.info("Empty mount table detected for {} and considering itself "
635662
+ "as a linkFallback.", theUri);
636-
rootFallbackLink =
637-
new INodeLink<T>(mountTableName, ugi, getTargetFileSystem(theUri),
638-
theUri);
663+
rootFallbackLink = new INodeLink<T>(mountTableName, ugi,
664+
initAndGetTargetFs(), theUri);
639665
getRootDir().addFallbackLink(rootFallbackLink);
640666
}
641667
}
@@ -733,10 +759,10 @@ boolean isLastInternalDirLink() {
733759
* @param p - input path
734760
* @param resolveLastComponent
735761
* @return ResolveResult which allows further resolution of the remaining path
736-
* @throws FileNotFoundException
762+
* @throws IOException
737763
*/
738764
ResolveResult<T> resolve(final String p, final boolean resolveLastComponent)
739-
throws FileNotFoundException {
765+
throws IOException {
740766
ResolveResult<T> resolveResult = null;
741767
String[] path = breakIntoPathComponents(p);
742768
if (path.length <= 1) { // special case for when path is "/"
@@ -880,19 +906,20 @@ protected ResolveResult<T> buildResolveResultForRegexMountPoint(
880906
ResultKind resultKind, String resolvedPathStr,
881907
String targetOfResolvedPathStr, Path remainingPath) {
882908
try {
883-
T targetFs = getTargetFileSystem(
884-
new URI(targetOfResolvedPathStr));
909+
T targetFs = initAndGetTargetFs()
910+
.apply(new URI(targetOfResolvedPathStr));
911+
if (targetFs == null) {
912+
LOGGER.error(String.format(
913+
"Not able to initialize target file system."
914+
+ " ResultKind:%s, resolvedPathStr:%s,"
915+
+ " targetOfResolvedPathStr:%s, remainingPath:%s,"
916+
+ " will return null.",
917+
resultKind, resolvedPathStr, targetOfResolvedPathStr,
918+
remainingPath));
919+
return null;
920+
}
885921
return new ResolveResult<T>(resultKind, targetFs, resolvedPathStr,
886922
remainingPath, true);
887-
} catch (IOException ex) {
888-
LOGGER.error(String.format(
889-
"Got Exception while build resolve result."
890-
+ " ResultKind:%s, resolvedPathStr:%s,"
891-
+ " targetOfResolvedPathStr:%s, remainingPath:%s,"
892-
+ " will return null.",
893-
resultKind, resolvedPathStr, targetOfResolvedPathStr, remainingPath),
894-
ex);
895-
return null;
896923
} catch (URISyntaxException uex) {
897924
LOGGER.error(String.format(
898925
"Got Exception while build resolve result."

hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/fs/viewfs/ViewFileSystem.java

Lines changed: 76 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -26,10 +26,12 @@
2626
import static org.apache.hadoop.fs.viewfs.Constants.CONFIG_VIEWFS_MOUNT_LINKS_AS_SYMLINKS_DEFAULT;
2727
import static org.apache.hadoop.fs.viewfs.Constants.PERMISSION_555;
2828

29+
import java.util.function.Function;
2930
import java.io.FileNotFoundException;
3031
import java.io.IOException;
3132
import java.net.URI;
3233
import java.net.URISyntaxException;
34+
import java.security.PrivilegedExceptionAction;
3335
import java.util.ArrayList;
3436
import java.util.Arrays;
3537
import java.util.Collection;
@@ -302,7 +304,7 @@ public void initialize(final URI theUri, final Configuration conf)
302304
enableInnerCache = config.getBoolean(CONFIG_VIEWFS_ENABLE_INNER_CACHE,
303305
CONFIG_VIEWFS_ENABLE_INNER_CACHE_DEFAULT);
304306
FsGetter fsGetter = fsGetter();
305-
final InnerCache innerCache = new InnerCache(fsGetter);
307+
cache = new InnerCache(fsGetter);
306308
// Now build client side view (i.e. client side mount table) from config.
307309
final String authority = theUri.getAuthority();
308310
String tableName = authority;
@@ -318,15 +320,32 @@ public void initialize(final URI theUri, final Configuration conf)
318320
fsState = new InodeTree<FileSystem>(conf, tableName, myUri,
319321
initingUriAsFallbackOnNoMounts) {
320322
@Override
321-
protected FileSystem getTargetFileSystem(final URI uri)
322-
throws URISyntaxException, IOException {
323-
FileSystem fs;
324-
if (enableInnerCache) {
325-
fs = innerCache.get(uri, config);
326-
} else {
327-
fs = fsGetter.get(uri, config);
328-
}
329-
return new ChRootedFileSystem(fs, uri);
323+
protected Function<URI, FileSystem> initAndGetTargetFs() {
324+
return new Function<URI, FileSystem>() {
325+
@Override
326+
public FileSystem apply(final URI uri) {
327+
FileSystem fs;
328+
try {
329+
fs = ugi.doAs(new PrivilegedExceptionAction<FileSystem>() {
330+
@Override
331+
public FileSystem run() throws IOException {
332+
if (enableInnerCache) {
333+
synchronized (cache) {
334+
return cache.get(uri, config);
335+
}
336+
} else {
337+
return fsGetter().get(uri, config);
338+
}
339+
}
340+
});
341+
return new ChRootedFileSystem(fs, uri);
342+
} catch (IOException | InterruptedException ex) {
343+
LOG.error("Could not initialize the underlying FileSystem "
344+
+ "object. Exception: " + ex.toString());
345+
}
346+
return null;
347+
}
348+
};
330349
}
331350

332351
@Override
@@ -350,13 +369,6 @@ protected FileSystem getTargetFileSystem(final String settings,
350369
} catch (URISyntaxException e) {
351370
throw new IOException("URISyntax exception: " + theUri);
352371
}
353-
354-
if (enableInnerCache) {
355-
// All fs instances are created and cached on startup. The cache is
356-
// readonly after the initialize() so the concurrent access of the cache
357-
// is safe.
358-
cache = innerCache;
359-
}
360372
}
361373

362374
/**
@@ -388,7 +400,7 @@ public URI getUri() {
388400
@Override
389401
public Path resolvePath(final Path f) throws IOException {
390402
final InodeTree.ResolveResult<FileSystem> res;
391-
res = fsState.resolve(getUriPath(f), true);
403+
res = fsState.resolve(getUriPath(f), true);
392404
if (res.isInternalDir()) {
393405
return f;
394406
}
@@ -908,10 +920,35 @@ public void removeXAttr(Path path, String name) throws IOException {
908920
public void setVerifyChecksum(final boolean verifyChecksum) {
909921
List<InodeTree.MountPoint<FileSystem>> mountPoints =
910922
fsState.getMountPoints();
923+
Map<String, FileSystem> fsMap = initializeMountedFileSystems(mountPoints);
911924
for (InodeTree.MountPoint<FileSystem> mount : mountPoints) {
912-
mount.target.targetFileSystem.setVerifyChecksum(verifyChecksum);
925+
fsMap.get(mount.src).setVerifyChecksum(verifyChecksum);
913926
}
914927
}
928+
929+
/**
930+
* Initialize the target filesystem for all mount points.
931+
* @param mountPoints The mount points
932+
* @return Mapping of mount point and the initialized target filesystems
933+
* @throws RuntimeException when the target file system cannot be initialized
934+
*/
935+
private Map<String, FileSystem> initializeMountedFileSystems(
936+
List<InodeTree.MountPoint<FileSystem>> mountPoints) {
937+
FileSystem fs = null;
938+
Map<String, FileSystem> fsMap = new HashMap<>(mountPoints.size());
939+
for (InodeTree.MountPoint<FileSystem> mount : mountPoints) {
940+
try {
941+
fs = mount.target.getTargetFileSystem();
942+
fsMap.put(mount.src, fs);
943+
} catch (IOException ex) {
944+
String errMsg = "Not able to initialize FileSystem for mount path " +
945+
mount.src + " with exception " + ex;
946+
LOG.error(errMsg);
947+
throw new RuntimeException(errMsg, ex);
948+
}
949+
}
950+
return fsMap;
951+
}
915952

916953
@Override
917954
public long getDefaultBlockSize() {
@@ -936,6 +973,9 @@ public long getDefaultBlockSize(Path f) {
936973
return res.targetFileSystem.getDefaultBlockSize(res.remainingPath);
937974
} catch (FileNotFoundException e) {
938975
throw new NotInMountpointException(f, "getDefaultBlockSize");
976+
} catch (IOException e) {
977+
throw new RuntimeException("Not able to initialize fs in "
978+
+ " getDefaultBlockSize for path " + f + " with exception", e);
939979
}
940980
}
941981

@@ -947,6 +987,9 @@ public short getDefaultReplication(Path f) {
947987
return res.targetFileSystem.getDefaultReplication(res.remainingPath);
948988
} catch (FileNotFoundException e) {
949989
throw new NotInMountpointException(f, "getDefaultReplication");
990+
} catch (IOException e) {
991+
throw new RuntimeException("Not able to initialize fs in "
992+
+ " getDefaultReplication for path " + f + " with exception", e);
950993
}
951994
}
952995

@@ -979,25 +1022,33 @@ public QuotaUsage getQuotaUsage(Path f) throws IOException {
9791022
public void setWriteChecksum(final boolean writeChecksum) {
9801023
List<InodeTree.MountPoint<FileSystem>> mountPoints =
9811024
fsState.getMountPoints();
1025+
Map<String, FileSystem> fsMap = initializeMountedFileSystems(mountPoints);
9821026
for (InodeTree.MountPoint<FileSystem> mount : mountPoints) {
983-
mount.target.targetFileSystem.setWriteChecksum(writeChecksum);
1027+
fsMap.get(mount.src).setWriteChecksum(writeChecksum);
9841028
}
9851029
}
9861030

9871031
@Override
9881032
public FileSystem[] getChildFileSystems() {
9891033
List<InodeTree.MountPoint<FileSystem>> mountPoints =
9901034
fsState.getMountPoints();
1035+
Map<String, FileSystem> fsMap = initializeMountedFileSystems(mountPoints);
9911036
Set<FileSystem> children = new HashSet<FileSystem>();
9921037
for (InodeTree.MountPoint<FileSystem> mountPoint : mountPoints) {
993-
FileSystem targetFs = mountPoint.target.targetFileSystem;
1038+
FileSystem targetFs = fsMap.get(mountPoint.src);
9941039
children.addAll(Arrays.asList(targetFs.getChildFileSystems()));
9951040
}
9961041

997-
if (fsState.isRootInternalDir() && fsState.getRootFallbackLink() != null) {
998-
children.addAll(Arrays.asList(
999-
fsState.getRootFallbackLink().targetFileSystem
1000-
.getChildFileSystems()));
1042+
try {
1043+
if (fsState.isRootInternalDir() &&
1044+
fsState.getRootFallbackLink() != null) {
1045+
children.addAll(Arrays.asList(
1046+
fsState.getRootFallbackLink().getTargetFileSystem()
1047+
.getChildFileSystems()));
1048+
}
1049+
} catch (IOException ex) {
1050+
LOG.error("Could not add child filesystems for source path "
1051+
+ fsState.getRootFallbackLink().fullPath + " with exception " + ex);
10011052
}
10021053
return children.toArray(new FileSystem[]{});
10031054
}

hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/fs/viewfs/ViewFileSystemOverloadScheme.java

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -348,7 +348,7 @@ public MountPathInfo<FileSystem> getMountPathInfo(Path path,
348348
FileSystem fs = res.isInternalDir() ?
349349
(fsState.getRootFallbackLink() != null ?
350350
((ChRootedFileSystem) fsState
351-
.getRootFallbackLink().targetFileSystem).getMyFs() :
351+
.getRootFallbackLink().getTargetFileSystem()).getMyFs() :
352352
fsGetter().get(path.toUri(), conf)) :
353353
((ChRootedFileSystem) res.targetFileSystem).getMyFs();
354354
return new MountPathInfo<FileSystem>(res.remainingPath, res.resolvedPath,
@@ -390,8 +390,13 @@ public FileSystem getFallbackFileSystem() {
390390
if (fsState.getRootFallbackLink() == null) {
391391
return null;
392392
}
393-
return ((ChRootedFileSystem) fsState.getRootFallbackLink().targetFileSystem)
394-
.getMyFs();
393+
try {
394+
return ((ChRootedFileSystem) fsState.getRootFallbackLink()
395+
.getTargetFileSystem()).getMyFs();
396+
} catch (IOException ex) {
397+
LOG.error("Could not get fallback filesystem ");
398+
}
399+
return null;
395400
}
396401

397402
@Override

0 commit comments

Comments
 (0)