Package org.apache.hadoop.raid

Source Code of org.apache.hadoop.raid.BlockFixer

/**
* 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.raid;

import java.io.BufferedOutputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.regex.Pattern;
import java.util.Random;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.nio.channels.SocketChannel;

import org.apache.hadoop.util.DataChecksum;
import org.apache.hadoop.hdfs.DistributedFileSystem;
import org.apache.hadoop.hdfs.protocol.Block;
import org.apache.hadoop.hdfs.protocol.DataTransferProtocol;
import org.apache.hadoop.hdfs.protocol.DatanodeInfo;
import org.apache.hadoop.hdfs.protocol.FSConstants;
import org.apache.hadoop.hdfs.protocol.FSConstants.DatanodeReportType;
import org.apache.hadoop.hdfs.protocol.LocatedBlock;
import org.apache.hadoop.hdfs.protocol.LocatedBlocks;
import org.apache.hadoop.hdfs.server.common.HdfsConstants;
import org.apache.hadoop.hdfs.server.datanode.FSDataset;
import org.apache.hadoop.hdfs.server.datanode.RaidBlockSender;
import org.apache.hadoop.io.Text;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.conf.Configured;
import org.apache.hadoop.fs.ChecksumException;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.fs.FileStatus;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.hdfs.BlockMissingException;
import org.apache.hadoop.hdfs.RaidDFSUtil;
import org.apache.hadoop.util.StringUtils;
import org.apache.hadoop.net.NetUtils;

import org.apache.hadoop.raid.RaidNode;
import org.apache.hadoop.raid.RaidUtils;
import org.apache.hadoop.raid.protocol.PolicyInfo.ErasureCodeType;


/**
* contains the core functionality of the block fixer
*
* raid.blockfix.interval          - interval between checks for corrupt files
*
* raid.blockfix.history.interval  - interval before fixing same file again
*
* raid.blockfix.read.timeout      - read time out
*
* raid.blockfix.write.timeout     - write time out
*/
public class BlockFixer extends Configured implements Runnable {
  public static final Log LOG = LogFactory.getLog(
                                  "org.apache.hadoop.raid.BlockFixer");

  public static final String BLOCKFIX_INTERVAL = "raid.blockfix.interval";
  public static final String BLOCKFIX_HISTORY_INTERVAL =
    "raid.blockfix.history.interval";
  public static final String BLOCKFIX_READ_TIMEOUT =
    "raid.blockfix.read.timeout";
  public static final String BLOCKFIX_WRITE_TIMEOUT =
    "raid.blockfix.write.timeout";

  public static final long DEFAULT_BLOCKFIX_INTERVAL = 60 * 1000; // 1 min
  public static final long DEFAULT_BLOCKFIX_HISTORY_INTERVAL =
    60 * 60 * 1000; // 60 mins

  private java.util.HashMap<String, java.util.Date> history;
  private long numFilesFixed = 0;
  private String xorPrefix;
  private String rsPrefix;
  private Encoder xorEncoder;
  private Decoder xorDecoder;
  private Encoder rsEncoder;
  private Decoder rsDecoder;

  // interval between checks for corrupt files
  protected long blockFixInterval = DEFAULT_BLOCKFIX_INTERVAL;

  // interval before fixing same file again
  protected long historyInterval = DEFAULT_BLOCKFIX_HISTORY_INTERVAL;

  public volatile boolean running = true;


