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