diff --git a/CHANGES.txt b/CHANGES.txt index 0a7fb2d540..b466e70fe1 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,4 +1,5 @@ 3.2 + * Improve performance of the folderSize function (CASSANDRA-10677) * Add support for type casting in selection clause (CASSANDRA-10310) * Added graphing option to cassandra-stress (CASSANDRA-7918) * Abort in-progress queries that time out (CASSANDRA-7392) diff --git a/src/java/org/apache/cassandra/io/util/FileUtils.java b/src/java/org/apache/cassandra/io/util/FileUtils.java index 46f2de53cf..d982e15dd3 100644 --- a/src/java/org/apache/cassandra/io/util/FileUtils.java +++ b/src/java/org/apache/cassandra/io/util/FileUtils.java @@ -23,6 +23,7 @@ import java.nio.channels.FileChannel; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import java.nio.file.*; +import java.nio.file.attribute.BasicFileAttributes; import java.text.DecimalFormat; import java.util.Arrays; import java.util.Collections; @@ -518,25 +519,34 @@ public class FileUtils break; } } + /** * Get the size of a directory in bytes - * @param directory The directory for which we need size. + * @param folder The directory for which we need size. * @return The size of the directory */ - public static long folderSize(File directory) + public static long folderSize(File folder) { - long length = 0; - for (File file : directory.listFiles()) + final long [] sizeArr = {0L}; + try { - if (file.isFile()) - length += file.length(); - else - length += folderSize(file); + Files.walkFileTree(folder.toPath(), new SimpleFileVisitor() + { + @Override + public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) + { + sizeArr[0] += attrs.size(); + return FileVisitResult.CONTINUE; + } + }); } - return length; + catch (IOException e) + { + logger.error("Error while getting {} folder size. {}", folder, e); + } + return sizeArr[0]; } - public static void copyTo(DataInput in, OutputStream out, int length) throws IOException { byte[] buffer = new byte[64 * 1024]; diff --git a/test/unit/org/apache/cassandra/io/util/FileUtilsTest.java b/test/unit/org/apache/cassandra/io/util/FileUtilsTest.java index 7110504b74..ee331072eb 100644 --- a/test/unit/org/apache/cassandra/io/util/FileUtilsTest.java +++ b/test/unit/org/apache/cassandra/io/util/FileUtilsTest.java @@ -20,11 +20,20 @@ package org.apache.cassandra.io.util; import java.io.File; import java.io.IOException; +import java.io.RandomAccessFile; import java.nio.charset.Charset; import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Arrays; import org.junit.Test; +import org.apache.cassandra.config.DatabaseDescriptor; +import org.apache.cassandra.cql3.CQLTester; +import org.apache.cassandra.schema.SchemaKeyspace; +import org.apache.cassandra.utils.FBUtilities; + import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; @@ -52,4 +61,47 @@ public class FileUtilsTest assertEquals(0, b.length); } + @Test + public void testFolderSize() throws Exception + { + File folder = createFolder(Paths.get(DatabaseDescriptor.getAllDataFileLocations()[0], "testFolderSize")); + folder.deleteOnExit(); + + File childFolder = createFolder(Paths.get(folder.getPath(), "child")); + + File[] files = { + createFile(new File(folder, "001"), 10000), + createFile(new File(folder, "002"), 1000), + createFile(new File(folder, "003"), 100), + createFile(new File(childFolder, "001"), 1000), + createFile(new File(childFolder, "002"), 2000), + }; + + assertEquals(0, FileUtils.folderSize(new File(folder, "i_dont_exist"))); + assertEquals(files[0].length(), FileUtils.folderSize(files[0])); + + long size = FileUtils.folderSize(folder); + assertEquals(Arrays.stream(files).mapToLong(f -> f.length()).sum(), size); + } + + private File createFolder(Path path) + { + File folder = path.toFile(); + FileUtils.createDirectory(folder); + return folder; + } + + private File createFile(File file, long size) + { + try + { + RandomAccessFile f = new RandomAccessFile(file, "rw"); + f.setLength(size); + } + catch (Exception e) + { + System.err.println(e); + } + return file; + } }