This is an automated email from the ASF dual-hosted git repository.

konstantinov pushed a commit to branch trunk
in repository https://gitbox.apache.org/repos/asf/cassandra.git


The following commit(s) were added to refs/heads/trunk by this push:
     new e621d8a6c1 Introduce separate GCInspector thresholds for concurrent GC 
events. For GC like ShenandoahGC/ZGC etc., there are GC events that do not have 
STW pauses (Concurrent phases). Operator might find it reasonable to use lower 
thresholds for events require STW pauses and higher thresholds for concurrent 
phases. gc_concurrent_phase_log_threshold and 
gc_concurrent_phase_warn_threshold configuration options are introduced in 
cassandra.yaml
e621d8a6c1 is described below

commit e621d8a6c1bb175bea4e7e25f1f39dcd3d648f76
Author: Yuqi Yan <[email protected]>
AuthorDate: Fri Oct 17 13:50:16 2025 -0700

    Introduce separate GCInspector thresholds for concurrent GC events.
    For GC like ShenandoahGC/ZGC etc., there are GC events that do not have STW 
pauses (Concurrent phases).
    Operator might find it reasonable to use lower thresholds for events 
require STW pauses and higher thresholds for concurrent phases.
    gc_concurrent_phase_log_threshold and gc_concurrent_phase_warn_threshold 
configuration options are introduced in cassandra.yaml
    
    Patch by Yuqi Yan; reviewed by Dmitry Konstantinov, Stefan Miklosovic for 
CASSANDRA-20980
    Co-authored-by: Stefan Miklosovic
---
 CHANGES.txt                                        |  1 +
 conf/cassandra.yaml                                | 16 ++++
 conf/cassandra_latest.yaml                         | 16 ++++
 src/java/org/apache/cassandra/config/Config.java   |  2 +
 .../cassandra/config/DatabaseDescriptor.java       | 80 ++++++++++++++++--
 .../org/apache/cassandra/service/GCInspector.java  | 95 ++++++++++++++++------
 .../cassandra/service/GCInspectorMXBean.java       |  4 +
 .../apache/cassandra/service/GCInspectorTest.java  | 84 +++++++++++++++----
 8 files changed, 251 insertions(+), 47 deletions(-)

diff --git a/CHANGES.txt b/CHANGES.txt
index 7b0116f005..858864ddd9 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -1,4 +1,5 @@
 5.1
+ * Introduce separate GCInspector thresholds for concurrent GC events 
(CASSANDRA-20980)
  * Reduce contention in MemtableAllocator.allocate (CASSANDRA-20226)
  * Add export, list, import sub-commands for nodetool compressiondictionary 
(CASSANDRA-20941)
  * Add support in the binary protocol to allow transactions to have multiple 
conditions (CASSANDRA-20883)
diff --git a/conf/cassandra.yaml b/conf/cassandra.yaml
index d000f6be36..20476ee547 100644
--- a/conf/cassandra.yaml
+++ b/conf/cassandra.yaml
@@ -2048,6 +2048,11 @@ batch_size_fail_threshold: 50KiB
 # Log WARN on any batches not of type LOGGED than span across more partitions 
than this limit
 unlogged_batch_across_partitions_warn_threshold: 10
 
+# GCInspector configs:
+# For GC like ShenandoahGC/ZGC etc., there are GC events that do not have STW 
pauses (Concurrent phases)
+# Operator might find it reasonable to use lower thresholds for events require 
STW pauses and higher thresholds
+# for concurrent phases.
+#
 # GC Pauses greater than 200 ms will be logged at INFO level
 # This threshold can be adjusted to minimize logging if necessary
 # Min unit: ms
@@ -2059,6 +2064,17 @@ unlogged_batch_across_partitions_warn_threshold: 10
 # Min unit: ms
 # gc_warn_threshold: 1000ms
 
+# GC Concurrent phase greater than 1000 ms will be logged at INFO level
+# This threshold can be adjusted to minimize logging if necessary
+# Min unit: ms
+# gc_concurrent_phase_log_threshold: 1000ms
+
+# GC Concurrent phase than gc_concurrent_phase_warn_threshold will be logged 
at WARN level
+# Adjust the threshold based on your application throughput requirement. 
Setting to 0
+# will deactivate the feature.
+# Min unit: ms
+# gc_concurrent_phase_warn_threshold: 2000ms
+
 # Maximum size of any value in SSTables. Safety measure to detect SSTable 
