All Downloads are FREE. Search and download functionalities are using the official Maven repository.

tachyon.client.TachyonFS Maven / Gradle / Ivy

The newest version!
/*
 * Licensed to the University of California, Berkeley 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 tachyon.client;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.common.base.Preconditions;
import com.google.common.io.Closer;

import tachyon.Constants;
import tachyon.TachyonURI;
import tachyon.annotation.PublicApi;
import tachyon.client.block.BlockStoreContext;
import tachyon.client.file.FileSystemContext;
import tachyon.client.file.options.CreateOptions;
import tachyon.client.file.options.MkdirOptions;
import tachyon.client.table.RawColumn;
import tachyon.client.table.RawTable;
import tachyon.conf.TachyonConf;
import tachyon.exception.ExceptionMessage;
import tachyon.exception.TachyonException;
import tachyon.thrift.DependencyInfo;
import tachyon.thrift.FileBlockInfo;
import tachyon.thrift.FileInfo;
import tachyon.thrift.RawTableInfo;
import tachyon.thrift.WorkerInfo;
import tachyon.util.IdUtils;
import tachyon.util.io.FileUtils;
import tachyon.util.network.NetworkAddressUtils;
import tachyon.util.network.NetworkAddressUtils.ServiceType;
import tachyon.worker.ClientMetrics;
import tachyon.worker.WorkerClient;

/**
 * Client API to use Tachyon as a file system. This API is not compatible with HDFS file system API;
 * while tachyon.hadoop.AbstractTFS provides another API that exposes Tachyon as HDFS file system.
 * Under the hood, this class maintains a MasterClientBase to talk to the master server and
 * WorkerClients to interact with different Tachyon workers.
 *
 * As of 0.8, replaced by {@link tachyon.client.file.TachyonFileSystem}
 */
@PublicApi
@Deprecated
public class TachyonFS extends AbstractTachyonFS {

  /**
   * Creates a TachyonFS handler for the given path.
   *
   * @param tachyonPath a Tachyon path contains master address. e.g., tachyon://localhost:19998,
   *        tachyon://localhost:19998/ab/c.txt
   * @return the corresponding TachyonFS handler
   * @see #get(tachyon.TachyonURI, tachyon.conf.TachyonConf)
   */
  @Deprecated
  public static synchronized TachyonFS get(String tachyonPath) {
    return get(new TachyonURI(tachyonPath), ClientContext.getConf());
  }

  /**
   * Creates a TachyonFS handler for the given Tachyon URI.
   *
   * @param tachyonURI a Tachyon URI to indicate master address. e.g., tachyon://localhost:19998,
   *        tachyon://localhost:19998/ab/c.txt
   * @return the corresponding TachyonFS handler
   * @see #get(tachyon.TachyonURI, tachyon.conf.TachyonConf)
   */
  @Deprecated
  public static synchronized TachyonFS get(final TachyonURI tachyonURI) {
    return get(tachyonURI, ClientContext.getConf());
  }

  /**
   * Creates a TachyonFS handler for the given Tachyon URI and configuration.
   *
   * @param tachyonURI a Tachyon URI to indicate master address. e.g., tachyon://localhost:19998,
   *        tachyon://localhost:19998/ab/c.txt
   * @param tachyonConf The TachyonConf instance.
   * @return the corresponding TachyonFS handler
   */
  public static synchronized TachyonFS get(final TachyonURI tachyonURI, TachyonConf tachyonConf) {
    Preconditions.checkArgument(tachyonConf != null, "TachyonConf cannot be null.");
    Preconditions.checkArgument(tachyonURI != null, "Tachyon URI cannot be null. Use "
        + Constants.HEADER + "host:port/ ," + Constants.HEADER_FT + "host:port/.");
    String scheme = tachyonURI.getScheme();
    Preconditions.checkNotNull(scheme, "Tachyon scheme cannot be null. Use " + Constants.SCHEME
        + " or " + Constants.SCHEME_FT + ".");
    Preconditions.checkNotNull(tachyonURI.getHost(), "Tachyon hostname cannot be null.");
    Preconditions.checkState(tachyonURI.getPort() != -1, "Tachyon URI must have a port number.");
    Preconditions.checkState(
        (Constants.SCHEME.equals(scheme) || Constants.SCHEME_FT.equals(scheme)),
        "Tachyon scheme must be either " + Constants.SCHEME + " or " + Constants.SCHEME_FT + ".");

    boolean useZookeeper = scheme.equals(Constants.SCHEME_FT);
    tachyonConf.set(Constants.ZOOKEEPER_ENABLED, Boolean.toString(useZookeeper));
    tachyonConf.set(Constants.MASTER_HOSTNAME, tachyonURI.getHost());
    tachyonConf.set(Constants.MASTER_PORT, Integer.toString(tachyonURI.getPort()));

    return get(tachyonConf);
  }

