You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cassandra.apache.org by jm...@apache.org on 2015/01/08 18:51:06 UTC

cassandra git commit: Switch CommitLogSegment from RandomAccessFile to nio

Repository: cassandra
Updated Branches:
  refs/heads/trunk 028fd2950 -> 2b4029a76


Switch CommitLogSegment from RandomAccessFile to nio

Patch by jmckenzie; reviewed by belliottsmith for CASSANDRA-8308


Project: http://git-wip-us.apache.org/repos/asf/cassandra/repo
Commit: http://git-wip-us.apache.org/repos/asf/cassandra/commit/2b4029a7
Tree: http://git-wip-us.apache.org/repos/asf/cassandra/tree/2b4029a7
Diff: http://git-wip-us.apache.org/repos/asf/cassandra/diff/2b4029a7

Branch: refs/heads/trunk
Commit: 2b4029a763173af31633274844a4a3de1f73fa99
Parents: 028fd29
Author: Joshua McKenzie <jm...@apache.org>
Authored: Thu Jan 8 11:49:09 2015 -0600
Committer: Joshua McKenzie <jm...@apache.org>
Committed: Thu Jan 8 11:49:09 2015 -0600

----------------------------------------------------------------------
 CHANGES.txt                                     |  1 +
 .../db/commitlog/CommitLogSegment.java          | 42 +++++++++++++-------
 .../org/apache/cassandra/utils/CLibrary.java    | 21 +++++++++-
 .../unit/org/apache/cassandra/SchemaLoader.java | 11 ++++-
 4 files changed, 57 insertions(+), 18 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cassandra/blob/2b4029a7/CHANGES.txt
----------------------------------------------------------------------
diff --git a/CHANGES.txt b/CHANGES.txt
index 9f946a3..71ccc58 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -1,4 +1,5 @@
 3.0
+ * Switch CommitLogSegment from RandomAccessFile to nio (CASSANDRA-8308)
  * Allow mixing token and partition key restrictions (CASSANDRA-7016)
  * Support index key/value entries on map collections (CASSANDRA-8473)
  * Modernize schema tables (CASSANDRA-8261)

http://git-wip-us.apache.org/repos/asf/cassandra/blob/2b4029a7/src/java/org/apache/cassandra/db/commitlog/CommitLogSegment.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/db/commitlog/CommitLogSegment.java b/src/java/org/apache/cassandra/db/commitlog/CommitLogSegment.java
index 185f57a..3383f1e 100644
--- a/src/java/org/apache/cassandra/db/commitlog/CommitLogSegment.java
+++ b/src/java/org/apache/cassandra/db/commitlog/CommitLogSegment.java
@@ -23,6 +23,7 @@ import java.io.RandomAccessFile;
 import java.nio.ByteBuffer;
 import java.nio.MappedByteBuffer;
 import java.nio.channels.FileChannel;
+import java.nio.file.StandardOpenOption;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Comparator;
@@ -104,7 +105,7 @@ public class CommitLogSegment
     public final long id;
 
     private final File logFile;
-    private final RandomAccessFile logFileAccessor;
+    private final FileChannel channel;
     private final int fd;
 
     private final MappedByteBuffer buffer;
@@ -134,7 +135,6 @@ public class CommitLogSegment
         id = getNextId();
         descriptor = new CommitLogDescriptor(id);
         logFile = new File(DatabaseDescriptor.getCommitLogLocation(), descriptor.fileName());
-        boolean isCreating = true;
 
         try
         {
@@ -147,25 +147,37 @@ public class CommitLogSegment
                     logger.debug("Re-using discarded CommitLog segment for {} from {}", id, filePath);
                     if (!oldFile.renameTo(logFile))
                         throw new IOException("Rename from " + filePath + " to " + id + " failed");
-                    isCreating = false;
+                }
+                else
+                {
+                    logger.debug("Creating new CommitLog segment: " + logFile);
                 }
             }
 
-            // Open the initial the segment file
-            logFileAccessor = new RandomAccessFile(logFile, "rw");
+            // Extend or truncate the file size to the standard segment size as we may have restarted after a segment
+            // size configuration change, leaving "incorrectly" sized segments on disk.
+            // NOTE: while we're using RAF to allow extension of file on disk w/out sparse, we need to avoid using RAF
+            // for grabbing the FileChannel due to FILE_SHARE_DELETE flag bug on windows.
+            // See: https://bugs.openjdk.java.net/browse/JDK-6357433 and CASSANDRA-8308
+            if (logFile.length() != DatabaseDescriptor.getCommitLogSegmentSize())
+            {
+                try (RandomAccessFile raf = new RandomAccessFile(logFile, "rw"))
+                {
+                    raf.setLength(DatabaseDescriptor.getCommitLogSegmentSize());
+                }
+                catch (IOException e)
+                {
+                    throw new FSWriteError(e, logFile);
+                }
+            }
 