corruption
 # early. Any value size larger than this threshold will result into marking an 
SSTable
 # as corrupted. This should be positive and less than 2GiB.
diff --git a/conf/cassandra_latest.yaml b/conf/cassandra_latest.yaml
index 6d3f7b7a49..b43f6cf45c 100644
--- a/conf/cassandra_latest.yaml
+++ b/conf/cassandra_latest.yaml
@@ -1913,6 +1913,11 @@ batch_size_fail_threshold: 50KiB
 # Log WARN on any batches not of type LOGGED than span across more partitions 
than this limit
 unlogged_batch_across_partitions_warn_threshold: 10
 
+# GCInspector configs:
+# For GC like ShenandoahGC/ZGC etc., there are GC events that do not have STW 
pauses.
+# Such events are called Concurrent phases. Operator might find it reasonable 
to use lower thresholds
+# for events require STW pauses and higher thresholds for concurrent phases.
+#
 # GC Pauses greater than 200 ms will be logged at INFO level
 # This threshold can be adjusted to minimize logging if necessary
 # Min unit: ms
@@ -1924,6 +1929,17 @@ unlogged_batch_across_partitions_warn_threshold: 10
 # Min unit: ms
 # gc_warn_threshold: 1000ms
 
+# GC Concurrent phase greater than 1000 ms will be logged at INFO level
+# This threshold can be adjusted to minimize logging if necessary
+# Min unit: ms
+# gc_concurrent_phase_log_threshold: 1000ms
+
+# GC Concurrent phase than gc_concurrent_phase_warn_threshold will be logged 
at WARN level
+# Adjust the threshold based on your application throughput requirement. 
Setting to 0
+# will deactivate the feature.
+# Min unit: ms
+# gc_concurrent_phase_warn_threshold: 2000ms
+
 # Maximum size of any value in SSTables. Safety measure to detect SSTable 
corruption
 # early. Any value size larger than this threshold will result into marking an 
SSTable
 # as corrupted. This should be positive and less than 2GiB.
diff --git a/src/java/org/apache/cassandra/config/Config.java 
b/src/java/org/apache/cassandra/config/Config.java
index 33a0a25283..7ea67ebc4c 100644
--- a/src/java/org/apache/cassandra/config/Config.java
+++ b/src/java/org/apache/cassandra/config/Config.java
@@ -593,6 +593,8 @@ public class Config
     public volatile DurationSpec.IntMillisecondsBound gc_log_threshold = new 
DurationSpec.IntMillisecondsBound("200ms");
     @Replaces(oldName = "gc_warn_threshold_in_ms", converter = 
Converters.MILLIS_DURATION_INT, deprecated = true)
     public volatile DurationSpec.IntMillisecondsBound gc_warn_threshold = new 
DurationSpec.IntMillisecondsBound("1s");
+    public volatile DurationSpec.IntMillisecondsBound 
gc_concurrent_phase_log_threshold = new DurationSpec.IntMillisecondsBound("1s");
+    public volatile DurationSpec.IntMillisecondsBound 
gc_concurrent_phase_warn_threshold = new 
DurationSpec.IntMillisecondsBound("2s");
 
     // TTL for different types of trace events.
     @Replaces(oldName = "tracetype_query_ttl", converter = 
Converters.SECONDS_DURATION, deprecated=true)
diff --git a/src/java/org/apache/cassandra/config/DatabaseDescriptor.java 
b/src/java/org/apache/cassandra/config/DatabaseDescriptor.java
index 753a7413ea..dec260f9b9 100644
--- a/src/java/org/apache/cassandra/config/DatabaseDescriptor.java
+++ b/src/java/org/apache/cassandra/config/DatabaseDescriptor.java
@@ -1220,6 +1220,22 @@ public class DatabaseDescriptor
         // run audit logging options through sanitation and validation
         if (conf.audit_logging_options != null)
             setAuditLoggingOptions(conf.audit_logging_options);