  /**
   * Creates a TachyonFS handler for the given hostname, port, and Zookeeper mode.
   *
   * @param masterHost master host details
   * @param masterPort port master listens on
   * @param zkMode use zookeeper
   * @return the corresponding TachyonFS handler
   */
  public static synchronized TachyonFS get(String masterHost, int masterPort, boolean zkMode) {
    TachyonConf tachyonConf = ClientContext.getConf();
    tachyonConf.set(Constants.MASTER_HOSTNAME, masterHost);
    tachyonConf.set(Constants.MASTER_PORT, Integer.toString(masterPort));
    tachyonConf.set(Constants.ZOOKEEPER_ENABLED, Boolean.toString(zkMode));
    return get(tachyonConf);
  }

  /**
   * Creates a TachyonFS handler for the given Tachyon configuration.
   *
   * @param tachyonConf The TachyonConf instance.
   * @return the corresponding TachyonFS handler
   */
  public static synchronized TachyonFS get(TachyonConf tachyonConf) {
    Preconditions.checkArgument(tachyonConf != null, "TachyonConf cannot be null.");
    return new TachyonFS(tachyonConf);
  }

  private static final Logger LOG = LoggerFactory.getLogger(Constants.LOGGER_TYPE);
  private final int mUserFailedSpaceRequestLimits;

  /** The RPC client talks to the file system master. */
  private final FileSystemMasterClient mFSMasterClient;
  /** The RPC client talks to the block store master. */
  private final BlockMasterClient mBlockMasterClient;
  /** The RPC client talks to the raw table master. */
  private final RawTableMasterClient mRawTableMasterClient;
  /** The Master address. */
  private final InetSocketAddress mMasterAddress;
  /** The RPC client talks to the local worker if there is one. */
  private final WorkerClient mWorkerClient;
  private final Closer mCloser = Closer.create();
  /** Whether to use ZooKeeper or not */
  private final boolean mZookeeperMode;
  // Cached FileInfo
  private final Map mPathToClientFileInfo = new HashMap();
  private final Map mIdToClientFileInfo = new HashMap();

  /** All Blocks that have been locked. */
  private final Map> mLockedBlockIds = new HashMap>();
  /** Mapping from block id to path of the block locked */
  private final Map mLockedBlockIdToPath = new HashMap();
  /** Each user facing block has a unique block lock id. */
  private final AtomicInteger mBlockLockId = new AtomicInteger(0);

  private TachyonURI mRootUri = null;
  private ClientMetrics mClientMetrics = new ClientMetrics();

  private TachyonFS(TachyonConf tachyonConf) {
    super(tachyonConf);

    mMasterAddress = NetworkAddressUtils.getConnectAddress(ServiceType.MASTER_RPC, tachyonConf);
    mZookeeperMode = mTachyonConf.getBoolean(Constants.ZOOKEEPER_ENABLED);
    mFSMasterClient = mCloser.register(FileSystemContext.INSTANCE.acquireMasterClient());
    mBlockMasterClient = mCloser.register(BlockStoreContext.INSTANCE.acquireMasterClient());
    mRawTableMasterClient =
        mCloser.register(new RawTableMasterClient(mMasterAddress, mTachyonConf));
    mWorkerClient = mCloser.register(BlockStoreContext.INSTANCE.acquireWorkerClient());
    mUserFailedSpaceRequestLimits = mTachyonConf.getInt(Constants.USER_FAILED_SPACE_REQUEST_LIMITS);
    String scheme = mZookeeperMode ? Constants.SCHEME_FT : Constants.SCHEME;
    String authority = mMasterAddress.getHostName() + ":" + mMasterAddress.getPort();
    mRootUri = new TachyonURI(scheme, authority, TachyonURI.SEPARATOR);
  }

  /**
   * Updates the latest block access time on the worker.
   *
   * @param blockId the local block's id
   * @throws IOException if the block cannot be accessed
   */
  synchronized void accessLocalBlock(long blockId) throws IOException {
    if (mWorkerClient.isLocal()) {
      mWorkerClient.accessBlock(blockId);
    }
  }

  /**
   * Notifies the worker that the checkpoint file of the indicated file id has been added.
   *
   * @param fid the file id
   * @throws IOException if the underlying worker RPC fails
   */
  synchronized void addCheckpoint(long fid) throws IOException {
    throw new UnsupportedOperationException("AddCheckpoint is not unsupported");
  }

  /**
   * Notifies the worker to checkpoint the file of the indicated file id asynchronously
   *
   * @param fid the file id
   * @return true if succeed, false otherwise
   * @throws IOException if the underlying worker RPC fails
   */
  synchronized boolean asyncCheckpoint(long fid) throws IOException {
    return mWorkerClient.asyncCheckpoint(fid);
  }