  public BlockFixer(Configuration conf) throws IOException {
    super(conf);
    history = new java.util.HashMap<String, java.util.Date>();
    blockFixInterval = getConf().getInt(BLOCKFIX_INTERVAL,
                                   (int) blockFixInterval);
    xorPrefix = RaidNode.xorDestinationPath(getConf()).toUri().getPath();
    if (!xorPrefix.endsWith(Path.SEPARATOR)) {
      xorPrefix += Path.SEPARATOR;
    }
    int stripeLength = RaidNode.getStripeLength(getConf());
    xorEncoder = new XOREncoder(getConf(), stripeLength);
    xorDecoder = new XORDecoder(getConf(), stripeLength);
    rsPrefix = RaidNode.rsDestinationPath(getConf()).toUri().getPath();
    if (!rsPrefix.endsWith(Path.SEPARATOR)) {
      rsPrefix += Path.SEPARATOR;
    }
    int parityLength = RaidNode.rsParityLength(getConf());
    rsEncoder = new ReedSolomonEncoder(getConf(), stripeLength, parityLength);
    rsDecoder = new ReedSolomonDecoder(getConf(), stripeLength, parityLength);
  }

  public void run() {
    while (running) {
      try {
        LOG.info("BlockFixer continuing to run...");
        doFix();
      } catch (Exception e) {
        LOG.error(StringUtils.stringifyException(e));
      } catch (Error err) {
        LOG.error("Exiting after encountering " +
                    StringUtils.stringifyException(err));
        throw err;
      }
    }
  }

  public long filesFixed() {
    return numFilesFixed;
  }

  void doFix() throws InterruptedException, IOException {
    while (running) {
      // Sleep before proceeding to fix files.
      Thread.sleep(blockFixInterval);

      // Purge history older than the history interval.
      purgeHistory();

      List<Path> corruptFiles = getCorruptFiles();
      if (corruptFiles.isEmpty()) {
        // If there are no corrupt files, retry after some time.
        continue;
      }
      LOG.info("Found " + corruptFiles.size() + " corrupt files.");

      sortCorruptFiles(corruptFiles);

      for (Path srcPath: corruptFiles) {
        if (!running) break;
        try {
          fixFile(srcPath);
        } catch (IOException ie) {
          LOG.error("Hit error while processing " + srcPath +
            ": " + StringUtils.stringifyException(ie));
          // Do nothing, move on to the next file.
        }
      }
    }
  }


  void fixFile(Path srcPath) throws IOException {

    if (RaidNode.isParityHarPartFile(srcPath)) {
      processCorruptParityHarPartFile(srcPath);
      return;
    }

    // The corrupted file is a XOR parity file
    if (isXorParityFile(srcPath)) {
      processCorruptParityFile(srcPath, xorEncoder);
      return;
    }

    // The corrupted file is a ReedSolomon parity file
    if (isRsParityFile(srcPath)) {
      processCorruptParityFile(srcPath, rsEncoder);
      return;
    }

    // The corrupted file is a source file
    RaidNode.ParityFilePair ppair =
      RaidNode.xorParityForSource(srcPath, getConf());
    Decoder decoder = null;
    if (ppair != null) {
      decoder = xorDecoder;
    } else  {
      ppair = RaidNode.rsParityForSource(srcPath, getConf());
      if (ppair != null) {
        decoder = rsDecoder;
      }
    }

    // If we have a parity file, process the file and fix it.
    if (ppair != null) {
      processCorruptFile(srcPath, ppair, decoder);
    }

  }

  /**
   * We maintain history of fixed files because a fixed file may appear in
   * the list of corrupt files if we loop around too quickly.
   * This function removes the old items in the history so that we can
   * recognize files that have actually become corrupt since being fixed.
   */
  void purgeHistory() {
    // Default history interval is 1 hour.
    long historyInterval = getConf().getLong(
                             BLOCKFIX_HISTORY_INTERVAL, 3600*1000);
    java.util.Date cutOff = new java.util.Date(
                                   System.currentTimeMillis()-historyInterval);
    List<String> toRemove = new java.util.ArrayList<String>();

    for (String key: history.keySet()) {
      java.util.Date item = history.get(key);
      if (item.before(cutOff)) {
        toRemove.add(key);
      }
    }
    for (String key: toRemove) {
      LOG.info("Removing " + key + " from history");
      history.remove(key);
    }
  }