+
+        try
+        {
+            // Run through the validation by setting current values back to 
their setters, so we are sure that their values are valid.
+            // We are catching IllegalArgumentException and translating it to 
ConfigurationException to comply with
+            // rest of the logic in this method. These setters are also called 
in GCInspectorMXBean were IllegalArgumentException
+            // is thrown when arguments are invalid instead 
ConfigurationException, on purpose.
+            DatabaseDescriptor.setGCLogThreshold((int) 
DatabaseDescriptor.getGCLogThreshold());
+            DatabaseDescriptor.setGCWarnThreshold((int) 
DatabaseDescriptor.getGCWarnThreshold());
+            
DatabaseDescriptor.setGCConcurrentPhaseLogThreshold(DatabaseDescriptor.getGCConcurrentPhaseLogThreshold());
+            
DatabaseDescriptor.setGCConcurrentPhaseWarnThreshold(DatabaseDescriptor.getGCConcurrentPhaseWarnThreshold());
+        }
+        catch (IllegalArgumentException ex)
+        {
+            throw new ConfigurationException(ex.getMessage());
+        }
     }
 
     @VisibleForTesting
@@ -4719,14 +4735,17 @@ public class DatabaseDescriptor
         return conf.gc_log_threshold.toMilliseconds();
     }
 