  /**
   * Notifies the worker that the block is cached.
   *
   * @param blockId the block id
   * @throws IOException if the underlying worker RPC fails
   */
  public synchronized void cacheBlock(long blockId) throws IOException {
    mWorkerClient.cacheBlock(blockId);
  }

  /**
   * Notifies the worker the block is canceled.
   *
   * @param blockId the block id
   * @throws IOException if the underlying worker RPC fails
   */
  public synchronized void cancelBlock(long blockId) throws IOException {
    mWorkerClient.cancelBlock(blockId);
  }

  /**
   * Closes the client connections to both the master and worker.
   *
   * @throws IOException if the connections could not be closed
   */
  @Override
  public synchronized void close() throws IOException {
    mCloser.close();
  }

  /**
   * Marks the file as complete.
   *
   * @param fid the file id
   * @throws IOException if the underlying master RPC fails
   */
  synchronized void completeFile(long fid) throws IOException {
    try {
      mFSMasterClient.completeFile(fid);
    } catch (TachyonException e) {
      throw new IOException(e);
    }
  }

  /**
   * Creates a dependency.
   *
   * @param parents the dependency's input files
   * @param children the dependency's output files
   * @param commandPrefix identifies a command prefix
   * @param data stores dependency data
   * @param comment records a dependency comment
   * @param framework identifies the framework
   * @param frameworkVersion identifies the framework version
   * @param dependencyType the dependency's type, Wide or Narrow
   * @param childrenBlockSizeByte the block size of the dependency's output files
   * @return the dependency's id
   * @throws IOException if the underlying master RPC fails
   */
  @Deprecated
  public synchronized int createDependency(List parents, List children,
      String commandPrefix, List data, String comment, String framework,
      String frameworkVersion, int dependencyType, long childrenBlockSizeByte) throws IOException {
    throw new UnsupportedOperationException("operation not supported");
  }

  /**
   * Creates a new file in the file system.
   *
   * @param path The path of the file
   * @param ufsPath The path of the file in the under file system. If this is empty, the file does
   *        not exist in the under file system yet.
   * @param blockSizeBytes The size of the block in bytes. It is -1 iff ufsPath is non-empty.
   * @param recursive Creates necessary parent folders if true, not otherwise.
   * @return The file id, which is globally unique
   * @throws IOException if the underlying master RPC fails
   */
  @Override
  public synchronized long createFile(TachyonURI path, TachyonURI ufsPath, long blockSizeBytes,
      boolean recursive) throws IOException {
    validateUri(path);
    try {
      if (blockSizeBytes > 0) {
        CreateOptions options =
            new CreateOptions.Builder(ClientContext.getConf()).setBlockSizeBytes(blockSizeBytes)
                .setRecursive(recursive).build();
        return mFSMasterClient.create(path.getPath(), options);
      } else {
        return mFSMasterClient.loadMetadata(path.getPath(), recursive);
      }
    } catch (TachyonException e) {
      throw new IOException(e);
    }
  }

  /**
   * Creates a RawTable and returns its id.
   *
   * @param path the RawTable's path
   * @param columns number of columns it has
   * @return the id if succeed, -1 otherwise
   * @throws IOException if the number of columns is invalid or the underlying master RPC fails
   */
  public synchronized long createRawTable(TachyonURI path, int columns) throws IOException {
    return createRawTable(path, columns, ByteBuffer.allocate(0));
  }

  /**
   * Creates a RawTable and returns its id.
   *
   * Currently unsupported.
   *
   * @param path the RawTable's path
   * @param columns number of columns it has
   * @param metadata the meta data of the RawTable
   * @return the id if succeed, {@link tachyon.util.IdUtils#INVALID_FILE_ID} otherwise
   * @throws IOException if the number of columns is invalid or the underlying master RPC fails
   */
  public synchronized long createRawTable(TachyonURI path, int columns, ByteBuffer metadata)
      throws IOException {
    validateUri(path);
    int maxColumns = mTachyonConf.getInt(Constants.MAX_COLUMNS);
    if (columns < 1 || columns > maxColumns) {
      throw new IOException("Column count " + columns + " is smaller than 1 or " + "bigger than "
          + maxColumns);
    }
    return mRawTableMasterClient.createRawTable(path, columns, metadata);
  }

