HDFS-16111. Add a configuration to RoundRobinVolumeChoosingPolicy to avoid failed volumes at datanodes. (#3175)
Change-Id: Iead25812d4073e3980893e3e76f7d2b03b57442a Co-authored-by: Zhihai Xu <zxu@apache.org>
This commit is contained in:
parent
10ba4cc892
commit
ae20516ebc
|
@ -1112,6 +1112,12 @@ public class DFSConfigKeys extends CommonConfigurationKeys {
|
||||||
public static final long DFS_DATANODE_AVAILABLE_SPACE_VOLUME_CHOOSING_POLICY_BALANCED_SPACE_THRESHOLD_DEFAULT = 1024L * 1024L * 1024L * 10L; // 10 GB
|
public static final long DFS_DATANODE_AVAILABLE_SPACE_VOLUME_CHOOSING_POLICY_BALANCED_SPACE_THRESHOLD_DEFAULT = 1024L * 1024L * 1024L * 10L; // 10 GB
|
||||||
public static final String DFS_DATANODE_AVAILABLE_SPACE_VOLUME_CHOOSING_POLICY_BALANCED_SPACE_PREFERENCE_FRACTION_KEY = "dfs.datanode.available-space-volume-choosing-policy.balanced-space-preference-fraction";
|
public static final String DFS_DATANODE_AVAILABLE_SPACE_VOLUME_CHOOSING_POLICY_BALANCED_SPACE_PREFERENCE_FRACTION_KEY = "dfs.datanode.available-space-volume-choosing-policy.balanced-space-preference-fraction";
|
||||||
public static final float DFS_DATANODE_AVAILABLE_SPACE_VOLUME_CHOOSING_POLICY_BALANCED_SPACE_PREFERENCE_FRACTION_DEFAULT = 0.75f;
|
public static final float DFS_DATANODE_AVAILABLE_SPACE_VOLUME_CHOOSING_POLICY_BALANCED_SPACE_PREFERENCE_FRACTION_DEFAULT = 0.75f;
|
||||||
|
public static final String
|
||||||
|
DFS_DATANODE_ROUND_ROBIN_VOLUME_CHOOSING_POLICY_ADDITIONAL_AVAILABLE_SPACE_KEY =
|
||||||
|
"dfs.datanode.round-robin-volume-choosing-policy.additional-available-space";
|
||||||
|
public static final long
|
||||||
|
DFS_DATANODE_ROUND_ROBIN_VOLUME_CHOOSING_POLICY_ADDITIONAL_AVAILABLE_SPACE_DEFAULT =
|
||||||
|
1024L * 1024L * 1024L; // 1 GB
|
||||||
public static final String DFS_DATANODE_SOCKET_WRITE_TIMEOUT_KEY =
|
public static final String DFS_DATANODE_SOCKET_WRITE_TIMEOUT_KEY =
|
||||||
HdfsClientConfigKeys.DFS_DATANODE_SOCKET_WRITE_TIMEOUT_KEY;
|
HdfsClientConfigKeys.DFS_DATANODE_SOCKET_WRITE_TIMEOUT_KEY;
|
||||||
public static final String DFS_DATANODE_STARTUP_KEY = "dfs.datanode.startup";
|
public static final String DFS_DATANODE_STARTUP_KEY = "dfs.datanode.startup";
|
||||||
|
|
|
@ -17,11 +17,16 @@
|
||||||
*/
|
*/
|
||||||
package org.apache.hadoop.hdfs.server.datanode.fsdataset;
|
package org.apache.hadoop.hdfs.server.datanode.fsdataset;
|
||||||
|
|
||||||
|
import static org.apache.hadoop.hdfs.DFSConfigKeys.DFS_DATANODE_ROUND_ROBIN_VOLUME_CHOOSING_POLICY_ADDITIONAL_AVAILABLE_SPACE_DEFAULT;
|
||||||
|
import static org.apache.hadoop.hdfs.DFSConfigKeys.DFS_DATANODE_ROUND_ROBIN_VOLUME_CHOOSING_POLICY_ADDITIONAL_AVAILABLE_SPACE_KEY;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.apache.hadoop.conf.Configurable;
|
||||||
|
import org.apache.hadoop.conf.Configuration;
|
||||||
import org.apache.hadoop.fs.StorageType;
|
import org.apache.hadoop.fs.StorageType;
|
||||||
import org.apache.hadoop.util.DiskChecker.DiskOutOfSpaceException;
|
import org.apache.hadoop.util.DiskChecker.DiskOutOfSpaceException;
|
||||||
|
|
||||||
|
@ -30,7 +35,7 @@ import org.apache.hadoop.util.DiskChecker.DiskOutOfSpaceException;
|
||||||
* Use fine-grained locks to synchronize volume choosing.
|
* Use fine-grained locks to synchronize volume choosing.
|
||||||
*/
|
*/
|
||||||
public class RoundRobinVolumeChoosingPolicy<V extends FsVolumeSpi>
|
public class RoundRobinVolumeChoosingPolicy<V extends FsVolumeSpi>
|
||||||
implements VolumeChoosingPolicy<V> {
|
implements VolumeChoosingPolicy<V>, Configurable {
|
||||||
public static final Logger LOG =
|
public static final Logger LOG =
|
||||||
LoggerFactory.getLogger(RoundRobinVolumeChoosingPolicy.class);
|
LoggerFactory.getLogger(RoundRobinVolumeChoosingPolicy.class);
|
||||||
|
|
||||||
|
@ -41,6 +46,9 @@ public class RoundRobinVolumeChoosingPolicy<V extends FsVolumeSpi>
|
||||||
// syncLocks stores the locks for each storage type.
|
// syncLocks stores the locks for each storage type.
|
||||||
private Object[] syncLocks;
|
private Object[] syncLocks;
|
||||||
|
|
||||||
|
// The required additional available space when choosing a volume.
|
||||||
|
private long additionalAvailableSpace;
|
||||||
|
|
||||||
public RoundRobinVolumeChoosingPolicy() {
|
public RoundRobinVolumeChoosingPolicy() {
|
||||||
int numStorageTypes = StorageType.values().length;
|
int numStorageTypes = StorageType.values().length;
|
||||||
curVolumes = new int[numStorageTypes];
|
curVolumes = new int[numStorageTypes];
|
||||||
|
@ -50,6 +58,23 @@ public class RoundRobinVolumeChoosingPolicy<V extends FsVolumeSpi>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setConf(Configuration conf) {
|
||||||
|
additionalAvailableSpace = conf.getLong(
|
||||||
|
DFS_DATANODE_ROUND_ROBIN_VOLUME_CHOOSING_POLICY_ADDITIONAL_AVAILABLE_SPACE_KEY,
|
||||||
|
DFS_DATANODE_ROUND_ROBIN_VOLUME_CHOOSING_POLICY_ADDITIONAL_AVAILABLE_SPACE_DEFAULT);
|
||||||
|
|
||||||
|
LOG.info("Round robin volume choosing policy initialized: " +
|
||||||
|
DFS_DATANODE_ROUND_ROBIN_VOLUME_CHOOSING_POLICY_ADDITIONAL_AVAILABLE_SPACE_KEY +
|
||||||
|
" = " + additionalAvailableSpace);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Configuration getConf() {
|
||||||
|
// Nothing to do. Only added to fulfill the Configurable contract.
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public V chooseVolume(final List<V> volumes, long blockSize, String storageId)
|
public V chooseVolume(final List<V> volumes, long blockSize, String storageId)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
|
@ -83,7 +108,7 @@ public class RoundRobinVolumeChoosingPolicy<V extends FsVolumeSpi>
|
||||||
final V volume = volumes.get(curVolume);
|
final V volume = volumes.get(curVolume);
|
||||||
curVolume = (curVolume + 1) % volumes.size();
|
curVolume = (curVolume + 1) % volumes.size();
|
||||||
long availableVolumeSize = volume.getAvailable();
|
long availableVolumeSize = volume.getAvailable();
|
||||||
if (availableVolumeSize > blockSize) {
|
if (availableVolumeSize > blockSize + additionalAvailableSpace) {
|
||||||
curVolumes[curVolumeIndex] = curVolume;
|
curVolumes[curVolumeIndex] = curVolume;
|
||||||
return volume;
|
return volume;
|
||||||
}
|
}
|
||||||
|
|
|
@ -2657,6 +2657,17 @@
|
||||||
</description>
|
</description>
|
||||||
</property>
|
</property>
|
||||||
|
|
||||||
|
<property>
|
||||||
|
<name>dfs.datanode.round-robin-volume-choosing-policy.additional-available-space</name>
|
||||||
|
<value>1073741824</value> <!-- 1 GB -->
|
||||||
|
<description>
|
||||||
|
Only used when the dfs.datanode.fsdataset.volume.choosing.policy is set to
|
||||||
|
org.apache.hadoop.hdfs.server.datanode.fsdataset.RoundRobinVolumeChoosingPolicy.
|
||||||
|
This setting controls how much additional available space (unit is byte) is needed
|
||||||
|
when choosing a volume.
|
||||||
|
</description>
|
||||||
|
</property>
|
||||||
|
|
||||||
<property>
|
<property>
|
||||||
<name>dfs.namenode.edits.noeditlogchannelflush</name>
|
<name>dfs.namenode.edits.noeditlogchannelflush</name>
|
||||||
<value>false</value>
|
<value>false</value>
|
||||||
|
|
|
@ -533,6 +533,9 @@ public class MiniDFSCluster implements AutoCloseable {
|
||||||
DEFAULT_DFS_NAMENODE_REDUNDANCY_CONSIDERLOAD);
|
DEFAULT_DFS_NAMENODE_REDUNDANCY_CONSIDERLOAD);
|
||||||
this.storagesPerDatanode =
|
this.storagesPerDatanode =
|
||||||
FsDatasetTestUtils.Factory.getFactory(conf).getDefaultNumOfDataDirs();
|
FsDatasetTestUtils.Factory.getFactory(conf).getDefaultNumOfDataDirs();
|
||||||
|
conf.setLong(DFSConfigKeys
|
||||||
|
.DFS_DATANODE_ROUND_ROBIN_VOLUME_CHOOSING_POLICY_ADDITIONAL_AVAILABLE_SPACE_KEY,
|
||||||
|
0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -17,10 +17,13 @@
|
||||||
*/
|
*/
|
||||||
package org.apache.hadoop.hdfs.server.datanode.fsdataset;
|
package org.apache.hadoop.hdfs.server.datanode.fsdataset;
|
||||||
|
|
||||||
|
import static org.apache.hadoop.hdfs.DFSConfigKeys.DFS_DATANODE_ROUND_ROBIN_VOLUME_CHOOSING_POLICY_ADDITIONAL_AVAILABLE_SPACE_KEY;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.apache.hadoop.conf.Configuration;
|
||||||
import org.apache.hadoop.fs.StorageType;
|
import org.apache.hadoop.fs.StorageType;
|
||||||
import org.apache.hadoop.util.DiskChecker.DiskOutOfSpaceException;
|
import org.apache.hadoop.util.DiskChecker.DiskOutOfSpaceException;
|
||||||
import org.apache.hadoop.util.ReflectionUtils;
|
import org.apache.hadoop.util.ReflectionUtils;
|
||||||
|
@ -71,6 +74,49 @@ public class TestRoundRobinVolumeChoosingPolicy {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Test the Round-Robin block-volume choosing algorithm with
|
||||||
|
// additional available space configured.
|
||||||
|
@Test
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public void testRRWithAdditionalAvailableSpace() throws Exception {
|
||||||
|
Configuration conf = new Configuration();
|
||||||
|
// Set the additional available space needed
|
||||||
|
conf.setLong(
|
||||||
|
DFS_DATANODE_ROUND_ROBIN_VOLUME_CHOOSING_POLICY_ADDITIONAL_AVAILABLE_SPACE_KEY,
|
||||||
|
100);
|
||||||
|
final RoundRobinVolumeChoosingPolicy<FsVolumeSpi> policy =
|
||||||
|
ReflectionUtils.newInstance(RoundRobinVolumeChoosingPolicy.class, conf);
|
||||||
|
testRRWithAdditionalAvailableSpace(policy);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void testRRWithAdditionalAvailableSpace(
|
||||||
|
VolumeChoosingPolicy<FsVolumeSpi> policy) throws Exception {
|
||||||
|
final List<FsVolumeSpi> volumes = new ArrayList<FsVolumeSpi>();
|
||||||
|
|
||||||
|
// First volume, with 100 bytes of space.
|
||||||
|
volumes.add(Mockito.mock(FsVolumeSpi.class));
|
||||||
|
Mockito.when(volumes.get(0).getAvailable()).thenReturn(100L);
|
||||||
|
|
||||||
|
// Second volume, with 200 bytes of space.
|
||||||
|
volumes.add(Mockito.mock(FsVolumeSpi.class));
|
||||||
|
Mockito.when(volumes.get(1).getAvailable()).thenReturn(200L);
|
||||||
|
|
||||||
|
// The first volume has only 100L space, so the policy should choose
|
||||||
|
// the second one with additional available space configured as 100L.
|
||||||
|
Assert.assertEquals(volumes.get(1), policy.chooseVolume(volumes, 0,
|
||||||
|
null));
|
||||||
|
Assert.assertEquals(volumes.get(1), policy.chooseVolume(volumes, 0,
|
||||||
|
null));
|
||||||
|
|
||||||
|
// Fail if no volume can be chosen?
|
||||||
|
try {
|
||||||
|
policy.chooseVolume(volumes, 100, null);
|
||||||
|
Assert.fail();
|
||||||
|
} catch (IOException e) {
|
||||||
|
// Passed.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// ChooseVolume should throw DiskOutOfSpaceException
|
// ChooseVolume should throw DiskOutOfSpaceException
|
||||||
// with volume and block sizes in exception message.
|
// with volume and block sizes in exception message.
|
||||||
@Test
|
@Test
|
||||||
|
|
Loading…
Reference in New Issue