switch to notification-based GCInspector patch by Yuki Morishita, Brandon Williams, and jbellis for CASSANDRA-7638
Project: http://git-wip-us.apache.org/repos/asf/cassandra/repo Commit: http://git-wip-us.apache.org/repos/asf/cassandra/commit/41a85afe Tree: http://git-wip-us.apache.org/repos/asf/cassandra/tree/41a85afe Diff: http://git-wip-us.apache.org/repos/asf/cassandra/diff/41a85afe Branch: refs/heads/cassandra-2.1.0 Commit: 41a85afe65e059662c3247de694481a405bc87b1 Parents: fe78e81 Author: Jonathan Ellis <[email protected]> Authored: Fri Aug 1 20:59:01 2014 -0500 Committer: Jonathan Ellis <[email protected]> Committed: Fri Aug 1 20:59:32 2014 -0500 ---------------------------------------------------------------------- CHANGES.txt | 1 + conf/cassandra-env.sh | 7 +- .../cassandra/service/CassandraDaemon.java | 2 +- .../apache/cassandra/service/GCInspector.java | 108 +++++++------------ 4 files changed, 48 insertions(+), 70 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/cassandra/blob/41a85afe/CHANGES.txt ---------------------------------------------------------------------- diff --git a/CHANGES.txt b/CHANGES.txt index c95a22c..f4b3921 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,4 +1,5 @@ 2.1.0-final + * Switch to notification-based GCInspector (CASSANDRA-7638) * (cqlsh) Handle nulls in UDTs and tuples correctly (CASSANDRA-7656) * Don't use strict consistency when replacing (CASSANDRA-7568) * Fix min/max cell name collection on 2.0 SSTables with range http://git-wip-us.apache.org/repos/asf/cassandra/blob/41a85afe/conf/cassandra-env.sh ---------------------------------------------------------------------- diff --git a/conf/cassandra-env.sh b/conf/cassandra-env.sh index f747438..21a4244 100644 --- a/conf/cassandra-env.sh +++ b/conf/cassandra-env.sh @@ -95,7 +95,12 @@ JVM_VERSION=${jvmver%_*} JVM_PATCH_VERSION=${jvmver#*_} if [ "$JVM_VERSION" \< "1.7" ] ; then - echo "Cassandra 2.0 and later require Java 7 or later." + echo "Cassandra 2.0 and later require Java 7u25 or later." + exit 1; +fi + +if [ "$JVM_VERSION" \< "1.8" ] && [ "$JVM_PATCH_VERSION" -lt "25" ] ; then + echo "Cassandra 2.0 and later require Java 7u25 or later." exit 1; fi http://git-wip-us.apache.org/repos/asf/cassandra/blob/41a85afe/src/java/org/apache/cassandra/service/CassandraDaemon.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/service/CassandraDaemon.java b/src/java/org/apache/cassandra/service/CassandraDaemon.java index fbee7ce..7c85f81 100644 --- a/src/java/org/apache/cassandra/service/CassandraDaemon.java +++ b/src/java/org/apache/cassandra/service/CassandraDaemon.java @@ -281,7 +281,7 @@ public class CassandraDaemon try { - GCInspector.instance.start(); + GCInspector.register(); } catch (Throwable t) { http://git-wip-us.apache.org/repos/asf/cassandra/blob/41a85afe/src/java/org/apache/cassandra/service/GCInspector.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/service/GCInspector.java b/src/java/org/apache/cassandra/service/GCInspector.java index 9961bf9..c766dc0 100644 --- a/src/java/org/apache/cassandra/service/GCInspector.java +++ b/src/java/org/apache/cassandra/service/GCInspector.java @@ -17,111 +17,83 @@ */ package org.apache.cassandra.service; -import java.lang.management.GarbageCollectorMXBean; import java.lang.management.ManagementFactory; -import java.lang.management.MemoryMXBean; import java.lang.management.MemoryUsage; import java.util.ArrayList; -import java.util.HashMap; +import java.util.Collections; import java.util.List; -import java.util.concurrent.TimeUnit; +import java.util.Map; import javax.management.MBeanServer; +import javax.management.Notification; +import javax.management.NotificationListener; import javax.management.ObjectName; +import javax.management.openmbean.CompositeData; +import com.sun.management.GarbageCollectionNotificationInfo; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.apache.cassandra.io.sstable.SSTableDeletingTask; import org.apache.cassandra.utils.StatusLogger; -public class GCInspector +public class GCInspector implements NotificationListener { private static final Logger logger = LoggerFactory.getLogger(GCInspector.class); - final static long INTERVAL_IN_MS = 1000; final static long MIN_DURATION = 200; final static long MIN_DURATION_TPSTATS = 1000; - public static final GCInspector instance = new GCInspector(); - - private final HashMap<String, Long> gctimes = new HashMap<String, Long>(); - private final HashMap<String, Long> gccounts = new HashMap<String, Long>(); - - final List<GarbageCollectorMXBean> beans = new ArrayList<GarbageCollectorMXBean>(); - final MemoryMXBean membean = ManagementFactory.getMemoryMXBean(); - - public GCInspector() + public static void register() throws Exception { + GCInspector inspector = new GCInspector(); MBeanServer server = ManagementFactory.getPlatformMBeanServer(); - try - { - ObjectName gcName = new ObjectName(ManagementFactory.GARBAGE_COLLECTOR_MXBEAN_DOMAIN_TYPE + ",*"); - for (ObjectName name : server.queryNames(gcName, null)) - { - GarbageCollectorMXBean gc = ManagementFactory.newPlatformMXBeanProxy(server, name.getCanonicalName(), GarbageCollectorMXBean.class); - beans.add(gc); - } - } - catch (Exception e) + ObjectName gcName = new ObjectName(ManagementFactory.GARBAGE_COLLECTOR_MXBEAN_DOMAIN_TYPE + ",*"); + for (ObjectName name : server.queryNames(gcName, null)) { - throw new RuntimeException(e); + server.addNotificationListener(name, inspector, null, null); } } - public void start() - { - // don't bother starting a thread that will do nothing. - if (beans.size() == 0) - return; - Runnable t = new Runnable() - { - public void run() - { - logGCResults(); - } - }; - StorageService.scheduledTasks.scheduleWithFixedDelay(t, INTERVAL_IN_MS, INTERVAL_IN_MS, TimeUnit.MILLISECONDS); - } - - private void logGCResults() + public void handleNotification(Notification notification, Object handback) { - for (GarbageCollectorMXBean gc : beans) + String type = notification.getType(); + if (type.equals(GarbageCollectionNotificationInfo.GARBAGE_COLLECTION_NOTIFICATION)) { - Long previousTotal = gctimes.get(gc.getName()); - Long total = gc.getCollectionTime(); - if (previousTotal == null) - previousTotal = 0L; - if (previousTotal.equals(total)) - continue; - gctimes.put(gc.getName(), total); - Long duration = total - previousTotal; // may be zero for a really fast collection - - Long previousCount = gccounts.get(gc.getName()); - Long count = gc.getCollectionCount(); + // retrieve the garbage collection notification information + CompositeData cd = (CompositeData) notification.getUserData(); + GarbageCollectionNotificationInfo info = GarbageCollectionNotificationInfo.from(cd); - if (previousCount == null) - previousCount = 0L; - if (count.equals(previousCount)) - continue; + long duration = info.getGcInfo().getDuration(); - gccounts.put(gc.getName(), count); + StringBuilder sb = new StringBuilder(); + sb.append(info.getGcName()).append(" GC in ").append(duration).append("ms. "); - MemoryUsage mu = membean.getHeapMemoryUsage(); - long memoryUsed = mu.getUsed(); - long memoryMax = mu.getMax(); + List<String> keys = new ArrayList<>(info.getGcInfo().getMemoryUsageBeforeGc().keySet()); + Collections.sort(keys); + for (String key : keys) + { + MemoryUsage before = info.getGcInfo().getMemoryUsageBeforeGc().get(key); + MemoryUsage after = info.getGcInfo().getMemoryUsageAfterGc().get(key); + if (after != null && after.getUsed() != before.getUsed()) + { + sb.append(key).append(": ").append(before.getUsed()); + sb.append(" -> "); + sb.append(after.getUsed()); + if (!key.equals(keys.get(keys.size() - 1))) + sb.append("; "); + } + } - String st = String.format("GC for %s: %s ms for %s collections, %s used; max is %s", - gc.getName(), duration, count - previousCount, memoryUsed, memoryMax); - long durationPerCollection = duration / (count - previousCount); - if (durationPerCollection > MIN_DURATION) + String st = sb.toString(); + if (duration > MIN_DURATION) logger.info(st); else if (logger.isDebugEnabled()) logger.debug(st); - if (durationPerCollection > MIN_DURATION_TPSTATS) + if (duration > MIN_DURATION_TPSTATS) StatusLogger.log(); // if we just finished a full collection and we're still using a lot of memory, try to reduce the pressure - if (gc.getName().equals("ConcurrentMarkSweep")) + if (info.getGcName().equals("ConcurrentMarkSweep")) SSTableDeletingTask.rescheduleFailedTasks(); } }