-    public static void setGCLogThreshold(int gcLogThreshold)
+    public static void setGCLogThreshold(int threshold)
     {
-        conf.gc_log_threshold = new 
DurationSpec.IntMillisecondsBound(gcLogThreshold);
-    }
+        if (threshold <= 0)
+            throw new IllegalArgumentException("Threshold value for 
gc_log_threshold must be greater than 0");
 
-    public static EncryptionContext getEncryptionContext()
-    {
-        return encryptionContext;
+        long gcWarnThresholdInMs = getGCWarnThreshold();
+        if (gcWarnThresholdInMs != 0 && threshold > gcWarnThresholdInMs)
+            throw new IllegalArgumentException("Threshold value for 
gc_log_threshold (" + threshold + ") must be less than gc_warn_threshold which 
is currently "
+                                               + gcWarnThresholdInMs);
+
+        conf.gc_log_threshold = new 
DurationSpec.IntMillisecondsBound(threshold);
     }
 
     public static long getGCWarnThreshold()
@@ -4736,9 +4755,58 @@ public class DatabaseDescriptor
 
     public static void setGCWarnThreshold(int threshold)
     {
+        if (threshold < 0)
+            throw new IllegalArgumentException("Threshold value for 
gc_warn_threshold must be greater than or equal to 0");
+
+        long gcLogThresholdInMs = getGCLogThreshold();
+        if (threshold != 0 && threshold <= gcLogThresholdInMs)
+            throw new IllegalArgumentException("Threshold value for 
gc_warn_threshold (" + threshold + ") must be greater than gc_log_threshold 
which is currently "
+                                               + gcLogThresholdInMs);
+
         conf.gc_warn_threshold = new 
DurationSpec.IntMillisecondsBound(threshold);
     }
 
+    public static int getGCConcurrentPhaseLogThreshold()
+    {
+        return conf.gc_concurrent_phase_log_threshold.toMilliseconds();
+    }
+
+    public static void setGCConcurrentPhaseLogThreshold(int threshold)
+    {
+        if (threshold <= 0)
+            throw new IllegalArgumentException("Threshold must be greater than 
0");
+
+        long gcConcurrentPhaseWarnThresholdInMs = 
getGCConcurrentPhaseWarnThreshold();
+        if (gcConcurrentPhaseWarnThresholdInMs != 0 && threshold > 
gcConcurrentPhaseWarnThresholdInMs)
+            throw new IllegalArgumentException("Threshold value for 
gc_concurrent_phase_log_threshold (" + threshold + ") must be less than 
gc_concurrent_phase_warn_threshold which is currently "
+                                               + 
gcConcurrentPhaseWarnThresholdInMs);
+
+        conf.gc_concurrent_phase_log_threshold = new 
DurationSpec.IntMillisecondsBound(threshold);
+    }
+
+    public static int getGCConcurrentPhaseWarnThreshold()
+    {
+        return conf.gc_concurrent_phase_warn_threshold.toMilliseconds();
+    }
+
+    public static void setGCConcurrentPhaseWarnThreshold(int threshold)
+    {
+        if (threshold < 0)
+            throw new IllegalArgumentException("Threshold value for 
gc_concurrent_phase_warn_threshold must be greater than or equal to 0");
+
+        long gcConcurrentPhaseLogThresholdInMs = 
getGCConcurrentPhaseLogThreshold();
+        if (threshold != 0 && threshold <= gcConcurrentPhaseLogThresholdInMs)
+            throw new IllegalArgumentException("Threshold value for 
gc_concurrent_phase_warn_threshold (" + threshold + ") must be greater than 
gc_concurrent_phase_log_threshold which is currently "
+                                               + 
gcConcurrentPhaseLogThresholdInMs);
+
+        conf.gc_concurrent_phase_warn_threshold = new 
DurationSpec.IntMillisecondsBound(threshold);
+    }
+
+    public static EncryptionContext getEncryptionContext()
+    {
+        return encryptionContext;
+    }
+
     public static boolean isCDCEnabled()
     {
         return conf.cdc_enabled;
diff --git a/src/java/org/apache/cassandra/service/GCInspector.java 
b/src/java/org/apache/cassandra/service/GCInspector.java
index 4bdc3a05ba..25d741b847 100644
--- a/src/java/org/apache/cassandra/service/GCInspector.java
+++ b/src/java/org/apache/cassandra/service/GCInspector.java
@@ -173,6 +173,7 @@ public class GCInspector implements NotificationListener, 
GCInspectorMXBean
         ObjectName gcName = new 
ObjectName(ManagementFactory.GARBAGE_COLLECTOR_MXBEAN_DOMAIN_TYPE + ",*");
         for (ObjectName name : server.queryNames(gcName, null))
         {
+            logger.info("Notification listener is added for: {}", name);
             server.addNotificationListener(name, inspector, null, null);
         }
     }
@@ -261,6 +262,11 @@ public class GCInspector implements NotificationListener, 
GCInspectorMXBean
                 duration = total - previousTotal; // may be zero for a really 
fast collection
             }
 
+            logger.trace("Received GarbageCollectionNotificationInfo with 
gcId: {}, gcName: {}, " +
+                         "gcCause: {}, gcAction: {}, event duration: {} ms, 
calculated duration: {} ms",
+                         gcInfo.getId(), info.getGcName(), info.getGcCause(), 
info.getGcAction(),
+                         gcInfo.getDuration(), duration);
+
             StringBuilder sb = new StringBuilder();
             sb.append(info.getGcName()).append(" GC in 
").append(duration).append("ms.  ");
             long bytes = 0;
@@ -287,16 +293,31 @@ public class GCInspector implements NotificationListener, 
GCInspectorMXBean
                 if (state.compareAndSet(prev, new State(duration, bytes, 
prev)))
                     break;
             }
-            
-            if (getGcWarnThresholdInMs() != 0 && duration > 
getGcWarnThresholdInMs())
-                logger.warn(sb.toString());
-            else if (duration > getGcLogThresholdInMs())
-                logger.info(sb.toString());
-            else if (logger.isTraceEnabled())
-                logger.trace(sb.toString());
 
-            if (duration > this.getStatusThresholdInMs())
-                StatusLogger.log();
+            if (isConcurrentPhase(info.getGcCause(), info.getGcName()))
+            {
+                if (getGcConcurrentPhaseWarnThresholdInMs() != 0 && duration > 
getGcConcurrentPhaseWarnThresholdInMs())
+                    logger.warn(sb.toString());
+                else if (duration > getGcConcurrentPhaseLogThresholdInMs())
+                    logger.info(sb.toString());
+                else if (logger.isTraceEnabled())
+                    logger.trace(sb.toString());
+
+                if (duration > this.getConcurrentStatusThresholdInMs())
+                    StatusLogger.log();
+            }
+            else
+            {
+                if (getGcWarnThresholdInMs() != 0 && duration > 
getGcWarnThresholdInMs())
+                    logger.warn(sb.toString());
+                else if (duration > getGcLogThresholdInMs())
+                    logger.info(sb.toString());
+                else if (logger.isTraceEnabled())
+                    logger.trace(sb.toString());
+
+                if (duration > this.getStatusThresholdInMs())
+                    StatusLogger.log();
+            }
 
             // if we just finished an old gen collection and we're still using 
a lot of memory, try to reduce the pressure
             if (gcState.assumeGCIsOldGen)
@@ -304,6 +325,24 @@ public class GCInspector implements NotificationListener, 
GCInspectorMXBean
         }
     }
 
+    static boolean isConcurrentPhase(String cause, String name) {
+        // Mostly taken from: 
https://github.com/Netflix/spectator/blob/v1.7.x/spectator-ext-gc/src/main/java/com/netflix/spectator/gc/GcLogger.java
+        // So far the only indicator known is that the cause will be reported 
as "No GC"
+        // when using CMS.
+        //
+        // For ZGC, behavior was changed in JDK17: 
https://bugs.openjdk.java.net/browse/JDK-8265136
+        // For ZGC in older versions, there is no way to accurately get the 
amount of time
+        // in STW pauses.
+        //
+        // For G1, a new bean is added in JDK20 to indicate time spent in 
concurrent phases:
+        // https://bugs.openjdk.org/browse/JDK-8297247
+
+        return "No GC".equals(cause)                // CMS
+               || "G1 Concurrent GC".equals(name)   // G1 in JDK20+
+               || name.endsWith(" Cycles");         // Shenandoah, ZGC
+    }
+
+
     public State getTotalSinceLastCheck()
     {
         return state.getAndSet(new State());
@@ -378,14 +417,6 @@ public class GCInspector implements NotificationListener, 
GCInspectorMXBean
 
     public void setGcWarnThresholdInMs(long threshold)
     {
-        long gcLogThresholdInMs = getGcLogThresholdInMs();
-        if (threshold < 0)
-            throw new IllegalArgumentException("Threshold must be greater than 
or equal to 0");
-        if (threshold != 0 && threshold <= gcLogThresholdInMs)
-            throw new IllegalArgumentException("Threshold must be greater than 
gcLogThresholdInMs which is currently "
-                    + gcLogThresholdInMs);
-        if (threshold > Integer.MAX_VALUE)
-            throw new IllegalArgumentException("Threshold must be less than 
Integer.MAX_VALUE");
         DatabaseDescriptor.setGCWarnThreshold((int)threshold);
     }
 
@@ -396,15 +427,27 @@ public class GCInspector implements NotificationListener, 
GCInspectorMXBean
 
     public void setGcLogThresholdInMs(long threshold)
     {
-        if (threshold <= 0)
-            throw new IllegalArgumentException("Threshold must be greater than 
0");
+        DatabaseDescriptor.setGCLogThreshold((int) threshold);
+    }
+
+    public int getGcConcurrentPhaseWarnThresholdInMs()
+    {
+        return DatabaseDescriptor.getGCConcurrentPhaseWarnThreshold();
+    }
 
-        long gcWarnThresholdInMs = getGcWarnThresholdInMs();
-        if (gcWarnThresholdInMs != 0 && threshold > gcWarnThresholdInMs)
-            throw new IllegalArgumentException("Threshold must be less than 
gcWarnThresholdInMs which is currently "
-                                               + gcWarnThresholdInMs);
+    public void setGcConcurrentPhaseWarnThresholdInMs(int threshold)
+    {
+        DatabaseDescriptor.setGCConcurrentPhaseWarnThreshold(threshold);
+    }
 
-        DatabaseDescriptor.setGCLogThreshold((int) threshold);
+    public int getGcConcurrentPhaseLogThresholdInMs()
+    {
+        return DatabaseDescriptor.getGCConcurrentPhaseLogThreshold();
+    }
+
+    public void setGcConcurrentPhaseLogThresholdInMs(int threshold)
+    {
+        DatabaseDescriptor.setGCConcurrentPhaseLogThreshold(threshold);
     }
 
     public long getGcLogThresholdInMs()
@@ -417,4 +460,8 @@ public class GCInspector implements NotificationListener, 
GCInspectorMXBean
         return getGcWarnThresholdInMs() != 0 ? getGcWarnThresholdInMs() : 
getGcLogThresholdInMs();
     }
 
+    public long getConcurrentStatusThresholdInMs()
+    {
+        return getGcConcurrentPhaseWarnThresholdInMs() != 0 ? 
getGcConcurrentPhaseWarnThresholdInMs() : 
getGcConcurrentPhaseLogThresholdInMs();
+    }
 }
diff --git a/src/java/org/apache/cassandra/service/GCInspectorMXBean.java 
b/src/java/org/apache/cassandra/service/GCInspectorMXBean.java
index 08795ed269..9765e9813c 100644
--- a/src/java/org/apache/cassandra/service/GCInspectorMXBean.java
+++ b/src/java/org/apache/cassandra/service/GCInspectorMXBean.java
@@ -27,6 +27,10 @@ public interface GCInspectorMXBean
     void setGcWarnThresholdInMs(long threshold);
     long getGcWarnThresholdInMs();
     void setGcLogThresholdInMs(long threshold);
+    int getGcConcurrentPhaseLogThresholdInMs();
+    void setGcConcurrentPhaseWarnThresholdInMs(int threshold);
+    int getGcConcurrentPhaseWarnThresholdInMs();
+    void setGcConcurrentPhaseLogThresholdInMs(int threshold);
     long getGcLogThresholdInMs();
     long getStatusThresholdInMs();
 }
diff --git a/test/unit/org/apache/cassandra/service/GCInspectorTest.java 
b/test/unit/org/apache/cassandra/service/GCInspectorTest.java
index bcdf023294..51d6a22a56 100644
--- a/test/unit/org/apache/cassandra/service/GCInspectorTest.java
+++ b/test/unit/org/apache/cassandra/service/GCInspectorTest.java
@@ -18,11 +18,16 @@
 package org.apache.cassandra.service;
 
 import org.apache.cassandra.config.DatabaseDescriptor;
-import org.junit.Assert;
+
 import org.junit.Before;
 import org.junit.BeforeClass;
 import org.junit.Test;
 
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
 public class GCInspectorTest
 {
     
@@ -43,38 +48,61 @@ public class GCInspectorTest
     @Test
     public void ensureStaticFieldsHydrateFromConfig()
     {    
-        Assert.assertEquals(DatabaseDescriptor.getGCLogThreshold(), 
gcInspector.getGcLogThresholdInMs());
-        Assert.assertEquals(DatabaseDescriptor.getGCWarnThreshold(), 
gcInspector.getGcWarnThresholdInMs());
+        assertEquals(DatabaseDescriptor.getGCLogThreshold(), 
gcInspector.getGcLogThresholdInMs());
+        assertEquals(DatabaseDescriptor.getGCWarnThreshold(), 
gcInspector.getGcWarnThresholdInMs());
+        assertEquals(DatabaseDescriptor.getGCConcurrentPhaseLogThreshold(), 
gcInspector.getGcConcurrentPhaseLogThresholdInMs());
+        assertEquals(DatabaseDescriptor.getGCConcurrentPhaseWarnThreshold(), 
gcInspector.getGcConcurrentPhaseWarnThresholdInMs());
     }
     
     @Test
     public void ensureStatusIsCalculated()
     {
-        Assert.assertTrue(gcInspector.getStatusThresholdInMs() > 0);
+        assertTrue(gcInspector.getStatusThresholdInMs() > 0);
     }
     
-    @Test(expected=IllegalArgumentException.class)
+    @Test
     public void ensureWarnGreaterThanLog()
     {
-        
gcInspector.setGcWarnThresholdInMs(gcInspector.getGcLogThresholdInMs());
+        assertThatThrownBy(() -> 
gcInspector.setGcWarnThresholdInMs(gcInspector.getGcLogThresholdInMs()))
+        .isInstanceOf(IllegalArgumentException.class)
+        .hasMessage("Threshold value for gc_warn_threshold (200) must be 
greater than gc_log_threshold which is currently 200");
+
+        assertThatThrownBy(() -> 
gcInspector.setGcConcurrentPhaseWarnThresholdInMs(gcInspector.getGcConcurrentPhaseLogThresholdInMs()))
+        .isInstanceOf(IllegalArgumentException.class)
+        .hasMessage("Threshold value for gc_concurrent_phase_warn_threshold 
(1000) must be greater than gc_concurrent_phase_log_threshold which is 
currently 1000");
     }
     
     @Test
     public void ensureZeroIsOk()
     {
         gcInspector.setGcWarnThresholdInMs(0);
-        Assert.assertEquals(gcInspector.getStatusThresholdInMs(), 
gcInspector.getGcLogThresholdInMs());
-        Assert.assertEquals(0, DatabaseDescriptor.getGCWarnThreshold());
-        Assert.assertEquals(200, DatabaseDescriptor.getGCLogThreshold());
+        assertEquals(gcInspector.getStatusThresholdInMs(), 
gcInspector.getGcLogThresholdInMs());
+        assertEquals(0, DatabaseDescriptor.getGCWarnThreshold());
+        assertEquals(200, DatabaseDescriptor.getGCLogThreshold());
+
+        gcInspector.setGcConcurrentPhaseWarnThresholdInMs(0);
+        assertEquals(0, 
DatabaseDescriptor.getGCConcurrentPhaseWarnThreshold());
+        assertEquals(1000, 
DatabaseDescriptor.getGCConcurrentPhaseLogThreshold());
     }
     
-    @Test(expected=IllegalArgumentException.class)
+    @Test
     public void ensureLogLessThanWarn()
     {
-        Assert.assertEquals(200, gcInspector.getGcLogThresholdInMs());
+        assertEquals(200, gcInspector.getGcLogThresholdInMs());
         gcInspector.setGcWarnThresholdInMs(1000);
-        Assert.assertEquals(1000, gcInspector.getGcWarnThresholdInMs());
-        gcInspector.setGcLogThresholdInMs(gcInspector.getGcWarnThresholdInMs() 
+ 1);
+        assertEquals(1000, gcInspector.getGcWarnThresholdInMs());
+
+        assertThatThrownBy(() -> 
gcInspector.setGcLogThresholdInMs(gcInspector.getGcWarnThresholdInMs() + 1))
+        .isInstanceOf(IllegalArgumentException.class)
+        .hasMessage("Threshold value for gc_log_threshold (1001) must be less 
than gc_warn_threshold which is currently 1000");
+
+        assertEquals(1000, gcInspector.getGcConcurrentPhaseLogThresholdInMs());
+        gcInspector.setGcConcurrentPhaseWarnThresholdInMs(2000);
+        assertEquals(2000, 
gcInspector.getGcConcurrentPhaseWarnThresholdInMs());
+
+        assertThatThrownBy(() -> 
gcInspector.setGcConcurrentPhaseLogThresholdInMs(gcInspector.getGcConcurrentPhaseWarnThresholdInMs()
 + 1))
+        .isInstanceOf(IllegalArgumentException.class)
+        .hasMessage("Threshold value for gc_concurrent_phase_log_threshold 
(2001) must be less than gc_concurrent_phase_warn_threshold which is currently 
2000");
     }
     
     @Test
@@ -82,10 +110,16 @@ public class GCInspectorTest
     {
         gcInspector.setGcLogThresholdInMs(200);
         gcInspector.setGcWarnThresholdInMs(1000);
-        Assert.assertEquals(200, DatabaseDescriptor.getGCLogThreshold());
-        Assert.assertEquals(200, gcInspector.getGcLogThresholdInMs());
-        Assert.assertEquals(1000, DatabaseDescriptor.getGCWarnThreshold());
-        Assert.assertEquals(1000, gcInspector.getGcWarnThresholdInMs());
+        gcInspector.setGcConcurrentPhaseLogThresholdInMs(1000);
+        gcInspector.setGcConcurrentPhaseWarnThresholdInMs(2000);
+        assertEquals(200, DatabaseDescriptor.getGCLogThreshold());
+        assertEquals(200, gcInspector.getGcLogThresholdInMs());
+        assertEquals(1000, DatabaseDescriptor.getGCWarnThreshold());
+        assertEquals(1000, gcInspector.getGcWarnThresholdInMs());
+        assertEquals(1000, 
DatabaseDescriptor.getGCConcurrentPhaseLogThreshold());
+        assertEquals(1000, gcInspector.getGcConcurrentPhaseLogThresholdInMs());
+        assertEquals(2000, 
DatabaseDescriptor.getGCConcurrentPhaseWarnThreshold());
+        assertEquals(2000, 
gcInspector.getGcConcurrentPhaseWarnThresholdInMs());
     }
 
     @Test(expected=IllegalArgumentException.class)
@@ -94,4 +128,20 @@ public class GCInspectorTest
         gcInspector.setGcLogThresholdInMs(200);
         gcInspector.setGcWarnThresholdInMs(Integer.MAX_VALUE+1L);
     }
+
+    @Test
+    public void testIsConcurrentPhase()
+    {
+        assertTrue("No GC cause should be considered concurrent",
+                         GCInspector.isConcurrentPhase("No GC", "SomeGCName"));
+        assertTrue("Shenandoah Cycles should be considered concurrent",
+                         GCInspector.isConcurrentPhase("SomeCause", 
"Shenandoah Cycles"));
+        assertTrue("ZGC Cycles should be considered concurrent",
+                         GCInspector.isConcurrentPhase("SomeCause", "ZGC 
Cycles"));
+
+        assertFalse("ParallelGC should not be considered concurrent",
+                          GCInspector.isConcurrentPhase("Allocation Failure", 
"PS Scavenge"));
+        assertFalse("G1 Young Generation should not be considered concurrent",
+                          GCInspector.isConcurrentPhase("G1 Evacuation Pause", 
"G1 Young Generation"));
+    }
 }


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to