|
@@ -19,10 +19,13 @@ package org.apache.hadoop.hdfs.server.datanode.fsdataset.impl;
|
|
|
|
|
|
import java.io.IOException;
|
|
|
import java.util.ArrayList;
|
|
|
+import java.util.Arrays;
|
|
|
import java.util.Collections;
|
|
|
import java.util.Iterator;
|
|
|
import java.util.List;
|
|
|
+import java.util.concurrent.atomic.AtomicReference;
|
|
|
|
|
|
+import com.google.common.collect.Lists;
|
|
|
import org.apache.hadoop.conf.Configuration;
|
|
|
import org.apache.hadoop.hdfs.StorageType;
|
|
|
import org.apache.hadoop.hdfs.server.datanode.fsdataset.FsVolumeSpi;
|
|
@@ -31,11 +34,8 @@ import org.apache.hadoop.util.DiskChecker.DiskErrorException;
|
|
|
import org.apache.hadoop.util.Time;
|
|
|
|
|
|
class FsVolumeList {
|
|
|
- /**
|
|
|
- * Read access to this unmodifiable list is not synchronized.
|
|
|
- * This list is replaced on modification holding "this" lock.
|
|
|
- */
|
|
|
- volatile List<FsVolumeImpl> volumes = null;
|
|
|
+ private final AtomicReference<FsVolumeImpl[]> volumes =
|
|
|
+ new AtomicReference<>(new FsVolumeImpl[0]);
|
|
|
private Object checkDirsMutex = new Object();
|
|
|
|
|
|
private final VolumeChoosingPolicy<FsVolumeImpl> blockChooser;
|
|
@@ -50,19 +50,27 @@ class FsVolumeList {
|
|
|
int numberOfFailedVolumes() {
|
|
|
return numFailedVolumes;
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Return an immutable list view of all the volumes.
|
|
|
+ */
|
|
|
+ List<FsVolumeImpl> getVolumes() {
|
|
|
+ return Collections.unmodifiableList(Arrays.asList(volumes.get()));
|
|
|
+ }
|
|
|
+
|
|
|
/**
|
|
|
- * Get next volume. Synchronized to ensure {@link #curVolume} is updated
|
|
|
- * by a single thread and next volume is chosen with no concurrent
|
|
|
- * update to {@link #volumes}.
|
|
|
+ * Get next volume.
|
|
|
+ *
|
|
|
* @param blockSize free space needed on the volume
|
|
|
* @param storageType the desired {@link StorageType}
|
|
|
* @return next volume to store the block in.
|
|
|
*/
|
|
|
- synchronized FsVolumeImpl getNextVolume(StorageType storageType,
|
|
|
- long blockSize) throws IOException {
|
|
|
- final List<FsVolumeImpl> list = new ArrayList<FsVolumeImpl>(volumes.size());
|
|
|
- for(FsVolumeImpl v : volumes) {
|
|
|
+ FsVolumeImpl getNextVolume(StorageType storageType, long blockSize)
|
|
|
+ throws IOException {
|
|
|
+ // Get a snapshot of currently available volumes.
|
|
|
+ final FsVolumeImpl[] curVolumes = volumes.get();
|
|
|
+ final List<FsVolumeImpl> list = new ArrayList<>(curVolumes.length);
|
|
|
+ for(FsVolumeImpl v : curVolumes) {
|
|
|
if (v.getStorageType() == storageType) {
|
|
|
list.add(v);
|
|
|
}
|
|
@@ -71,16 +79,16 @@ class FsVolumeList {
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * Get next volume. Synchronized to ensure {@link #curVolume} is updated
|
|
|
- * by a single thread and next volume is chosen with no concurrent
|
|
|
- * update to {@link #volumes}.
|
|
|
+ * Get next volume.
|
|
|
+ *
|
|
|
* @param blockSize free space needed on the volume
|
|
|
* @return next volume to store the block in.
|
|
|
*/
|
|
|
- synchronized FsVolumeImpl getNextTransientVolume(
|
|
|
- long blockSize) throws IOException {
|
|
|
- final List<FsVolumeImpl> list = new ArrayList<FsVolumeImpl>(volumes.size());
|
|
|
- for(FsVolumeImpl v : volumes) {
|
|
|
+ FsVolumeImpl getNextTransientVolume(long blockSize) throws IOException {
|
|
|
+ // Get a snapshot of currently available volumes.
|
|
|
+ final List<FsVolumeImpl> curVolumes = getVolumes();
|
|
|
+ final List<FsVolumeImpl> list = new ArrayList<>(curVolumes.size());
|
|
|
+ for(FsVolumeImpl v : curVolumes) {
|
|
|
if (v.isTransientStorage()) {
|
|
|
list.add(v);
|
|
|
}
|
|
@@ -90,7 +98,7 @@ class FsVolumeList {
|
|
|
|
|
|
long getDfsUsed() throws IOException {
|
|
|
long dfsUsed = 0L;
|
|
|
- for (FsVolumeImpl v : volumes) {
|
|
|
+ for (FsVolumeImpl v : volumes.get()) {
|
|
|
dfsUsed += v.getDfsUsed();
|
|
|
}
|
|
|
return dfsUsed;
|
|
@@ -98,7 +106,7 @@ class FsVolumeList {
|
|
|
|
|
|
long getBlockPoolUsed(String bpid) throws IOException {
|
|
|
long dfsUsed = 0L;
|
|
|
- for (FsVolumeImpl v : volumes) {
|
|
|
+ for (FsVolumeImpl v : volumes.get()) {
|
|
|
dfsUsed += v.getBlockPoolUsed(bpid);
|
|
|
}
|
|
|
return dfsUsed;
|
|
@@ -106,7 +114,7 @@ class FsVolumeList {
|
|
|
|
|
|
long getCapacity() {
|
|
|
long capacity = 0L;
|
|
|
- for (FsVolumeImpl v : volumes) {
|
|
|
+ for (FsVolumeImpl v : volumes.get()) {
|
|
|
capacity += v.getCapacity();
|
|
|
}
|
|
|
return capacity;
|
|
@@ -114,7 +122,7 @@ class FsVolumeList {
|
|
|
|
|
|
long getRemaining() throws IOException {
|
|
|
long remaining = 0L;
|
|
|
- for (FsVolumeSpi vol : volumes) {
|
|
|
+ for (FsVolumeSpi vol : volumes.get()) {
|
|
|
remaining += vol.getAvailable();
|
|
|
}
|
|
|
return remaining;
|
|
@@ -128,7 +136,7 @@ class FsVolumeList {
|
|
|
final List<IOException> exceptions = Collections.synchronizedList(
|
|
|
new ArrayList<IOException>());
|
|
|
List<Thread> replicaAddingThreads = new ArrayList<Thread>();
|
|
|
- for (final FsVolumeImpl v : volumes) {
|
|
|
+ for (final FsVolumeImpl v : volumes.get()) {
|
|
|
Thread t = new Thread() {
|
|
|
public void run() {
|
|
|
try {
|
|
@@ -177,7 +185,7 @@ class FsVolumeList {
|
|
|
ArrayList<FsVolumeImpl> removedVols = null;
|
|
|
|
|
|
// Make a copy of volumes for performing modification
|
|
|
- final List<FsVolumeImpl> volumeList = new ArrayList<FsVolumeImpl>(volumes);
|
|
|
+ final List<FsVolumeImpl> volumeList = getVolumes();
|
|
|
|
|
|
for(Iterator<FsVolumeImpl> i = volumeList.iterator(); i.hasNext(); ) {
|
|
|
final FsVolumeImpl fsv = i.next();
|
|
@@ -189,7 +197,7 @@ class FsVolumeList {
|
|
|
removedVols = new ArrayList<FsVolumeImpl>(1);
|
|
|
}
|
|
|
removedVols.add(fsv);
|
|
|
- removeVolume(fsv.getBasePath());
|
|
|
+ removeVolume(fsv);
|
|
|
numFailedVolumes++;
|
|
|
}
|
|
|
}
|
|
@@ -212,31 +220,71 @@ class FsVolumeList {
|
|
|
* Dynamically add new volumes to the existing volumes that this DN manages.
|
|
|
* @param newVolume the instance of new FsVolumeImpl.
|
|
|
*/
|
|
|
- synchronized void addVolume(FsVolumeImpl newVolume) {
|
|
|
+ void addVolume(FsVolumeImpl newVolume) {
|
|
|
// Make a copy of volumes to add new volumes.
|
|
|
- final List<FsVolumeImpl> volumeList = volumes == null ?
|
|
|
- new ArrayList<FsVolumeImpl>() :
|
|
|
- new ArrayList<FsVolumeImpl>(volumes);
|
|
|
- volumeList.add(newVolume);
|
|
|
- volumes = Collections.unmodifiableList(volumeList);
|
|
|
+ while (true) {
|
|
|
+ final FsVolumeImpl[] curVolumes = volumes.get();
|
|
|
+ final List<FsVolumeImpl> volumeList = Lists.newArrayList(curVolumes);
|
|
|
+ volumeList.add(newVolume);
|
|
|
+ if (volumes.compareAndSet(curVolumes,
|
|
|
+ volumeList.toArray(new FsVolumeImpl[volumeList.size()]))) {
|
|
|
+ break;
|
|
|
+ } else {
|
|
|
+ if (FsDatasetImpl.LOG.isDebugEnabled()) {
|
|
|
+ FsDatasetImpl.LOG.debug(
|
|
|
+ "The volume list has been changed concurrently, " +
|
|
|
+ "retry to remove volume: " + newVolume);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
FsDatasetImpl.LOG.info("Added new volume: " + newVolume.toString());
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * Dynamically remove volume to the list.
|
|
|
+ * Dynamically remove a volume in the list.
|
|
|
+ * @param target the volume instance to be removed.
|
|
|
+ */
|
|
|
+ private void removeVolume(FsVolumeImpl target) {
|
|
|
+ while (true) {
|
|
|
+ final FsVolumeImpl[] curVolumes = volumes.get();
|
|
|
+ final List<FsVolumeImpl> volumeList = Lists.newArrayList(curVolumes);
|
|
|
+ if (volumeList.remove(target)) {
|
|
|
+ if (volumes.compareAndSet(curVolumes,
|
|
|
+ volumeList.toArray(new FsVolumeImpl[volumeList.size()]))) {
|
|
|
+ target.shutdown();
|
|
|
+ FsDatasetImpl.LOG.info("Removed volume: " + target);
|
|
|
+ break;
|
|
|
+ } else {
|
|
|
+ if (FsDatasetImpl.LOG.isDebugEnabled()) {
|
|
|
+ FsDatasetImpl.LOG.debug(
|
|
|
+ "The volume list has been changed concurrently, " +
|
|
|
+ "retry to remove volume: " + target);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ if (FsDatasetImpl.LOG.isDebugEnabled()) {
|
|
|
+ FsDatasetImpl.LOG.debug("Volume " + target +
|
|
|
+ " does not exist or is removed by others.");
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Dynamically remove volume in the list.
|
|
|
* @param volume the volume to be removed.
|
|
|
*/
|
|
|
- synchronized void removeVolume(String volume) {
|
|
|
+ void removeVolume(String volume) {
|
|
|
// Make a copy of volumes to remove one volume.
|
|
|
- final List<FsVolumeImpl> volumeList = new ArrayList<FsVolumeImpl>(volumes);
|
|
|
+ final FsVolumeImpl[] curVolumes = volumes.get();
|
|
|
+ final List<FsVolumeImpl> volumeList = Lists.newArrayList(curVolumes);
|
|
|
for (Iterator<FsVolumeImpl> it = volumeList.iterator(); it.hasNext(); ) {
|
|
|
FsVolumeImpl fsVolume = it.next();
|
|
|
if (fsVolume.getBasePath().equals(volume)) {
|
|
|
- fsVolume.shutdown();
|
|
|
- it.remove();
|
|
|
- volumes = Collections.unmodifiableList(volumeList);
|
|
|
- FsDatasetImpl.LOG.info("Removed volume: " + volume);
|
|
|
- break;
|
|
|
+ // Make sure the removed volume is the one in the curVolumes.
|
|
|
+ removeVolume(fsVolume);
|
|
|
}
|
|
|
}
|
|
|
}
|
|
@@ -247,7 +295,7 @@ class FsVolumeList {
|
|
|
final List<IOException> exceptions = Collections.synchronizedList(
|
|
|
new ArrayList<IOException>());
|
|
|
List<Thread> blockPoolAddingThreads = new ArrayList<Thread>();
|
|
|
- for (final FsVolumeImpl v : volumes) {
|
|
|
+ for (final FsVolumeImpl v : volumes.get()) {
|
|
|
Thread t = new Thread() {
|
|
|
public void run() {
|
|
|
try {
|
|
@@ -285,13 +333,13 @@ class FsVolumeList {
|
|
|
}
|
|
|
|
|
|
void removeBlockPool(String bpid) {
|
|
|
- for (FsVolumeImpl v : volumes) {
|
|
|
+ for (FsVolumeImpl v : volumes.get()) {
|
|
|
v.shutdownBlockPool(bpid);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
void shutdown() {
|
|
|
- for (FsVolumeImpl volume : volumes) {
|
|
|
+ for (FsVolumeImpl volume : volumes.get()) {
|
|
|
if(volume != null) {
|
|
|
volume.shutdown();
|
|
|
}
|