  /**
   * Deletes a file or folder.
   *
   * @param fileId The id of the file / folder. If it is not INVALID_FILE_ID, path parameter is
   *        ignored. Otherwise, the method uses the path parameter.
   * @param path The path of the file / folder. It could be empty iff id is not INVALID_FILE_ID.
   * @param recursive If fileId or path represents a non-empty folder, delete the folder recursively
   *        or not.
   * @return true if deletes successfully, false otherwise
   * @throws IOException if the underlying master RPC fails
   */
  @Override
  public synchronized boolean delete(long fileId, TachyonURI path, boolean recursive)
      throws IOException {
    validateUri(path);
    fileId = getValidFileId(fileId, path.getPath());
    try {
      return mFSMasterClient.deleteFile(fileId, recursive);
    } catch (TachyonException e) {
      throw new IOException(e);
    }
  }

  /**
   * Returns whether the file exists or not.
   *
   * @param path the file's path in Tachyon file system
   * @return true if it exists, false otherwise
   * @throws IOException if the underlying master RPC fails
   */
  // TODO(calvin): Consider making an exists function.
  public synchronized boolean exist(TachyonURI path) throws IOException {
    try {
      FileInfo info = getFileStatus(IdUtils.INVALID_FILE_ID, path, false);
      return info != null;
    } catch (IOException ioe) {
      return false;
    }
  }

  /**
   * Get the block id by the file id and block index. it will check whether the file and the block
   * exist.
   *
   * @param fileId the file id
   * @param blockIndex The index of the block in the file.
   * @return the block id if exists
   * @throws IOException if the file does not exist, or connection issue was encountered
   */
  public synchronized long getBlockId(long fileId, int blockIndex) throws IOException {
    FileInfo info = getFileStatus(fileId, true);

    if (info == null) {
      throw new IOException("File " + fileId + " does not exist.");
    }

    if (info.blockIds.size() > blockIndex) {
      return info.blockIds.get(blockIndex);
    }

    try {
      return mFSMasterClient.getFileBlockInfo(fileId, blockIndex).blockInfo.getBlockId();
    } catch (TachyonException e) {
      throw new IOException(e);
    }
  }

  /**
   * @return a new block lock id
   */
  synchronized int getBlockLockId() {
    return mBlockLockId.getAndIncrement();
  }

  /**
   * Gets a ClientBlockInfo by the file and index.
   *
   * @param fileId the id of the file
   * @param blockIndex the index of the block in the file, starting from 0
   * @return the ClientBlockInfo of the specified block
   * @throws IOException if the underlying master RPC fails
   */
  synchronized FileBlockInfo getClientBlockInfo(long fileId, int blockIndex) throws IOException {
    try {
      return mFSMasterClient.getFileBlockInfo(fileId, blockIndex);
    } catch (TachyonException e) {
      // TODO: Should not cast this to Tachyon Exception
      throw new IOException(e);
    }
  }

  /**
   *
   * @param depId the dependency id
   * @return the DependencyInfo of the specified dependency
   * @throws IOException if the underlying master RPC fails
   */
  @Deprecated
  public synchronized DependencyInfo getClientDependencyInfo(int depId) throws IOException {
    throw new UnsupportedOperationException("operation not supported");
  }

  /**
   * Gets the user's ClientMetrics.
   *
   * @return the ClientMetrics object
   */
  ClientMetrics getClientMetrics() {
    return mClientMetrics;
  }

  /**
   * Gets TachyonFile based on the file id.
   *
   * NOTE: This *will* use cached file metadata, and so will not see changes to dynamic properties,
   * such as the pinned flag. This is also different from the behavior of getFile(path), which by
   * default will not use cached metadata.
   *
   * @param fid file id.
   * @return TachyonFile of the file id, or null if the file does not exist
   * @throws IOException if the underlying master RPC fails
   */
  public synchronized TachyonFile getFile(long fid) throws IOException {
    return getFile(fid, true);
  }

  /**
   * Gets TachyonFile based on the file id. If useCachedMetadata, this will not see
   * changes to the file's pin setting, or other dynamic properties.
   *
   * @param fid the file id
   * @param useCachedMetadata whether to use cached metadata
   * @return TachyonFile of the file id, or null if the file does not exist
   * @throws IOException if the underlying master RPC fails
   */
  public synchronized TachyonFile getFile(long fid, boolean useCachedMetadata) throws IOException {
    FileInfo fileInfo = getFileStatus(fid, TachyonURI.EMPTY_URI, useCachedMetadata);
    if (fileInfo == null) {
      return null;
    }
    return new TachyonFile(this, fid, mTachyonConf);
  }

  /**
   * Gets TachyonFile based on the path. Does not utilize the file metadata cache.
   *
   * @param path file path.
   * @return TachyonFile of the path, or null if the file does not exist
   * @throws IOException if the underlying master RPC fails
   */
  public synchronized TachyonFile getFile(TachyonURI path) throws IOException {
    validateUri(path);
    return getFile(path, false);
  }

