diff --git a/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/common/HdfsServerConstants.java b/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/common/HdfsServerConstants.java index c3098f3cff0..7434347f0f0 100644 --- a/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/common/HdfsServerConstants.java +++ b/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/common/HdfsServerConstants.java @@ -156,7 +156,9 @@ enum StartupOption{ // only used for StorageDirectory.analyzeStorage() in hot swap drive scenario. // TODO refactor StorageDirectory.analyzeStorage() so that we can do away with // this in StartupOption. - HOTSWAP("-hotswap"); + HOTSWAP("-hotswap"), + // Startup the namenode in observer mode. + OBSERVER("-observer"); private static final Pattern ENUM_WITH_ROLLING_UPGRADE_OPTION = Pattern.compile( "(\\w+)\\((\\w+)\\)"); diff --git a/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/namenode/FSNamesystem.java b/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/namenode/FSNamesystem.java index b03685a7191..51f6c1e0978 100644 --- a/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/namenode/FSNamesystem.java +++ b/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/namenode/FSNamesystem.java @@ -537,7 +537,8 @@ private void logAuditEvent(boolean succeeded, private final ReentrantLock cpLock; /** - * Used when this NN is in standby state to read from the shared edit log. + * Used when this NN is in standby or observer state to read from the + * shared edit log. */ private EditLogTailer editLogTailer = null; @@ -1368,24 +1369,25 @@ void stopActiveServices() { } /** - * Start services required in standby state + * Start services required in standby or observer state * * @throws IOException */ - void startStandbyServices(final Configuration conf) throws IOException { - LOG.info("Starting services required for standby state"); + void startStandbyServices(final Configuration conf, boolean isObserver) + throws IOException { + LOG.info("Starting services required for " + + (isObserver ? "observer" : "standby") + " state"); if (!getFSImage().editLog.isOpenForRead()) { // During startup, we're already open for read. getFSImage().editLog.initSharedJournalsForRead(); } - blockManager.setPostponeBlocksFromFuture(true); // Disable quota checks while in standby. dir.disableQuotaChecks(); editLogTailer = new EditLogTailer(this, conf); editLogTailer.start(); - if (standbyShouldCheckpoint) { + if (!isObserver && standbyShouldCheckpoint) { standbyCheckpointer = new StandbyCheckpointer(conf, this); standbyCheckpointer.start(); } diff --git a/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/namenode/NameNode.java b/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/namenode/NameNode.java index 21523ee7928..47ef8cfd6f0 100644 --- a/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/namenode/NameNode.java +++ b/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/namenode/NameNode.java @@ -369,6 +369,7 @@ public long getProtocolVersion(String protocol, LoggerFactory.getLogger("BlockStateChange"); public static final HAState ACTIVE_STATE = new ActiveState(); public static final HAState STANDBY_STATE = new StandbyState(); + public static final HAState OBSERVER_STATE = new StandbyState(true); private static final String NAMENODE_HTRACE_PREFIX = "namenode.htrace."; @@ -972,9 +973,11 @@ private void stopAtException(Exception e){ } protected HAState createHAState(StartupOption startOpt) { - if (!haEnabled || startOpt == StartupOption.UPGRADE + if (!haEnabled || startOpt == StartupOption.UPGRADE || startOpt == StartupOption.UPGRADEONLY) { return ACTIVE_STATE; + } else if (startOpt == StartupOption.OBSERVER) { + return OBSERVER_STATE; } else { return STANDBY_STATE; } @@ -1440,6 +1443,8 @@ static StartupOption parseArguments(String args[]) { startOpt = StartupOption.BACKUP; } else if (StartupOption.CHECKPOINT.getName().equalsIgnoreCase(cmd)) { startOpt = StartupOption.CHECKPOINT; + } else if (StartupOption.OBSERVER.getName().equalsIgnoreCase(cmd)) { + startOpt = StartupOption.OBSERVER; } else if (StartupOption.UPGRADE.getName().equalsIgnoreCase(cmd) || StartupOption.UPGRADEONLY.getName().equalsIgnoreCase(cmd)) { startOpt = StartupOption.UPGRADE.getName().equalsIgnoreCase(cmd) ? @@ -1753,6 +1758,11 @@ synchronized void transitionToActive() if (!haEnabled) { throw new ServiceFailedException("HA for namenode is not enabled"); } + if (state == OBSERVER_STATE) { + // TODO: we may need to remove this when enabling failover for observer + throw new ServiceFailedException( + "Cannot transition from Observer to Active"); + } state.setState(haContext, ACTIVE_STATE); } @@ -1762,6 +1772,11 @@ synchronized void transitionToStandby() if (!haEnabled) { throw new ServiceFailedException("HA for namenode is not enabled"); } + if (state == OBSERVER_STATE) { + // TODO: we may need to remove this when enabling failover for observer + throw new ServiceFailedException( + "Cannot transition from Observer to Standby"); + } state.setState(haContext, STANDBY_STATE); } @@ -1816,6 +1831,7 @@ public String getNNRole() { @Override // NameNodeStatusMXBean public String getState() { + // TODO: maybe we should return a different result for observer namenode? String servStateStr = ""; HAServiceState servState = getServiceState(); if (null != servState) { @@ -1916,7 +1932,8 @@ public void stopActiveServices() throws IOException { @Override public void startStandbyServices() throws IOException { try { - namesystem.startStandbyServices(getConf()); + namesystem.startStandbyServices(getConf(), + state == NameNode.OBSERVER_STATE); } catch (Throwable t) { doImmediateShutdown(t); } @@ -1963,6 +1980,9 @@ public void checkOperation(final OperationCategory op) @Override public boolean allowStaleReads() { + if (state == OBSERVER_STATE) { + return true; + } return allowStaleStandbyReads; } @@ -1976,6 +1996,10 @@ public boolean isActiveState() { return (state.equals(ACTIVE_STATE)); } + public boolean isObserverState() { + return state.equals(OBSERVER_STATE); + } + /** * Returns whether the NameNode is completely started */ diff --git a/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/namenode/NameNodeRpcServer.java b/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/namenode/NameNodeRpcServer.java index a23e579158a..4fafd42cb18 100644 --- a/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/namenode/NameNodeRpcServer.java +++ b/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/namenode/NameNodeRpcServer.java @@ -1551,7 +1551,7 @@ public Boolean call() throws IOException { if (nn.getFSImage().isUpgradeFinalized() && !namesystem.isRollingUpgrade() && - !nn.isStandbyState() && + nn.isActiveState() && noStaleStorages) { return new FinalizeCommand(poolId); } diff --git a/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/namenode/ha/StandbyState.java b/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/namenode/ha/StandbyState.java index d782bdf3a36..9a218881589 100644 --- a/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/namenode/ha/StandbyState.java +++ b/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/namenode/ha/StandbyState.java @@ -39,8 +39,15 @@ */ @InterfaceAudience.Private public class StandbyState extends HAState { + private final boolean isObserver; + public StandbyState() { + this(false); + } + + public StandbyState(boolean isObserver) { super(HAServiceState.STANDBY); + this.isObserver = isObserver; } @Override @@ -49,6 +56,11 @@ public void setState(HAContext context, HAState s) throws ServiceFailedException setStateInternal(context, s); return; } + if (isObserver && s == NameNode.STANDBY_STATE) { + // To guard against the exception in the following super call. + // The other case, standby -> observer, should not happen. + return; + } super.setState(context, s); } @@ -92,5 +104,10 @@ public void checkOperation(HAContext context, OperationCategory op) public boolean shouldPopulateReplQueues() { return false; } + + @Override + public String toString() { + return isObserver ? "observer" : "standby"; + } }