org.apache.hadoop.hdfs.server.namenode.InitialReportWorker.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.hadoop.hdfs.server.namenode.InitialReportWorker.java

Source

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

import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.hadoop.hdfs.protocol.Block;
import org.apache.hadoop.hdfs.protocol.BlockListAsLongs;
import org.apache.hadoop.hdfs.server.namenode.BlocksMap.BlockInfo;
import org.apache.hadoop.hdfs.util.InjectionEvent;
import org.apache.hadoop.util.InjectionHandler;

/**
 * Worker class for initial in-safemode block reports. Each worker traverses a
 * part of input block report. For each block it calls addStoredBlock, which
 * only changes stored blocks in the blocks map, the datanode descriptor itself
 * is not changed!! The worker collects all its blocks into a linked list, which
 * is later inserted into the datanode descriptor.
 * 
 * The class has a static method for processing a block report, so it can be
 * directly called from FSNamesystem.
 */
class InitialReportWorker implements Callable<List<Block>> {

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

    // parent namesystem
    private final FSNamesystem namesystem;

    private final BlockListAsLongs report;
    private final int startIndex;
    private final int jump;
    private final DatanodeDescriptor node;

    // safe blocks for this worker
    private int localBlocksSafe = 0;
    private int addedBlocks = 0;

    // we keep local linked list of blocks inserted
    // to DN's descriptor by this thread
    // we keep tail for fast final insertion
    private BlockInfo head = null;
    private int headIndex = -1;
    private BlockInfo tail = null;
    private int tailIndex = -1;

    // avatar dependent local retry list
    private final boolean shouldRetryAbsentBlocks;
    private final List<Block> toRetry;

    // currently processed block
    BlockInfo currentStoredBlock = null;
    int currentStoredBlockIndex = -1;

    private InitialReportWorker(BlockListAsLongs report, int startIndex, int jump, DatanodeDescriptor node,
            boolean shouldRetryAbsentBlocks, FSNamesystem namesystem) {
        this.report = report;
        this.startIndex = startIndex;
        this.jump = jump;
        this.node = node;
        this.shouldRetryAbsentBlocks = shouldRetryAbsentBlocks;
        this.toRetry = shouldRetryAbsentBlocks ? new LinkedList<Block>() : null;
        this.namesystem = namesystem;
    }

    @Override
    public List<Block> call() throws Exception {
        InjectionHandler.processEvent(InjectionEvent.FSNAMESYSTEM_INITIALBR_WORKER);
        Block iblk = new Block();
        final int numberOfBlocks = report.getNumberOfBlocks();
        for (int i = startIndex; i < numberOfBlocks; i += jump) {
            report.getBlock(iblk, i);
            boolean added = false;

            // clear information about the current stored block
            resetCurrentStoredBlock();

            // add each block to blocks map, and store it in the local list for
            // datanode sescriptor
            try {
                // add location to the blocks map
                added = namesystem.addStoredBlockInternal(iblk, node, null, true, this);

                // store the block locally
                insertCurrentStoredBlockIntoList();

                // check if it needs to be retried
                if (shouldRetryAbsentBlocks && !added
                        && namesystem.getNameNode().shouldRetryAbsentBlock(iblk, null)) {
                    toRetry.add(new Block(iblk));
                }
            } catch (Exception e) {
                LOG.info("Initial lock report worker got exception:", e);
            }
        }

        // insert the block into the local list for datanode descriptor.
        namesystem.lockParallelBRLock(true);
        try {
            // now insert local list into datanode descriptor
            node.insertIntoList(head, headIndex, tail, tailIndex, addedBlocks);
            // we know we are in safemode
            namesystem.incrementSafeBlockCount(localBlocksSafe);
        } finally {
            namesystem.unlockParallelBRLock(true);
        }

        // blocks to be re-tried
        return toRetry;
    }

    /**
     * Clear information about the local stored block.
     */
    private void resetCurrentStoredBlock() {
        this.currentStoredBlock = null;
        this.currentStoredBlockIndex = -1;
    }

    /**
     * FSNmaesystem.addStoredBlock will inform this worker about the stored block,
     * so it can be later inserted into the local list of blocks.
     */
    void setCurrentStoredBlock(BlockInfo storedBlock, int index) {
        this.currentStoredBlock = storedBlock;
        this.currentStoredBlockIndex = index;
    }

    /**
     * Insert the current stored block into the local list of blocks belonging to
     * the datanode descriptor.
     */
    private void insertCurrentStoredBlockIntoList() {
        // index < 0 - block is already in the DN's list
        if (currentStoredBlock == null || currentStoredBlockIndex < 0)
            return;

        if (head == null) {
            // local list is empty,
            // make head and tail point to the input block
            head = currentStoredBlock;
            headIndex = currentStoredBlockIndex;

            tail = currentStoredBlock;
            tailIndex = currentStoredBlockIndex;

            // for sanity, make sure the block is not pointing to anything
            head.setNext(currentStoredBlockIndex, null);
            head.setPrevious(currentStoredBlockIndex, null);
        } else {
            // connect input block with current head
            head.setPrevious(headIndex, currentStoredBlock);
            currentStoredBlock.setNext(currentStoredBlockIndex, head);

            // stored block is the new head
            head = currentStoredBlock;
            headIndex = currentStoredBlockIndex;
        }
        // increment number of blocks in the local list
        addedBlocks++;

        // clear the current stored block information
        resetCurrentStoredBlock();
    }

    /**
     * Increment local count of safe blocks for this worker
     */
    public void incrementSafeBlockCount(int replication) {
        if (replication == namesystem.getMinReplication()) {
            localBlocksSafe++;
        }
    }

    /**
     * Processes a single initial block reports, by spawning multiple threads to
     * handle insertion to the blocks map. Each thread stores the inserted blocks
     * in a local list, and at the end, the list are concatenated for a single
     * datanode descriptor.
     */
    static void processReport(FSNamesystem namesystem, Collection<Block> toRetry, BlockListAsLongs newReport,
            DatanodeDescriptor node, ExecutorService initialBlockReportExecutor) throws IOException {
        // spawn one thread for blocksPerShardBR blocks
        int numShards = Math.min(namesystem.parallelProcessingThreads,
                ((newReport.getNumberOfBlocks() + namesystem.parallelBRblocksPerShard - 1)
                        / namesystem.parallelBRblocksPerShard));
        List<Future<List<Block>>> workers = new ArrayList<Future<List<Block>>>(numShards);

        // submit tasks for execution
        for (int i = 0; i < numShards; i++) {
            workers.add(initialBlockReportExecutor.submit(new InitialReportWorker(newReport, i, numShards, node,
                    namesystem.getNameNode().shouldRetryAbsentBlocks(), namesystem)));
        }

        // get results and add to retry list if need
        try {
            for (Future<List<Block>> worker : workers) {
                if (namesystem.getNameNode().shouldRetryAbsentBlocks()) {
                    toRetry.addAll(worker.get());
                } else {
                    worker.get();
                }
            }
        } catch (ExecutionException e) {
            LOG.warn("Parallel report failed", e);
            throw new IOException(e);
        } catch (InterruptedException e) {
            throw new IOException("Interruption", e);
        }
    }
}