  /**
   * Gets TachyonFile based on the path. If useCachedMetadata is true, this will not
   * see changes to the file's pin setting, or other dynamic properties.
   *
   * @param path file path.
   * @param useCachedMetadata whether to use the file metadata cache
   * @return TachyonFile of the path, or null if the file does not exist
   * @throws IOException if the underlying master RPC fails
   */
  public synchronized TachyonFile getFile(TachyonURI path, boolean useCachedMetadata)
      throws IOException {
    validateUri(path);
    FileInfo fileInfo = getFileStatus(IdUtils.INVALID_FILE_ID, path, useCachedMetadata);
    if (fileInfo == null) {
      return null;
    }
    return new TachyonFile(this, fileInfo.getFileId(), mTachyonConf);
  }

  /**
   * If the given file id is INVALID_FILE_ID, tries to fetch the file id for the given path
   *
   * @param fileId
   * @param path the path to search for if fileId is INVALID_FILE_ID
   * @return the original fileId if it wasn't INVALID_FILE_ID, or a new fileId corresponding to the
   *         given path
   * @throws IOException if the given fileId is INVALID_FILE_ID and no fileId was found for the
   *         given path
   */
  private long getValidFileId(long fileId, String path) throws IOException {
    if (fileId == IdUtils.INVALID_FILE_ID) {
      fileId = mFSMasterClient.getFileId(path);
      if (fileId == IdUtils.INVALID_FILE_ID) {
        throw new IOException(ExceptionMessage.PATH_DOES_NOT_EXIST.getMessage(path));
      }
    }
    return fileId;
  }

  /**
   * Gets all the blocks' info of the file.
   *
   * @param fid the file id
   * @return the list of the blocks' info
   * @throws IOException if the underlying master RPC fails
   */
  public synchronized List getFileBlocks(long fid) throws IOException {
    // TODO(hy) Should read from mClientFileInfos if possible.
    // TODO(hy) Should add timeout to improve this.
    try {
      return mFSMasterClient.getFileBlockInfoList(fid);
    } catch (TachyonException e) {
      throw new IOException(e);
    }
  }

  /**
   * Get file id by the path. It will check if the path exists.
   *
   * @param path the path in Tachyon file system
   * @return the file id if exists, INVALID_FILE_ID otherwise
   */
  public synchronized long getFileId(TachyonURI path) {
    try {
      FileInfo fileInfo = getFileStatus(IdUtils.INVALID_FILE_ID, path, false);
      return fileInfo == null ? -IdUtils.INVALID_FILE_ID : fileInfo.getFileId();
    } catch (IOException e) {
      return IdUtils.INVALID_FILE_ID;
    }
  }

  /**
   * Gets a file status.
   *
   * @param cache FileInfo cache.
   * @param key the key in the cache.
   * @param fileId the id of the queried file. If it is INVALID_FILE_ID, uses path.
   * @param path the path of the queried file. If fielId is not INVALID_FILE_ID, this parameter is
   *        ignored.
   * @param useCachedMetaData whether to use the cached data or not.
   * @return the clientFileInfo
   * @throws IOException if the underlying master RPC fails
   */
  private synchronized  FileInfo getFileStatus(Map cache, K key, long fileId,
      String path, boolean useCachedMetaData) throws IOException {
    FileInfo info = null;
    if (useCachedMetaData) {
      info = cache.get(key);
      if (info != null) {
        return info;
      }
    }

    fileId = getValidFileId(fileId, path);
    try {
      info = mFSMasterClient.getFileInfo(fileId);
    } catch (TachyonException e) {
      throw new IOException(e);
    }
    path = info.getPath();

    // TODO(hy): LRU
    mIdToClientFileInfo.put(fileId, info);
    mPathToClientFileInfo.put(path, info);

    return info;
  }

  /**
   * Advanced API.
   *
   * Gets the FileInfo object that represents the fileId, or the path if fileId is INVALID_FILE_ID.
   *
   * @param fileId the file id of the file or folder.
   * @param path the path of the file or folder. valid iff fileId is INVALID_FILE_ID.
   * @param useCachedMetadata if true use the local cached meta data
   * @return the FileInfo of the file. null if the file does not exist
   * @throws IOException if the underlying master RPC fails
   */
  public synchronized FileInfo getFileStatus(long fileId, TachyonURI path,
      boolean useCachedMetadata) throws IOException {
    fileId = getValidFileId(fileId, path.getPath());
    return getFileStatus(mIdToClientFileInfo, fileId, fileId, TachyonURI.EMPTY_URI.getPath(),
        useCachedMetadata);
  }

  @Override
  public FileInfo getFileStatus(long fileId, TachyonURI path) throws IOException {
    return getFileStatus(fileId, path, false);
  }