-            if (isCreating)
-                logger.debug("Creating new commit log segment {}", logFile.getPath());
+            channel = FileChannel.open(logFile.toPath(), StandardOpenOption.WRITE, StandardOpenOption.READ);
 
-            // Map the segment, extending or truncating it to the standard segment size.
-            // (We may have restarted after a segment size configuration change, leaving "incorrectly"
-            // sized segments on disk.)
-            logFileAccessor.setLength(DatabaseDescriptor.getCommitLogSegmentSize());
-            fd = CLibrary.getfd(logFileAccessor.getFD());
+            fd = CLibrary.getfd(channel);
+            buffer = channel.map(FileChannel.MapMode.READ_WRITE, 0, DatabaseDescriptor.getCommitLogSegmentSize());
 
-            buffer = logFileAccessor.getChannel().map(FileChannel.MapMode.READ_WRITE, 0, DatabaseDescriptor.getCommitLogSegmentSize());
-            // write the header
             CommitLogDescriptor.writeHeader(buffer, descriptor);
+
             // mark the initial sync marker as uninitialised
             buffer.putInt(CommitLogDescriptor.HEADER_SIZE, 0);
             buffer.putLong(CommitLogDescriptor.HEADER_SIZE + 4, 0);
@@ -415,7 +427,7 @@ public class CommitLogSegment
         {
             if (FileUtils.isCleanerAvailable())
                 FileUtils.clean(buffer);
-            logFileAccessor.close();
+            channel.close();
         }
         catch (IOException e)
         {

http://git-wip-us.apache.org/repos/asf/cassandra/blob/2b4029a7/src/java/org/apache/cassandra/utils/CLibrary.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/utils/CLibrary.java b/src/java/org/apache/cassandra/utils/CLibrary.java
index b2595f6..4dbd904 100644
--- a/src/java/org/apache/cassandra/utils/CLibrary.java
+++ b/src/java/org/apache/cassandra/utils/CLibrary.java
@@ -20,6 +20,7 @@ package org.apache.cassandra.utils;
 import java.io.FileDescriptor;
 import java.io.RandomAccessFile;
 import java.lang.reflect.Field;
+import java.nio.channels.FileChannel;
 
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -276,6 +277,24 @@ public final class CLibrary
         }
     }
 
+    public static int getfd(FileChannel channel)
+    {
+        Field field = FBUtilities.getProtectedField(channel.getClass(), "fd");
+
+        if (field == null)
+            return -1;
+
+        try
+        {
+            return getfd((FileDescriptor)field.get(channel));
+        }
+        catch (IllegalArgumentException|IllegalAccessException e)
+        {
+            logger.warn("Unable to read fd field from FileChannel");
+        }
+        return -1;
+    }
+
     /**
      * Get system file descriptor from FileDescriptor object.
      * @param descriptor - FileDescriptor objec to get fd from
@@ -295,7 +314,7 @@ public final class CLibrary
         catch (Exception e)
         {
             JVMStabilityInspector.inspectThrowable(e);
-            logger.warn("unable to read fd field from FileDescriptor");
+            logger.warn("Unable to read fd field from FileDescriptor");
         }
 
         return -1;

http://git-wip-us.apache.org/repos/asf/cassandra/blob/2b4029a7/test/unit/org/apache/cassandra/SchemaLoader.java
----------------------------------------------------------------------
diff --git a/test/unit/org/apache/cassandra/SchemaLoader.java b/test/unit/org/apache/cassandra/SchemaLoader.java
index 2fcfd55..2be4cc0 100644
--- a/test/unit/org/apache/cassandra/SchemaLoader.java
+++ b/test/unit/org/apache/cassandra/SchemaLoader.java
@@ -419,6 +419,7 @@ public class SchemaLoader
 
     public static void cleanupAndLeaveDirs()
     {
+        CommitLog.instance.resetUnsafe(); // unmap CLS before attempting to delete or Windows complains
         mkdirs();
         cleanup();
         mkdirs();
@@ -434,7 +435,11 @@ public class SchemaLoader
             File dir = new File(dirName);
             if (!dir.exists())
                 throw new RuntimeException("No such directory: " + dir.getAbsolutePath());
-            FileUtils.deleteRecursive(dir);
+
+            // Leave the folder around as Windows will complain about directory deletion w/handles open to children files
+            String[] children = dir.list();
+            for (String child : children)
+                FileUtils.deleteRecursive(new File(dir, child));
         }
 
         cleanupSavedCaches();
@@ -445,7 +450,9 @@ public class SchemaLoader
             File dir = new File(dirName);
             if (!dir.exists())
                 throw new RuntimeException("No such directory: " + dir.getAbsolutePath());
-            FileUtils.deleteRecursive(dir);
+            String[] children = dir.list();
+            for (String child : children)
+                FileUtils.deleteRecursive(new File(dir, child));
         }
     }