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

org.apache.hadoop.hdfs.DFSLocatedBlocks 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;

import java.util.ArrayList;
import java.util.List;

import java.util.concurrent.locks.ReentrantReadWriteLock;

import org.apache.hadoop.hdfs.protocol.Block;
import org.apache.hadoop.hdfs.protocol.LocatedBlock;
import org.apache.hadoop.hdfs.protocol.LocatedBlocks;

/**
 * Wrapper for LocatedBlocks with lock protection for concurrent updates.
 */
public class DFSLocatedBlocks extends LocatedBlocks {

  private volatile long fileLength = 0; // an optimization to get fileLength without locks
  private ReentrantReadWriteLock lock;

  public DFSLocatedBlocks(LocatedBlocks lbs) {
    super(lbs.getFileLength(), lbs.getLocatedBlocks(), lbs.isUnderConstruction());
    this.fileLength = lbs.getFileLength();
    lock = new ReentrantReadWriteLock(true); // fair
  }
  
  public DFSLocatedBlocks(long flength, List blks, boolean isUnderConstuction) {
    super(flength, blks, isUnderConstuction);
    this.fileLength = flength;
  }
  
  /**
   * Get located blocks.
   */
  public List getLocatedBlocks() {
    readLock();
    try {
      return super.getLocatedBlocks();
    } finally {
      readUnlock();
    }
  }

  /**
   * @return a copy of the block location array. Used in testing.
   */
  List getLocatedBlocksCopy() {
    readLock();
    try {
      return new ArrayList(super.getLocatedBlocks());
    } finally {
      readUnlock();
    }
  }

  /**
   * Get located block.
   */
  public LocatedBlock get(int index) {
    readLock();
    try {
      return super.get(index);
    } finally {
      readUnlock();
    }
  }
  
  /**
   * Get number of located blocks.
   */
  public int locatedBlockCount() {
    readLock();
    try {
      return super.locatedBlockCount();
    } finally {
      readUnlock();
    }
  }

  /**
   * 
   */
  public long getFileLength() {
    return this.fileLength;
  }

  /**
   * Return true if file was under construction when 
   * this LocatedBlocks was constructed, false otherwise.
   */
  public boolean isUnderConstruction() {
    readLock();
    try {
      return super.isUnderConstruction();
    } finally {
      readUnlock();
    }
  }

  /**
   * Sets the file length of the file.
   */
  public void setFileLength(long length) {
    writeLock();
    try {
      super.setFileLength(length);
      this.fileLength = length;
    } finally {
      writeUnlock();
    }
  }

  public void insertRange(List newBlocks) {
    if (newBlocks.isEmpty())
      return;

    writeLock();
    try {
      super.insertRange(newBlocks);
    } finally {
      writeUnlock();
    }
  }

  public LocatedBlock getBlockContainingOffset(long offset) {
    readLock();
    try {
      int blockIdx = super.binarySearchBlockStartOffsets(offset);
      List locatedBlocks = super.getLocatedBlocks();
      if (blockIdx >= 0)
        return locatedBlocks.get(blockIdx);  // exact match

      blockIdx = LocatedBlocks.getInsertIndex(blockIdx);
      // Here, blockIdx is the "insertion point" of the queried offset in
      // the array (the index of the first element greater than the offset),
      // which by definition means that
      //
      // locatedBlocks.get(blockIdx - 1).getStartOffset() < offset &&
      // offset < locatedBlocks.get(blockIdx).getStartOffset().
      //
      // In particular, if blockIdx == 0, then
      // offset < locatedBlocks.get(0).getStartOffset().

      if (blockIdx == 0)
        return null;  // The offset is not found in known blocks.

      LocatedBlock blk = locatedBlocks.get(blockIdx - 1);
      long blkStartOffset = blk.getStartOffset();
      if (offset < blkStartOffset) {
        // By definition of insertion point, 
        // locatedBlocks.get(blockIdx - 1).getStartOffset() < offset.
        throw new AssertionError("Invalid insertion point: " +
            blockIdx + " for offset " + offset + " (located blocks: " +
            locatedBlocks + ")");
      }

      long blkLen = blk.getBlockSize();
      if (offset < blkStartOffset + blkLen
          || (offset == blkStartOffset + blkLen && isUnderConstruction() &&
          blockIdx == locatedBlocks.size())) {
        return blk;
      }

      // Block not found in the location cache, the caller should ask the
      // namenode instead.
      return null;  

    } finally {
      readUnlock();
    }
  }

  /**
   * Determine whether the input block is the block under-construction
   * for the file. If the current file is not under-construction, always
   * false is returned.
   * 
   * The result is the best guess based on unknown
   * information. The bottom line is, when the position equals to file length,
   * the block selected will be return true. This has to be guaranteed to make
   * sure the available size updating logic will always be triggered when
   * reading to the end of a under-construction file.
   * 
   * @param block
   * @return
   */
  public boolean isUnderConstructionBlock(Block block) {
    if (!isUnderConstruction()) {
      return false;
    }
    LocatedBlock lastBlock = this.get(this.locatedBlockCount() - 1);

    // There are potential inconsistency when counting the size of the
    // last block, but fileLength is not likely to be under-estimated
    // the size, unless the last block size is 0. 
    if ((this.fileLength <= lastBlock.getStartOffset()
        + lastBlock.getBlockSize())
        && lastBlock.getBlock().equals(block)) {
      return true;
    }
    return false;
  }

  private void readLock() {
    lock.readLock().lock();
  }
  private void readUnlock() {
    lock.readLock().unlock();
  }
  private void writeLock() {
    lock.writeLock().lock();
  }
  private void writeUnlock() {
    lock.writeLock().unlock();
  }

  @Override
  public String toString() {
    return getLocatedBlocks().toString();
  }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy