package org.apache.hadoop.hdfs.server.namenode;
import java.io.IOException;
import java.util.Date;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.util.Daemon;
import org.apache.hadoop.util.FlushableLogger;
import org.apache.hadoop.util.StringUtils;
/**
* NameNodeSafeModeInfo contains information related to the safe mode.
* <p/>
* An instance of {@link NameNodeSafeModeInfo} is created when the name node
* enters safe mode.
* <p/>
* During name node startup {@link NameNodeSafeModeInfo} counts the number of
* <em>safe blocks</em>, those that have at least the minimal number of
* replicas, and calculates the ratio of safe blocks to the total number of
* blocks in the system, which is the size of {@link FSNamesystem#blocksMap}.
* When the ratio reaches the {@link #threshold} it starts the
* {@link SafeModeMonitor} daemon in order to monitor whether the safe mode
* {@link #extension} is passed. Then it leaves safe mode and destroys itself.
* <p/>
* If safe mode is turned on manually then the number of safe blocks is not
* tracked because the name node is not intended to leave safe mode
* automatically in the case.
*
* @see ClientProtocol#setSafeMode(FSConstants.SafeModeAction)
* @see SafeModeMonitor
*/
public class NameNodeSafeModeInfo implements SafeModeInfo {
// configuration fields
/**
* Safe mode threshold condition %.
*/
protected double threshold;
/**
* Safe mode extension after the threshold.
*/
private long extension;
/** threshold for populating needed replication queues */
private double replQueueThreshold;
// internal fields
/**
* Time when threshold was reached.
* <p/>
* <br>
* -1 safe mode is off <br>
* 0 safe mode is on, but threshold is not reached yet
*/
volatile private long reached = -1;
/**
* time of the last status printout
*/
private long lastStatusReport = 0;
private final FSNamesystem namesystem;
private final NameNode nameNode;
private Daemon smmthread = null; // SafeModeMonitor thread
private final static Log LOG = LogFactory.getLog(NameNodeSafeModeInfo.class);
private final static Log FLOG = FlushableLogger.getLogger(LOG);
NameNodeSafeModeInfo(FSNamesystem namesystem) {
this(new Configuration(), namesystem);
}
/**
* Creates SafeModeInfo when the name node enters automatic safe mode at
* startup.
*
* @param conf
* configuration
*/
NameNodeSafeModeInfo(Configuration conf, FSNamesystem namesystem) {
this.threshold = conf.getFloat("dfs.safemode.threshold.pct", 0.95f);
this.extension = conf.getLong("dfs.safemode.extension", 0);
// default to safe mode threshold
// (i.e., don't populate queues before leaving safe mode)
this.replQueueThreshold = conf.getFloat(
"dfs.namenode.replqueue.threshold-pct", (float) threshold);
this.namesystem = namesystem;
this.nameNode = namesystem.getNameNode();
// start initial block report executor
this.namesystem.setupInitialBlockReportExecutor(false);
// set fields of fsnamesystem to trigger replication queues initialization
this.namesystem.setInitializedReplicationQueues(false);
this.namesystem.blocksSafe = 0;
}
@Override
public boolean isOn() {
assert isConsistent() : " SafeMode: Inconsistent filesystem state: "
+ "Total num of blocks, active blocks, or "
+ "total safe blocks don't match.";
return this.reached >= 0;
}
/**
* Enter safe mode.
*/
private void enter() {
this.reached = 0;
}
protected boolean needUpgrade(boolean checkForUpgrades) {
if (checkForUpgrades) {
// verify whether a distributed upgrade needs to be started
boolean needUpgrade = false;
try {
needUpgrade = namesystem.startDistributedUpgradeIfNeeded();
} catch (IOException e) {
FSNamesystem.LOG.error(StringUtils.stringifyException(e));
}
if (needUpgrade) {
setManual();
return true;
}
}
return false;
}
protected void startPostSafeModeProcessing() {
// initialize replication queues
initializeReplQueues();
long timeInSafemode = FSNamesystem.now() - namesystem.systemStart;
NameNode.stateChangeLog.info("STATE* Leaving safe mode after "
+ timeInSafemode / 1000 + " secs.");
NameNode.getNameNodeMetrics().safeModeTime.set((int) timeInSafemode);
if (reached >= 0) {
NameNode.stateChangeLog.info("STATE* Safe mode is OFF.");
}
reached = -1;
try {
nameNode.startServerForClientRequests();
} catch (IOException ex) {
LOG.fatal("Got exception when starting server for client requests: ", ex);
nameNode.stop();
}
NameNode.stateChangeLog.info("STATE* Network topology has "
+ namesystem.clusterMap.getNumOfRacks() + " racks and "
+ namesystem.clusterMap.getNumOfLeaves() + " datanodes");
NameNode.stateChangeLog.info("STATE* UnderReplicatedBlocks has "
+ namesystem.getUnderReplicatedBlocks() + " blocks");
}
@Override
public void leave(boolean checkForUpgrades) {
namesystem.writeLock();
try {
if (needUpgrade(checkForUpgrades)) {
return;
}
startPostSafeModeProcessing();
} finally {
namesystem.writeUnlock();
}
}
/**
* Initializes replication queues *without* leaving safemode.
* This should only be used ONLY through dfsadmin command.
*/
@Override
public void initializeReplicationQueues() {
// this function internally holds FSNamesystem.writeLock
initializeReplQueues();
}
protected void initializeReplQueues() {
LOG.info("initializing replication queues");
namesystem.processMisReplicatedBlocks();
}
/**
* Check whether we have reached the threshold for initializing replication
* queues.
*/
private boolean canInitializeReplQueues() {
return namesystem.getSafeBlockRatio() >= this.replQueueThreshold;
}
/**
* Safe mode can be turned off iff the threshold is reached and the extension
* time have passed.
*
* @return true if can leave or false otherwise.
*/
@Override
public boolean canLeave() {
if (reached == 0) {
return false;
}
if (namesystem.now() - reached < extension) {
reportStatus("STATE* Safe mode ON.", false);
return false;
}
return !needEnter();
}
/**
* There is no need to enter safe mode if DFS is empty or {@link #threshold}
* == 0
*/
private boolean needEnter() {
return isManual() || namesystem.getSafeBlockRatio() < threshold;
}
@Override
public void checkMode() {
if (needEnter()) {
enter();
// check if we are ready to initialize replication queues
if (!isManual() && canInitializeReplQueues()
&& !namesystem.isPopulatingReplQueues()) {
initializeReplQueues();
}
reportStatus("STATE* Safe mode ON.", false);
return;
}
// the threshold is reached
if (!isOn() || // safe mode is off
extension <= 0 || threshold <= 0) { // don't need to wait
this.leave(true); // leave safe mode
return;
}
if (reached > 0) { // threshold has already been reached before
reportStatus("STATE* Safe mode ON.", false);
return;
}
// start monitor
reached = namesystem.now();
smmthread = new Daemon(new SafeModeMonitor(namesystem, this));
smmthread.start();
reportStatus("STATE* Safe mode extension entered.", true);
// check if we are ready to initialize replication queues
if (canInitializeReplQueues() && !namesystem.isPopulatingReplQueues()) {
initializeReplQueues();
}
}
@Override
public boolean isManual() {
return extension == Long.MAX_VALUE;
}
/**
* Enter safemode manually.
* <p/>
* The {@link #threshold} is set to 1.5 so that it could never be reached.
* {@link #namesystem.getTotalBlocks()} is set to -1 to indicate that safe
* mode is manual.
*/
@Override
public void setManual() {
this.threshold = 1.5f; // this threshold can never be reached
this.extension = Long.MAX_VALUE;
this.replQueueThreshold = 1.5f; // can never be reached
this.reached = -1;
enter();
reportStatus("STATE* Safe mode is ON.", true);
}
@Override
public String getTurnOffTip() {
String leaveMsg = "Safe mode will be turned off automatically";
if (reached < 0) {
return "Safe mode is OFF.";
}
if (isManual()) {
if (namesystem.getDistributedUpgradeState()) {
return leaveMsg + " upon completion of "
+ "the distributed upgrade: upgrade progress = "
+ namesystem.getDistributedUpgradeStatus() + "%";
}
leaveMsg = "Use \"hadoop dfsadmin -safemode leave\" to turn safe mode off";
}
if (namesystem.getTotalBlocks() < 0) {
return leaveMsg + ".";
}
String initReplicationQueues = namesystem.isPopulatingReplQueues()
? " Replication queues have been initialized. "
: "";
String safeBlockRatioMsg = String.format(
initReplicationQueues
+"The ratio of reported blocks %.8f has " + (reached == 0 ? "not " : "")
+ "reached the threshold %.8f. ", namesystem.getSafeBlockRatio(),
threshold)
+ "Safe blocks = "
+ namesystem.getSafeBlocks()
+ ", Total blocks = "
+ namesystem.getTotalBlocks()
+ ", Remaining blocks = "
+ (namesystem.getTotalBlocks() - namesystem.getSafeBlocks())
+ ". "
+ "Reporting nodes = " + namesystem.getReportingNodes() + ". " + leaveMsg;
if (reached == 0 || isManual()) // threshold is not reached or manual
{
return safeBlockRatioMsg + ".";
}
// extension period is in progress
return safeBlockRatioMsg + " in " + Math.abs(reached + extension - namesystem.now())
/ 1000 + " seconds.";
}
/**
* Print status every 20 seconds.
*/
private void reportStatus(String msg, boolean rightNow) {
long curTime = FSNamesystem.now();
if (!rightNow && (curTime - lastStatusReport < 20 * 1000)) {
return;
}
FLOG.info(msg + " \n" + getTurnOffTip());
lastStatusReport = curTime;
}
/**
* Returns printable state of the class.
*/
public String toString() {
String resText = "Current safe block ratio = "
+ namesystem.getSafeBlockRatio() + ". Safe blocks = "
+ namesystem.getSafeBlocks() + ". Total blocks = "
+ namesystem.getTotalBlocks()
+ ". Target threshold = " + threshold + ". Minimal replication = "
+ namesystem.getMinReplication() + ".";
if (reached > 0) {
resText += " Threshold was reached " + new Date(reached) + ".";
}
return resText;
}
/**
* Checks consistency of the class state. This is costly and currently called
* only in assert.
*/
private boolean isConsistent() {
if (this.reached < 0) {
return true; // Safemode is off.
}
if (namesystem.getTotalBlocks() == -1 && namesystem.getSafeBlocks() == -1) {
return true; // manual safe mode
}
long activeBlocks = namesystem.getBlocksTotal()
- namesystem.getPendingDeletionBlocks();
return (namesystem.getTotalBlocks() == activeBlocks)
|| (namesystem.getSafeBlocks() >= 0 && namesystem.getSafeBlocks() <= namesystem
.getTotalBlocks());
}
@Override
public void shutdown() {
if (smmthread != null) {
smmthread.interrupt();
}
}
@Override
public boolean shouldProcessRBWReports() {
// Primary namenode always processed RBW reports.
return true;
}
}