HDFS-2894. HA: automatically determine the nameservice Id if only one nameservice is configured. Contributed by Eli Collins

git-svn-id: https://svn.apache.org/repos/asf/hadoop/common/branches/HDFS-1623@1240917 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Eli Collins 2012-02-06 08:25:52 +00:00
parent 2e4cf977ae
commit 296b6c0063
5 changed files with 89 additions and 43 deletions

View File

@ -172,3 +172,5 @@ HDFS-2808. HA: haadmin should use namenode ids. (eli)
HDFS-2819. Document new HA-related configs in hdfs-default.xml. (eli) HDFS-2819. Document new HA-related configs in hdfs-default.xml. (eli)
HDFS-2752. HA: exit if multiple shared dirs are configured. (eli) HDFS-2752. HA: exit if multiple shared dirs are configured. (eli)
HDFS-2894. HA: automatically determine the nameservice Id if only one nameservice is configured. (eli)

View File

@ -907,9 +907,10 @@ public class DFSUtil {
* the address of the local node. * the address of the local node.
* *
* If {@link DFSConfigKeys#DFS_FEDERATION_NAMESERVICE_ID} is not specifically * If {@link DFSConfigKeys#DFS_FEDERATION_NAMESERVICE_ID} is not specifically
* configured, this method determines the nameservice Id by matching the local * configured, and more than one nameservice Id is configured, this method
* node's address with the configured addresses. When a match is found, it * determines the nameservice Id by matching the local node's address with the
* returns the nameservice Id from the corresponding configuration key. * configured addresses. When a match is found, it returns the nameservice Id
* from the corresponding configuration key.
* *
* @param conf Configuration * @param conf Configuration
* @param addressKey configuration key to get the address. * @param addressKey configuration key to get the address.
@ -921,6 +922,10 @@ public class DFSUtil {
if (nameserviceId != null) { if (nameserviceId != null) {
return nameserviceId; return nameserviceId;
} }
Collection<String> nsIds = getNameServiceIds(conf);
if (1 == nsIds.size()) {
return nsIds.toArray(new String[1])[0];
}
String nnId = conf.get(DFS_HA_NAMENODE_ID_KEY); String nnId = conf.get(DFS_HA_NAMENODE_ID_KEY);
return getSuffixIDs(conf, addressKey, null, nnId, LOCAL_ADDRESS_MATCHER)[0]; return getSuffixIDs(conf, addressKey, null, nnId, LOCAL_ADDRESS_MATCHER)[0];
@ -1057,11 +1062,11 @@ public class DFSUtil {
if (nsId == null) { if (nsId == null) {
Collection<String> nsIds = getNameServiceIds(conf); Collection<String> nsIds = getNameServiceIds(conf);
if (nsIds.size() != 1) { if (1 == nsIds.size()) {
nsId = nsIds.toArray(new String[1])[0];
} else {
// No nameservice ID was given and more than one is configured // No nameservice ID was given and more than one is configured
return null; return null;
} else {
nsId = nsIds.toArray(new String[1])[0];
} }
} }

View File

@ -47,7 +47,6 @@ import static org.apache.hadoop.hdfs.DFSConfigKeys.*;
import org.apache.hadoop.hdfs.DFSUtil; import org.apache.hadoop.hdfs.DFSUtil;
import org.apache.hadoop.hdfs.HAUtil; import org.apache.hadoop.hdfs.HAUtil;
import org.apache.hadoop.hdfs.DFSUtil.ErrorSimulator; import org.apache.hadoop.hdfs.DFSUtil.ErrorSimulator;
import org.apache.hadoop.hdfs.HAUtil;
import org.apache.hadoop.hdfs.HdfsConfiguration; import org.apache.hadoop.hdfs.HdfsConfiguration;
import org.apache.hadoop.hdfs.protocol.HdfsConstants; import org.apache.hadoop.hdfs.protocol.HdfsConstants;
import org.apache.hadoop.hdfs.protocolPB.NamenodeProtocolTranslatorPB; import org.apache.hadoop.hdfs.protocolPB.NamenodeProtocolTranslatorPB;
@ -170,20 +169,17 @@ public class SecondaryNameNode implements Runnable {
try { try {
String nsId = DFSUtil.getSecondaryNameServiceId(conf); String nsId = DFSUtil.getSecondaryNameServiceId(conf);
if (HAUtil.isHAEnabled(conf, nsId)) { if (HAUtil.isHAEnabled(conf, nsId)) {
LOG.fatal("Cannot use SecondaryNameNode in an HA cluster." + throw new IOException(
"Cannot use SecondaryNameNode in an HA cluster." +
" The Standby Namenode will perform checkpointing."); " The Standby Namenode will perform checkpointing.");
shutdown();
return;
} }
NameNode.initializeGenericKeys(conf, nsId, null); NameNode.initializeGenericKeys(conf, nsId, null);
initialize(conf, commandLineOpts); initialize(conf, commandLineOpts);
} catch(IOException e) { } catch (IOException e) {
shutdown(); shutdown();
LOG.fatal("Failed to start secondary namenode. ", e);
throw e; throw e;
} catch(HadoopIllegalArgumentException e) { } catch (HadoopIllegalArgumentException e) {
shutdown(); shutdown();
LOG.fatal("Failed to start secondary namenode. ", e);
throw e; throw e;
} }
} }
@ -335,7 +331,6 @@ public class SecondaryNameNode implements Runnable {
// The main work loop // The main work loop
// //
public void doWork() { public void doWork() {
// //
// Poll the Namenode (once every checkpointCheckPeriod seconds) to find the // Poll the Namenode (once every checkpointCheckPeriod seconds) to find the
// number of transactions in the edit log that haven't yet been checkpointed. // number of transactions in the edit log that haven't yet been checkpointed.
@ -612,7 +607,13 @@ public class SecondaryNameNode implements Runnable {
StringUtils.startupShutdownMessage(SecondaryNameNode.class, argv, LOG); StringUtils.startupShutdownMessage(SecondaryNameNode.class, argv, LOG);
Configuration tconf = new HdfsConfiguration(); Configuration tconf = new HdfsConfiguration();
SecondaryNameNode secondary = new SecondaryNameNode(tconf, opts); SecondaryNameNode secondary = null;
try {
secondary = new SecondaryNameNode(tconf, opts);
} catch (IOException ioe) {
LOG.fatal("Failed to start secondary namenode", ioe);
System.exit(-1);
}
if (opts.getCommand() != null) { if (opts.getCommand() != null) {
int ret = secondary.processStartupCommand(opts); int ret = secondary.processStartupCommand(opts);

View File

@ -214,6 +214,10 @@ public class TestDFSUtil {
checkNameServiceId(conf, NN1_ADDRESS, "nn1"); checkNameServiceId(conf, NN1_ADDRESS, "nn1");
checkNameServiceId(conf, NN2_ADDRESS, "nn2"); checkNameServiceId(conf, NN2_ADDRESS, "nn2");
checkNameServiceId(conf, NN3_ADDRESS, null); checkNameServiceId(conf, NN3_ADDRESS, null);
// HA is not enabled in a purely federated config
assertFalse(HAUtil.isHAEnabled(conf, "nn1"));
assertFalse(HAUtil.isHAEnabled(conf, "nn2"));
} }
public void checkNameServiceId(Configuration conf, String addr, public void checkNameServiceId(Configuration conf, String addr,
@ -399,8 +403,10 @@ public class TestDFSUtil {
Map<String, Map<String, InetSocketAddress>> map = Map<String, Map<String, InetSocketAddress>> map =
DFSUtil.getHaNnRpcAddresses(conf); DFSUtil.getHaNnRpcAddresses(conf);
System.err.println("TestHANameNodesWithFederation:\n" +
DFSUtil.addressMapToString(map)); assertTrue(HAUtil.isHAEnabled(conf, "ns1"));
assertTrue(HAUtil.isHAEnabled(conf, "ns2"));
assertFalse(HAUtil.isHAEnabled(conf, "ns3"));
assertEquals(NS1_NN1_HOST, map.get("ns1").get("ns1-nn1").toString()); assertEquals(NS1_NN1_HOST, map.get("ns1").get("ns1-nn1").toString());
assertEquals(NS1_NN2_HOST, map.get("ns1").get("ns1-nn2").toString()); assertEquals(NS1_NN2_HOST, map.get("ns1").get("ns1-nn2").toString());
@ -414,9 +420,13 @@ public class TestDFSUtil {
assertEquals(NS2_NN1_HOST, assertEquals(NS2_NN1_HOST,
DFSUtil.getNamenodeServiceAddr(conf, "ns2", "ns2-nn1")); DFSUtil.getNamenodeServiceAddr(conf, "ns2", "ns2-nn1"));
// No nameservice was given and we can't determine which to use // No nameservice was given and we can't determine which service addr
// as two nameservices could share a namenode ID. // to use as two nameservices could share a namenode ID.
assertEquals(null, DFSUtil.getNamenodeServiceAddr(conf, null, "ns1-nn1")); assertEquals(null, DFSUtil.getNamenodeServiceAddr(conf, null, "ns1-nn1"));
// Ditto for nameservice IDs, if multiple are defined
assertEquals(null, DFSUtil.getNamenodeNameServiceId(conf));
assertEquals(null, DFSUtil.getSecondaryNameServiceId(conf));
} }
@Test @Test
@ -453,6 +463,10 @@ public class TestDFSUtil {
assertEquals(NS1_NN1_HOST_SVC, DFSUtil.getNamenodeServiceAddr(conf, null, "nn1")); assertEquals(NS1_NN1_HOST_SVC, DFSUtil.getNamenodeServiceAddr(conf, null, "nn1"));
assertEquals(NS1_NN2_HOST_SVC, DFSUtil.getNamenodeServiceAddr(conf, null, "nn2")); assertEquals(NS1_NN2_HOST_SVC, DFSUtil.getNamenodeServiceAddr(conf, null, "nn2"));
// We can determine the nameservice ID, there's only one listed
assertEquals("ns1", DFSUtil.getNamenodeNameServiceId(conf));
assertEquals("ns1", DFSUtil.getSecondaryNameServiceId(conf));
} }
@Test @Test

View File

@ -29,6 +29,7 @@ import org.apache.hadoop.hdfs.DFSConfigKeys;
import org.apache.hadoop.hdfs.DFSUtil; import org.apache.hadoop.hdfs.DFSUtil;
import org.apache.hadoop.hdfs.server.namenode.FSNamesystem; import org.apache.hadoop.hdfs.server.namenode.FSNamesystem;
import org.apache.hadoop.hdfs.server.namenode.NameNode; import org.apache.hadoop.hdfs.server.namenode.NameNode;
import org.apache.hadoop.hdfs.server.namenode.SecondaryNameNode;
import org.apache.hadoop.test.GenericTestUtils; import org.apache.hadoop.test.GenericTestUtils;
import org.junit.Test; import org.junit.Test;
import org.mockito.Mockito; import org.mockito.Mockito;
@ -39,16 +40,13 @@ import org.mockito.Mockito;
* which don't start daemons. * which don't start daemons.
*/ */
public class TestHAConfiguration { public class TestHAConfiguration {
private static final String NSID = "ns1";
private static String HOST_A = "1.2.3.1";
private static String HOST_B = "1.2.3.2";
private FSNamesystem fsn = Mockito.mock(FSNamesystem.class); private FSNamesystem fsn = Mockito.mock(FSNamesystem.class);
private Configuration conf = new Configuration();
@Test @Test
public void testCheckpointerValidityChecks() throws Exception { public void testCheckpointerValidityChecks() throws Exception {
try { try {
Configuration conf = new Configuration();
new StandbyCheckpointer(conf, fsn); new StandbyCheckpointer(conf, fsn);
fail("Bad config did not throw an error"); fail("Bad config did not throw an error");
} catch (IllegalArgumentException iae) { } catch (IllegalArgumentException iae) {
@ -56,30 +54,37 @@ public class TestHAConfiguration {
"Invalid URI for NameNode address", iae); "Invalid URI for NameNode address", iae);
} }
} }
@Test private Configuration getHAConf(String nsId, String host1, String host2) {
public void testGetOtherNNHttpAddress() { Configuration conf = new Configuration();
conf.set(DFSConfigKeys.DFS_FEDERATION_NAMESERVICES, NSID); conf.set(DFSConfigKeys.DFS_FEDERATION_NAMESERVICES, nsId);
conf.set(DFSConfigKeys.DFS_FEDERATION_NAMESERVICE_ID, NSID);
conf.set(DFSUtil.addKeySuffixes( conf.set(DFSUtil.addKeySuffixes(
DFSConfigKeys.DFS_HA_NAMENODES_KEY_PREFIX, NSID), DFSConfigKeys.DFS_HA_NAMENODES_KEY_PREFIX, nsId),
"nn1,nn2"); "nn1,nn2");
conf.set(DFSConfigKeys.DFS_HA_NAMENODE_ID_KEY, "nn1"); conf.set(DFSConfigKeys.DFS_HA_NAMENODE_ID_KEY, "nn1");
conf.set(DFSUtil.addKeySuffixes( conf.set(DFSUtil.addKeySuffixes(
DFSConfigKeys.DFS_NAMENODE_RPC_ADDRESS_KEY, DFSConfigKeys.DFS_NAMENODE_RPC_ADDRESS_KEY, nsId, "nn1"),
NSID, "nn1"), host1 + ":12345");
HOST_A + ":12345");
conf.set(DFSUtil.addKeySuffixes( conf.set(DFSUtil.addKeySuffixes(
DFSConfigKeys.DFS_NAMENODE_RPC_ADDRESS_KEY, DFSConfigKeys.DFS_NAMENODE_RPC_ADDRESS_KEY, nsId, "nn2"),
NSID, "nn2"), host2 + ":12345");
HOST_B + ":12345"); return conf;
NameNode.initializeGenericKeys(conf, NSID, "nn1"); }
@Test
public void testGetOtherNNHttpAddress() {
// Use non-local addresses to avoid host address matching
Configuration conf = getHAConf("ns1", "1.2.3.1", "1.2.3.2");
conf.set(DFSConfigKeys.DFS_FEDERATION_NAMESERVICE_ID, "ns1");
// This is done by the NN before the StandbyCheckpointer is created
NameNode.initializeGenericKeys(conf, "ns1", "nn1");
// Since we didn't configure the HTTP address, and the default is // Since we didn't configure the HTTP address, and the default is
// 0.0.0.0, it should substitute the address from the RPC configuratoin // 0.0.0.0, it should substitute the address from the RPC configuration
// above. // above.
StandbyCheckpointer checkpointer = new StandbyCheckpointer(conf, fsn); StandbyCheckpointer checkpointer = new StandbyCheckpointer(conf, fsn);
assertEquals(HOST_B + ":" + DFSConfigKeys.DFS_NAMENODE_HTTP_PORT_DEFAULT, assertEquals("1.2.3.2:" + DFSConfigKeys.DFS_NAMENODE_HTTP_PORT_DEFAULT,
checkpointer.getActiveNNAddress()); checkpointer.getActiveNNAddress());
} }
@ -89,14 +94,33 @@ public class TestHAConfiguration {
*/ */
@Test @Test
public void testHAUniqueEditDirs() throws IOException { public void testHAUniqueEditDirs() throws IOException {
Configuration config = new Configuration(); Configuration conf = new Configuration();
config.set(DFS_NAMENODE_EDITS_DIR_KEY, "file://edits/dir, " conf.set(DFS_NAMENODE_EDITS_DIR_KEY, "file://edits/dir, "
+ "file://edits/shared/dir"); // overlapping + "file://edits/shared/dir"); // overlapping
config.set(DFS_NAMENODE_SHARED_EDITS_DIR_KEY, "file://edits/shared/dir"); conf.set(DFS_NAMENODE_SHARED_EDITS_DIR_KEY, "file://edits/shared/dir");
// getNamespaceEditsDirs removes duplicates across edits and shared.edits // getNamespaceEditsDirs removes duplicates across edits and shared.edits
Collection<URI> editsDirs = FSNamesystem.getNamespaceEditsDirs(config); Collection<URI> editsDirs = FSNamesystem.getNamespaceEditsDirs(conf);
assertEquals(2, editsDirs.size()); assertEquals(2, editsDirs.size());
} }
/**
* Test that the 2NN does not start if given a config with HA NNs.
*/
@Test
public void testSecondaryNameNodeDoesNotStart() throws IOException {
// Note we're not explicitly setting the nameservice Id in the
// config as it is not required to be set and we want to test
// that we can determine if HA is enabled when the nameservice Id
// is not explicitly defined.
Configuration conf = getHAConf("ns1", "1.2.3.1", "1.2.3.2");
try {
new SecondaryNameNode(conf);
fail("Created a 2NN with an HA config");
} catch (IOException ioe) {
GenericTestUtils.assertExceptionContains(
"Cannot use SecondaryNameNode in an HA cluster", ioe);
}
}
} }