  /**
   * @return A list of corrupt files as obtained from the namenode
   */
  List<Path> getCorruptFiles() throws IOException {
    DistributedFileSystem dfs = getDFS(new Path("/"));

    String[] nnCorruptFiles = RaidDFSUtil.getCorruptFiles(getConf());
    List<Path> corruptFiles = new LinkedList<Path>();
    for (String file: nnCorruptFiles) {
      if (!history.containsKey(file)) {
        corruptFiles.add(new Path(file));
      }
    }
    RaidUtils.filterTrash(getConf(), corruptFiles);
    return corruptFiles;
  }

  /**
   * Sorts source files ahead of parity files.
   */
  void sortCorruptFiles(List<Path> files) {
    // TODO: We should first fix the files that lose more blocks
    Comparator<Path> comp = new Comparator<Path>() {
      public int compare(Path p1, Path p2) {
        if (isXorParityFile(p2) || isRsParityFile(p2)) {
          // If p2 is a parity file, p1 is smaller.
          return -1;
        }
        if (isXorParityFile(p1) || isRsParityFile(p1)) {
          // If p1 is a parity file, p2 is smaller.
          return 1;
        }
        // If both are source files, they are equal.
        return 0;
      }
    };
    Collections.sort(files, comp);
  }

  /**
   * Reads through a corrupt source file fixing corrupt blocks on the way.
   * @param srcPath Path identifying the corrupt file.
   * @throws IOException
   */
  void processCorruptFile(Path srcPath, RaidNode.ParityFilePair parityPair,
      Decoder decoder) throws IOException {
    LOG.info("Processing corrupt file " + srcPath);

    DistributedFileSystem srcFs = getDFS(srcPath);
    FileStatus srcStat = srcFs.getFileStatus(srcPath);
    long blockSize = srcStat.getBlockSize();
    long srcFileSize = srcStat.getLen();
    String uriPath = srcPath.toUri().getPath();

    int numBlocksFixed = 0;
    List<LocatedBlock> corrupt =
      RaidDFSUtil.corruptBlocksInFile(srcFs, uriPath, 0, srcFileSize);
    for (LocatedBlock lb: corrupt) {
      Block corruptBlock = lb.getBlock();
      long corruptOffset = lb.getStartOffset();

      LOG.info("Found corrupt block " + corruptBlock +
          ", offset " + corruptOffset);

      final long blockContentsSize =
        Math.min(blockSize, srcFileSize - corruptOffset);
      File localBlockFile =
        File.createTempFile(corruptBlock.getBlockName(), ".tmp");
      localBlockFile.deleteOnExit();

      try {
        decoder.recoverBlockToFile(srcFs, srcPath, parityPair.getFileSystem(),
          parityPair.getPath(), blockSize, corruptOffset, localBlockFile,
          blockContentsSize);

        // We have a the contents of the block, send them.
        DatanodeInfo datanode = chooseDatanode(lb.getLocations());
        computeMetdataAndSendFixedBlock(
          datanode, localBlockFile, lb, blockContentsSize);
        numBlocksFixed++;

        LOG.info("Adding " + srcPath + " to history");
        history.put(srcPath.toString(), new java.util.Date());
      } finally {
        localBlockFile.delete();
      }
    }
    LOG.info("Fixed " + numBlocksFixed + " blocks in " + srcPath);
    numFilesFixed++;
  }

  /**
   * checks whether file is xor parity file
   */
  boolean isXorParityFile(Path p) {
    String pathStr = p.toUri().getPath();
    if (pathStr.contains(RaidNode.HAR_SUFFIX)) {
      return false;
    }
    return pathStr.startsWith(xorPrefix);
  }

  /**
   * checks whether file is rs parity file
   */
  boolean isRsParityFile(Path p) {
    String pathStr = p.toUri().getPath();
    if (pathStr.contains(RaidNode.HAR_SUFFIX)) {
      return false;
    }
    return pathStr.startsWith(rsPrefix);
  }