  /**
   * Gets ClientFileInfo object based on fileId.
   *
   * @param fileId the file id of the file or folder.
   * @param useCachedMetadata if true use the local cached meta data
   * @return the FileInfo of the file. null if the file does not exist
   * @throws IOException if the underlying master RPC fails
   */
  public synchronized FileInfo getFileStatus(long fileId, boolean useCachedMetadata)
      throws IOException {
    return getFileStatus(fileId, TachyonURI.EMPTY_URI, useCachedMetadata);
  }

  /**
   * Gets block's temporary path from worker with initial space allocated.
   *
   * @param blockId the id of the block
   * @param initialBytes the initial bytes allocated for the block file
   * @return the temporary path of the block file
   * @throws IOException if the underlying worker RPC fails
   */
  public synchronized String getLocalBlockTemporaryPath(long blockId, long initialBytes)
      throws IOException {
    String blockPath = mWorkerClient.requestBlockLocation(blockId, initialBytes);
    // TODO(haoyuan): Handle this in the worker?
    FileUtils.createBlockPath(blockPath);
    return blockPath;
  }

  /**
   * Gets RawTable by id.
   *
   * Currently unsupported.
   *
   * @param id the id of the raw table
   * @return the RawTable
   * @throws IOException if the underlying master RPC fails
   */
  public synchronized RawTable getRawTable(long id) throws IOException {
    RawTableInfo rawTableInfo = mRawTableMasterClient.getClientRawTableInfo(id);
    return new RawTable(this, rawTableInfo);
  }

  /**
   * Get the RawTable by path.
   *
   * Currently unsupported.
   *
   * @param path the path of the raw table
   * @return the RawTable
   * @throws IOException if the underlying master RPC fails
   */
  public synchronized RawTable getRawTable(TachyonURI path) throws IOException {
    validateUri(path);
    RawTableInfo rawTableInfo = mRawTableMasterClient.getClientRawTableInfo(path);
    return new RawTable(this, rawTableInfo);
  }

  /**
   * @return the address of the UnderFileSystem
   * @throws IOException if the underlying master RPC fails
   */
  public synchronized String getUfsAddress() throws IOException {
    return mFSMasterClient.getUfsAddress();
  }

  /**
   * @return URI of the root of the filesystem
   */
  @Override
  public synchronized TachyonURI getUri() {
    return mRootUri;
  }

  /**
   * @return the sessionId of the worker client. This is only used for testing
   * @throws IOException when the underlying master RPC fails
   */
  long getSessionId() throws IOException {
    return mWorkerClient.getSessionId();
  }

  /**
   * Currently unsupported.
   *
   * @return get the total number of bytes used in Tachyon cluster
   * @throws IOException if the underlying master RPC fails
   */
  public synchronized long getUsedBytes() throws IOException {
    throw new UnsupportedOperationException("Currently unsupported.");
  }

  /**
   * Currently unsupported.
   *
   * @return get the capacity of Tachyon cluster
   * @throws IOException if the underlying master RPC fails
   */
  public synchronized long getCapacityBytes() throws IOException {
    throw new UnsupportedOperationException("Currently unsupported.");
  }

  /**
   * @return The address of the data server on the worker
   */
  public synchronized InetSocketAddress getWorkerDataServerAddress() {
    return mWorkerClient.getDataServerAddress();
  }

  /**
   * @return all the works' info
   * @throws IOException if the underlying master RPC fails
   */
  public synchronized List getWorkersInfo() throws IOException {
    return mBlockMasterClient.getWorkerInfoList();
  }

  /**
   * @return true if there is a local worker, false otherwise
   */
  public synchronized boolean hasLocalWorker() {
    return mWorkerClient.isLocal();
  }

  /**
   * Checks if this client is connected to master.
   *
   * @return true if this client is connected to master, false otherwise
   */
  public synchronized boolean isConnected() {
    return mFSMasterClient.isConnected();
  }

  /**
   * Checks if the indicated file id is a directory.
   *
   * @param fid the file id
   * @return true if the file is a directory, false otherwise
   */
  synchronized boolean isDirectory(int fid) {
    return mIdToClientFileInfo.get(fid).isFolder;
  }

  /**
   * If the path is a directory, returns all the direct entries in it. If the
   * path is a file, returns its ClientFileInfo.
   *
   * @param path the target directory/file path
   * @return A list of FileInfo, null if the file or folder does not exist
   * @throws IOException when the underlying master RPC fails
   */
  @Override
  public synchronized List listStatus(TachyonURI path) throws IOException {
    validateUri(path);
    try {
      return mFSMasterClient.getFileInfoList(
          getFileStatus(IdUtils.INVALID_FILE_ID, path).getFileId());
    } catch (TachyonException e) {
      throw new IOException(e);
    }
  }

