You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cassandra.apache.org by bl...@apache.org on 2017/08/08 15:16:57 UTC
[04/10] cassandra git commit: Prevent integer overflow on exabyte
filesystems
Prevent integer overflow on exabyte filesystems
patch by Matt Wringe and Benjamin Lerer; reviewed by Alex Petrov for CASSANDRA-13067
Project: http://git-wip-us.apache.org/repos/asf/cassandra/repo
Commit: http://git-wip-us.apache.org/repos/asf/cassandra/commit/270f690f
Tree: http://git-wip-us.apache.org/repos/asf/cassandra/tree/270f690f
Diff: http://git-wip-us.apache.org/repos/asf/cassandra/diff/270f690f
Branch: refs/heads/trunk
Commit: 270f690ff6047cc3e797a3f34b7efa26e7232183
Parents: 739cd2b
Author: Benjamin Lerer <b....@gmail.com>
Authored: Tue Aug 8 16:51:03 2017 +0200
Committer: Benjamin Lerer <b....@gmail.com>
Committed: Tue Aug 8 16:51:03 2017 +0200
----------------------------------------------------------------------
CHANGES.txt | 1 +
.../cassandra/config/DatabaseDescriptor.java | 28 ++-
.../org/apache/cassandra/db/Directories.java | 2 +-
.../org/apache/cassandra/io/util/FileUtils.java | 182 +++++++++++++++++--
4 files changed, 193 insertions(+), 20 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/cassandra/blob/270f690f/CHANGES.txt
----------------------------------------------------------------------
diff --git a/CHANGES.txt b/CHANGES.txt
index 36c34a1..f712333 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -1,4 +1,5 @@
2.2.11
+ * Prevent integer overflow on exabyte filesystems (CASSANDRA-13067)
* Fix queries with LIMIT and filtering on clustering columns (CASSANDRA-11223)
* Fix potential NPE when resume bootstrap fails (CASSANDRA-13272)
* Fix toJSONString for the UDT, tuple and collection types (CASSANDRA-13592)
http://git-wip-us.apache.org/repos/asf/cassandra/blob/270f690f/src/java/org/apache/cassandra/config/DatabaseDescriptor.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/config/DatabaseDescriptor.java b/src/java/org/apache/cassandra/config/DatabaseDescriptor.java
index 981026d..90a82fe 100644
--- a/src/java/org/apache/cassandra/config/DatabaseDescriptor.java
+++ b/src/java/org/apache/cassandra/config/DatabaseDescriptor.java
@@ -21,7 +21,6 @@ import java.io.File;
import java.io.IOException;
import java.net.*;
import java.nio.file.FileStore;
-import java.nio.file.Files;
import java.nio.file.NoSuchFileException;
import java.nio.file.Path;
import java.nio.file.Paths;
@@ -56,6 +55,9 @@ import org.apache.cassandra.thrift.ThriftServer;
import org.apache.cassandra.utils.FBUtilities;
import org.apache.cassandra.utils.memory.*;
+import static org.apache.cassandra.io.util.FileUtils.ONE_GB;
+import static org.apache.cassandra.io.util.FileUtils.ONE_MB;
+
public class DatabaseDescriptor
{
private static final Logger logger = LoggerFactory.getLogger(DatabaseDescriptor.class);
@@ -530,7 +532,7 @@ public class DatabaseDescriptor
try
{
// use 1/4 of available space. See discussion on #10013 and #10199
- minSize = Ints.checkedCast((guessFileStore(conf.commitlog_directory).getTotalSpace() / 1048576) / 4);
+ minSize = Ints.saturatedCast((guessFileStore(conf.commitlog_directory).getTotalSpace() / 1048576) / 4);
}
catch (IOException e)
{
@@ -576,7 +578,7 @@ public class DatabaseDescriptor
try
{
- dataFreeBytes += guessFileStore(datadir).getUnallocatedSpace();
+ dataFreeBytes = saturatedSum(dataFreeBytes, guessFileStore(datadir).getUnallocatedSpace());
}
catch (IOException e)
{
@@ -585,9 +587,9 @@ public class DatabaseDescriptor
datadir), e);
}
}
- if (dataFreeBytes < 64L * 1024 * 1048576) // 64 GB
+ if (dataFreeBytes < 64 * ONE_GB)
logger.warn("Only {} MB free across all data volumes. Consider adding more capacity to your cluster or removing obsolete snapshots",
- dataFreeBytes / 1048576);
+ dataFreeBytes / ONE_MB);
if (conf.commitlog_directory.equals(conf.saved_caches_directory))
@@ -697,6 +699,20 @@ public class DatabaseDescriptor
throw new ConfigurationException("otc_coalescing_enough_coalesced_messages must be positive", false);
}
+ /**
+ * Computes the sum of the 2 specified positive values returning {@code Long.MAX_VALUE} if the sum overflow.
+ *
+ * @param left the left operand
+ * @param right the right operand
+ * @return the sum of the 2 specified positive values of {@code Long.MAX_VALUE} if the sum overflow.
+ */
+ private static long saturatedSum(long left, long right)
+ {
+ assert left >= 0 && right >= 0;
+ long sum = left + right;
+ return sum < 0 ? Long.MAX_VALUE : sum;
+ }
+
private static FileStore guessFileStore(String dir) throws IOException
{
Path path = Paths.get(dir);
@@ -704,7 +720,7 @@ public class DatabaseDescriptor
{
try
{
- return Files.getFileStore(path);
+ return FileUtils.getFileStore(path);
}
catch (IOException e)
{
http://git-wip-us.apache.org/repos/asf/cassandra/blob/270f690f/src/java/org/apache/cassandra/db/Directories.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/db/Directories.java b/src/java/org/apache/cassandra/db/Directories.java
index 2b3662f..fa76b61 100644
--- a/src/java/org/apache/cassandra/db/Directories.java
+++ b/src/java/org/apache/cassandra/db/Directories.java
@@ -482,7 +482,7 @@ public class Directories
public long getAvailableSpace()
{
- return location.getUsableSpace();
+ return FileUtils.getUsableSpace(location);
}
}
http://git-wip-us.apache.org/repos/asf/cassandra/blob/270f690f/src/java/org/apache/cassandra/io/util/FileUtils.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/io/util/FileUtils.java b/src/java/org/apache/cassandra/io/util/FileUtils.java
index 8d122dd..bf0fae5 100644
--- a/src/java/org/apache/cassandra/io/util/FileUtils.java
+++ b/src/java/org/apache/cassandra/io/util/FileUtils.java
@@ -21,11 +21,12 @@ import java.io.*;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.file.*;
+import java.nio.file.attribute.FileAttributeView;
+import java.nio.file.attribute.FileStoreAttributeView;
import java.text.DecimalFormat;
import java.util.Arrays;
import java.util.concurrent.atomic.AtomicReference;
-import org.apache.cassandra.config.Config;
import sun.nio.ch.DirectBuffer;
import org.slf4j.Logger;
@@ -45,10 +46,10 @@ import static org.apache.cassandra.utils.Throwables.merge;
public final class FileUtils
{
private static final Logger logger = LoggerFactory.getLogger(FileUtils.class);
- private static final double KB = 1024d;
- private static final double MB = 1024*1024d;
- private static final double GB = 1024*1024*1024d;
- private static final double TB = 1024*1024*1024*1024d;
+ public static final long ONE_KB = 1024;
+ public static final long ONE_MB = 1024 * ONE_KB;
+ public static final long ONE_GB = 1024 * ONE_MB;
+ public static final long ONE_TB = 1024 * ONE_GB;
private static final DecimalFormat df = new DecimalFormat("#.##");
private static final boolean canCleanDirectBuffers;
@@ -330,27 +331,27 @@ public final class FileUtils
public static String stringifyFileSize(double value)
{
double d;
- if ( value >= TB )
+ if ( value >= ONE_TB )
{
- d = value / TB;
+ d = value / ONE_TB;
String val = df.format(d);
return val + " TB";
}
- else if ( value >= GB )
+ else if ( value >= ONE_GB )
{
- d = value / GB;
+ d = value / ONE_GB;
String val = df.format(d);
return val + " GB";
}
- else if ( value >= MB )
+ else if ( value >= ONE_MB )
{
- d = value / MB;
+ d = value / ONE_MB;
String val = df.format(d);
return val + " MB";
}
- else if ( value >= KB )
+ else if ( value >= ONE_KB )
{
- d = value / KB;
+ d = value / ONE_KB;
String val = df.format(d);
return val + " KB";
}
@@ -478,4 +479,159 @@ public final class FileUtils
{
fsErrorHandler.getAndSet(handler);
}
+
+ /**
+ * Returns the size of the specified partition.
+ * <p>This method handles large file system by returning {@code Long.MAX_VALUE} if the size overflow.
+ * See <a href='https://bugs.openjdk.java.net/browse/JDK-8179320'>JDK-8179320</a> for more information.</p>
+ *
+ * @param file the partition
+ * @return the size, in bytes, of the partition or {@code 0L} if the abstract pathname does not name a partition
+ */
+ public static long getTotalSpace(File file)
+ {
+ return handleLargeFileSystem(file.getTotalSpace());
+ }
+
+ /**
+ * Returns the number of unallocated bytes on the specified partition.
+ * <p>This method handles large file system by returning {@code Long.MAX_VALUE} if the number of unallocated bytes
+ * overflow. See <a href='https://bugs.openjdk.java.net/browse/JDK-8179320'>JDK-8179320</a> for more information</p>
+ *
+ * @param file the partition
+ * @return the number of unallocated bytes on the partition or {@code 0L}
+ * if the abstract pathname does not name a partition.
+ */
+ public static long getFreeSpace(File file)
+ {
+ return handleLargeFileSystem(file.getFreeSpace());
+ }
+
+ /**
+ * Returns the number of available bytes on the specified partition.
+ * <p>This method handles large file system by returning {@code Long.MAX_VALUE} if the number of available bytes
+ * overflow. See <a href='https://bugs.openjdk.java.net/browse/JDK-8179320'>JDK-8179320</a> for more information</p>
+ *
+ * @param file the partition
+ * @return the number of available bytes on the partition or {@code 0L}
+ * if the abstract pathname does not name a partition.
+ */
+ public static long getUsableSpace(File file)
+ {
+ return handleLargeFileSystem(file.getUsableSpace());
+ }
+
+ /**
+ * Returns the {@link FileStore} representing the file store where a file
+ * is located. This {@link FileStore} handles large file system by returning {@code Long.MAX_VALUE}
+ * from {@code FileStore#getTotalSpace()}, {@code FileStore#getUnallocatedSpace()} and {@code FileStore#getUsableSpace()}
+ * it the value is bigger than {@code Long.MAX_VALUE}. See <a href='https://bugs.openjdk.java.net/browse/JDK-8162520'>JDK-8162520</a>
+ * for more information.
+ *
+ * @param path the path to the file
+ * @return the file store where the file is stored
+ */
+ public static FileStore getFileStore(Path path) throws IOException
+ {
+ return new SafeFileStore(Files.getFileStore(path));
+ }
+
+ /**
+ * Handle large file system by returning {@code Long.MAX_VALUE} when the size overflows.
+ * @param size returned by the Java's FileStore methods
+ * @return the size or {@code Long.MAX_VALUE} if the size was bigger than {@code Long.MAX_VALUE}
+ */
+ private static long handleLargeFileSystem(long size)
+ {
+ return size < 0 ? Long.MAX_VALUE : size;
+ }
+
+ /**
+ * Private constructor as the class contains only static methods.
+ */
+ private FileUtils()
+ {
+ }
+
+ /**
+ * FileStore decorator used to safely handle large file system.
+ *
+ * <p>Java's FileStore methods (getTotalSpace/getUnallocatedSpace/getUsableSpace) are limited to reporting bytes as
+ * signed long (2^63-1), if the filesystem is any bigger, then the size overflows. {@code SafeFileStore} will
+ * return {@code Long.MAX_VALUE} if the size overflow.</p>
+ *
+ * @see https://bugs.openjdk.java.net/browse/JDK-8162520.
+ */
+ private static final class SafeFileStore extends FileStore
+ {
+ /**
+ * The decorated {@code FileStore}
+ */
+ private final FileStore fileStore;
+
+ public SafeFileStore(FileStore fileStore)
+ {
+ this.fileStore = fileStore;
+ }
+
+ @Override
+ public String name()
+ {
+ return fileStore.name();
+ }
+
+ @Override
+ public String type()
+ {
+ return fileStore.type();
+ }
+
+ @Override
+ public boolean isReadOnly()
+ {
+ return fileStore.isReadOnly();
+ }
+
+ @Override
+ public long getTotalSpace() throws IOException
+ {
+ return handleLargeFileSystem(fileStore.getTotalSpace());
+ }
+
+ @Override
+ public long getUsableSpace() throws IOException
+ {
+ return handleLargeFileSystem(fileStore.getUsableSpace());
+ }
+
+ @Override
+ public long getUnallocatedSpace() throws IOException
+ {
+ return handleLargeFileSystem(fileStore.getUnallocatedSpace());
+ }
+
+ @Override
+ public boolean supportsFileAttributeView(Class<? extends FileAttributeView> type)
+ {
+ return fileStore.supportsFileAttributeView(type);
+ }
+
+ @Override
+ public boolean supportsFileAttributeView(String name)
+ {
+ return fileStore.supportsFileAttributeView(name);
+ }
+
+ @Override
+ public <V extends FileStoreAttributeView> V getFileStoreAttributeView(Class<V> type)
+ {
+ return fileStore.getFileStoreAttributeView(type);
+ }
+
+ @Override
+ public Object getAttribute(String attribute) throws IOException
+ {
+ return fileStore.getAttribute(attribute);
+ }
+ }
}
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@cassandra.apache.org
For additional commands, e-mail: commits-help@cassandra.apache.org