  /**
   * Returns a DistributedFileSystem hosting the path supplied.
   */
  protected DistributedFileSystem getDFS(Path p) throws IOException {
    return (DistributedFileSystem) p.getFileSystem(getConf());
  }

  /**
   * Fixes corrupt blocks in a parity file.
   * This function uses the corresponding source file to regenerate parity
   * file blocks.
   */
  void processCorruptParityFile(Path parityPath, Encoder encoder)
      throws IOException {
    LOG.info("Processing corrupt file " + parityPath);
    Path srcPath = sourcePathFromParityPath(parityPath);
    if (srcPath == null) {
      LOG.warn("Unusable parity file " + parityPath);
      return;
    }

    DistributedFileSystem parityFs = getDFS(parityPath);
    FileStatus parityStat = parityFs.getFileStatus(parityPath);
    long blockSize = parityStat.getBlockSize();
    long parityFileSize = parityStat.getLen();
    FileStatus srcStat = getDFS(srcPath).getFileStatus(srcPath);
    long srcFileSize = srcStat.getLen();

    // Check timestamp.
    if (srcStat.getModificationTime() != parityStat.getModificationTime()) {
      LOG.info("Mismatching timestamp for " + srcPath + " and " + parityPath +
               ", moving on...");
      return;
    }

    String uriPath = parityPath.toUri().getPath();
    int numBlocksFixed = 0;
    List<LocatedBlock> corrupt = RaidDFSUtil.corruptBlocksInFile(
      parityFs, uriPath, 0, parityFileSize);
    for (LocatedBlock lb: corrupt) {
      Block corruptBlock = lb.getBlock();
      long corruptOffset = lb.getStartOffset();

      LOG.info("Found corrupt block " + corruptBlock +
          ", offset " + corruptOffset);

      File localBlockFile =
        File.createTempFile(corruptBlock.getBlockName(), ".tmp");
      localBlockFile.deleteOnExit();

      try {
        encoder.recoverParityBlockToFile(parityFs, srcPath, srcFileSize,
            blockSize, parityPath, corruptOffset, localBlockFile);
        // We have a the contents of the block, send them.
        DatanodeInfo datanode = chooseDatanode(lb.getLocations());
        computeMetdataAndSendFixedBlock(
          datanode, localBlockFile, lb, blockSize);

        numBlocksFixed++;
        LOG.info("Adding " + parityPath + " to history");
        history.put(parityPath.toString(), new java.util.Date());
      } finally {
        localBlockFile.delete();
      }
    }
    LOG.info("Fixed " + numBlocksFixed + " blocks in " + parityPath);
    numFilesFixed++;
  }

