You are viewing a plain text version of this content. The canonical link for it is here.
Posted to pr@cassandra.apache.org by GitBox <gi...@apache.org> on 2022/01/02 14:55:41 UTC

[GitHub] [cassandra] pauloricardomg commented on a change in pull request #1354: [16436] - Added startup check for read_ahead_kb setting.

pauloricardomg commented on a change in pull request #1354:
URL: https://github.com/apache/cassandra/pull/1354#discussion_r777216698



##########
File path: src/java/org/apache/cassandra/service/StartupChecks.java
##########
@@ -498,23 +573,45 @@ public void execute() throws StartupException
             throw new StartupException(StartupException.ERR_WRONG_CONFIG, errMsg.get());
     };
 
+    @VisibleForTesting
+    public static String getReadAheadKBPath(String blockDirectoryPath)
+    {
+        String readAheadKBPath = null;
+
+        final String READ_AHEAD_KB_SETTING_PATH = "/sys/block/%s/queue/read_ahead_kb";
+        try
+        {
+            String deviceName = blockDirectoryPath.split("/")[2].replaceAll("[0-9]*$", "");
+
+            if (StringUtils.isNotEmpty(deviceName))
+            {
+                readAheadKBPath = String.format(READ_AHEAD_KB_SETTING_PATH, deviceName);
+            }
+        }
+        catch (Exception e)
+        {
+            logger.error("Error retrieving device path for {}.", blockDirectoryPath);
+        }
+
+        return readAheadKBPath;
+    }
+
     @VisibleForTesting
     static Optional<String> checkLegacyAuthTablesMessage()
     {
         List<String> existing = new ArrayList<>(SchemaConstants.LEGACY_AUTH_TABLES).stream().filter((legacyAuthTable) ->
-            {
-                UntypedResultSet result = QueryProcessor.executeOnceInternal(String.format("SELECT table_name FROM %s.%s WHERE keyspace_name='%s' AND table_name='%s'",
-                                                                                           SchemaConstants.SCHEMA_KEYSPACE_NAME,
-                                                                                           "tables",
-                                                                                           SchemaConstants.AUTH_KEYSPACE_NAME,
-                                                                                           legacyAuthTable));
-                return result != null && !result.isEmpty();
-            }).collect(Collectors.toList());
+        {
+            UntypedResultSet result = QueryProcessor.executeOnceInternal(String.format("SELECT table_name FROM %s.%s WHERE keyspace_name='%s' AND table_name='%s'",
+                                                                                       SchemaConstants.SCHEMA_KEYSPACE_NAME, "tables", SchemaConstants.AUTH_KEYSPACE_NAME, legacyAuthTable));
+            return result != null && !result.isEmpty();
+        }).collect(Collectors.toList());
 
         if (!existing.isEmpty())
             return Optional.of(String.format("Legacy auth tables %s in keyspace %s still exist and have not been properly migrated.",
-                        Joiner.on(", ").join(existing), SchemaConstants.AUTH_KEYSPACE_NAME));
+                                             Joiner.on(", ").join(existing), SchemaConstants.AUTH_KEYSPACE_NAME));

Review comment:
       Please remove unrelated formatting change.

##########
File path: test/unit/org/apache/cassandra/service/StartupChecksTest.java
##########
@@ -103,6 +103,30 @@ public void compatibilityCheckIgnoresNonDbFiles() throws Exception
         startupChecks.verify();
     }
 
+    @Test
+    public void checkReadAheadKbSettingCheck() throws Exception
+    {
+        // This test just validates if the verify function
+        // doesn't throw any exceptions
+        startupChecks = startupChecks.withTest(StartupChecks.checkReadAheadKbSetting);
+        startupChecks.verify();
+    }
+
+    @Test
+    public void getReadAheadKBPath()
+    {
+        startupChecks = startupChecks.withTest(StartupChecks.checkReadAheadKbSetting);
+
+        String sdaDirectory = startupChecks.getReadAheadKBPath("/dev/sda12");

Review comment:
       use `StartupChecks.getReadAheadKBPath` to make explicit this is an static method.

##########
File path: test/unit/org/apache/cassandra/service/StartupChecksTest.java
##########
@@ -103,6 +103,30 @@ public void compatibilityCheckIgnoresNonDbFiles() throws Exception
         startupChecks.verify();
     }
 
