/**
* 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.datanode;
import java.io.File;
import java.io.IOException;
import java.util.Iterator;
import java.util.NoSuchElementException;
import java.util.TreeMap;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.hdfs.protocol.Block;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* DataBlockScannerSet manages block scanning for all the namespaces For each
* namespace a {@link DataBlockScannerScanner} is created to scan the blocks for
* that block namespace. When a {@link NSOfferService} becomes alive or dies,
* namespaceScannerMap in this class is updated.
*/
public class DataBlockScannerSet implements Runnable {
public static final Log LOG = LogFactory.getLog(DataBlockScannerSet.class);
private final DataNode datanode;
private final FSDataset dataset;
private final Configuration conf;
/**
* Map to find the BlockDataScanner for a given namespaceId. This is updated
* when a NSOfferService becomes alive or dies.
*/
private final TreeMap<Integer, DataBlockScanner> namespaceScannerMap = new TreeMap<Integer, DataBlockScanner>();
Thread blockScannerThread = null;
private boolean initialized = false;
DataBlockScannerSet(DataNode datanode, FSDataset dataset, Configuration conf) {
this.datanode = datanode;
this.dataset = dataset;
this.conf = conf;
}
public void run() {
int currentNamespaceId = -1;
boolean firstRun = true;
while (datanode.shouldRun && !Thread.interrupted()) {
// Sleep everytime except in the first interation.
if (!firstRun) {
try {
Thread.sleep(5000);
} catch (InterruptedException ex) {
// Interrupt itself again to set the interrupt status
blockScannerThread.interrupt();
continue;
}
} else {
firstRun = false;
}
DataBlockScanner nsScanner = getNextNamespaceSliceScanner(currentNamespaceId);
if (nsScanner == null) {
// Possible if thread is interrupted
continue;
}
currentNamespaceId = nsScanner.getNamespaceId();
waitForUpgradeDone(currentNamespaceId);
if (!datanode.isNamespaceAlive(currentNamespaceId)) {
LOG.warn("Namespace: " + currentNamespaceId + " is not alive");
// Remove in case NS service died abruptly without proper shutdown
removeNamespace(currentNamespaceId);
continue;
}
nsScanner.scanNamespace();
}
}
private void waitForOneNameSpaceUp() {
while (getNamespaceSetSize() < 1 && datanode.shouldRun) {
try {
Thread.sleep(5000);
LOG.info("sleeping ............");
} catch (InterruptedException e) {
blockScannerThread.interrupt();
return;
}
}
}
// Wait for upgrading done for the given namespace
private void waitForUpgradeDone(int namespaceId) {
UpgradeManagerDatanode um = datanode.getUpgradeManager(namespaceId);
while (!um.isUpgradeCompleted()) {
try {
Thread.sleep(5000);
LOG.info("sleeping ............");
} catch (InterruptedException e) {
blockScannerThread.interrupt();
return;
}
}
}
/**
* Find next namespaceId to scan. There should be only one current
* verification log file. Find which namespace contains the current
* verification log file and that is used as the starting namespaceId. If no
* current files are found start with first namespace. However, if more than
* one current files are found, the one with latest modification time is used
* to find the next namespaceId.
*/
private DataBlockScanner getNextNamespaceSliceScanner(int currentNamespaceId) {
Integer nextNsId = null;
while ((nextNsId == null) && datanode.shouldRun
&& !blockScannerThread.isInterrupted()) {
waitForOneNameSpaceUp();
synchronized (this) {
if (getNamespaceSetSize() > 0) {
// Find nextNsId by finding the last modified current log file, if any
long lastScanTime = -1;
Iterator<Integer> nsidIterator = namespaceScannerMap.keySet()
.iterator();
while (nsidIterator.hasNext()) {
int nsid = nsidIterator.next();
for (FSDataset.FSVolume vol : dataset.volumes.getVolumes()) {
try {
File currFile = DataBlockScanner.getCurrentFile(vol, nsid);
if (currFile.exists()) {
long lastModified = currFile.lastModified();
if (lastScanTime < lastModified) {
lastScanTime = lastModified;
nextNsId = nsid;
}
}
} catch (IOException e) {
LOG.warn("Received exception: ", e);
}
}
}
// nextNsId can still be -1 if no current log is found,
// find nextNsId sequentially.
if (nextNsId == null) {
try {
if (currentNamespaceId == -1) {
nextNsId = namespaceScannerMap.firstKey();
} else {
nextNsId = namespaceScannerMap.higherKey(currentNamespaceId);
if (nextNsId == null) {
nextNsId = namespaceScannerMap.firstKey();
}
}
} catch (NoSuchElementException e) {
// if firstKey throws an exception
continue;
}
}
if (nextNsId != null) {
return getNSScanner(nextNsId);
}
}
}
LOG.warn("No namespace is up, going to wait");
try {
Thread.sleep(5000);
} catch (InterruptedException ex) {
LOG.warn("Received exception: " + ex);
blockScannerThread.interrupt();
return null;
}
}
return null;
}
private synchronized int getNamespaceSetSize() {
return namespaceScannerMap.size();
}
private synchronized DataBlockScanner getNSScanner(int namespaceId) {
return namespaceScannerMap.get(namespaceId);
}
private synchronized Integer[] getNsIdList() {
return namespaceScannerMap.keySet().toArray(
new Integer[namespaceScannerMap.keySet().size()]);
}
public void addBlock(int namespaceId, Block block) {
DataBlockScanner nsScanner = getNSScanner(namespaceId);
if (nsScanner != null) {
nsScanner.addBlock(block);
} else {
LOG.warn("No namespace scanner found for namespace id: " + namespaceId);
}
}
public synchronized boolean isInitialized(int namespaceId) {
DataBlockScanner nsScanner = getNSScanner(namespaceId);
if (nsScanner != null) {
return nsScanner.isInitialized();
}
return false;
}
public synchronized void printBlockReport(StringBuilder buffer,
boolean summary) {
Integer[] nsIdList = getNsIdList();
if (nsIdList == null || nsIdList.length == 0) {
buffer.append("Periodic block scanner is not yet initialized. "
+ "Please check back again after some time.");
return;
}
for (Integer nsId : nsIdList) {
DataBlockScanner nsScanner = getNSScanner(nsId);
buffer.append("\n\nBlock report for namespace: " + nsId + "\n");
nsScanner.printBlockReport(buffer, summary);
buffer.append("\n");
}
}
public void deleteBlock(int namespaceId, Block toDelete) {
DataBlockScanner nsScanner = getNSScanner(namespaceId);
if (nsScanner != null) {
nsScanner.deleteBlock(toDelete);
} else {
LOG.warn("No namespace scanner found for namespaceId: " + nsScanner);
}
}
public void deleteBlocks(int namespaceId, Block[] toDelete) {
DataBlockScanner nsScanner = getNSScanner(namespaceId);
if (nsScanner != null) {
nsScanner.deleteBlocks(toDelete);
} else {
LOG.warn("No namespace scanner found for namespaceId: " + nsScanner);
}
}
public synchronized void shutdown() {
if (blockScannerThread != null) {
blockScannerThread.interrupt();
}
}
public synchronized void addNamespace(int namespaceId) {
if (namespaceScannerMap.get(namespaceId) != null) {
return;
}
DataBlockScanner nsScanner = new DataBlockScanner(datanode, dataset, conf,
namespaceId);
try {
nsScanner.init();
} catch (IOException ex) {
LOG.warn("Failed to initialized block scanner for namespace id="
+ namespaceId);
return;
}
namespaceScannerMap.put(namespaceId, nsScanner);
LOG.info("Added namespaceId=" + namespaceId
+ " to namespaceScannerMap, new size=" + namespaceScannerMap.size());
}
public synchronized void removeNamespace(int namespaceId) {
namespaceScannerMap.remove(namespaceId);
LOG.info("Removed namespaceId=" + namespaceId + " from namespaceScannerMap");
}
public synchronized void start() {
if(initialized ){
return;
}
initialized = true;
blockScannerThread = new Thread(this);
blockScannerThread.setDaemon(true);
blockScannerThread.start();
}
public static class Servlet extends HttpServlet {
private static final long serialVersionUID = 1L;
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws IOException {
response.setContentType("text/plain");
DataNode datanode = (DataNode) getServletContext().getAttribute(
"datanode");
DataBlockScannerSet blockScanner = datanode.blockScanner;
boolean summary = (request.getParameter("listblocks") == null);
StringBuilder buffer = new StringBuilder(8 * 1024);
if (blockScanner == null) {
LOG.warn("Periodic block scanner is not running");
buffer.append("Periodic block scanner is not running. "
+ "Please check the datanode log if this is unexpected.");
} else {
blockScanner.printBlockReport(buffer, summary);
}
response.getWriter().write(buffer.toString()); // extra copy!
}
}
public long getLastScanTime(int namespaceId, Block block) {
DataBlockScanner nsScanner = getNSScanner(namespaceId);
if (nsScanner != null) {
return nsScanner.getLastScanTime(block);
} else {
LOG.warn("No namespace scanner found for namespaceId: " + nsScanner);
return -1;
}
}
public int getBlockMapSize(int namespaceId) {
DataBlockScanner nsScanner = getNSScanner(namespaceId);
if (nsScanner != null) {
return nsScanner.getBlockCount();
} else {
LOG.warn("No namespace scanner found for namespaceId: " + nsScanner);
return -1;
}
}
/*
* A reader will try to indicate a block is verified and will add blocks to
* the DataBlockScannerSet before they are finished (due to concurrent
* readers).
*
* fixed so a read verification can't add the block
*/
synchronized void verifiedByClient(int namespaceId, Block block) {
DataBlockScanner nsScanner = getNSScanner(namespaceId);
if (nsScanner != null) {
nsScanner.updateScanStatusUpdateOnly(block,
DataBlockScanner.ScanType.REMOTE_READ, true);
} else {
LOG.warn("No namespace scanner found for namespaceId: " + nsScanner);
}
}
}