  /**
   * Reads through a parity HAR part file, fixing corrupt blocks on the way.
   * A HAR block can contain many file blocks, as long as the HAR part file
   * block size is a multiple of the file block size.
   */
  void processCorruptParityHarPartFile(Path partFile) throws IOException {
    LOG.info("Processing corrupt file " + partFile);
    // Get some basic information.
    DistributedFileSystem dfs = getDFS(partFile);
    FileStatus partFileStat = dfs.getFileStatus(partFile);
    long partFileSize = partFileStat.getLen();
    long partFileBlockSize = partFileStat.getBlockSize();
    LOG.info(partFile + " has block size " + partFileBlockSize);

    // Find the path to the index file.
    // Parity file HARs are only one level deep, so the index files is at the
    // same level as the part file.
    String harDirectory = partFile.toUri().getPath(); // Temporarily.
    harDirectory =
      harDirectory.substring(0, harDirectory.lastIndexOf(Path.SEPARATOR));
    Path indexFile = new Path(harDirectory + "/" + HarIndex.indexFileName);
    FileStatus indexStat = dfs.getFileStatus(indexFile);
    // Parses through the HAR index file.
    HarIndex harIndex = new HarIndex(dfs.open(indexFile), indexStat.getLen());

    String uriPath = partFile.toUri().getPath();
    int numBlocksFixed = 0;
    List<LocatedBlock> corrupt = RaidDFSUtil.corruptBlocksInFile(
      dfs, uriPath, 0, partFileSize);
    for (LocatedBlock lb: corrupt) {
      Block corruptBlock = lb.getBlock();
      long corruptOffset = lb.getStartOffset();

      File localBlockFile =
        File.createTempFile(corruptBlock.getBlockName(), ".tmp");
      localBlockFile.deleteOnExit();
      processCorruptParityHarPartBlock(
        dfs, partFile, corruptBlock, corruptOffset, partFileStat, harIndex,
        localBlockFile);
      // Now we have recovered the part file block locally, send it.
      try {
        DatanodeInfo datanode = chooseDatanode(lb.getLocations());
        computeMetdataAndSendFixedBlock(datanode, localBlockFile,
          lb, localBlockFile.length());
        numBlocksFixed++;

        LOG.info("Adding " + partFile + " to history");
        history.put(partFile.toString(), new java.util.Date());
      } finally {
        localBlockFile.delete();
      }
    }
    LOG.info("Fixed " + numBlocksFixed + " blocks in " + partFile);
    numFilesFixed++;
  }

  /**
   * This fixes a single part file block by recovering in sequence each
   * parity block in the part file block.
   */
  private void processCorruptParityHarPartBlock(
    FileSystem dfs, Path partFile, Block corruptBlock, long corruptOffset,
    FileStatus partFileStat, HarIndex harIndex, File localBlockFile)
    throws IOException {
    String partName = partFile.toUri().getPath(); // Temporarily.
    partName = partName.substring(1 + partName.lastIndexOf(Path.SEPARATOR));

    OutputStream out = new FileOutputStream(localBlockFile);

    try {
      // A HAR part file block could map to several parity files. We need to
      // use all of them to recover this block.
      final long corruptEnd = Math.min(corruptOffset + partFileStat.getBlockSize(),
                                      partFileStat.getLen());
      for (long offset = corruptOffset; offset < corruptEnd; ) {
        HarIndex.IndexEntry entry = harIndex.findEntry(partName, offset);
        if (entry == null) {
          String msg = "Corrupt index file has no matching index entry for " +
            partName + ":" + offset;
          LOG.warn(msg);
          throw new IOException(msg);
        }
        Path parityFile = new Path(entry.fileName);
        Encoder encoder;
        if (isXorParityFile(parityFile)) {
          encoder = xorEncoder;
        } else if (isRsParityFile(parityFile)) {
          encoder = rsEncoder;
        } else {
          String msg = "Could not figure out parity file correctly";
          LOG.warn(msg);
          throw new IOException(msg);
        }
        Path srcFile = sourcePathFromParityPath(parityFile);
        FileStatus srcStat = dfs.getFileStatus(srcFile);
        if (srcStat.getModificationTime() != entry.mtime) {
          String msg = "Modification times of " + parityFile + " and " + srcFile +
            " do not match.";
          LOG.warn(msg);
          throw new IOException(msg);
        }
        long corruptOffsetInParity = offset - entry.startOffset;
        LOG.info(partFile + ":" + offset + " maps to " +
                 parityFile + ":" + corruptOffsetInParity +
                 " and will be recovered from " + srcFile);
        encoder.recoverParityBlockToStream(dfs, srcFile, srcStat.getLen(),
          srcStat.getBlockSize(), parityFile, corruptOffsetInParity, out);
        // Finished recovery of one parity block. Since a parity block has the
        // same size as a source block, we can move offset by source block size.
        offset += srcStat.getBlockSize();
        LOG.info("Recovered " + srcStat.getBlockSize() + " part file bytes ");
        if (offset > corruptEnd) {
          String msg =
            "Recovered block spills across part file blocks. Cannot continue...";
          throw new IOException(msg);
        }
      }
    } finally {
      out.close();
    }
  }