+    @Test
+    public void checkReadAheadKbSettingCheck() throws Exception
+    {
+        // This test just validates if the verify function
+        // doesn't throw any exceptions
+        startupChecks = startupChecks.withTest(StartupChecks.checkReadAheadKbSetting);
+        startupChecks.verify();
+    }
+
+    @Test
+    public void getReadAheadKBPath()

Review comment:
       call this `testGetReadAheadKBPath`

##########
File path: src/java/org/apache/cassandra/service/StartupChecks.java
##########
@@ -498,23 +573,45 @@ public void execute() throws StartupException
             throw new StartupException(StartupException.ERR_WRONG_CONFIG, errMsg.get());
     };
 
+    @VisibleForTesting
+    public static String getReadAheadKBPath(String blockDirectoryPath)
+    {
+        String readAheadKBPath = null;
+
+        final String READ_AHEAD_KB_SETTING_PATH = "/sys/block/%s/queue/read_ahead_kb";
+        try
+        {
+            String deviceName = blockDirectoryPath.split("/")[2].replaceAll("[0-9]*$", "");
+
+            if (StringUtils.isNotEmpty(deviceName))
+            {
+                readAheadKBPath = String.format(READ_AHEAD_KB_SETTING_PATH, deviceName);
+            }
+        }
+        catch (Exception e)
+        {
+            logger.error("Error retrieving device path for {}.", blockDirectoryPath);
+        }
+
+        return readAheadKBPath;
+    }
+
     @VisibleForTesting
     static Optional<String> checkLegacyAuthTablesMessage()
     {
         List<String> existing = new ArrayList<>(SchemaConstants.LEGACY_AUTH_TABLES).stream().filter((legacyAuthTable) ->
-            {
-                UntypedResultSet result = QueryProcessor.executeOnceInternal(String.format("SELECT table_name FROM %s.%s WHERE keyspace_name='%s' AND table_name='%s'",
-                                                                                           SchemaConstants.SCHEMA_KEYSPACE_NAME,
-                                                                                           "tables",
-                                                                                           SchemaConstants.AUTH_KEYSPACE_NAME,
-                                                                                           legacyAuthTable));
-                return result != null && !result.isEmpty();
-            }).collect(Collectors.toList());
+        {

Review comment:
       Please remove unrelated formatting change.

##########
File path: src/java/org/apache/cassandra/service/StartupChecks.java
##########
@@ -285,6 +289,77 @@ public void execute()
         }
     };
 
