/** * 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.datanode.fsdataset; import java.io.IOException; import java.util.List; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.hadoop.fs.StorageType; import org.apache.hadoop.util.DiskChecker.DiskOutOfSpaceException; /** * Choose volumes with the same storage type in round-robin order. * Use fine-grained locks to synchronize volume choosing. */ public class RoundRobinVolumeChoosingPolicy<V extends FsVolumeSpi> implements VolumeChoosingPolicy<V> { public static final Log LOG = LogFactory.getLog(RoundRobinVolumeChoosingPolicy.class); // curVolumes stores the RR counters of each storage type. // The ordinal of storage type in org.apache.hadoop.fs.StorageType // is used as the index to get data from the array. private int[] curVolumes; // syncLocks stores the locks for each storage type. private Object[] syncLocks; public RoundRobinVolumeChoosingPolicy() { int numStorageTypes = StorageType.values().length; curVolumes = new int[numStorageTypes]; syncLocks = new Object[numStorageTypes]; for (int i = 0; i < numStorageTypes; i++) { syncLocks[i] = new Object(); } } @Override public V chooseVolume(final List<V> volumes, long blockSize) throws IOException { if (volumes.size() < 1) { throw new DiskOutOfSpaceException("No more available volumes"); } // As all the items in volumes are with the same storage type, // so only need to get the storage type index of the first item in volumes StorageType storageType = volumes.get(0).getStorageType(); int index = storageType != null ? storageType.ordinal() : StorageType.DEFAULT.ordinal(); synchronized (syncLocks[index]) { return chooseVolume(index, volumes, blockSize); } } private V chooseVolume(final int curVolumeIndex, final List<V> volumes, long blockSize) throws IOException { // since volumes could've been removed because of the failure // make sure we are not out of bounds int curVolume = curVolumes[curVolumeIndex] < volumes.size() ? curVolumes[curVolumeIndex] : 0; int startVolume = curVolume; long maxAvailable = 0; while (true) { final V volume = volumes.get(curVolume); curVolume = (curVolume + 1) % volumes.size(); long availableVolumeSize = volume.getAvailable(); if (availableVolumeSize > blockSize) { curVolumes[curVolumeIndex] = curVolume; return volume; } if (availableVolumeSize > maxAvailable) { maxAvailable = availableVolumeSize; } if (curVolume == startVolume) { throw new DiskOutOfSpaceException("Out of space: " + "The volume with the most available space (=" + maxAvailable + " B) is less than the block size (=" + blockSize + " B)."); } } } }