  /**
   * Choose a datanode (hostname:portnumber). The datanode is chosen at
   * random from the live datanodes.
   * @param locationsToAvoid locations to avoid.
   * @return A string in the format name:port.
   * @throws IOException
   */
  private DatanodeInfo chooseDatanode(DatanodeInfo[] locationsToAvoid)
    throws IOException {
    DistributedFileSystem dfs = getDFS(new Path("/"));
    DatanodeInfo[] live = dfs.getClient().datanodeReport(
                                                 DatanodeReportType.LIVE);
    LOG.info("Choosing a datanode from " + live.length +
      " live nodes while avoiding " + locationsToAvoid.length);
    Random rand = new Random();
    DatanodeInfo chosen = null;
    int maxAttempts = 1000;
    for (int i = 0; i < maxAttempts && chosen == null; i++) {
      int idx = rand.nextInt(live.length);
      chosen = live[idx];
      for (DatanodeInfo avoid: locationsToAvoid) {
        if (chosen.name.equals(avoid.name)) {
          LOG.info("Avoiding " + avoid.name);
          chosen = null;
          break;
        }
      }
    }
    if (chosen == null) {
      throw new IOException("Could not choose datanode");
    }
    LOG.info("Choosing datanode " + chosen.name);
    return chosen;
  }

  /**
   * Reads data from the data stream provided and computes metadata.
   */
  static DataInputStream computeMetadata(
    Configuration conf, InputStream dataStream) throws IOException {
    ByteArrayOutputStream mdOutBase = new ByteArrayOutputStream(1024*1024);
    DataOutputStream mdOut = new DataOutputStream(mdOutBase);

    // First, write out the version.
    mdOut.writeShort(FSDataset.METADATA_VERSION);

    // Create a summer and write out its header.
    int bytesPerChecksum = conf.getInt("io.bytes.per.checksum", 512);
    DataChecksum sum = DataChecksum.newDataChecksum(
                        DataChecksum.CHECKSUM_CRC32,
                        bytesPerChecksum);
    sum.writeHeader(mdOut);

    // Buffer to read in a chunk of data.
    byte[] buf = new byte[bytesPerChecksum];
    // Buffer to store the checksum bytes.
    byte[] chk = new byte[sum.getChecksumSize()];

    // Read data till we reach the end of the input stream.
    int bytesSinceFlush = 0;
    while (true) {
      // Read some bytes.
      int bytesRead = dataStream.read(
        buf, bytesSinceFlush, bytesPerChecksum-bytesSinceFlush);
      if (bytesRead == -1) {
        if (bytesSinceFlush > 0) {
          boolean reset = true;
          sum.writeValue(chk, 0, reset); // This also resets the sum.
          // Write the checksum to the stream.
          mdOut.write(chk, 0, chk.length);
          bytesSinceFlush = 0;
        }
        break;
      }
      // Update the checksum.
      sum.update(buf, bytesSinceFlush, bytesRead);
      bytesSinceFlush += bytesRead;

      // Flush the checksum if necessary.
      if (bytesSinceFlush == bytesPerChecksum) {
        boolean reset = true;
        sum.writeValue(chk, 0, reset); // This also resets the sum.
        // Write the checksum to the stream.
        mdOut.write(chk, 0, chk.length);
        bytesSinceFlush = 0;
      }
    }

    byte[] mdBytes = mdOutBase.toByteArray();
    return new DataInputStream(new ByteArrayInputStream(mdBytes));
  }