+    public static final StartupCheck checkReadAheadKbSetting = new StartupCheck()
+    {
+        // This value is in KB.
+        private static final long MAX_RECOMMENDED_READ_AHEAD_KB_SETTING = 128;
+
+        @Override
+        public void execute() throws StartupException
+        {
+            if (!FBUtilities.isLinux)
+                return;
+
+            String[] dataDirectories = DatabaseDescriptor.getRawConfig().data_file_directories;
+            Map<String, String> blockDevices = new HashMap<String, String>();
+            for (String dataDirectory : dataDirectories)
+            {
+                try
+                {
+                    Path p = Path.of(dataDirectory);
+                    FileStore fs = Files.getFileStore(p);
+
+                    String blockDirectory = fs.name();
+                    if(StringUtils.isNotEmpty(blockDirectory))
+                    {
+                        blockDevices.put(blockDirectory, dataDirectory);
+                    }
+                }
+                catch (IOException e)
+                {
+                    logger.warn("IO exception while reading file {}.", dataDirectory, e);
+                }
+            }
+            for (Map.Entry<String, String> entry: blockDevices.entrySet())
+            {
+                String blockDeviceDirectory = entry.getKey();
+                String dataDirectory = entry.getValue();
+                try
+                {
+
+                    String readAheadKBPath = StartupChecks.getReadAheadKBPath(blockDeviceDirectory);
+
+                    final BufferedReader bufferedReader = Files.newBufferedReader(Path.of(readAheadKBPath));

Review comment:
       Perhaps we can have a slightly more succint check:
   ```
   Path readAheadKBPath = StartupChecks.getReadAheadKBPath(blockDeviceDirectory);
   if (readAheadKBPath != null && Files.exists(readAheadKBPath))
   {
       int readAheadKbSetting = Ints.fromBytesArray(Files.readAllBytes(readAheadKBPath));
       // perform  check
   }
   ```
   wdyt?

##########
File path: src/java/org/apache/cassandra/service/StartupChecks.java
##########
@@ -243,8 +247,8 @@ private void checkOutOfMemoryHandling()
             {
                 if (!jvmOptionsContainsOneOf("-XX:OnOutOfMemoryError="))
                     logger.warn("The JVM is not configured to stop on OutOfMemoryError which can cause data corruption."
-                            + " Either upgrade your JRE to a version greater or equal to 8u92 and use -XX:+ExitOnOutOfMemoryError/-XX:+CrashOnOutOfMemoryError"
-                            + " or use -XX:OnOutOfMemoryError=\"<cmd args>;<cmd args>\" on your current JRE.");
+                                + " Either upgrade your JRE to a version greater or equal to 8u92 and use -XX:+ExitOnOutOfMemoryError/-XX:+CrashOnOutOfMemoryError"

Review comment:
       Please remove unrelated formatting change.

##########
File path: src/java/org/apache/cassandra/service/StartupChecks.java
##########
@@ -285,6 +289,77 @@ public void execute()
         }
     };
 
+    public static final StartupCheck checkReadAheadKbSetting = new StartupCheck()
+    {
+        // This value is in KB.
+        private static final long MAX_RECOMMENDED_READ_AHEAD_KB_SETTING = 128;
+
+        @Override
+        public void execute() throws StartupException
+        {
+            if (!FBUtilities.isLinux)
+                return;
+
+            String[] dataDirectories = DatabaseDescriptor.getRawConfig().data_file_directories;
+            Map<String, String> blockDevices = new HashMap<String, String>();
+            for (String dataDirectory : dataDirectories)
+            {
+                try
+                {
+                    Path p = Path.of(dataDirectory);
+                    FileStore fs = Files.getFileStore(p);
+
+                    String blockDirectory = fs.name();
+                    if(StringUtils.isNotEmpty(blockDirectory))
+                    {
+                        blockDevices.put(blockDirectory, dataDirectory);
+                    }
+                }
+                catch (IOException e)
+                {
+                    logger.warn("IO exception while reading file {}.", dataDirectory, e);
+                }
+            }
+            for (Map.Entry<String, String> entry: blockDevices.entrySet())
+            {
+                String blockDeviceDirectory = entry.getKey();
+                String dataDirectory = entry.getValue();
+                try
+                {
+
+                    String readAheadKBPath = StartupChecks.getReadAheadKBPath(blockDeviceDirectory);
+
+                    final BufferedReader bufferedReader = Files.newBufferedReader(Path.of(readAheadKBPath));
+                    final String data = bufferedReader.readLine();
+                    if (data != null)
+                    {
+                        try
+                        {
+                            Integer readAheadKbSetting = Integer.parseInt(data);
+
+                            if (readAheadKbSetting > MAX_RECOMMENDED_READ_AHEAD_KB_SETTING)
+                            {
+                                logger.warn("Detected high 'read_ahead_kb' setting for device {} " +
+                                            "of data directory.{} It is Recommended to set this value to 8KB " +

Review comment:
       Please fix message

##########
File path: src/java/org/apache/cassandra/service/StartupChecks.java
##########
@@ -285,6 +289,77 @@ public void execute()
         }
     };
 
+    public static final StartupCheck checkReadAheadKbSetting = new StartupCheck()
+    {
+        // This value is in KB.
+        private static final long MAX_RECOMMENDED_READ_AHEAD_KB_SETTING = 128;
+
+        @Override
+        public void execute() throws StartupException
+        {
+            if (!FBUtilities.isLinux)
+                return;
+
+            String[] dataDirectories = DatabaseDescriptor.getRawConfig().data_file_directories;

Review comment:
       To improve readability and testability, maybe extract this to a different method?
   Something like:
   ```java
   Map<String, String> blockDevices = getBlockDevices(DatabaseDescriptor.getRawConfig().data_file_directories);
   ```
   
   Also perhaps add a simple test with a temporary directory (see: https://www.javacodegeeks.com/2021/03/creating-temporary-files-with-junit-5.html).




-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: pr-unsubscribe@cassandra.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



---------------------------------------------------------------------
To unsubscribe, e-mail: pr-unsubscribe@cassandra.apache.org
For additional commands, e-mail: pr-help@cassandra.apache.org