  /**
   * Locks a block in the current TachyonFS.
   *
   * @param blockId The id of the block to lock. blockId must be positive.
   * @param blockLockId The block lock id of the block of lock. blockLockId must be
   *        non-negative.
   * @return the path of the block file locked
   * @throws IOException if the underlying worker RPC fails
   */
  synchronized String lockBlock(long blockId, int blockLockId) throws IOException {
    if (blockId <= 0 || blockLockId < 0) {
      return null;
    }

    if (mLockedBlockIds.containsKey(blockId)) {
      mLockedBlockIds.get(blockId).add(blockLockId);
      return mLockedBlockIdToPath.get(blockId);
    }

    if (!mWorkerClient.isLocal()) {
      return null;
    }
    String blockPath = mWorkerClient.lockBlock(blockId);

    if (blockPath != null) {
      Set lockIds = new HashSet(4);
      lockIds.add(blockLockId);
      mLockedBlockIds.put(blockId, lockIds);
      mLockedBlockIdToPath.put(blockId, blockPath);
      return blockPath;
    }
    return null;
  }

  /**
   * Creates a folder.
   *
   * @param path the path of the folder to be created
   * @param recursive Creates necessary parent folders if true, not otherwise.
   * @return true if the folder is created successfully or already existing. false otherwise
   * @throws IOException if the underlying master RPC fails
   */
  @Override
  public synchronized boolean mkdirs(TachyonURI path, boolean recursive) throws IOException {
    validateUri(path);
    try {
      MkdirOptions options =
          new MkdirOptions.Builder(ClientContext.getConf()).setRecursive(recursive).build();
      return mFSMasterClient.mkdir(path.getPath(), options);
    } catch (TachyonException e) {
      throw new IOException(e);
    }
  }

  /**
   * An alias for setPinned(fid, true).
   *
   * @throws IOException when the underlying worker RPC fails
   * @see #setPinned(long, boolean)
   */
  public synchronized void pinFile(long fid) throws IOException {
    setPinned(fid, true);
  }

  /**
   * Frees an in-memory file or folder.
   *
   * @param fileId The id of the file / folder. If it is not INVALID_FILE_ID, path parameter is
   *        ignored. Otherwise, the method uses the path parameter.
   * @param path The path of the file / folder. It could be empty iff id is not INVALID_FILE_ID.
   * @param recursive If fileId or path represents a non-empty folder, free the folder recursively
   *        or not
   * @return true if in-memory free successfully, false otherwise
   * @throws IOException if the underlying master RPC fails
   */
  @Override
  public synchronized boolean freepath(long fileId, TachyonURI path, boolean recursive)
      throws IOException {
    validateUri(path);
    fileId = getValidFileId(fileId, path.getPath());
    try {
      return mFSMasterClient.free(fileId, recursive);
    } catch (TachyonException e) {
      throw new IOException(e);
    }
  }

  /**
   * Promotes a block to the top StorageTier, after the block file is accessed.
   *
   * @param blockId the id of the block
   * @return true if success, false otherwise
   * @throws IOException if the underlying worker RPC fails
   */
  public synchronized boolean promoteBlock(long blockId) throws IOException {
    if (mWorkerClient.isLocal()) {
      return mWorkerClient.promoteBlock(blockId);
    }
    return false;
  }

  /**
   * Renames a file or folder to the indicated new path.
   *
   * @param fileId The id of the source file / folder. If it is not INVALID_FILE_ID, path parameter
   *        is ignored. Otherwise, the method uses the srcPath parameter.
   * @param srcPath The path of the source file / folder. It could be empty iff id is not
   *        INVALID_FILE_ID.
   * @param dstPath The path of the destination file / folder. It could be empty iff id is not
   *        INVALID_FILE_ID.
   * @return true if renames successfully, false otherwise
   * @throws IOException if the underlying master RPC fails
   */
  @Override
  public synchronized boolean rename(long fileId, TachyonURI srcPath, TachyonURI dstPath)
      throws IOException {
    validateUri(srcPath);
    validateUri(dstPath);
    fileId = getValidFileId(fileId, srcPath.getPath());
    try {
      return mFSMasterClient.renameFile(fileId, dstPath.getPath());
    } catch (TachyonException e) {
      throw new IOException(e);
    }
  }

  /**
   * Reports the lost file to master.
   *
   * @param fileId the lost file id
   * @throws IOException if the underlying master RPC fails
   */
  public synchronized void reportLostFile(long fileId) throws IOException {
    try {
      mFSMasterClient.reportLostFile(fileId);
    } catch (TachyonException e) {
      throw new IOException(e);
    }
  }