  private void computeMetdataAndSendFixedBlock(
    DatanodeInfo datanode,
    File localBlockFile, LocatedBlock block, long blockSize
    ) throws IOException {

    LOG.info("Computing metdata");
    InputStream blockContents = null;
    DataInputStream blockMetadata = null;
    try {
      blockContents = new FileInputStream(localBlockFile);
      blockMetadata = computeMetadata(getConf(), blockContents);
      blockContents.close();
      // Reopen
      blockContents = new FileInputStream(localBlockFile);
      sendFixedBlock(datanode, blockContents, blockMetadata, block, blockSize);
    } finally {
      if (blockContents != null) {
        blockContents.close();
        blockContents = null;
      }
      if (blockMetadata != null) {
        blockMetadata.close();
        blockMetadata = null;
      }
    }
  }

  /**
   * Send a generated block to a datanode.
   * @param datanode Chosen datanode name in host:port form.
   * @param blockContents Stream with the block contents.
   * @param corruptBlock Block identifying the block to be sent.
   * @param blockSize size of the block.
   * @throws IOException
   */
  private void sendFixedBlock(
    DatanodeInfo datanode,
    final InputStream blockContents, DataInputStream metadataIn,
    LocatedBlock block, long blockSize
    ) throws IOException {
    InetSocketAddress target = NetUtils.createSocketAddr(datanode.name);
    Socket sock = SocketChannel.open().socket();

    int readTimeout = getConf().getInt(BLOCKFIX_READ_TIMEOUT,
      HdfsConstants.READ_TIMEOUT);
    NetUtils.connect(sock, target, readTimeout);
    sock.setSoTimeout(readTimeout);

    int writeTimeout = getConf().getInt(BLOCKFIX_WRITE_TIMEOUT,
      HdfsConstants.WRITE_TIMEOUT);

    OutputStream baseStream = NetUtils.getOutputStream(sock, writeTimeout);
    DataOutputStream out = new DataOutputStream(
        new BufferedOutputStream(baseStream, FSConstants.SMALL_BUFFER_SIZE));

    boolean corruptChecksumOk = false;
    boolean chunkOffsetOK = false;
    boolean verifyChecksum = true;
    boolean transferToAllowed = false;

    try {
      LOG.info("Sending block " + block.getBlock() +
          " from " + sock.getLocalSocketAddress().toString() +
          " to " + sock.getRemoteSocketAddress().toString() +
          " " + blockSize + " bytes");
      RaidBlockSender blockSender = new RaidBlockSender(
          block.getBlock(), blockSize, 0, blockSize,
          corruptChecksumOk, chunkOffsetOK, verifyChecksum, transferToAllowed,
          metadataIn, new RaidBlockSender.InputStreamFactory() {
          @Override
          public InputStream createStream(long offset) throws IOException {
            // we are passing 0 as the offset above, so we can safely ignore
            // the offset passed
            return blockContents;
          }
        });

      DatanodeInfo[] nodes = new DatanodeInfo[]{datanode};
      DataTransferProtocol.Sender.opWriteBlock(
        out, block.getBlock(), 1,
        DataTransferProtocol.BlockConstructionStage.PIPELINE_SETUP_CREATE,
        0, blockSize, 0, "", null, nodes, block.getBlockToken());
      blockSender.sendBlock(out, baseStream);

      LOG.info("Sent block " + block.getBlock() + " to " + datanode.name);
    } finally {
      out.close();
    }
  }

  /**
   * returns the source file corresponding to a parity file
   */
  Path sourcePathFromParityPath(Path parityPath) {
    String parityPathStr = parityPath.toUri().getPath();
    if (parityPathStr.startsWith(xorPrefix)) {
      // Remove the prefix to get the source file.
      String src = parityPathStr.replaceFirst(xorPrefix, "/");
      return new Path(src);
    } else if (parityPathStr.startsWith(rsPrefix)) {
      // Remove the prefix to get the source file.
      String src = parityPathStr.replaceFirst(rsPrefix, "/");
      return new Path(src);
    }
    return null;
  }
}
TOP

Related Classes of org.apache.hadoop.raid.BlockFixer

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.