Package org.apache.hadoop.hdfs.server.namenode.bookkeeper

Source Code of org.apache.hadoop.hdfs.server.namenode.bookkeeper.BookKeeperEditLogInputStream$LedgerHeaderCorruptException

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

import com.google.common.annotations.VisibleForTesting;
import org.apache.bookkeeper.client.LedgerHandle;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.hadoop.hdfs.protocol.FSConstants;
import org.apache.hadoop.hdfs.server.common.HdfsConstants;
import org.apache.hadoop.hdfs.server.namenode.EditLogFileInputStream;
import org.apache.hadoop.hdfs.server.namenode.EditLogInputStream;
import org.apache.hadoop.hdfs.server.namenode.FSEditLogLoader;
import org.apache.hadoop.hdfs.server.namenode.FSEditLogLoader.PositionTrackingInputStream;
import org.apache.hadoop.hdfs.server.namenode.FSEditLogOp;
import org.apache.hadoop.hdfs.server.namenode.FSEditLogOp.Reader;
import org.apache.hadoop.hdfs.server.namenode.JournalStream.JournalType;
import org.apache.hadoop.hdfs.server.namenode.bookkeeper.metadata.EditLogLedgerMetadata;
import org.apache.hadoop.io.IOUtils;

import java.io.BufferedInputStream;
import java.io.DataInputStream;
import java.io.EOFException;
import java.io.IOException;

/**
* An implement of the abstract class {@link EditLogInputStream}, that reads
* edits from a BookKeeper ledger, which stores edits for a single log
* segment.
*/
public class BookKeeperEditLogInputStream extends EditLogInputStream {

  private static final Log LOG =
      LogFactory.getLog(BookKeeperEditLogInputStream.class);

  private final LedgerHandleProvider ledgerProvider;

  private final long ledgerId;
  private final long firstTxId;
  // Last transaction id in the segment, or -1 if the segment is in-progress
  private final long lastTxId;

  // The underlying input stream implementation that abstracts away reading
  // from a BookKeeper ledger into a streaming interface as well as "rewinding"
  // back to either the beginning or the last known position
  private BookKeeperJournalInputStream journalInputStream;
  private PositionTrackingInputStream tracker; // Tracks reader position
  @VisibleForTesting
  BufferedInputStream bin; // Buffers reading from journalInputStream
  private Reader reader; // Reads transactions from the stream

  @VisibleForTesting
  int logVersion;
  private final long firstBookKeeperEntry;
  private final boolean inProgress;

  /**
   * Exception indicating that the header of a ledger containing an edit
   * log segment is corrupted. This could be because the header is not
   * present or because the version in the header claims to be a version
   * newer than the version in the running NameNode. Mimics an exception
   * that thrown by {@link EditLogFileInputStream} in similar conditions
   * @see EditLogFileInputStream.LogHeaderCorruptException
   */
  private static class LedgerHeaderCorruptException extends IOException {
    private static final long serialVersionUID = 1L;

    private LedgerHeaderCorruptException(String msg) {
      super(msg);
    }
  }

  /**
   * Opens an EditLogInputStream for a BookKeeper ledger with a specified id,
   * starting from a BookKeeper entry with a specific entry id. Note that the
   * BookKeeper entry id is different from an HDFS transaction id: the entry id
   * is specific to BookKeeper itself and not related to the HDFS transaction
   * id.
   * @param ledgerProvider {@link BookKeeperJournalManager} instance used for
   *                       opening and re-opening ledgers based on the id
   * @param ledgerId BookKeeper specific id of the ledger. This is read from
   *                 ZooKeeper
   * @param firstBookKeeperEntry Entry id of the first BookKeeper entry
   *                             containing edit log segment data
   * @param firstTxId Id of the first HDFS transaction id expected to be
   *                  in the segment stored in the specific ledger
   * @param lastTxId Id of the last transaction id expected to be in the segment
   *                 stored in the specified ledger or -1 if the segment is in
   *                 progress
   * @throws IOException
   */
  public BookKeeperEditLogInputStream(LedgerHandleProvider ledgerProvider,
      long ledgerId,
      long firstBookKeeperEntry,
      long firstTxId,
      long lastTxId,
      boolean inProgress) throws IOException {
    if (firstBookKeeperEntry < 0) {
      throw new IllegalArgumentException("Invalid first BookKeeper entry id to read: " +
          firstBookKeeperEntry + ", cannot be < 0!");
    }
    this.firstBookKeeperEntry = firstBookKeeperEntry;
    this.ledgerProvider = ledgerProvider;
    this.ledgerId = ledgerId;
    this.firstTxId = firstTxId;
    this.lastTxId = lastTxId;
    this.inProgress = inProgress;
  }

  public void init() throws IOException {
    LedgerHandle ledger = ledgerProvider.openForReading(ledgerId);
    if (!isInProgress() && firstBookKeeperEntry > ledger.getLastAddConfirmed()) {
      // ledger.getLastAddConfirmed() returns the last quorum-acknowledged entry
      // id in the ledger. Unless the segment is in-progress, we should throw
      // an exception if this last entry is lower than the first expected entry id.
      throw new IllegalArgumentException(
          "Invalid first BookKeeper entry to read: " + firstBookKeeperEntry +
              " > last confirmed entry id(" + ledger.getLastAddConfirmed() +
              ")");
    }

    journalInputStream = new BookKeeperJournalInputStream(ledger,
        firstBookKeeperEntry);
    bin = new BufferedInputStream(journalInputStream);
    tracker = new PositionTrackingInputStream(bin, 0);
    DataInputStream in = new DataInputStream(tracker);
    try {
      logVersion = readLogVersion(in);
    } catch (EOFException e) {
      throw new LedgerHeaderCorruptException("No header file in the ledger");
    }
    reader = new Reader(in, logVersion);
    LOG.info("Reading from ledger id " + ledgerId +
        ", starting with book keeper entry id " + firstBookKeeperEntry +
        ", log version " + logVersion +
        ", first txn id " +
        firstTxId +  (isInProgress() ?
                          ", in-progress log segment"
                          : (" last txn id " + lastTxId))
        + ".");
  }