  /**
   * Requests the dependency's needed files.
   *
   * @param depId the dependency id
   * @throws IOException if the underlying master RPC fails
   */
  @Deprecated
  public synchronized void requestFilesInDependency(int depId) throws IOException {
    throw new UnsupportedOperationException("operation not supported");
  }

  /**
   * Tries to request space for certain block. Only works when a local worker exists.
   *
   * @param blockId the id of the block that space will be allocated for
   * @param requestSpaceBytes size to request in bytes
   * @return the size bytes that allocated to the block, -1 if no local worker exists
   * @throws IOException if the underlying file does not exist
   */
  public synchronized long requestSpace(long blockId, long requestSpaceBytes) throws IOException {
    if (!hasLocalWorker()) {
      return -1;
    }

    long userQuotaUnitBytes = mTachyonConf.getBytes(Constants.USER_QUOTA_UNIT_BYTES);

    long toRequestSpaceBytes = Math.max(requestSpaceBytes, userQuotaUnitBytes);
    for (int attempt = 0; attempt < mUserFailedSpaceRequestLimits; attempt ++) {
      if (mWorkerClient.requestSpace(blockId, toRequestSpaceBytes)) {
        return toRequestSpaceBytes;
      }
    }
    return 0;
  }

  /**
   * Sets the "pinned" flag for the given file. Pinned files are never evicted by Tachyon until they
   * are unpinned.
   *
   * Calling setPinned() on a folder will recursively set the "pinned" flag on all of that folder's
   * children. This may be an expensive operation for folders with many files/subfolders.
   *
   * @param fid the file id
   * @param pinned the target "pinned" flag value
   * @throws IOException if the underlying master RPC fails
   */
  public synchronized void setPinned(long fid, boolean pinned) throws IOException {
    try {
      mFSMasterClient.setPinned(fid, pinned);
    } catch (TachyonException e) {
      throw new IOException(e);
    }
  }

  /**
   * Prints out the string representation of this Tachyon server address.
   *
   * @return the string representation like tachyon://host:port or tachyon-ft://host:port
   */
  @Override
  public String toString() {
    return (mZookeeperMode ? Constants.HEADER_FT : Constants.HEADER) + mMasterAddress.toString();
  }

  /**
   * Unlocks a block in the current TachyonFS.
   *
   * @param blockId The id of the block to unlock. blockId must be positive.
   * @param blockLockId The block lock id of the block of unlock. blockLockId must be
   *        non-negative.
   * @throws IOException if the underlying worker RPC fails
   */
  synchronized boolean unlockBlock(long blockId, int blockLockId) throws IOException {
    if (blockId <= 0 || blockLockId < 0) {
      return false;
    }

    if (!mLockedBlockIds.containsKey(blockId)) {
      return true;
    }
    Set lockIds = mLockedBlockIds.get(blockId);
    lockIds.remove(blockLockId);
    if (!lockIds.isEmpty()) {
      return true;
    }

    if (!mWorkerClient.isLocal()) {
      return false;
    }

    mLockedBlockIdToPath.remove(blockId);
    mLockedBlockIds.remove(blockId);
    return mWorkerClient.unlockBlock(blockId);
  }

  /**
   * An alias for setPinned(fid, false).
   *
   * @throws IOException when the underlying worker RPC fails
   * @see #setPinned(long, boolean)
   */
  public synchronized void unpinFile(long fid) throws IOException {
    setPinned(fid, false);
  }

  /**
   * Updates the RawTable's meta data.
   *
   * Currently unsupported.
   *
   * @param id the raw table's id
   * @param metadata the new meta data
   * @throws IOException if the underlying master RPC fails
   */
  public synchronized void updateRawTableMetadata(long id, ByteBuffer metadata) throws IOException {
    mRawTableMasterClient.updateRawTableMetadata(id, metadata);
  }

  /**
   * Validates the given uri, throws an appropriate Exception if the uri is invalid.
   *
   * @param uri the uri to validate
   */
  private void validateUri(TachyonURI uri) {
    Preconditions.checkNotNull(uri, "URI cannot be null.");
    Preconditions.checkArgument(uri.isPathAbsolute() || TachyonURI.EMPTY_URI.equals(uri),
        "URI must be absolute, unless it's empty.");
    Preconditions.checkArgument(
        !uri.hasScheme() || mRootUri.getScheme().equals(uri.getScheme()),
        "URI's scheme: " + uri.getScheme() + " must match the file system's scheme: "
            + mRootUri.getScheme() + ", unless it doesn't have a scheme.");
    Preconditions.checkArgument(
        !uri.hasAuthority() || mRootUri.getAuthority().equals(uri.getAuthority()),
        "URI's authority: " + uri.getAuthority() + " must match the file system's authority: "
            + mRootUri.getAuthority() + ", unless it doesn't have an authority.");
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy