blob: f86ce5fc067720e7da8b2127b0b6c641b9d8fc75 [file] [log] [blame]
/**
* 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 static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import java.io.File;
import java.io.IOException;
import java.net.URISyntaxException;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.hdfs.DFSConfigKeys;
import org.apache.hadoop.hdfs.MiniDFSCluster;
import org.apache.hadoop.hdfs.server.namenode.FSNamesystem.NameNodeResourceMonitor;
import org.apache.hadoop.hdfs.server.namenode.NameNodeResourceChecker.CheckedVolume;
import org.apache.hadoop.test.PathUtils;
import org.apache.hadoop.util.Time;
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mockito;
public class TestNameNodeResourceChecker {
private final static File BASE_DIR = PathUtils.getTestDir(TestNameNodeResourceChecker.class);
private Configuration conf;
private File baseDir;
private File nameDir;
@Before
public void setUp () throws IOException {
conf = new Configuration();
nameDir = new File(BASE_DIR, "resource-check-name-dir");
nameDir.mkdirs();
conf.set(DFSConfigKeys.DFS_NAMENODE_EDITS_DIR_KEY, nameDir.getAbsolutePath());
}
/**
* Tests that hasAvailableDiskSpace returns true if disk usage is below
* threshold.
*/
@Test
public void testCheckAvailability()
throws IOException {
conf.setLong(DFSConfigKeys.DFS_NAMENODE_DU_RESERVED_KEY, 0);
NameNodeResourceChecker nb = new NameNodeResourceChecker(conf);
assertTrue(
"isResourceAvailable must return true if " +
"disk usage is lower than threshold",
nb.hasAvailableDiskSpace());
}
/**
* Tests that hasAvailableDiskSpace returns false if disk usage is above
* threshold.
*/
@Test
public void testCheckAvailabilityNeg() throws IOException {
conf.setLong(DFSConfigKeys.DFS_NAMENODE_DU_RESERVED_KEY, Long.MAX_VALUE);
NameNodeResourceChecker nb = new NameNodeResourceChecker(conf);
assertFalse(
"isResourceAvailable must return false if " +
"disk usage is higher than threshold",
nb.hasAvailableDiskSpace());
}
/**
* Tests that NameNode resource monitor causes the NN to enter safe mode when
* resources are low.
*/
@Test
public void testCheckThatNameNodeResourceMonitorIsRunning()
throws IOException, InterruptedException {
MiniDFSCluster cluster = null;
try {
conf.set(DFSConfigKeys.DFS_NAMENODE_EDITS_DIR_KEY, nameDir.getAbsolutePath());
conf.setLong(DFSConfigKeys.DFS_NAMENODE_RESOURCE_CHECK_INTERVAL_KEY, 1);
cluster = new MiniDFSCluster.Builder(conf)
.numDataNodes(1).build();
MockNameNodeResourceChecker mockResourceChecker =
new MockNameNodeResourceChecker(conf);
cluster.getNameNode()
.getNamesystem().nnResourceChecker = mockResourceChecker;
cluster.waitActive();
String name = NameNodeResourceMonitor.class.getName();
boolean isNameNodeMonitorRunning = false;
Set<Thread> runningThreads = Thread.getAllStackTraces().keySet();
for (Thread runningThread : runningThreads) {
if (runningThread.toString().startsWith("Thread[" + name)) {
isNameNodeMonitorRunning = true;
break;
}
}
assertTrue("NN resource monitor should be running",
isNameNodeMonitorRunning);
assertFalse("NN should not presently be in safe mode",
cluster.getNameNode().isInSafeMode());
mockResourceChecker.setResourcesAvailable(false);
// Make sure the NNRM thread has a chance to run.
long startMillis = Time.now();
while (!cluster.getNameNode().isInSafeMode() &&
Time.now() < startMillis + (60 * 1000)) {
Thread.sleep(1000);
}
assertTrue("NN should be in safe mode after resources crossed threshold",
cluster.getNameNode().isInSafeMode());
} finally {
if (cluster != null)
cluster.shutdown();
}
}
/**
* Tests that only a single space check is performed if two name dirs are
* supplied which are on the same volume.
*/
@Test
public void testChecking2NameDirsOnOneVolume() throws IOException {
Configuration conf = new Configuration();
File nameDir1 = new File(BASE_DIR, "name-dir1");
File nameDir2 = new File(BASE_DIR, "name-dir2");
nameDir1.mkdirs();
nameDir2.mkdirs();
conf.set(DFSConfigKeys.DFS_NAMENODE_EDITS_DIR_KEY,
nameDir1.getAbsolutePath() + "," + nameDir2.getAbsolutePath());
conf.setLong(DFSConfigKeys.DFS_NAMENODE_DU_RESERVED_KEY, Long.MAX_VALUE);
NameNodeResourceChecker nb = new NameNodeResourceChecker(conf);
assertEquals("Should not check the same volume more than once.",
1, nb.getVolumesLowOnSpace().size());
}
/**
* Tests that only a single space check is performed if extra volumes are
* configured manually which also coincide with a volume the name dir is on.
*/
@Test
public void testCheckingExtraVolumes() throws IOException {
Configuration conf = new Configuration();
File nameDir = new File(BASE_DIR, "name-dir");
nameDir.mkdirs();
conf.set(DFSConfigKeys.DFS_NAMENODE_EDITS_DIR_KEY, nameDir.getAbsolutePath());
conf.set(DFSConfigKeys.DFS_NAMENODE_CHECKED_VOLUMES_KEY, nameDir.getAbsolutePath());
conf.setLong(DFSConfigKeys.DFS_NAMENODE_DU_RESERVED_KEY, Long.MAX_VALUE);
NameNodeResourceChecker nb = new NameNodeResourceChecker(conf);
assertEquals("Should not check the same volume more than once.",
1, nb.getVolumesLowOnSpace().size());
}
/**
* Test that the NN is considered to be out of resources only once all
* redundant configured volumes are low on resources, or when any required
* volume is low on resources.
*/
@Test
public void testLowResourceVolumePolicy() throws IOException, URISyntaxException {
Configuration conf = new Configuration();
File nameDir1 = new File(BASE_DIR, "name-dir1");
File nameDir2 = new File(BASE_DIR, "name-dir2");
nameDir1.mkdirs();
nameDir2.mkdirs();
conf.set(DFSConfigKeys.DFS_NAMENODE_EDITS_DIR_KEY,
nameDir1.getAbsolutePath() + "," + nameDir2.getAbsolutePath());
conf.setInt(DFSConfigKeys.DFS_NAMENODE_CHECKED_VOLUMES_MINIMUM_KEY, 2);
NameNodeResourceChecker nnrc = new NameNodeResourceChecker(conf);
// For the purpose of this test, we need to force the name dirs to appear to
// be on different volumes.
Map<String, CheckedVolume> volumes = new HashMap<String, CheckedVolume>();
CheckedVolume volume1 = Mockito.mock(CheckedVolume.class);
CheckedVolume volume2 = Mockito.mock(CheckedVolume.class);
CheckedVolume volume3 = Mockito.mock(CheckedVolume.class);
CheckedVolume volume4 = Mockito.mock(CheckedVolume.class);
CheckedVolume volume5 = Mockito.mock(CheckedVolume.class);
Mockito.when(volume1.isResourceAvailable()).thenReturn(true);
Mockito.when(volume2.isResourceAvailable()).thenReturn(true);
Mockito.when(volume3.isResourceAvailable()).thenReturn(true);
Mockito.when(volume4.isResourceAvailable()).thenReturn(true);
Mockito.when(volume5.isResourceAvailable()).thenReturn(true);
// Make volumes 4 and 5 required.
Mockito.when(volume4.isRequired()).thenReturn(true);
Mockito.when(volume5.isRequired()).thenReturn(true);
volumes.put("volume1", volume1);
volumes.put("volume2", volume2);
volumes.put("volume3", volume3);
volumes.put("volume4", volume4);
volumes.put("volume5", volume5);
nnrc.setVolumes(volumes);
// Initially all dirs have space.
assertTrue(nnrc.hasAvailableDiskSpace());
// 1/3 redundant dir is low on space.
Mockito.when(volume1.isResourceAvailable()).thenReturn(false);
assertTrue(nnrc.hasAvailableDiskSpace());
// 2/3 redundant dirs are low on space.
Mockito.when(volume2.isResourceAvailable()).thenReturn(false);
assertFalse(nnrc.hasAvailableDiskSpace());
// Lower the minimum number of redundant volumes that must be available.
nnrc.setMinimumReduntdantVolumes(1);
assertTrue(nnrc.hasAvailableDiskSpace());
// Just one required dir is low on space.
Mockito.when(volume3.isResourceAvailable()).thenReturn(false);
assertFalse(nnrc.hasAvailableDiskSpace());
// Just the other required dir is low on space.
Mockito.when(volume3.isResourceAvailable()).thenReturn(true);
Mockito.when(volume4.isResourceAvailable()).thenReturn(false);
assertFalse(nnrc.hasAvailableDiskSpace());
}
}