  /**
   * Safely reads the log version from the stream. Logic is exactly the same
   * as in the equivalent {@link EditLogFileInputStream} method.
   * @see EditLogFileInputStream#readLogVersion(DataInputStream)
   * @return The log version or 0 if stream is empty
   */
  private static int readLogVersion(DataInputStream in) throws IOException {
    int logVersion = 0;
    in.mark(4);
    // See comments in EditLogFileInputStream as to why readLogVersion is
    // implemented in this way
    boolean available = true;
    try {
      logVersion = in.readByte();
    } catch (EOFException e) {
      available = false;
    }

    if (available) {
      in.reset();
      logVersion = in.readInt();
      if (logVersion < FSConstants.LAYOUT_VERSION) {
        throw new LedgerHeaderCorruptException(
            "Unexpected version of the log segment in the ledger: " + logVersion +
                ". Current version is " + FSConstants.LAYOUT_VERSION + ".");
      }
    }
    return logVersion;
  }

  public static FSEditLogLoader.EditLogValidation validateEditLog(
      LedgerHandleProvider ledgerProvider,
      EditLogLedgerMetadata ledgerMetadata) throws IOException {
    BookKeeperEditLogInputStream in;
    try {
      in = new BookKeeperEditLogInputStream(ledgerProvider,
          ledgerMetadata.getLedgerId(), 0, ledgerMetadata.getFirstTxId(),
          ledgerMetadata.getLastTxId(), ledgerMetadata.getLastTxId() == -1);
    } catch (LedgerHeaderCorruptException e) {
      LOG.warn("Log at ledger id" + ledgerMetadata.getLedgerId() +
          " has no valid header", e);
      return new FSEditLogLoader.EditLogValidation(0,
          HdfsConstants.INVALID_TXID, HdfsConstants.INVALID_TXID, true);
    }

    try {
      return FSEditLogLoader.validateEditLog(in);
    } finally {
      IOUtils.closeStream(in);
    }
  }

  /**
   * Returns true if the segment is in progress from the reader point of view
   * at the time of instantiation.
   */
  @Override
  public boolean isInProgress() {
    return inProgress;
  }

  @Override
  public long getFirstTxId() {
    return firstTxId;
  }

  @Override
  public long getLastTxId() {
    return lastTxId;
  }

  @Override
  public void close() throws IOException {
    checkInitialized();

    // Let JournalInputStream handle cleanup
    journalInputStream.close();
  }

  @Override
  public FSEditLogOp nextOp() throws IOException {
    checkInitialized();

    return reader.readOp(false);
  }

  @Override
  public int getVersion() throws IOException {
    checkInitialized();

    return logVersion;
  }

  @Override
  public long getPosition() throws IOException {
    checkInitialized();

    long pos = tracker.getPos();
    journalInputStream.savePosition(pos);
    return pos;
  }

  @Override
  public void position(long position) throws IOException {
    throw new IOException("position() not supported by " +
        getClass());
  }

  /**
   * Gets the size of the ledger in bytes
   * @return The size of the ledger in bytes
   */
  @Override
  public long length() throws IOException {
    checkInitialized();

    return journalInputStream.getLedgerLength();
  }

  public synchronized void checkInitialized() throws IOException {
    if (journalInputStream == null) {
      init();
    }
  }

  /**
   * Refresh, preferably to a known position
   * @see EditLogInputStream#refresh(long)
   * @see BookKeeperJournalInputStream#position(long)
   */
  @Override
  public void refresh(long position, long skippedUntilTxid) throws IOException {
    checkInitialized();

    if (isInProgress()) {
      // If a ledger is in progress, re-open it for reading in order
      // to determine the correct bounds of the ledger.
      LedgerHandle ledger = ledgerProvider.openForReading(ledgerId);
      journalInputStream.resetLedger(ledger);
    }
    // Try to set the underlying stream to the specified position
    journalInputStream.position(position);
    // Reload the position tracker and log reader to adjust to the newly
    // refreshed position
    bin = new BufferedInputStream(journalInputStream);
    tracker = new PositionTrackingInputStream(bin, position);
    DataInputStream in = new DataInputStream(tracker);
    if (position == 0) { // If we are at the beginning, re-read the version
      logVersion = readLogVersion(in);
    }
    reader = new Reader(in, logVersion);
  }

  @Override
  public String getName() {
    return journalInputStream.getLedgerName();
  }

  @Override
  public long getReadChecksum() {
    return reader.getChecksum();
  }
 
  @Override
  public JournalType getType() {
    return JournalType.EXTERNAL;
  }

  @Override
  public String toString() {
    return "BookKeeperEditLogInputStream{" +
        "ledgerId=" + ledgerId +
        ", firstTxId=" + firstTxId +
        ", lastTxId=" + lastTxId +
        ", journalInputStream=" + journalInputStream +
        ", tracker=" + tracker +
        ", bin=" + bin +
        ", reader=" + reader +
        ", logVersion=" + logVersion +
        '}';
  }
}
TOP

Related Classes of org.apache.hadoop.hdfs.server.namenode.bookkeeper.BookKeeperEditLogInputStream$LedgerHeaderCorruptException

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.