This is an automated email from the ASF dual-hosted git repository. devmadhuu pushed a commit to branch HDDS-11233 in repository https://gitbox.apache.org/repos/asf/ozone.git
commit 014710e72afbc0ce1eb01a03e660ddb90272cf32 Author: XiChen <[email protected]> AuthorDate: Sat May 23 14:27:03 2026 +0800 HDDS-15240. Support selecting volumes by StorageType when creating containers (#10303) --- .../container/common/interfaces/Container.java | 11 +++ .../common/interfaces/VolumeChoosingPolicy.java | 17 ++++ .../volume/AbstractStorageTypeChoosingPolicy.java | 53 ++++++++++++ .../volume/CapacityVolumeChoosingPolicy.java | 6 +- .../volume/RoundRobinVolumeChoosingPolicy.java | 6 +- .../container/keyvalue/KeyValueContainer.java | 16 +++- .../TestStorageTypeVolumeChoosingPolicy.java | 94 ++++++++++++++++++++++ .../container/keyvalue/TestKeyValueContainer.java | 29 +++++++ 8 files changed, 225 insertions(+), 7 deletions(-) diff --git a/hadoop-hdds/container-service/src/main/java/org/apache/hadoop/ozone/container/common/interfaces/Container.java b/hadoop-hdds/container-service/src/main/java/org/apache/hadoop/ozone/container/common/interfaces/Container.java index d7f71e6e3b7..ddb7de4140b 100644 --- a/hadoop-hdds/container-service/src/main/java/org/apache/hadoop/ozone/container/common/interfaces/Container.java +++ b/hadoop-hdds/container-service/src/main/java/org/apache/hadoop/ozone/container/common/interfaces/Container.java @@ -24,6 +24,7 @@ import java.nio.file.Path; import java.time.Instant; import java.util.Map; +import org.apache.hadoop.fs.StorageType; import org.apache.hadoop.hdds.protocol.datanode.proto.ContainerProtos; import org.apache.hadoop.hdds.protocol.proto.StorageContainerDatanodeProtocolProtos.ContainerReplicaProto; import org.apache.hadoop.hdds.scm.container.common.helpers.StorageContainerException; @@ -47,6 +48,16 @@ public interface Container<CONTAINERDATA extends ContainerData> { void create(VolumeSet volumeSet, VolumeChoosingPolicy volumeChoosingPolicy, String scmId) throws StorageContainerException; + /** + * Creates a container and optionally constrains the destination volume to a + * specific storage type. + */ + default void create(VolumeSet volumeSet, + VolumeChoosingPolicy volumeChoosingPolicy, String scmId, + StorageType storageType) throws StorageContainerException { + create(volumeSet, volumeChoosingPolicy, scmId); + } + /** * Deletes the container. * diff --git a/hadoop-hdds/container-service/src/main/java/org/apache/hadoop/ozone/container/common/interfaces/VolumeChoosingPolicy.java b/hadoop-hdds/container-service/src/main/java/org/apache/hadoop/ozone/container/common/interfaces/VolumeChoosingPolicy.java index 72d05d38739..63d9dccc74b 100644 --- a/hadoop-hdds/container-service/src/main/java/org/apache/hadoop/ozone/container/common/interfaces/VolumeChoosingPolicy.java +++ b/hadoop-hdds/container-service/src/main/java/org/apache/hadoop/ozone/container/common/interfaces/VolumeChoosingPolicy.java @@ -19,6 +19,7 @@ import java.io.IOException; import java.util.List; +import org.apache.hadoop.fs.StorageType; import org.apache.hadoop.hdds.annotation.InterfaceAudience; import org.apache.hadoop.ozone.container.common.volume.HddsVolume; @@ -40,6 +41,22 @@ public interface VolumeChoosingPolicy { * @return the chosen volume. * @throws IOException when disks are unavailable or are full. */ + @Deprecated HddsVolume chooseVolume(List<HddsVolume> volumes, long maxContainerSize) throws IOException; + + /** + * Choose a volume to place a container, optionally constraining the choice + * to a specific storage type. + * + * @param volumes a list of available volumes. + * @param maxContainerSize the maximum size of the container for which a + * volume is sought. + * @param storageType the requested storage type, or {@code null} to allow + * the policy to choose from any volume. + * @return the chosen volume. + * @throws IOException when disks are unavailable or are full. + */ + HddsVolume chooseVolume(List<HddsVolume> volumes, + long maxContainerSize, StorageType storageType) throws IOException; } diff --git a/hadoop-hdds/container-service/src/main/java/org/apache/hadoop/ozone/container/common/volume/AbstractStorageTypeChoosingPolicy.java b/hadoop-hdds/container-service/src/main/java/org/apache/hadoop/ozone/container/common/volume/AbstractStorageTypeChoosingPolicy.java new file mode 100644 index 00000000000..d73ab885e5f --- /dev/null +++ b/hadoop-hdds/container-service/src/main/java/org/apache/hadoop/ozone/container/common/volume/AbstractStorageTypeChoosingPolicy.java @@ -0,0 +1,53 @@ +/* + * 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 java.io.IOException; +import java.util.List; +import java.util.stream.Collectors; +import org.apache.hadoop.fs.StorageType; +import org.apache.hadoop.ozone.container.common.interfaces.VolumeChoosingPolicy; + +/** + * Base volume choosing policy with optional storage-type filtering. + */ +public abstract class AbstractStorageTypeChoosingPolicy + implements VolumeChoosingPolicy { + + @Override + @Deprecated + public HddsVolume chooseVolume(List<HddsVolume> volumes, + long maxContainerSize) throws IOException { + return chooseVolume(volumes, maxContainerSize, null); + } + + @Override + public HddsVolume chooseVolume(List<HddsVolume> volumes, + long maxContainerSize, StorageType storageType) throws IOException { + List<HddsVolume> candidates = volumes; + if (storageType != null) { + candidates = volumes.stream() + .filter(volume -> volume.getStorageType() == storageType) + .collect(Collectors.toList()); + } + return chooseVolumeInternal(candidates, maxContainerSize); + } + + protected abstract HddsVolume chooseVolumeInternal(List<HddsVolume> volumes, + long maxContainerSize) throws IOException; +} diff --git a/hadoop-hdds/container-service/src/main/java/org/apache/hadoop/ozone/container/common/volume/CapacityVolumeChoosingPolicy.java b/hadoop-hdds/container-service/src/main/java/org/apache/hadoop/ozone/container/common/volume/CapacityVolumeChoosingPolicy.java index 325b98260bc..513e3482934 100644 --- a/hadoop-hdds/container-service/src/main/java/org/apache/hadoop/ozone/container/common/volume/CapacityVolumeChoosingPolicy.java +++ b/hadoop-hdds/container-service/src/main/java/org/apache/hadoop/ozone/container/common/volume/CapacityVolumeChoosingPolicy.java @@ -27,7 +27,6 @@ import java.util.concurrent.locks.ReentrantLock; import java.util.stream.Collectors; import org.apache.hadoop.hdds.fs.SpaceUsageSource; -import org.apache.hadoop.ozone.container.common.interfaces.VolumeChoosingPolicy; import org.apache.hadoop.util.DiskChecker.DiskOutOfSpaceException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -42,7 +41,8 @@ * <p> * Same algorithm as the SCMContainerPlacementCapacity. */ -public class CapacityVolumeChoosingPolicy implements VolumeChoosingPolicy { +public class CapacityVolumeChoosingPolicy + extends AbstractStorageTypeChoosingPolicy { private static final Logger LOG = LoggerFactory.getLogger( CapacityVolumeChoosingPolicy.class); @@ -59,7 +59,7 @@ public CapacityVolumeChoosingPolicy() { } @Override - public HddsVolume chooseVolume(List<HddsVolume> volumes, + protected HddsVolume chooseVolumeInternal(List<HddsVolume> volumes, long maxContainerSize) throws IOException { // No volumes available to choose from diff --git a/hadoop-hdds/container-service/src/main/java/org/apache/hadoop/ozone/container/common/volume/RoundRobinVolumeChoosingPolicy.java b/hadoop-hdds/container-service/src/main/java/org/apache/hadoop/ozone/container/common/volume/RoundRobinVolumeChoosingPolicy.java index ff6f5713001..2318b7d7311 100644 --- a/hadoop-hdds/container-service/src/main/java/org/apache/hadoop/ozone/container/common/volume/RoundRobinVolumeChoosingPolicy.java +++ b/hadoop-hdds/container-service/src/main/java/org/apache/hadoop/ozone/container/common/volume/RoundRobinVolumeChoosingPolicy.java @@ -24,7 +24,6 @@ import java.io.IOException; import java.util.List; import java.util.concurrent.locks.ReentrantLock; -import org.apache.hadoop.ozone.container.common.interfaces.VolumeChoosingPolicy; import org.apache.hadoop.util.DiskChecker.DiskOutOfSpaceException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -33,7 +32,8 @@ * Choose volumes in round-robin order. * The caller should synchronize access to the list of volumes. */ -public class RoundRobinVolumeChoosingPolicy implements VolumeChoosingPolicy { +public class RoundRobinVolumeChoosingPolicy + extends AbstractStorageTypeChoosingPolicy { private static final Logger LOG = LoggerFactory.getLogger( RoundRobinVolumeChoosingPolicy.class); @@ -53,7 +53,7 @@ public RoundRobinVolumeChoosingPolicy() { } @Override - public HddsVolume chooseVolume(List<HddsVolume> volumes, + protected HddsVolume chooseVolumeInternal(List<HddsVolume> volumes, long maxContainerSize) throws IOException { // No volumes available to choose from diff --git a/hadoop-hdds/container-service/src/main/java/org/apache/hadoop/ozone/container/keyvalue/KeyValueContainer.java b/hadoop-hdds/container-service/src/main/java/org/apache/hadoop/ozone/container/keyvalue/KeyValueContainer.java index 06a73b33373..460c957e5c5 100644 --- a/hadoop-hdds/container-service/src/main/java/org/apache/hadoop/ozone/container/keyvalue/KeyValueContainer.java +++ b/hadoop-hdds/container-service/src/main/java/org/apache/hadoop/ozone/container/keyvalue/KeyValueContainer.java @@ -56,6 +56,7 @@ import org.apache.commons.io.FileUtils; import org.apache.hadoop.fs.FileAlreadyExistsException; import org.apache.hadoop.fs.FileUtil; +import org.apache.hadoop.fs.StorageType; import org.apache.hadoop.hdds.HddsUtils; import org.apache.hadoop.hdds.conf.ConfigurationSource; import org.apache.hadoop.hdds.protocol.datanode.proto.ContainerProtos; @@ -146,8 +147,16 @@ public void setCheckChunksFilePath(boolean bCheckChunksDirFilePath) { } @Override + @Deprecated public void create(VolumeSet volumeSet, VolumeChoosingPolicy volumeChoosingPolicy, String clusterId) throws StorageContainerException { + create(volumeSet, volumeChoosingPolicy, clusterId, null); + } + + @Override + public void create(VolumeSet volumeSet, VolumeChoosingPolicy + volumeChoosingPolicy, String clusterId, StorageType storageType) + throws StorageContainerException { Objects.requireNonNull(volumeChoosingPolicy, "VolumeChoosingPolicy == null"); Objects.requireNonNull(volumeSet, "volumeSet == null"); Objects.requireNonNull(clusterId, "clusterId == null"); @@ -163,11 +172,16 @@ public void create(VolumeSet volumeSet, VolumeChoosingPolicy HddsVolume containerVolume; String hddsVolumeDir; try { - containerVolume = volumeChoosingPolicy.chooseVolume(volumes, maxSize); + // TODO Use the `chooseVolume` that supports `storageType` + containerVolume = storageType == null + ? volumeChoosingPolicy.chooseVolume(volumes, maxSize) + : volumeChoosingPolicy.chooseVolume( + volumes, maxSize, storageType); hddsVolumeDir = containerVolume.getHddsRootDir().toString(); // Set volume before getContainerDBFile(), because we may need the // volume to deduce the db file. containerData.setVolume(containerVolume); + containerData.setStorageType(containerVolume.getStorageType()); // commit bytes have been reserved in volumeChoosingPolicy#chooseVolume containerData.setCommittedSpace(true); } catch (DiskOutOfSpaceException ex) { diff --git a/hadoop-hdds/container-service/src/test/java/org/apache/hadoop/ozone/container/common/volume/TestStorageTypeVolumeChoosingPolicy.java b/hadoop-hdds/container-service/src/test/java/org/apache/hadoop/ozone/container/common/volume/TestStorageTypeVolumeChoosingPolicy.java new file mode 100644 index 00000000000..72a58e48f97 --- /dev/null +++ b/hadoop-hdds/container-service/src/test/java/org/apache/hadoop/ozone/container/common/volume/TestStorageTypeVolumeChoosingPolicy.java @@ -0,0 +1,94 @@ +/* + * 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 static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import java.io.File; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.UUID; +import org.apache.hadoop.fs.StorageType; +import org.apache.hadoop.hdds.conf.OzoneConfiguration; +import org.apache.hadoop.ozone.container.common.utils.StorageVolumeUtil; +import org.apache.hadoop.util.DiskChecker.DiskOutOfSpaceException; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; + +/** + * Tests for storage-type-aware volume selection policies. + */ +public class TestStorageTypeVolumeChoosingPolicy { + + private static final OzoneConfiguration CONF = new OzoneConfiguration(); + + @TempDir + private File folder; + + private final String scmId = UUID.randomUUID().toString(); + private final String datanodeId = UUID.randomUUID().toString(); + private final List<HddsVolume> createdVolumes = new ArrayList<>(); + + @AfterEach + public void tearDown() { + createdVolumes.forEach(HddsVolume::shutdown); + } + + @Test + public void testRoundRobinChoosesRequestedStorageType() throws Exception { + HddsVolume diskVolume = createVolume("disk", StorageType.DISK); + HddsVolume ssdVolume = createVolume("ssd", StorageType.SSD); + + RoundRobinVolumeChoosingPolicy policy = new RoundRobinVolumeChoosingPolicy(); + HddsVolume chosen = policy.chooseVolume( + Arrays.asList(diskVolume, ssdVolume), 1L, StorageType.SSD); + + assertSame(ssdVolume, chosen); + assertEquals(1L, ssdVolume.getCommittedBytes()); + assertEquals(0L, diskVolume.getCommittedBytes()); + } + + @Test + public void testCapacityPolicyRejectsMissingStorageType() throws Exception { + HddsVolume diskVolume = createVolume("disk", StorageType.DISK); + + CapacityVolumeChoosingPolicy policy = new CapacityVolumeChoosingPolicy(); + + assertThrows(DiskOutOfSpaceException.class, + () -> policy.chooseVolume( + Collections.singletonList(diskVolume), 1L, StorageType.SSD)); + } + + private HddsVolume createVolume(String name, StorageType storageType) + throws Exception { + HddsVolume volume = new HddsVolume.Builder( + new File(folder, name).getAbsolutePath()) + .conf(CONF) + .datanodeUuid(datanodeId) + .storageType(storageType) + .build(); + StorageVolumeUtil.checkVolume(volume, scmId, scmId, CONF, null, null); + createdVolumes.add(volume); + return volume; + } +} diff --git a/hadoop-hdds/container-service/src/test/java/org/apache/hadoop/ozone/container/keyvalue/TestKeyValueContainer.java b/hadoop-hdds/container-service/src/test/java/org/apache/hadoop/ozone/container/keyvalue/TestKeyValueContainer.java index 134770befc0..23018153756 100644 --- a/hadoop-hdds/container-service/src/test/java/org/apache/hadoop/ozone/container/keyvalue/TestKeyValueContainer.java +++ b/hadoop-hdds/container-service/src/test/java/org/apache/hadoop/ozone/container/keyvalue/TestKeyValueContainer.java @@ -33,12 +33,14 @@ import static org.junit.jupiter.api.Assertions.assertNotEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertSame; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; import static org.junit.jupiter.api.Assumptions.assumeTrue; import static org.mockito.Mockito.anyList; import static org.mockito.Mockito.anyLong; +import static org.mockito.Mockito.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.reset; import static org.mockito.Mockito.spy; @@ -65,6 +67,7 @@ import java.util.stream.Stream; import org.apache.commons.io.FileUtils; import org.apache.hadoop.conf.StorageUnit; +import org.apache.hadoop.fs.StorageType; import org.apache.hadoop.hdds.client.BlockID; import org.apache.hadoop.hdds.conf.ConfigurationSource; import org.apache.hadoop.hdds.conf.OzoneConfiguration; @@ -204,6 +207,32 @@ private void testCreateContainer() throws StorageContainerException { "DB does not exist"); } + @ContainerTestVersionInfo.ContainerTest + public void testCreateContainerWithStorageType( + ContainerTestVersionInfo versionInfo) throws Exception { + init(versionInfo); + + HddsVolume ssdVolume = new HddsVolume.Builder( + new File(folder, "ssd-volume").getAbsolutePath()) + .conf(CONF) + .datanodeUuid(datanodeId.toString()) + .storageType(StorageType.SSD) + .build(); + StorageVolumeUtil.checkVolume(ssdVolume, scmId, scmId, CONF, null, null); + hddsVolumes.add(ssdVolume); + + when(volumeChoosingPolicy.chooseVolume(anyList(), anyLong(), + eq(StorageType.SSD))).thenReturn(ssdVolume); + + keyValueContainer.create(volumeSet, volumeChoosingPolicy, scmId, + StorageType.SSD); + + assertSame(ssdVolume, keyValueContainerData.getVolume()); + assertEquals(StorageType.SSD, keyValueContainerData.getStorageType()); + verify(volumeChoosingPolicy).chooseVolume(anyList(), anyLong(), + eq(StorageType.SSD)); + } + /** * Tests repair of containers affected by the bug reported in HDDS-6235. */ --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
