Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,9 @@ public final class ScmConfigKeys {
public static final String HDDS_DATANODE_DIR_KEY = "hdds.datanode.dir";
public static final String HDDS_DATANODE_DIR_DU_RESERVED =
"hdds.datanode.dir.du.reserved";
public static final String HDDS_DATANODE_DIR_DU_RESERVED_PERCENT =
"hdds.datanode.dir.du.reserved.percent";
public static final float HDDS_DATANODE_DIR_DU_RESERVED_PERCENT_DEFAULT = 0;
public static final String HDDS_REST_CSRF_ENABLED_KEY =
"hdds.rest.rest-csrf.enabled";
public static final boolean HDDS_REST_CSRF_ENABLED_DEFAULT = false;
Expand Down
8 changes: 8 additions & 0 deletions hadoop-hdds/common/src/main/resources/ozone-default.xml
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,14 @@
Such as /dir1:100B, /dir2:200MB, means dir1 reserves 100 bytes and dir2 reserves 200 MB.
</description>
</property>
<property>
<name>hdds.datanode.dir.du.reserved.percent</name>
<value/>
<tag>OZONE, CONTAINER, STORAGE, MANAGEMENT</tag>
<description>Percentage of volume that should be reserved. This space is left free for other usage.
The value should be between 0-1. Such as 0.1 which means 10% of volume space will be reserved.
</description>
</property>
<property>
<name>hdds.datanode.volume.choosing.policy</name>
<value/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,14 @@ default Map<String, String> getPropsMatchPrefix(String keyPrefix) {
return configMap;
}

/**
* Checks if the property <value> is set.
* @param key The property name.
* @return true if the value is set else false.
*/
default boolean isConfigured(String key) {
return get(key) != null;
}
/**
* Create a Configuration object and inject the required configuration values.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@
import org.slf4j.LoggerFactory;

import static org.apache.hadoop.hdds.scm.ScmConfigKeys.HDDS_DATANODE_DIR_DU_RESERVED;
import static org.apache.hadoop.hdds.scm.ScmConfigKeys.HDDS_DATANODE_DIR_DU_RESERVED_PERCENT;
import static org.apache.hadoop.hdds.scm.ScmConfigKeys.HDDS_DATANODE_DIR_DU_RESERVED_PERCENT_DEFAULT;

/**
* Stores information about a disk/volume.
Expand Down Expand Up @@ -134,6 +136,16 @@ public VolumeInfo build() throws IOException {
}

private long getReserved(ConfigurationSource conf) {
if (conf.isConfigured(HDDS_DATANODE_DIR_DU_RESERVED_PERCENT)
&& conf.isConfigured(HDDS_DATANODE_DIR_DU_RESERVED)) {
LOG.error("Both {} and {} are set. Set either one, not both. If the " +
"volume matches with volume parameter in former config, it is set " +
"as reserved space. If not it fall backs to the latter config.",
HDDS_DATANODE_DIR_DU_RESERVED, HDDS_DATANODE_DIR_DU_RESERVED_PERCENT);
}

// 1. If hdds.datanode.dir.du.reserved is set for a volume then make it
// as the reserved bytes.
Collection<String> reserveList = conf.getTrimmedStringCollection(
HDDS_DATANODE_DIR_DU_RESERVED);
for (String reserve : reserveList) {
Expand All @@ -149,12 +161,26 @@ private long getReserved(ConfigurationSource conf) {
StorageSize size = StorageSize.parse(words[1].trim());
return (long) size.getUnit().toBytes(size.getValue());
} catch (Exception e) {
LOG.error("Failed to parse StorageSize:{}", words[1].trim(), e);
return 0;
LOG.error("Failed to parse StorageSize: {}", words[1].trim(), e);
break;
}
}
}

// 2. If hdds.datanode.dir.du.reserved not set and
// hdds.datanode.dir.du.reserved.percent is set, fall back to this config.
if (conf.isConfigured(HDDS_DATANODE_DIR_DU_RESERVED_PERCENT)) {
float percentage = conf.getFloat(HDDS_DATANODE_DIR_DU_RESERVED_PERCENT,
HDDS_DATANODE_DIR_DU_RESERVED_PERCENT_DEFAULT);
if (0 <= percentage && percentage <= 1) {
return (long) Math.ceil(this.usage.getCapacity() * percentage);
}
//If it comes here then the percentage is not between 0-1.
LOG.error("The value of {} should be between 0 to 1. Defaulting to 0.",
HDDS_DATANODE_DIR_DU_RESERVED_PERCENT);
}

//Both configs are not set, return 0.
return 0;
}

Expand Down Expand Up @@ -183,8 +209,9 @@ private VolumeInfo(Builder b) throws IOException {
SpaceUsageCheckParams checkParams =
usageCheckFactory.paramsFor(root);

this.usage = new VolumeUsage(checkParams);
this.reservedInBytes = getReserved(b.conf);
this.usage = new VolumeUsage(checkParams, reservedInBytes);
this.usage.setReserved(reservedInBytes);
}

public long getCapacity() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,9 @@ public class VolumeUsage implements SpaceUsageSource {

private final CachingSpaceUsageSource source;
private boolean shutdownComplete;
private final long reservedInBytes;
private long reservedInBytes;

VolumeUsage(SpaceUsageCheckParams checkParams, long reservedInBytes) {
this.reservedInBytes = reservedInBytes;
VolumeUsage(SpaceUsageCheckParams checkParams) {
source = new CachingSpaceUsageSource(checkParams);
start(); // TODO should start only on demand
}
Expand Down Expand Up @@ -98,4 +97,8 @@ public synchronized void shutdown() {
public void refreshNow() {
source.refreshNow();
}

public void setReserved(long reserved) {
this.reservedInBytes = reserved;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
/**
* 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.ozone.container.common.volume;

import org.apache.hadoop.hdds.conf.OzoneConfiguration;
import org.apache.hadoop.hdds.fs.MockSpaceUsageCheckFactory;
import org.apache.hadoop.hdds.scm.ScmConfigKeys;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;

import java.util.UUID;

import static org.apache.hadoop.hdds.scm.ScmConfigKeys.HDDS_DATANODE_DIR_DU_RESERVED_PERCENT;
import static org.apache.hadoop.hdds.scm.ScmConfigKeys.HDDS_DATANODE_DIR_DU_RESERVED_PERCENT_DEFAULT;

/**
* To test the reserved volume space.
*/
public class TestReservedVolumeSpace {

@Rule
public TemporaryFolder folder = new TemporaryFolder();
@Rule
public TemporaryFolder temp = new TemporaryFolder();
private static final String DATANODE_UUID = UUID.randomUUID().toString();
private HddsVolume.Builder volumeBuilder;

@Before
public void setup() throws Exception {
volumeBuilder = new HddsVolume.Builder(folder.getRoot().getPath())
.datanodeUuid(DATANODE_UUID)
.usageCheckFactory(MockSpaceUsageCheckFactory.NONE);
}

/**
* Test reserved capacity with respect to the percentage of actual capacity.
* @throws Exception
*/
@Test
public void testVolumeCapacityAfterReserve() throws Exception {
OzoneConfiguration conf = new OzoneConfiguration();
conf.set(HDDS_DATANODE_DIR_DU_RESERVED_PERCENT, "0.3");
HddsVolume hddsVolume = volumeBuilder.conf(conf).build();
//Reserving
float percentage = conf.getFloat(HDDS_DATANODE_DIR_DU_RESERVED_PERCENT,
HDDS_DATANODE_DIR_DU_RESERVED_PERCENT_DEFAULT);

long volumeCapacity = hddsVolume.getCapacity();
//Gets the actual total capacity
long totalCapacity = hddsVolume.getVolumeInfo()
.getUsageForTesting().getCapacity();
long reservedCapacity = hddsVolume.getVolumeInfo().getReservedInBytes();
//Volume Capacity with Reserved
long volumeCapacityReserved = totalCapacity - reservedCapacity;

long reservedFromVolume = hddsVolume.getVolumeInfo().getReservedInBytes();
long reservedCalculated = (long) Math.ceil(totalCapacity * percentage);

Assert.assertEquals(reservedFromVolume, reservedCalculated);
Assert.assertEquals(volumeCapacity, volumeCapacityReserved);
}

/**
* When both configs are set, hdds.datanode.dir.du.reserved is set
* if the volume matches with volume in config parameter.
* @throws Exception
*/
@Test
public void testReservedWhenBothConfigSet() throws Exception {
OzoneConfiguration conf = new OzoneConfiguration();
conf.set(HDDS_DATANODE_DIR_DU_RESERVED_PERCENT, "0.3");
conf.set(ScmConfigKeys.HDDS_DATANODE_DIR_DU_RESERVED,
folder.getRoot() + ":500B");
HddsVolume hddsVolume = volumeBuilder.conf(conf).build();

long reservedFromVolume = hddsVolume.getVolumeInfo().getReservedInBytes();
Assert.assertEquals(reservedFromVolume, 500);
}

@Test
public void testReservedToZeroWhenBothConfigNotSet() throws Exception {
OzoneConfiguration conf = new OzoneConfiguration();
HddsVolume hddsVolume = volumeBuilder.conf(conf).build();

long reservedFromVolume = hddsVolume.getVolumeInfo().getReservedInBytes();
Assert.assertEquals(reservedFromVolume, 0);
}

@Test
public void testFallbackToPercentConfig() throws Exception {
OzoneConfiguration conf = new OzoneConfiguration();
conf.set(HDDS_DATANODE_DIR_DU_RESERVED_PERCENT, "0.3");
//Setting config for different volume, hence fallback happens
conf.set(ScmConfigKeys.HDDS_DATANODE_DIR_DU_RESERVED,
temp.getRoot() + ":500B");
HddsVolume hddsVolume = volumeBuilder.conf(conf).build();

long reservedFromVolume = hddsVolume.getVolumeInfo().getReservedInBytes();
Assert.assertNotEquals(reservedFromVolume, 0);

long totalCapacity = hddsVolume.getVolumeInfo()
.getUsageForTesting().getCapacity();
float percentage = conf.getFloat(HDDS_DATANODE_DIR_DU_RESERVED_PERCENT,
HDDS_DATANODE_DIR_DU_RESERVED_PERCENT_DEFAULT);
long reservedCalculated = (long) Math.ceil(totalCapacity * percentage);
Assert.assertEquals(reservedFromVolume, reservedCalculated);
}

@Test
public void testInvalidConfig() throws Exception {
OzoneConfiguration conf1 = new OzoneConfiguration();

// 500C doesn't match with any Storage Unit
conf1.set(ScmConfigKeys.HDDS_DATANODE_DIR_DU_RESERVED,
folder.getRoot() + ":500C");
HddsVolume hddsVolume1 = volumeBuilder.conf(conf1).build();

long reservedFromVolume1 = hddsVolume1.getVolumeInfo().getReservedInBytes();
Assert.assertEquals(reservedFromVolume1, 0);

OzoneConfiguration conf2 = new OzoneConfiguration();

//Should be between 0-1.
conf2.set(HDDS_DATANODE_DIR_DU_RESERVED_PERCENT, "20");
HddsVolume hddsVolume2 = volumeBuilder.conf(conf2).build();

long reservedFromVolume2 = hddsVolume2.getVolumeInfo().getReservedInBytes();
Assert.assertEquals(reservedFromVolume2, 0);
}
}