Skip to content

Commit 05f77fd

Browse files
committed
HADOOP-17028. ViewFS should initialize mounted target filesystems lazily. Contributed by Abhishek Das (apache#2260)
(cherry picked from commit 1dd03cc)
1 parent 992d2c5 commit 05f77fd

File tree

7 files changed

+338
-45
lines changed

7 files changed

+338
-45
lines changed

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

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

20+
import com.google.common.base.Function;
2021
import java.io.FileNotFoundException;
2122
import java.io.IOException;
2223
import java.net.URI;
@@ -158,8 +159,10 @@ void addLink(final String pathComponent, final INodeLink<T> link)
158159
static class INodeLink<T> extends INode<T> {
159160
final boolean isMergeLink; // true if MergeLink
160161
final URI[] targetDirLinkList;
161-
final T targetFileSystem; // file system object created from the link.
162-
162+
private T targetFileSystem; // file system object created from the link.
163+
// Function to initialize file system. Only applicable for simple links
164+
private Function<URI, T> fileSystemInitMethod;
165+
private final Object lock = new Object();
163166
/**
164167
* Construct a mergeLink
165168
*/
@@ -175,12 +178,14 @@ static class INodeLink<T> extends INode<T> {
175178
* Construct a simple link (i.e. not a mergeLink)
176179
*/
177180
INodeLink(final String pathToNode, final UserGroupInformation aUgi,
178-
final T targetFs, final URI aTargetDirLink) {
181+
Function<URI, T> createFileSystemMethod,
182+
final URI aTargetDirLink) {
179183
super(pathToNode, aUgi);
180-
targetFileSystem = targetFs;
184+
targetFileSystem = null;
181185
targetDirLinkList = new URI[1];
182186
targetDirLinkList[0] = aTargetDirLink;
183187
isMergeLink = false;
188+
this.fileSystemInitMethod = createFileSystemMethod;
184189
}
185190

186191
/**
@@ -196,6 +201,33 @@ Path getTargetLink() {
196201
}
197202
return new Path(result.toString());
198203
}
204+
205+
/**
206+
* Get the instance of FileSystem to use, creating one if needed.
207+
* @return An Initialized instance of T
208+
* @throws IOException
209+
*/
210+
public T getTargetFileSystem() throws IOException {
211+
if (targetFileSystem != null) {
212+
return targetFileSystem;
213+
}
214+
// For non NFLY and MERGE links, we initialize the FileSystem when the
215+
// corresponding mount path is accessed.
216+
if (targetDirLinkList.length == 1) {
217+
synchronized (lock) {
218+
if (targetFileSystem != null) {
219+
return targetFileSystem;
220+
}
221+
targetFileSystem = fileSystemInitMethod.apply(targetDirLinkList[0]);
222+
if (targetFileSystem == null) {
223+
throw new IOException(
224+
"Could not initialize target File System for URI : " +
225+
targetDirLinkList[0]);
226+
}
227+
}
228+
}
229+
return targetFileSystem;
230+
}
199231
}
200232

201233

@@ -258,7 +290,7 @@ private void createLink(final String src, final String target,
258290
getTargetFileSystem(targetsListURI), targetsListURI);
259291
} else {
260292
newLink = new INodeLink<T>(fullPath, aUgi,
261-
getTargetFileSystem(new URI(target)), new URI(target));
293+
initAndGetTargetFs(), new URI(target));
262294
}
263295
curInode.addLink(iPath, newLink);
264296
mountPoints.add(new MountPoint<T>(src, newLink));
@@ -267,14 +299,13 @@ private void createLink(final String src, final String target,
267299
/**
268300
* Below the "public" methods of InodeTree
269301
*/
270-
302+
271303
/**
272304
* The user of this class must subclass and implement the following
273305
* 3 abstract methods.
274306
* @throws IOException
275307
*/
276-
protected abstract T getTargetFileSystem(final URI uri)
277-
throws UnsupportedFileSystemException, URISyntaxException, IOException;
308+
protected abstract Function<URI, T> initAndGetTargetFs();
278309

279310
protected abstract T getTargetFileSystem(final INodeDir<T> dir)
280311
throws URISyntaxException;
@@ -385,7 +416,7 @@ boolean isInternalDir() {
385416
* @throws FileNotFoundException
386417
*/
387418
ResolveResult<T> resolve(final String p, final boolean resolveLastComponent)
388-
throws FileNotFoundException {
419+
throws IOException {
389420
// TO DO: - more efficient to not split the path, but simply compare
390421
String[] path = breakIntoPathComponents(p);
391422
if (path.length <= 1) { // special case for when path is "/"
@@ -422,7 +453,7 @@ ResolveResult<T> resolve(final String p, final boolean resolveLastComponent)
422453
}
423454
final ResolveResult<T> res =
424455
new ResolveResult<T>(ResultKind.isExternalDir,
425-
link.targetFileSystem, nextInode.fullPath, remainingPath);
456+
link.getTargetFileSystem(), nextInode.fullPath, remainingPath);
426457
return res;
427458
} else if (nextInode instanceof INodeDir) {
428459
curInode = (INodeDir<T>) nextInode;

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

Lines changed: 65 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,12 @@
2121
import static org.apache.hadoop.fs.viewfs.Constants.CONFIG_VIEWFS_ENABLE_INNER_CACHE;
2222
import static org.apache.hadoop.fs.viewfs.Constants.CONFIG_VIEWFS_ENABLE_INNER_CACHE_DEFAULT;
2323

24+
import com.google.common.base.Function;
2425
import java.io.FileNotFoundException;
2526
import java.io.IOException;
2627
import java.net.URI;
2728
import java.net.URISyntaxException;
29+
import java.security.PrivilegedExceptionAction;
2830
import java.util.Arrays;
2931
import java.util.Collections;
3032
import java.util.EnumSet;
@@ -237,24 +239,40 @@ public void initialize(final URI theUri, final Configuration conf)
237239
config = conf;
238240
enableInnerCache = config.getBoolean(CONFIG_VIEWFS_ENABLE_INNER_CACHE,
239241
CONFIG_VIEWFS_ENABLE_INNER_CACHE_DEFAULT);
240-
final InnerCache innerCache = new InnerCache();
242+
cache = new InnerCache();
241243
// Now build client side view (i.e. client side mount table) from config.
242244
final String authority = theUri.getAuthority();
243245
try {
244246
myUri = new URI(FsConstants.VIEWFS_SCHEME, authority, "/", null, null);
245247
fsState = new InodeTree<FileSystem>(conf, authority) {
246248

247249
@Override
248-
protected
249-
FileSystem getTargetFileSystem(final URI uri)
250-
throws URISyntaxException, IOException {
251-
FileSystem fs;
252-
if (enableInnerCache) {
253-
fs = innerCache.get(uri, config);
254-
} else {
255-
fs = FileSystem.get(uri, config);
250+
protected Function<URI, FileSystem> initAndGetTargetFs() {
251+
return new Function<URI, FileSystem>() {
252+
@Override
253+
public FileSystem apply(final URI uri) {
254+
FileSystem fs;
255+
try {
256+
fs = ugi.doAs(new PrivilegedExceptionAction<FileSystem>() {
257+
@Override
258+
public FileSystem run() throws IOException {
259+
if (enableInnerCache) {
260+
synchronized (cache) {
261+
return cache.get(uri, config);
262+
}
263+
} else {
264+
return FileSystem.get(uri, config);
265+
}
266+
}
267+
});
268+
return new ChRootedFileSystem(fs, uri);
269+
} catch (IOException | InterruptedException ex) {
270+
LOG.error("Could not initialize the underlying FileSystem "
271+
+ "object. Exception: " + ex.toString());
272+
}
273+
return null;
256274
}
257-
return new ChRootedFileSystem(fs, uri);
275+
};
258276
}
259277

260278
@Override
@@ -273,12 +291,6 @@ FileSystem getTargetFileSystem(URI[] mergeFsURIList)
273291
}
274292
};
275293

276-
if (enableInnerCache) {
277-
// All fs instances are created and cached on startup. The cache is
278-
// readonly after the initialize() so the concurrent access of the cache
279-
// is safe.
280-
cache = innerCache.unmodifiableCache();
281-
}
282294
workingDir = this.getHomeDirectory();
283295
renameStrategy = RenameStrategy.valueOf(
284296
conf.get(Constants.CONFIG_VIEWFS_RENAME_STRATEGY,
@@ -311,7 +323,7 @@ public ViewFileSystem(final Configuration conf) throws IOException {
311323
this(FsConstants.VIEWFS_URI, conf);
312324
}
313325

314-
public Path getTrashCanLocation(final Path f) throws FileNotFoundException {
326+
public Path getTrashCanLocation(final Path f) throws IOException {
315327
final InodeTree.ResolveResult<FileSystem> res =
316328
fsState.resolve(getUriPath(f), true);
317329
return res.isInternalDir() ? null : res.targetFileSystem.getHomeDirectory();
@@ -767,9 +779,34 @@ public void removeXAttr(Path path, String name) throws IOException {
767779
public void setVerifyChecksum(final boolean verifyChecksum) {
768780
List<InodeTree.MountPoint<FileSystem>> mountPoints =
769781
fsState.getMountPoints();
782+
Map<String, FileSystem> fsMap = initializeMountedFileSystems(mountPoints);
783+
for (InodeTree.MountPoint<FileSystem> mount : mountPoints) {
784+
fsMap.get(mount.src).setVerifyChecksum(verifyChecksum);
785+
}
786+
}
787+
788+
/**
789+
* Initialize the target filesystem for all mount points.
790+
* @param mountPoints The mount points
791+
* @return Mapping of mount point and the initialized target filesystems
792+
* @throws RuntimeException when the target file system cannot be initialized
793+
*/
794+
private Map<String, FileSystem> initializeMountedFileSystems(
795+
List<InodeTree.MountPoint<FileSystem>> mountPoints) {
796+
FileSystem fs = null;
797+
Map<String, FileSystem> fsMap = new HashMap<>(mountPoints.size());
770798
for (InodeTree.MountPoint<FileSystem> mount : mountPoints) {
771-
mount.target.targetFileSystem.setVerifyChecksum(verifyChecksum);
799+
try {
800+
fs = mount.target.getTargetFileSystem();
801+
fsMap.put(mount.src, fs);
802+
} catch (IOException ex) {
803+
String errMsg = "Not able to initialize FileSystem for mount path " +
804+
mount.src + " with exception " + ex;
805+
LOG.error(errMsg);
806+
throw new RuntimeException(errMsg, ex);
807+
}
772808
}
809+
return fsMap;
773810
}
774811

775812
@Override
@@ -795,6 +832,9 @@ public long getDefaultBlockSize(Path f) {
795832
return res.targetFileSystem.getDefaultBlockSize(res.remainingPath);
796833
} catch (FileNotFoundException e) {
797834
throw new NotInMountpointException(f, "getDefaultBlockSize");
835+
} catch (IOException e) {
836+
throw new RuntimeException("Not able to initialize fs in "
837+
+ " getDefaultBlockSize for path " + f + " with exception", e);
798838
}
799839
}
800840

@@ -806,6 +846,9 @@ public short getDefaultReplication(Path f) {
806846
return res.targetFileSystem.getDefaultReplication(res.remainingPath);
807847
} catch (FileNotFoundException e) {
808848
throw new NotInMountpointException(f, "getDefaultReplication");
849+
} catch (IOException e) {
850+
throw new RuntimeException("Not able to initialize fs in "
851+
+ " getDefaultReplication for path " + f + " with exception", e);
809852
}
810853
}
811854

@@ -834,18 +877,20 @@ public QuotaUsage getQuotaUsage(Path f) throws IOException {
834877
public void setWriteChecksum(final boolean writeChecksum) {
835878
List<InodeTree.MountPoint<FileSystem>> mountPoints =
836879
fsState.getMountPoints();
880+
Map<String, FileSystem> fsMap = initializeMountedFileSystems(mountPoints);
837881
for (InodeTree.MountPoint<FileSystem> mount : mountPoints) {
838-
mount.target.targetFileSystem.setWriteChecksum(writeChecksum);
882+
fsMap.get(mount.src).setWriteChecksum(writeChecksum);
839883
}
840884
}
841885

842886
@Override
843887
public FileSystem[] getChildFileSystems() {
844888
List<InodeTree.MountPoint<FileSystem>> mountPoints =
845889
fsState.getMountPoints();
890+
Map<String, FileSystem> fsMap = initializeMountedFileSystems(mountPoints);
846891
Set<FileSystem> children = new HashSet<FileSystem>();
847892
for (InodeTree.MountPoint<FileSystem> mountPoint : mountPoints) {
848-
FileSystem targetFs = mountPoint.target.targetFileSystem;
893+
FileSystem targetFs = fsMap.get(mountPoint.src);
849894
children.addAll(Arrays.asList(targetFs.getChildFileSystems()));
850895
}
851896
return children.toArray(new FileSystem[]{});

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

Lines changed: 32 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,12 @@
1919

2020
import static org.apache.hadoop.fs.viewfs.Constants.PERMISSION_555;
2121

22+
import com.google.common.base.Function;
2223
import java.io.FileNotFoundException;
2324
import java.io.IOException;
2425
import java.net.URI;
2526
import java.net.URISyntaxException;
27+
import java.security.PrivilegedExceptionAction;
2628
import java.util.ArrayList;
2729
import java.util.EnumSet;
2830
import java.util.List;
@@ -65,6 +67,8 @@
6567
import org.apache.hadoop.security.token.Token;
6668
import org.apache.hadoop.util.Progressable;
6769
import org.apache.hadoop.util.Time;
70+
import org.slf4j.Logger;
71+
import org.slf4j.LoggerFactory;
6872

6973

7074
/**
@@ -152,6 +156,7 @@
152156
@InterfaceAudience.Public
153157
@InterfaceStability.Evolving /*Evolving for a release,to be changed to Stable */
154158
public class ViewFs extends AbstractFileSystem {
159+
static final Logger LOG = LoggerFactory.getLogger(ViewFs.class);
155160
final long creationTime; // of the the mount table
156161
final UserGroupInformation ugi; // the user/group of user who created mtable
157162
final Configuration config;
@@ -212,16 +217,32 @@ public ViewFs(final Configuration conf) throws IOException,
212217
fsState = new InodeTree<AbstractFileSystem>(conf, authority) {
213218

214219
@Override
215-
protected
216-
AbstractFileSystem getTargetFileSystem(final URI uri)
217-
throws URISyntaxException, UnsupportedFileSystemException {
218-
String pathString = uri.getPath();
219-
if (pathString.isEmpty()) {
220-
pathString = "/";
220+
protected Function<URI, AbstractFileSystem> initAndGetTargetFs() {
221+
return new Function<URI, AbstractFileSystem>() {
222+
@Override
223+
public AbstractFileSystem apply(final URI uri) {
224+
AbstractFileSystem fs;
225+
try {
226+
fs = ugi.doAs(
227+
new PrivilegedExceptionAction<AbstractFileSystem>() {
228+
@Override
229+
public AbstractFileSystem run() throws IOException {
230+
return AbstractFileSystem.createFileSystem(uri, config);
231+
}
232+
});
233+
String pathString = uri.getPath();
234+
if (pathString.isEmpty()) {
235+
pathString = "/";
236+
}
237+
return new ChRootedFs(fs, new Path(pathString));
238+
} catch (IOException | URISyntaxException |
239+
InterruptedException ex) {
240+
LOG.error("Could not initialize underlying FileSystem object"
241+
+" for uri " + uri + "with exception: " + ex.toString());
242+
}
243+
return null;
221244
}
222-
return new ChRootedFs(
223-
AbstractFileSystem.createFileSystem(uri, config),
224-
new Path(pathString));
245+
};
225246
}
226247

227248
@Override
@@ -624,7 +645,8 @@ public List<Token<?>> getDelegationTokens(String renewer) throws IOException {
624645
List<Token<?>> result = new ArrayList<Token<?>>(initialListSize);
625646
for ( int i = 0; i < mountPoints.size(); ++i ) {
626647
List<Token<?>> tokens =
627-
mountPoints.get(i).target.targetFileSystem.getDelegationTokens(renewer);
648+
mountPoints.get(i).target.getTargetFileSystem()
649+
.getDelegationTokens(renewer);
628650
if (tokens != null) {
629651
result.addAll(tokens);
630652
}

hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/fs/viewfs/TestViewFsConfig.java

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

20+
import com.google.common.base.Function;
2021
import java.io.IOException;
2122
import java.net.URI;
2223
import java.net.URISyntaxException;
@@ -47,10 +48,8 @@ class Foo { };
4748
new InodeTree<Foo>(conf, null) {
4849

4950
@Override
50-
protected
51-
Foo getTargetFileSystem(final URI uri)
52-
throws URISyntaxException, UnsupportedFileSystemException {
53-
return null;
51+
protected Function<URI, Foo> initAndGetTargetFs() {
52+
return null;
5453
}
5554

5655
@Override

0 commit comments

Comments
 (0)