mirror of https://github.com/apache/cassandra
Backport CASSANDRA-7386
This commit is contained in:
parent
41469ecf6a
commit
8b5cf64043
|
|
@ -25,6 +25,7 @@
|
|||
CQLSSTableWriter (CASSANDRA-7463)
|
||||
* Fix totalDiskSpaceUsed calculation (CASSANDRA-8205)
|
||||
* Add DC-aware sequential repair (CASSANDRA-8193)
|
||||
* Improve JBOD disk utilization (CASSANDRA-7386)
|
||||
|
||||
|
||||
2.0.11:
|
||||
|
|
|
|||
|
|
@ -1879,11 +1879,6 @@ public class ColumnFamilyStore implements ColumnFamilyStoreMBean
|
|||
Directories.clearSnapshot(snapshotName, snapshotDirs);
|
||||
}
|
||||
|
||||
public boolean hasUnreclaimedSpace()
|
||||
{
|
||||
return getLiveDiskSpaceUsed() < getTotalDiskSpaceUsed();
|
||||
}
|
||||
|
||||
public long getTotalDiskSpaceUsed()
|
||||
{
|
||||
return metric.totalDiskSpaceUsed.count();
|
||||
|
|
|
|||
|
|
@ -22,8 +22,7 @@ import java.io.FileFilter;
|
|||
import java.io.IOError;
|
||||
import java.io.IOException;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.concurrent.ThreadLocalRandom;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
|
|
@ -199,74 +198,114 @@ public class Directories
|
|||
* @return SSTable location
|
||||
*/
|
||||
public File getLocationForDisk(DataDirectory dataDirectory)
|
||||
{
|
||||
if (dataDirectory != null)
|
||||
for (File dir : sstableDirectories)
|
||||
if (dir.getAbsolutePath().startsWith(dataDirectory.location.getAbsolutePath()))
|
||||
return dir;
|
||||
return null;
|
||||
}
|
||||
|
||||
public Descriptor find(String filename)
|
||||
{
|
||||
for (File dir : sstableDirectories)
|
||||
{
|
||||
if (dir.getAbsolutePath().startsWith(dataDirectory.location.getAbsolutePath()))
|
||||
return dir;
|
||||
if (new File(dir, filename).exists())
|
||||
return Descriptor.fromFilename(dir, filename).left;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public File getDirectoryForNewSSTables()
|
||||
{
|
||||
File path = getWriteableLocationAsFile();
|
||||
|
||||
// Requesting GC has a chance to free space only if we're using mmap and a non SUN jvm
|
||||
if (path == null
|
||||
&& (DatabaseDescriptor.getDiskAccessMode() == Config.DiskAccessMode.mmap || DatabaseDescriptor.getIndexAccessMode() == Config.DiskAccessMode.mmap)
|
||||
&& !FileUtils.isCleanerAvailable())
|
||||
{
|
||||
logger.info("Forcing GC to free up disk space. Upgrade to the Oracle JVM to avoid this");
|
||||
StorageService.instance.requestGC();
|
||||
// retry after GCing has forced unmap of compacted SSTables so they can be deleted
|
||||
// Note: GCInspector will do this already, but only sun JVM supports GCInspector so far
|
||||
SSTableDeletingTask.rescheduleFailedTasks();
|
||||
Uninterruptibles.sleepUninterruptibly(10, TimeUnit.SECONDS);
|
||||
path = getWriteableLocationAsFile();
|
||||
}
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
public File getWriteableLocationAsFile()
|
||||
{
|
||||
return getLocationForDisk(getWriteableLocation());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return a non-blacklisted directory with the most free space and least current tasks.
|
||||
* Basically the same as calling {@link #getWriteableLocationAsFile(long)} with an unknown size ({@code -1L}),
|
||||
* which may return any non-blacklisted directory - even a data directory that has no usable space.
|
||||
* Do not use this method in production code.
|
||||
*
|
||||
* @throws IOError if all directories are blacklisted.
|
||||
*/
|
||||
public DataDirectory getWriteableLocation()
|
||||
public File getDirectoryForNewSSTables()
|
||||
{
|
||||
List<DataDirectory> candidates = new ArrayList<DataDirectory>();
|
||||
return getWriteableLocationAsFile(-1L);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a non-blacklisted data directory that _currently_ has {@code writeSize} bytes as usable space.
|
||||
*
|
||||
* @throws IOError if all directories are blacklisted.
|
||||
*/
|
||||
public File getWriteableLocationAsFile(long writeSize)
|
||||
{
|
||||
return getLocationForDisk(getWriteableLocation(writeSize));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a non-blacklisted data directory that _currently_ has {@code writeSize} bytes as usable space.
|
||||
*
|
||||
* @throws IOError if all directories are blacklisted.
|
||||
*/
|
||||
public DataDirectory getWriteableLocation(long writeSize)
|
||||
{
|
||||
List<DataDirectoryCandidate> candidates = new ArrayList<>();
|
||||
|
||||
long totalAvailable = 0L;
|
||||
|
||||
// pick directories with enough space and so that resulting sstable dirs aren't blacklisted for writes.
|
||||
boolean tooBig = false;
|
||||
for (DataDirectory dataDir : dataFileLocations)
|
||||
{
|
||||
if (BlacklistedDirectories.isUnwritable(getLocationForDisk(dataDir)))
|
||||
continue;
|
||||
candidates.add(dataDir);
|
||||
DataDirectoryCandidate candidate = new DataDirectoryCandidate(dataDir);
|
||||
// exclude directory if its total writeSize does not fit to data directory
|
||||
if (candidate.availableSpace < writeSize)
|
||||
{
|
||||
tooBig = true;
|
||||
continue;
|
||||
}
|
||||
candidates.add(candidate);
|
||||
totalAvailable += candidate.availableSpace;
|
||||
}
|
||||
|
||||
if (candidates.isEmpty())
|
||||
throw new IOError(new IOException("All configured data directories have been blacklisted as unwritable for erroring out"));
|
||||
if (tooBig)
|
||||
return null;
|
||||
else
|
||||
throw new IOError(new IOException("All configured data directories have been blacklisted as unwritable for erroring out"));
|
||||
|
||||
// sort directories by free space, in _descending_ order.
|
||||
Collections.sort(candidates);
|
||||
// shortcut for single data directory systems
|
||||
if (candidates.size() == 1)
|
||||
return candidates.get(0).dataDirectory;
|
||||
|
||||
// sort directories by load, in _ascending_ order.
|
||||
Collections.sort(candidates, new Comparator<DataDirectory>()
|
||||
sortWriteableCandidates(candidates, totalAvailable);
|
||||
|
||||
return pickWriteableDirectory(candidates);
|
||||
}
|
||||
|
||||
// separated for unit testing
|
||||
static DataDirectory pickWriteableDirectory(List<DataDirectoryCandidate> candidates)
|
||||
{
|
||||
// weighted random
|
||||
double rnd = ThreadLocalRandom.current().nextDouble();
|
||||
for (DataDirectoryCandidate candidate : candidates)
|
||||
{
|
||||
public int compare(DataDirectory a, DataDirectory b)
|
||||
{
|
||||
return a.currentTasks.get() - b.currentTasks.get();
|
||||
}
|
||||
});
|
||||
rnd -= candidate.perc;
|
||||
if (rnd <= 0)
|
||||
return candidate.dataDirectory;
|
||||
}
|
||||
|
||||
return candidates.get(0);
|
||||
// last resort
|
||||
return candidates.get(0).dataDirectory;
|
||||
}
|
||||
|
||||
// separated for unit testing
|
||||
static void sortWriteableCandidates(List<DataDirectoryCandidate> candidates, long totalAvailable)
|
||||
{
|
||||
// calculate free-space-percentage
|
||||
for (DataDirectoryCandidate candidate : candidates)
|
||||
candidate.calcFreePerc(totalAvailable);
|
||||
|
||||
// sort directories by perc
|
||||
Collections.sort(candidates);
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -285,31 +324,50 @@ public class Directories
|
|||
return new SSTableLister();
|
||||
}
|
||||
|
||||
public static class DataDirectory implements Comparable<DataDirectory>
|
||||
public static class DataDirectory
|
||||
{
|
||||
public final File location;
|
||||
public final AtomicInteger currentTasks = new AtomicInteger();
|
||||
public final AtomicLong estimatedWorkingSize = new AtomicLong();
|
||||
|
||||
public DataDirectory(File location)
|
||||
{
|
||||
this.location = location;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return estimated available disk space for bounded directory,
|
||||
* excluding the expected size written by tasks in the queue.
|
||||
*/
|
||||
public long getEstimatedAvailableSpace()
|
||||
public long getAvailableSpace()
|
||||
{
|
||||
// Load factor of 0.9 we do not want to use the entire disk that is too risky.
|
||||
return location.getUsableSpace() - estimatedWorkingSize.get();
|
||||
return location.getUsableSpace();
|
||||
}
|
||||
}
|
||||
|
||||
static final class DataDirectoryCandidate implements Comparable<DataDirectoryCandidate>
|
||||
{
|
||||
final DataDirectory dataDirectory;
|
||||
final long availableSpace;
|
||||
double perc;
|
||||
|
||||
public DataDirectoryCandidate(DataDirectory dataDirectory)
|
||||
{
|
||||
this.dataDirectory = dataDirectory;
|
||||
this.availableSpace = dataDirectory.getAvailableSpace();
|
||||
}
|
||||
|
||||
public int compareTo(DataDirectory o)
|
||||
void calcFreePerc(long totalAvailableSpace)
|
||||
{
|
||||
// we want to sort by free space in descending order
|
||||
return -1 * Longs.compare(getEstimatedAvailableSpace(), o.getEstimatedAvailableSpace());
|
||||
double w = availableSpace;
|
||||
w /= totalAvailableSpace;
|
||||
perc = w;
|
||||
}
|
||||
|
||||
public int compareTo(DataDirectoryCandidate o)
|
||||
{
|
||||
if (this == o)
|
||||
return 0;
|
||||
|
||||
int r = Double.compare(perc, o.perc);
|
||||
if (r != 0)
|
||||
return -r;
|
||||
// last resort
|
||||
return System.identityHashCode(this) - System.identityHashCode(o);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -342,17 +342,9 @@ public class Memtable
|
|||
Directories.DataDirectory dataDirectory = getWriteDirectory(writeSize);
|
||||
File sstableDirectory = cfs.directories.getLocationForDisk(dataDirectory);
|
||||
assert sstableDirectory != null : "Flush task is not bound to any disk";
|
||||
try
|
||||
{
|
||||
SSTableReader sstable = writeSortedContents(context, sstableDirectory);
|
||||
cfs.replaceFlushed(Memtable.this, sstable);
|
||||
latch.countDown();
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (dataDirectory != null)
|
||||
returnWriteDirectory(dataDirectory, writeSize);
|
||||
}
|
||||
SSTableReader sstable = writeSortedContents(context, sstableDirectory);
|
||||
cfs.replaceFlushed(Memtable.this, sstable);
|
||||
latch.countDown();
|
||||
}
|
||||
|
||||
protected Directories getDirectories()
|
||||
|
|
|
|||
|
|
@ -555,8 +555,7 @@ public class CompactionManager implements CompactionManagerMBean
|
|||
logger.debug("Expected bloom filter size : " + expectedBloomFilterSize);
|
||||
|
||||
logger.info("Cleaning up " + sstable);
|
||||
|
||||
File compactionFileLocation = cfs.directories.getDirectoryForNewSSTables();
|
||||
File compactionFileLocation = cfs.directories.getWriteableLocationAsFile(cfs.getExpectedCompactedFileSize(sstables, OperationType.CLEANUP));
|
||||
if (compactionFileLocation == null)
|
||||
throw new IOException("disk full");
|
||||
|
||||
|
|
|
|||
|
|
@ -153,55 +153,44 @@ public class CompactionTask extends AbstractCompactionTask
|
|||
Directories.DataDirectory dataDirectory = getWriteDirectory(writeSize);
|
||||
SSTableWriter writer = createCompactionWriter(cfs.directories.getLocationForDisk(dataDirectory), keysPerSSTable);
|
||||
writers.add(writer);
|
||||
try
|
||||
while (iter.hasNext())
|
||||
{
|
||||
while (iter.hasNext())
|
||||
if (ci.isStopRequested())
|
||||
throw new CompactionInterruptedException(ci.getCompactionInfo());
|
||||
|
||||
AbstractCompactedRow row = iter.next();
|
||||
RowIndexEntry indexEntry = writer.append(row);
|
||||
if (indexEntry == null)
|
||||
{
|
||||
if (ci.isStopRequested())
|
||||
throw new CompactionInterruptedException(ci.getCompactionInfo());
|
||||
controller.invalidateCachedRow(row.key);
|
||||
row.close();
|
||||
continue;
|
||||
}
|
||||
|
||||
AbstractCompactedRow row = iter.next();
|
||||
RowIndexEntry indexEntry = writer.append(row);
|
||||
if (indexEntry == null)
|
||||
totalkeysWritten++;
|
||||
|
||||
if (DatabaseDescriptor.getPreheatKeyCache())
|
||||
{
|
||||
for (SSTableReader sstable : actuallyCompact)
|
||||
{
|
||||
controller.invalidateCachedRow(row.key);
|
||||
row.close();
|
||||
continue;
|
||||
}
|
||||
|
||||
totalkeysWritten++;
|
||||
|
||||
if (DatabaseDescriptor.getPreheatKeyCache())
|
||||
{
|
||||
for (SSTableReader sstable : actuallyCompact)
|
||||
if (sstable.getCachedPosition(row.key, false) != null)
|
||||
{
|
||||
if (sstable.getCachedPosition(row.key, false) != null)
|
||||
{
|
||||
cachedKeys.put(row.key, indexEntry);
|
||||
break;
|
||||
}
|
||||
cachedKeys.put(row.key, indexEntry);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (newSSTableSegmentThresholdReached(writer))
|
||||
{
|
||||
// tmp = false because later we want to query it with descriptor from SSTableReader
|
||||
cachedKeyMap.put(writer.descriptor.asTemporary(false), cachedKeys);
|
||||
returnWriteDirectory(dataDirectory, writeSize);
|
||||
// make sure we don't try to call returnWriteDirectory in finally {..} if we throw exception in getWriteDirectory() below:
|
||||
dataDirectory = null;
|
||||
writeSize = getExpectedWriteSize() / estimatedSSTables;
|
||||
dataDirectory = getWriteDirectory(writeSize);
|
||||
writer = createCompactionWriter(cfs.directories.getLocationForDisk(dataDirectory), keysPerSSTable);
|
||||
writers.add(writer);
|
||||
cachedKeys = new HashMap<DecoratedKey, RowIndexEntry>();
|
||||
}
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (dataDirectory != null)
|
||||
returnWriteDirectory(dataDirectory, writeSize);
|
||||
|
||||
if (newSSTableSegmentThresholdReached(writer))
|
||||
{
|
||||
// tmp = false because later we want to query it with descriptor from SSTableReader
|
||||
cachedKeyMap.put(writer.descriptor.asTemporary(false), cachedKeys);
|
||||
writeSize = getExpectedWriteSize() / estimatedSSTables;
|
||||
dataDirectory = getWriteDirectory(writeSize);
|
||||
writer = createCompactionWriter(cfs.directories.getLocationForDisk(dataDirectory), keysPerSSTable);
|
||||
writers.add(writer);
|
||||
cachedKeys = new HashMap<>();
|
||||
}
|
||||
}
|
||||
|
||||
if (writer.getFilePointer() > 0)
|
||||
|
|
|
|||
|
|
@ -76,12 +76,13 @@ public class Scrubber implements Closeable
|
|||
this.outputHandler = outputHandler;
|
||||
this.skipCorrupted = skipCorrupted;
|
||||
|
||||
List<SSTableReader> toScrub = Collections.singletonList(sstable);
|
||||
|
||||
// Calculate the expected compacted filesize
|
||||
this.destination = cfs.directories.getDirectoryForNewSSTables();
|
||||
this.destination = cfs.directories.getWriteableLocationAsFile(cfs.getExpectedCompactedFileSize(toScrub, OperationType.SCRUB));
|
||||
if (destination == null)
|
||||
throw new IOException("disk full");
|
||||
|
||||
List<SSTableReader> toScrub = Collections.singletonList(sstable);
|
||||
// If we run scrub offline, we should never purge tombstone, as we cannot know if other sstable have data that the tombstone deletes.
|
||||
this.controller = isOffline
|
||||
? new ScrubController(cfs)
|
||||
|
|
|
|||
|
|
@ -27,24 +27,16 @@ public abstract class DiskAwareRunnable extends WrappedRunnable
|
|||
Directories.DataDirectory directory;
|
||||
while (true)
|
||||
{
|
||||
directory = getDirectories().getWriteableLocation();
|
||||
directory = getDirectories().getWriteableLocation(writeSize);
|
||||
if (directory != null || !reduceScopeForLimitedSpace())
|
||||
break;
|
||||
}
|
||||
if (directory == null)
|
||||
throw new RuntimeException("Insufficient disk space to write " + writeSize + " bytes");
|
||||
|
||||
directory.currentTasks.incrementAndGet();
|
||||
directory.estimatedWorkingSize.addAndGet(writeSize);
|
||||
return directory;
|
||||
}
|
||||
|
||||
protected void returnWriteDirectory(Directories.DataDirectory directory, long writeSize)
|
||||
{
|
||||
directory.estimatedWorkingSize.addAndGet(-1 * writeSize);
|
||||
directory.currentTasks.decrementAndGet();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get sstable directories for the CF.
|
||||
* @return Directories instance for the CF.
|
||||
|
|
|
|||
|
|
@ -3378,26 +3378,6 @@ public class StorageService extends NotificationBroadcasterSupport implements IE
|
|||
return isClientMode;
|
||||
}
|
||||
|
||||
public synchronized void requestGC()
|
||||
{
|
||||
if (hasUnreclaimedSpace())
|
||||
{
|
||||
logger.info("requesting GC to free disk space");
|
||||
System.gc();
|
||||
Uninterruptibles.sleepUninterruptibly(1, TimeUnit.SECONDS);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean hasUnreclaimedSpace()
|
||||
{
|
||||
for (ColumnFamilyStore cfs : ColumnFamilyStore.all())
|
||||
{
|
||||
if (cfs.hasUnreclaimedSpace())
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public String getOperationMode()
|
||||
{
|
||||
return operationMode.toString();
|
||||
|
|
|
|||
|
|
@ -108,7 +108,7 @@ public class StreamReader
|
|||
|
||||
protected SSTableWriter createWriter(ColumnFamilyStore cfs, long totalSize) throws IOException
|
||||
{
|
||||
Directories.DataDirectory localDir = cfs.directories.getWriteableLocation();
|
||||
Directories.DataDirectory localDir = cfs.directories.getWriteableLocation(totalSize);
|
||||
if (localDir == null)
|
||||
throw new IOException("Insufficient disk space to store " + totalSize + " bytes");
|
||||
desc = Descriptor.fromFilename(cfs.getTempSSTablePath(cfs.directories.getLocationForDisk(localDir)));
|
||||
|
|
|
|||
|
|
@ -17,6 +17,9 @@
|
|||
*/
|
||||
package org.apache.cassandra.streaming;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOError;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
|
@ -113,7 +116,10 @@ public class StreamReceiveTask extends StreamTask
|
|||
}
|
||||
ColumnFamilyStore cfs = Keyspace.open(kscf.left).getColumnFamilyStore(kscf.right);
|
||||
|
||||
StreamLockfile lockfile = new StreamLockfile(cfs.directories.getWriteableLocationAsFile(), UUID.randomUUID());
|
||||
File lockfiledir = cfs.directories.getWriteableLocationAsFile(task.sstables.size() * 256);
|
||||
if (lockfiledir == null)
|
||||
throw new IOError(new IOException("All disks full"));
|
||||
StreamLockfile lockfile = new StreamLockfile(lockfiledir, UUID.randomUUID());
|
||||
lockfile.create(task.sstables);
|
||||
List<SSTableReader> readers = new ArrayList<>();
|
||||
for (SSTableWriter writer : task.sstables)
|
||||
|
|
|
|||
|
|
@ -235,7 +235,7 @@ public class CommitLogTest extends SchemaLoader
|
|||
String newCLName = "CommitLog-" + CommitLogDescriptor.current_version + "-1340512736956320000.log";
|
||||
Assert.assertEquals(MessagingService.current_version, CommitLogDescriptor.fromFileName(newCLName).getMessagingVersion());
|
||||
}
|
||||
|
||||
/*
|
||||
@Test
|
||||
public void testCommitFailurePolicy_stop()
|
||||
{
|
||||
|
|
@ -262,7 +262,7 @@ public class CommitLogTest extends SchemaLoader
|
|||
commitDir.setWritable(true);
|
||||
}
|
||||
}
|
||||
|
||||
*/
|
||||
@Test
|
||||
public void testTruncateWithoutSnapshot() throws ExecutionException, InterruptedException
|
||||
{
|
||||
|
|
|
|||
|
|
@ -35,6 +35,12 @@ import org.apache.cassandra.io.sstable.Component;
|
|||
import org.apache.cassandra.io.sstable.Descriptor;
|
||||
import org.apache.cassandra.io.util.FileUtils;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertSame;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
public class DirectoriesTest
|
||||
{
|
||||
private static File tempDataDir;
|
||||
|
|
@ -231,4 +237,128 @@ public class DirectoriesTest
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDiskFreeSpace()
|
||||
{
|
||||
DataDirectory[] dataDirectories = new DataDirectory[]
|
||||
{
|
||||
new DataDirectory(new File("/nearlyFullDir1"))
|
||||
{
|
||||
public long getAvailableSpace()
|
||||
{
|
||||
return 11L;
|
||||
}
|
||||
},
|
||||
new DataDirectory(new File("/nearlyFullDir2"))
|
||||
{
|
||||
public long getAvailableSpace()
|
||||
{
|
||||
return 10L;
|
||||
}
|
||||
},
|
||||
new DataDirectory(new File("/uniformDir1"))
|
||||
{
|
||||
public long getAvailableSpace()
|
||||
{
|
||||
return 1000L;
|
||||
}
|
||||
},
|
||||
new DataDirectory(new File("/uniformDir2"))
|
||||
{
|
||||
public long getAvailableSpace()
|
||||
{
|
||||
return 999L;
|
||||
}
|
||||
},
|
||||
new DataDirectory(new File("/veryFullDir"))
|
||||
{
|
||||
public long getAvailableSpace()
|
||||
{
|
||||
return 4L;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// directories should be sorted
|
||||
// 1. by their free space ratio
|
||||
// before weighted random is applied
|
||||
List<Directories.DataDirectoryCandidate> candidates = getWriteableDirectories(dataDirectories, 0L);
|
||||
assertSame(dataDirectories[2], candidates.get(0).dataDirectory); // available: 1000
|
||||
assertSame(dataDirectories[3], candidates.get(1).dataDirectory); // available: 999
|
||||
assertSame(dataDirectories[0], candidates.get(2).dataDirectory); // available: 11
|
||||
assertSame(dataDirectories[1], candidates.get(3).dataDirectory); // available: 10
|
||||
|
||||
// check for writeSize == 5
|
||||
Map<DataDirectory, DataDirectory> testMap = new IdentityHashMap<>();
|
||||
for (int i=0; ; i++)
|
||||
{
|
||||
candidates = getWriteableDirectories(dataDirectories, 5L);
|
||||
assertEquals(4, candidates.size());
|
||||
|
||||
DataDirectory dir = Directories.pickWriteableDirectory(candidates);
|
||||
testMap.put(dir, dir);
|
||||
|
||||
assertFalse(testMap.size() > 4);
|
||||
if (testMap.size() == 4)
|
||||
{
|
||||
// at least (rule of thumb) 100 iterations to see whether there are more (wrong) directories returned
|
||||
if (i >= 100)
|
||||
break;
|
||||
}
|
||||
|
||||
// random weighted writeable directory algorithm fails to return all possible directories after
|
||||
// many tries
|
||||
if (i >= 10000000)
|
||||
fail();
|
||||
}
|
||||
|
||||
// check for writeSize == 11
|
||||
testMap.clear();
|
||||
for (int i=0; ; i++)
|
||||
{
|
||||
candidates = getWriteableDirectories(dataDirectories, 11L);
|
||||
assertEquals(3, candidates.size());
|
||||
for (Directories.DataDirectoryCandidate candidate : candidates)
|
||||
assertTrue(candidate.dataDirectory.getAvailableSpace() >= 11L);
|
||||
|
||||
DataDirectory dir = Directories.pickWriteableDirectory(candidates);
|
||||
testMap.put(dir, dir);
|
||||
|
||||
assertFalse(testMap.size() > 3);
|
||||
if (testMap.size() == 3)
|
||||
{
|
||||
// at least (rule of thumb) 100 iterations
|
||||
if (i >= 100)
|
||||
break;
|
||||
}
|
||||
|
||||
// random weighted writeable directory algorithm fails to return all possible directories after
|
||||
// many tries
|
||||
if (i >= 10000000)
|
||||
fail();
|
||||
}
|
||||
}
|
||||
|
||||
private List<Directories.DataDirectoryCandidate> getWriteableDirectories(DataDirectory[] dataDirectories, long writeSize)
|
||||
{
|
||||
// copied from Directories.getWriteableLocation(long)
|
||||
List<Directories.DataDirectoryCandidate> candidates = new ArrayList<>();
|
||||
|
||||
long totalAvailable = 0L;
|
||||
|
||||
for (DataDirectory dataDir : dataDirectories)
|
||||
{
|
||||
Directories.DataDirectoryCandidate candidate = new Directories.DataDirectoryCandidate(dataDir);
|
||||
// exclude directory if its total writeSize does not fit to data directory
|
||||
if (candidate.availableSpace < writeSize)
|
||||
continue;
|
||||
candidates.add(candidate);
|
||||
totalAvailable += candidate.availableSpace;
|
||||
}
|
||||
|
||||
Directories.sortWriteableCandidates(candidates, totalAvailable);
|
||||
|
||||
return candidates;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue