org.apache.hadoop.hdfs.server.datanode.FileIoProvider Maven / Gradle / Ivy
/**
* 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.datanode;
import org.apache.commons.io.FileUtils;
import org.apache.hadoop.classification.InterfaceAudience;
import org.apache.hadoop.classification.InterfaceStability;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FileUtil;
import org.apache.hadoop.fs.HardLink;
import org.apache.hadoop.hdfs.DFSConfigKeys;
import org.apache.hadoop.hdfs.server.common.Storage;
import org.apache.hadoop.hdfs.server.datanode.fsdataset.FsVolumeSpi;
import org.apache.hadoop.hdfs.server.datanode.fsdataset.impl.FsDatasetUtil;
import org.apache.hadoop.io.IOUtils;
import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.nativeio.NativeIO;
import org.apache.hadoop.io.nativeio.NativeIOException;
import org.apache.hadoop.net.SocketOutputStream;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.io.File;
import java.io.FileDescriptor;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FilenameFilter;
import java.io.Flushable;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.channels.FileChannel;
import java.nio.file.CopyOption;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List;
import static org.apache.hadoop.hdfs.server.datanode.FileIoProvider.OPERATION.*;
/**
* This class abstracts out various file IO operations performed by the
* DataNode and invokes profiling (for collecting stats) and fault injection
* (for testing) event hooks before and after each file IO.
*
* Behavior can be injected into these events by enabling the
* profiling and/or fault injection event hooks through
* {@link DFSConfigKeys#DFS_DATANODE_FILEIO_PROFILING_SAMPLING_PERCENTAGE_KEY}
* and {@link DFSConfigKeys#DFS_DATANODE_ENABLE_FILEIO_FAULT_INJECTION_KEY}.
* These event hooks are disabled by default.
*
* Most functions accept an optional {@link FsVolumeSpi} parameter for
* instrumentation/logging.
*
* Some methods may look redundant, especially the multiple variations of
* move/rename/list. They exist to retain behavior compatibility for existing
* code.
*/
@InterfaceAudience.Private
@InterfaceStability.Unstable
public class FileIoProvider {
public static final Logger LOG = LoggerFactory.getLogger(
FileIoProvider.class);
private final ProfilingFileIoEvents profilingEventHook;
private final FaultInjectorFileIoEvents faultInjectorEventHook;
private final DataNode datanode;
private static final int LEN_INT = 4;
/**
* @param conf Configuration object. May be null. When null,
* the event handlers are no-ops.
* @param datanode datanode that owns this FileIoProvider. Used for
* IO error based volume checker callback
*/
public FileIoProvider(@Nullable Configuration conf,
final DataNode datanode) {
profilingEventHook = new ProfilingFileIoEvents(conf);
faultInjectorEventHook = new FaultInjectorFileIoEvents(conf);
this.datanode = datanode;
}
/**
* Lists the types of file system operations. Passed to the
* IO hooks so implementations can choose behavior based on
* specific operations.
*/
public enum OPERATION {
OPEN,
EXISTS,
LIST,
DELETE,
MOVE,
MKDIRS,
TRANSFER,
SYNC,
FADVISE,
READ,
WRITE,
FLUSH,
NATIVE_COPY
}
/**
* See {@link Flushable#flush()}.
*
* @param volume target volume. null if unavailable.
* @throws IOException
*/
public void flush(
@Nullable FsVolumeSpi volume, Flushable f) throws IOException {
final long begin = profilingEventHook.beforeFileIo(volume, FLUSH, 0);
try {
faultInjectorEventHook.beforeFileIo(volume, FLUSH, 0);
f.flush();
profilingEventHook.afterFileIo(volume, FLUSH, begin, 0);
} catch (Exception e) {
onFailure(volume, begin);
throw e;
}
}
/**
* Sync the given {@link FileOutputStream}.
*
* @param volume target volume. null if unavailable.
* @throws IOException
*/
public void sync(
@Nullable FsVolumeSpi volume, FileOutputStream fos) throws IOException {
final long begin = profilingEventHook.beforeFileIo(volume, SYNC, 0);
try {
faultInjectorEventHook.beforeFileIo(volume, SYNC, 0);
IOUtils.fsync(fos.getChannel(), false);
profilingEventHook.afterFileIo(volume, SYNC, begin, 0);
} catch (Exception e) {
onFailure(volume, begin);
throw e;
}
}
/**
* Sync the given directory changes to durable device.
* @throws IOException
*/
public void dirSync(@Nullable FsVolumeSpi volume, File dir)
throws IOException {
final long begin = profilingEventHook.beforeFileIo(volume, SYNC, 0);
try {
faultInjectorEventHook.beforeFileIo(volume, SYNC, 0);
IOUtils.fsync(dir);
profilingEventHook.afterFileIo(volume, SYNC, begin, 0);
} catch (Exception e) {
onFailure(volume, begin);
throw e;
}
}
/**
* Call sync_file_range on the given file descriptor.
*
* @param volume target volume. null if unavailable.
* @throws IOException
*/
public void syncFileRange(
@Nullable FsVolumeSpi volume, FileDescriptor outFd,
long offset, long numBytes, int flags) throws NativeIOException {
final long begin = profilingEventHook.beforeFileIo(volume, SYNC, 0);
try {
faultInjectorEventHook.beforeFileIo(volume, SYNC, 0);
NativeIO.POSIX.syncFileRangeIfPossible(outFd, offset, numBytes, flags);
profilingEventHook.afterFileIo(volume, SYNC, begin, 0);
} catch (Exception e) {
onFailure(volume, begin);
throw e;
}
}
/**
* Call posix_fadvise on the given file descriptor.
*
* @param volume target volume. null if unavailable.
* @throws IOException
*/
public void posixFadvise(
@Nullable FsVolumeSpi volume, String identifier, FileDescriptor outFd,
long offset, long length, int flags) throws NativeIOException {
final long begin = profilingEventHook.beforeMetadataOp(volume, FADVISE);
try {
faultInjectorEventHook.beforeMetadataOp(volume, FADVISE);
NativeIO.POSIX.getCacheManipulator().posixFadviseIfPossible(
identifier, outFd, offset, length, flags);
profilingEventHook.afterMetadataOp(volume, FADVISE, begin);
} catch (Exception e) {
onFailure(volume, begin);
throw e;
}
}
/**
* Delete a file.
* @param volume target volume. null if unavailable.
* @param f File to delete.
* @return true if the file was successfully deleted.
*/
public boolean delete(@Nullable FsVolumeSpi volume, File f) {
final long begin = profilingEventHook.beforeMetadataOp(volume, DELETE);
try {
faultInjectorEventHook.beforeMetadataOp(volume, DELETE);
boolean deleted = f.delete();
profilingEventHook.afterMetadataOp(volume, DELETE, begin);
return deleted;
} catch (Exception e) {
onFailure(volume, begin);
throw e;
}
}
/**
* Delete a file, first checking to see if it exists.
* @param volume target volume. null if unavailable.
* @param f File to delete
* @return true if the file was successfully deleted or if it never
* existed.
*/
public boolean deleteWithExistsCheck(@Nullable FsVolumeSpi volume, File f) {
final long begin = profilingEventHook.beforeMetadataOp(volume, DELETE);
try {
faultInjectorEventHook.beforeMetadataOp(volume, DELETE);
boolean deleted = !f.exists() || f.delete();
profilingEventHook.afterMetadataOp(volume, DELETE, begin);
if (!deleted) {
LOG.warn("Failed to delete file {}", f);
}
return deleted;
} catch (Exception e) {
onFailure(volume, begin);
throw e;
}
}
/**
* Transfer data from a FileChannel to a SocketOutputStream.
*
* @param volume target volume. null if unavailable.
* @param sockOut SocketOutputStream to write the data.
* @param fileCh FileChannel from which to read data.
* @param position position within the channel where the transfer begins.
* @param count number of bytes to transfer.
* @param waitTime returns the nanoseconds spent waiting for the socket
* to become writable.
* @param transferTime returns the nanoseconds spent transferring data.
* @throws IOException
*/
public void transferToSocketFully(
@Nullable FsVolumeSpi volume, SocketOutputStream sockOut,
FileChannel fileCh, long position, int count,
LongWritable waitTime, LongWritable transferTime) throws IOException {
final long begin = profilingEventHook.beforeFileIo(volume, TRANSFER, count);
try {
faultInjectorEventHook.beforeFileIo(volume, TRANSFER, count);
sockOut.transferToFully(fileCh, position, count,
waitTime, transferTime);
profilingEventHook.afterFileIo(volume, TRANSFER, begin, count);
} catch (Exception e) {
onFailure(volume, begin);
throw e;
}
}
/**
* Create a file.
* @param volume target volume. null if unavailable.
* @param f File to be created.
* @return true if the file does not exist and was successfully created.
* false if the file already exists.
* @throws IOException
*/
public boolean createFile(
@Nullable FsVolumeSpi volume, File f) throws IOException {
final long begin = profilingEventHook.beforeMetadataOp(volume, OPEN);
try {
faultInjectorEventHook.beforeMetadataOp(volume, OPEN);
boolean created = f.createNewFile();
profilingEventHook.afterMetadataOp(volume, OPEN, begin);
return created;
} catch (Exception e) {
onFailure(volume, begin);
throw e;
}
}
/**
* Create a FileInputStream using
* {@link FileInputStream#FileInputStream(File)}.
*
* Wraps the created input stream to intercept read calls
* before delegating to the wrapped stream.
*
* @param volume target volume. null if unavailable.
* @param f File object.
* @return FileInputStream to the given file.
* @throws FileNotFoundException
*/
public FileInputStream getFileInputStream(
@Nullable FsVolumeSpi volume, File f) throws FileNotFoundException {
final long begin = profilingEventHook.beforeMetadataOp(volume, OPEN);
FileInputStream fis = null;
try {
faultInjectorEventHook.beforeMetadataOp(volume, OPEN);
fis = new WrappedFileInputStream(volume, f);
profilingEventHook.afterMetadataOp(volume, OPEN, begin);
return fis;
} catch(Exception e) {
org.apache.commons.io.IOUtils.closeQuietly(fis);
onFailure(volume, begin);
throw e;
}
}
/**
* Create a FileOutputStream using
* {@link FileOutputStream#FileOutputStream(File, boolean)}.
*
* Wraps the created output stream to intercept write calls
* before delegating to the wrapped stream.
*
* @param volume target volume. null if unavailable.
* @param f File object.
* @param append if true, then bytes will be written to the end of the
* file rather than the beginning.
* @return FileOutputStream to the given file object.
* @throws FileNotFoundException
*/
public FileOutputStream getFileOutputStream(
@Nullable FsVolumeSpi volume, File f,
boolean append) throws FileNotFoundException {
final long begin = profilingEventHook.beforeMetadataOp(volume, OPEN);
FileOutputStream fos = null;
try {
faultInjectorEventHook.beforeMetadataOp(volume, OPEN);
fos = new WrappedFileOutputStream(volume, f, append);
profilingEventHook.afterMetadataOp(volume, OPEN, begin);
return fos;
} catch(Exception e) {
org.apache.commons.io.IOUtils.closeQuietly(fos);
onFailure(volume, begin);
throw e;
}
}
/**
* Create a FileOutputStream using
* {@link FileOutputStream#FileOutputStream(File, boolean)}.
*
* Wraps the created output stream to intercept write calls
* before delegating to the wrapped stream.
*
* @param volume target volume. null if unavailable.
* @param f File object.
* @return FileOutputStream to the given file object.
* @throws FileNotFoundException
*/
public FileOutputStream getFileOutputStream(
@Nullable FsVolumeSpi volume, File f) throws FileNotFoundException {
return getFileOutputStream(volume, f, false);
}
/**
* Create a FileOutputStream using
* {@link FileOutputStream#FileOutputStream(FileDescriptor)}.
*
* Wraps the created output stream to intercept write calls
* before delegating to the wrapped stream.
*
* @param volume target volume. null if unavailable.
* @param fd File descriptor object.
* @return FileOutputStream to the given file object.
* @throws FileNotFoundException
*/
public FileOutputStream getFileOutputStream(
@Nullable FsVolumeSpi volume, FileDescriptor fd) {
return new WrappedFileOutputStream(volume, fd);
}
/**
* Create a FileInputStream using
* {@link NativeIO#getShareDeleteFileDescriptor}.
* Wraps the created input stream to intercept input calls
* before delegating to the wrapped stream.
*
* @param volume target volume. null if unavailable.
* @param f File object.
* @param offset the offset position, measured in bytes from the
* beginning of the file, at which to set the file
* pointer.
* @return FileOutputStream to the given file object.
* @throws FileNotFoundException
*/
public FileInputStream getShareDeleteFileInputStream(
@Nullable FsVolumeSpi volume, File f,
long offset) throws IOException {
final long begin = profilingEventHook.beforeMetadataOp(volume, OPEN);
FileInputStream fis = null;
try {
faultInjectorEventHook.beforeMetadataOp(volume, OPEN);
fis = new WrappedFileInputStream(volume,
NativeIO.getShareDeleteFileDescriptor(f, offset));
profilingEventHook.afterMetadataOp(volume, OPEN, begin);
return fis;
} catch(Exception e) {
org.apache.commons.io.IOUtils.closeQuietly(fis);
onFailure(volume, begin);
throw e;
}
}
/**
* Create a FileInputStream using
* {@link FileInputStream#FileInputStream(File)} and position
* it at the given offset.
*
* Wraps the created input stream to intercept read calls
* before delegating to the wrapped stream.
*
* @param volume target volume. null if unavailable.
* @param f File object.
* @param offset the offset position, measured in bytes from the
* beginning of the file, at which to set the file
* pointer.
* @throws FileNotFoundException
*/
public FileInputStream openAndSeek(
@Nullable FsVolumeSpi volume, File f, long offset) throws IOException {
final long begin = profilingEventHook.beforeMetadataOp(volume, OPEN);
FileInputStream fis = null;
try {
faultInjectorEventHook.beforeMetadataOp(volume, OPEN);
fis = new WrappedFileInputStream(volume,
FsDatasetUtil.openAndSeek(f, offset));
profilingEventHook.afterMetadataOp(volume, OPEN, begin);
return fis;
} catch(Exception e) {
org.apache.commons.io.IOUtils.closeQuietly(fis);
onFailure(volume, begin);
throw e;
}
}
/**
* Create a RandomAccessFile using
* {@link RandomAccessFile#RandomAccessFile(File, String)}.
*
* Wraps the created input stream to intercept IO calls
* before delegating to the wrapped RandomAccessFile.
*
* @param volume target volume. null if unavailable.
* @param f File object.
* @param mode See {@link RandomAccessFile} for a description
* of the mode string.
* @return RandomAccessFile representing the given file.
* @throws FileNotFoundException
*/
public RandomAccessFile getRandomAccessFile(
@Nullable FsVolumeSpi volume, File f,
String mode) throws FileNotFoundException {
final long begin = profilingEventHook.beforeMetadataOp(volume, OPEN);
RandomAccessFile raf = null;
try {
faultInjectorEventHook.beforeMetadataOp(volume, OPEN);
raf = new WrappedRandomAccessFile(volume, f, mode);
profilingEventHook.afterMetadataOp(volume, OPEN, begin);
return raf;
} catch(Exception e) {
org.apache.commons.io.IOUtils.closeQuietly(raf);
onFailure(volume, begin);
throw e;
}
}
/**
* Delete the given directory using {@link FileUtil#fullyDelete(File)}.
*
* @param volume target volume. null if unavailable.
* @param dir directory to be deleted.
* @return true on success false on failure.
*/
public boolean fullyDelete(@Nullable FsVolumeSpi volume, File dir) {
final long begin = profilingEventHook.beforeMetadataOp(volume, DELETE);
try {
faultInjectorEventHook.beforeMetadataOp(volume, DELETE);
boolean deleted = FileUtil.fullyDelete(dir);
profilingEventHook.afterMetadataOp(volume, DELETE, begin);
return deleted;
} catch(Exception e) {
onFailure(volume, begin);
throw e;
}
}
/**
* Move the src file to the target using
* {@link FileUtil#replaceFile(File, File)}.
*
* @param volume target volume. null if unavailable.
* @param src source path.
* @param target target path.
* @throws IOException
*/
public void replaceFile(
@Nullable FsVolumeSpi volume, File src, File target) throws IOException {
final long begin = profilingEventHook.beforeMetadataOp(volume, MOVE);
try {
faultInjectorEventHook.beforeMetadataOp(volume, MOVE);
FileUtil.replaceFile(src, target);
profilingEventHook.afterMetadataOp(volume, MOVE, begin);
} catch(Exception e) {
onFailure(volume, begin);
throw e;
}
}
/**
* Move the src file to the target using
* {@link Storage#rename(File, File)}.
*
* @param volume target volume. null if unavailable.
* @param src source path.
* @param target target path.
* @throws IOException
*/
public void rename(
@Nullable FsVolumeSpi volume, File src, File target)
throws IOException {
final long begin = profilingEventHook.beforeMetadataOp(volume, MOVE);
try {
faultInjectorEventHook.beforeMetadataOp(volume, MOVE);
Storage.rename(src, target);
profilingEventHook.afterMetadataOp(volume, MOVE, begin);
} catch(Exception e) {
onFailure(volume, begin);
throw e;
}
}
/**
* Move the src file to the target using
* {@link FileUtils#moveFile(File, File)}.
*
* @param volume target volume. null if unavailable.
* @param src source path.
* @param target target path.
* @throws IOException
*/
public void moveFile(
@Nullable FsVolumeSpi volume, File src, File target)
throws IOException {
final long begin = profilingEventHook.beforeMetadataOp(volume, MOVE);
try {
faultInjectorEventHook.beforeMetadataOp(volume, MOVE);
FileUtils.moveFile(src, target);
profilingEventHook.afterMetadataOp(volume, MOVE, begin);
} catch(Exception e) {
onFailure(volume, begin);
throw e;
}
}
/**
* Move the src file to the target using
* {@link Files#move(Path, Path, CopyOption...)}.
*
* @param volume target volume. null if unavailable.
* @param src source path.
* @param target target path.
* @param options See {@link Files#move} for a description
* of the options.
* @throws IOException
*/
public void move(
@Nullable FsVolumeSpi volume, Path src, Path target,
CopyOption... options) throws IOException {
final long begin = profilingEventHook.beforeMetadataOp(volume, MOVE);
try {
faultInjectorEventHook.beforeMetadataOp(volume, MOVE);
Files.move(src, target, options);
profilingEventHook.afterMetadataOp(volume, MOVE, begin);
} catch(Exception e) {
onFailure(volume, begin);
throw e;
}
}
/**
* Move the src file to the target using
* {@link NativeIO#renameTo(File, File)}.
*
* @param volume target volume. null if unavailable.
* @param src source file.
* @param target target file.
* @param options See {@link Files#move} for a description
* of the options.
* @throws IOException
*/
public void renameTo(
@Nullable FsVolumeSpi volume, File src, File target)
throws IOException {
final long begin = profilingEventHook.beforeMetadataOp(volume, MOVE);
try {
faultInjectorEventHook.beforeMetadataOp(volume, MOVE);
NativeIO.renameTo(src, target);
profilingEventHook.afterMetadataOp(volume, MOVE, begin);
} catch(Exception e) {
onFailure(volume, begin);
throw e;
}
}
/**
* See {@link Storage#nativeCopyFileUnbuffered(File, File, boolean)}.
*
* @param volume target volume. null if unavailable.
* @param src an existing file to copy, must not be {@code null}
* @param target the new file, must not be {@code null}
* @param preserveFileDate true if the file date of the copy
* should be the same as the original
* @throws IOException
*/
public void nativeCopyFileUnbuffered(
@Nullable FsVolumeSpi volume, File src, File target,
boolean preserveFileDate) throws IOException {
final long length = src.length();
final long begin = profilingEventHook.beforeFileIo(volume, NATIVE_COPY,
length);
try {
faultInjectorEventHook.beforeFileIo(volume, NATIVE_COPY, length);
Storage.nativeCopyFileUnbuffered(src, target, preserveFileDate);
profilingEventHook.afterFileIo(volume, NATIVE_COPY, begin, length);
} catch(Exception e) {
onFailure(volume, begin);
throw e;
}
}
/**
* See {@link File#mkdirs()}.
*
* @param volume target volume. null if unavailable.
* @param dir directory to be created.
* @return true only if the directory was created. false if
* the directory already exists.
* @throws IOException if a directory with the given name does
* not exist and could not be created.
*/
public boolean mkdirs(
@Nullable FsVolumeSpi volume, File dir) throws IOException {
final long begin = profilingEventHook.beforeMetadataOp(volume, MKDIRS);
boolean created = false;
boolean isDirectory;
try {
faultInjectorEventHook.beforeMetadataOp(volume, MKDIRS);
created = dir.mkdirs();
isDirectory = !created && dir.isDirectory();
profilingEventHook.afterMetadataOp(volume, MKDIRS, begin);
} catch(Exception e) {
onFailure(volume, begin);
throw e;
}
if (!created && !isDirectory) {
throw new IOException("Mkdirs failed to create " + dir);
}
return created;
}
/**
* Create the target directory using {@link File#mkdirs()} only if
* it doesn't exist already.
*
* @param volume target volume. null if unavailable.
* @param dir directory to be created.
* @throws IOException if the directory could not created
*/
public void mkdirsWithExistsCheck(
@Nullable FsVolumeSpi volume, File dir) throws IOException {
final long begin = profilingEventHook.beforeMetadataOp(volume, MKDIRS);
boolean succeeded = false;
try {
faultInjectorEventHook.beforeMetadataOp(volume, MKDIRS);
succeeded = dir.isDirectory() || dir.mkdirs();
profilingEventHook.afterMetadataOp(volume, MKDIRS, begin);
} catch(Exception e) {
onFailure(volume, begin);
throw e;
}
if (!succeeded) {
throw new IOException("Mkdirs failed to create " + dir);
}
}
/**
* Get a listing of the given directory using
* {@link FileUtil#listFiles(File)}.
*
* @param volume target volume. null if unavailable.
* @param dir Directory to be listed.
* @return array of file objects representing the directory entries.
* @throws IOException
*/
public File[] listFiles(
@Nullable FsVolumeSpi volume, File dir) throws IOException {
final long begin = profilingEventHook.beforeMetadataOp(volume, LIST);
try {
faultInjectorEventHook.beforeMetadataOp(volume, LIST);
File[] children = FileUtil.listFiles(dir);
profilingEventHook.afterMetadataOp(volume, LIST, begin);
return children;
} catch(Exception e) {
onFailure(volume, begin);
throw e;
}
}
/**
* Get a listing of the given directory using
* {@link FileUtil#listFiles(File)}.
*
* @param volume target volume. null if unavailable.
* @param dir directory to be listed.
* @return array of strings representing the directory entries.
* @throws IOException
*/
public String[] list(
@Nullable FsVolumeSpi volume, File dir) throws IOException {
final long begin = profilingEventHook.beforeMetadataOp(volume, LIST);
try {
faultInjectorEventHook.beforeMetadataOp(volume, LIST);
String[] children = FileUtil.list(dir);
profilingEventHook.afterMetadataOp(volume, LIST, begin);
return children;
} catch(Exception e) {
onFailure(volume, begin);
throw e;
}
}
/**
* Get a listing of the given directory using
* {@link IOUtils#listDirectory(File, FilenameFilter)}.
*
* @param volume target volume. null if unavailable.
* @param dir Directory to list.
* @param filter {@link FilenameFilter} to filter the directory entries.
* @throws IOException
*/
public List listDirectory(
@Nullable FsVolumeSpi volume, File dir,
FilenameFilter filter) throws IOException {
final long begin = profilingEventHook.beforeMetadataOp(volume, LIST);
try {
faultInjectorEventHook.beforeMetadataOp(volume, LIST);
List children = IOUtils.listDirectory(dir, filter);
profilingEventHook.afterMetadataOp(volume, LIST, begin);
return children;
} catch(Exception e) {
onFailure(volume, begin);
throw e;
}
}
/**
* Retrieves the number of links to the specified file.
*
* @param volume target volume. null if unavailable.
* @param f file whose link count is being queried.
* @return number of hard-links to the given file, including the
* given path itself.
* @throws IOException
*/
public int getHardLinkCount(
@Nullable FsVolumeSpi volume, File f) throws IOException {
final long begin = profilingEventHook.beforeMetadataOp(volume, LIST);
try {
faultInjectorEventHook.beforeMetadataOp(volume, LIST);
int count = HardLink.getLinkCount(f);
profilingEventHook.afterMetadataOp(volume, LIST, begin);
return count;
} catch(Exception e) {
onFailure(volume, begin);
throw e;
}
}
/**
* Check for file existence using {@link File#exists()}.
*
* @param volume target volume. null if unavailable.
* @param f file object.
* @return true if the file exists.
*/
public boolean exists(@Nullable FsVolumeSpi volume, File f) {
final long begin = profilingEventHook.beforeMetadataOp(volume, EXISTS);
try {
faultInjectorEventHook.beforeMetadataOp(volume, EXISTS);
boolean exists = f.exists();
profilingEventHook.afterMetadataOp(volume, EXISTS, begin);
return exists;
} catch(Exception e) {
onFailure(volume, begin);
throw e;
}
}
/**
* A thin wrapper over {@link FileInputStream} that allows
* instrumenting disk IO.
*/
private final class WrappedFileInputStream extends FileInputStream {
private @Nullable final FsVolumeSpi volume;
/**
* {@inheritDoc}.
*/
private WrappedFileInputStream(@Nullable FsVolumeSpi volume, File f)
throws FileNotFoundException {
super(f);
this.volume = volume;
}
/**
* {@inheritDoc}.
*/
private WrappedFileInputStream(
@Nullable FsVolumeSpi volume, FileDescriptor fd) {
super(fd);
this.volume = volume;
}
/**
* {@inheritDoc}.
*/
@Override
public int read() throws IOException {
final long begin = profilingEventHook.beforeFileIo(volume, READ, LEN_INT);
try {
faultInjectorEventHook.beforeFileIo(volume, READ, LEN_INT);
int b = super.read();
profilingEventHook.afterFileIo(volume, READ, begin, LEN_INT);
return b;
} catch(Exception e) {
onFailure(volume, begin);
throw e;
}
}
/**
* {@inheritDoc}.
*/
@Override
public int read(@Nonnull byte[] b) throws IOException {
final long begin = profilingEventHook.beforeFileIo(volume, READ, b
.length);
try {
faultInjectorEventHook.beforeFileIo(volume, READ, b.length);
int numBytesRead = super.read(b);
profilingEventHook.afterFileIo(volume, READ, begin, numBytesRead);
return numBytesRead;
} catch(Exception e) {
onFailure(volume, begin);
throw e;
}
}
/**
* {@inheritDoc}.
*/
@Override
public int read(@Nonnull byte[] b, int off, int len) throws IOException {
final long begin = profilingEventHook.beforeFileIo(volume, READ, len);
try {
faultInjectorEventHook.beforeFileIo(volume, READ, len);
int numBytesRead = super.read(b, off, len);
profilingEventHook.afterFileIo(volume, READ, begin, numBytesRead);
return numBytesRead;
} catch(Exception e) {
onFailure(volume, begin);
throw e;
}
}
}
/**
* A thin wrapper over {@link FileOutputStream} that allows
* instrumenting disk IO.
*/
private final class WrappedFileOutputStream extends FileOutputStream {
private @Nullable final FsVolumeSpi volume;
/**
* {@inheritDoc}.
*/
private WrappedFileOutputStream(
@Nullable FsVolumeSpi volume, File f,
boolean append) throws FileNotFoundException {
super(f, append);
this.volume = volume;
}
/**
* {@inheritDoc}.
*/
private WrappedFileOutputStream(
@Nullable FsVolumeSpi volume, FileDescriptor fd) {
super(fd);
this.volume = volume;
}
/**
* {@inheritDoc}.
*/
@Override
public void write(int b) throws IOException {
final long begin = profilingEventHook.beforeFileIo(volume, WRITE,
LEN_INT);
try {
faultInjectorEventHook.beforeFileIo(volume, WRITE, LEN_INT);
super.write(b);
profilingEventHook.afterFileIo(volume, WRITE, begin, LEN_INT);
} catch(Exception e) {
onFailure(volume, begin);
throw e;
}
}
/**
* {@inheritDoc}.
*/
@Override
public void write(@Nonnull byte[] b) throws IOException {
final long begin = profilingEventHook.beforeFileIo(volume, WRITE, b
.length);
try {
faultInjectorEventHook.beforeFileIo(volume, WRITE, b.length);
super.write(b);
profilingEventHook.afterFileIo(volume, WRITE, begin, b.length);
} catch(Exception e) {
onFailure(volume, begin);
throw e;
}
}
/**
* {@inheritDoc}.
*/
@Override
public void write(@Nonnull byte[] b, int off, int len) throws IOException {
final long begin = profilingEventHook.beforeFileIo(volume, WRITE, len);
try {
faultInjectorEventHook.beforeFileIo(volume, WRITE, len);
super.write(b, off, len);
profilingEventHook.afterFileIo(volume, WRITE, begin, len);
} catch(Exception e) {
onFailure(volume, begin);
throw e;
}
}
}
/**
* A thin wrapper over {@link FileInputStream} that allows
* instrumenting IO.
*/
private final class WrappedRandomAccessFile extends RandomAccessFile {
private @Nullable final FsVolumeSpi volume;
public WrappedRandomAccessFile(
@Nullable FsVolumeSpi volume, File f, String mode)
throws FileNotFoundException {
super(f, mode);
this.volume = volume;
}
@Override
public int read() throws IOException {
final long begin = profilingEventHook.beforeFileIo(volume, READ, LEN_INT);
try {
faultInjectorEventHook.beforeFileIo(volume, READ, LEN_INT);
int b = super.read();
profilingEventHook.afterFileIo(volume, READ, begin, LEN_INT);
return b;
} catch(Exception e) {
onFailure(volume, begin);
throw e;
}
}
@Override
public int read(byte[] b, int off, int len) throws IOException {
final long begin = profilingEventHook.beforeFileIo(volume, READ, len);
try {
faultInjectorEventHook.beforeFileIo(volume, READ, len);
int numBytesRead = super.read(b, off, len);
profilingEventHook.afterFileIo(volume, READ, begin, numBytesRead);
return numBytesRead;
} catch(Exception e) {
onFailure(volume, begin);
throw e;
}
}
@Override
public int read(byte[] b) throws IOException {
final long begin = profilingEventHook.beforeFileIo(volume, READ, b
.length);
try {
faultInjectorEventHook.beforeFileIo(volume, READ, b.length);
int numBytesRead = super.read(b);
profilingEventHook.afterFileIo(volume, READ, begin, numBytesRead);
return numBytesRead;
} catch(Exception e) {
onFailure(volume, begin);
throw e;
}
}
@Override
public void write(int b) throws IOException {
final long begin = profilingEventHook.beforeFileIo(volume, WRITE,
LEN_INT);
try {
faultInjectorEventHook.beforeFileIo(volume, WRITE, LEN_INT);
super.write(b);
profilingEventHook.afterFileIo(volume, WRITE, begin, LEN_INT);
} catch(Exception e) {
onFailure(volume, begin);
throw e;
}
}
@Override
public void write(@Nonnull byte[] b) throws IOException {
final long begin = profilingEventHook.beforeFileIo(volume, WRITE, b
.length);
try {
faultInjectorEventHook.beforeFileIo(volume, WRITE, b.length);
super.write(b);
profilingEventHook.afterFileIo(volume, WRITE, begin, b.length);
} catch(Exception e) {
onFailure(volume, begin);
throw e;
}
}
@Override
public void write(byte[] b, int off, int len) throws IOException {
final long begin = profilingEventHook.beforeFileIo(volume, WRITE, len);
try {
faultInjectorEventHook.beforeFileIo(volume, WRITE, len);
super.write(b, off, len);
profilingEventHook.afterFileIo(volume, WRITE, begin, len);
} catch(Exception e) {
onFailure(volume, begin);
throw e;
}
}
}
private void onFailure(@Nullable FsVolumeSpi volume, long begin) {
if (datanode != null && volume != null) {
datanode.checkDiskErrorAsync(volume);
}
profilingEventHook.onFailure(volume, begin);
}
}