/* * Copyright 2007-2010 Sun Microsystems, Inc. * * This file is part of Project Darkstar Server. * * Project Darkstar Server is free software: you can redistribute it * and/or modify it under the terms of the GNU General Public License * version 2 as published by the Free Software Foundation and * distributed hereunder to you. * * Project Darkstar Server is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * * -- */ package com.sun.sgs.impl.profile; import com.sun.sgs.impl.kernel.ConfigManager; import com.sun.sgs.management.TaskAggregateMXBean; import com.sun.sgs.profile.AggregateProfileCounter; import com.sun.sgs.profile.AggregateProfileSample; import com.sun.sgs.profile.ProfileCollector; import com.sun.sgs.profile.ProfileCollector.ProfileLevel; import com.sun.sgs.profile.ProfileConsumer; import com.sun.sgs.profile.ProfileConsumer.ProfileDataType; import java.util.concurrent.atomic.AtomicLong; import javax.management.MBeanNotificationInfo; import javax.management.Notification; import javax.management.NotificationBroadcasterSupport; /** * The central location to aggregate information on tasks run through the * system. Only bounded transactions are aggregated. Successful * transactions that take longer than the standard transaction timeout * are typically used for application startup tasks, and are not included. */ public class TaskAggregateStats extends NotificationBroadcasterSupport implements TaskAggregateMXBean { private static final double DEFAULT_SMOOTHING_FACTOR = 0.01; /* Task counts */ private final AggregateProfileCounter numTasks; private final AggregateProfileCounter numTransactionalTasks; private final AggregateProfileCounter numFailedTasks; /* Statistics for all tasks */ private final AggregateProfileSample readyCount; /* Statistics for successful tasks */ private final AggregateProfileSample runtime; private final AggregateProfileSample lagtime; private final AggregateProfileSample latency; /** * Smoothing factor for exponential smoothing, between 0 and 1. * A value closer to one provides less smoothing of the data, and * more weight to recent data; a value closer to zero provides more * smoothing but is less responsive to recent changes. * <p> * There's a lot of data coming through here, so we want a lot of smoothing. */ private double smoothingFactor = DEFAULT_SMOOTHING_FACTOR; /** * The last time {@link #clear} was called, or when this object * was created if {@code clear} has not been called. */ private volatile long lastClear = System.currentTimeMillis(); /** * The standard transaction timeout, used to determine if a successful * task was unbounded or not. We don't count the unbounded tasks in * our statistics. */ private volatile long standardTimeout; /** * Our profile collector, used to lazily set the standardTimeout. */ private final ProfileCollector collector; /** * A latch for the first task, so we can lazily retrieve the * standard transaction timeout. */ private boolean firstTask = true; // Notification information - we do not yet emit notifications for // this MBean. We do not yet document these notification types, as // they aren't used yet. /** Description of the notifications. */ private static MBeanNotificationInfo[] notificationInfo = new MBeanNotificationInfo[] { new MBeanNotificationInfo( new String[] {"com.sun.sgs.task.queue.behind"}, Notification.class.getName(), "Task queue is not keeping up") }; /** The sequence number for notifications */ private AtomicLong seqNumber = new AtomicLong(); /** * Creates an MXBean object for gathering task data in the system. * * @param collector the system profile collector * @param name the name of the profile consumer created to support this * object */ TaskAggregateStats(ProfileCollector collector, String name) { super(notificationInfo); this.collector = collector; ProfileConsumer consumer = collector.getConsumer(name); // We could determine that some of these statistics need to be // on all the time (level MIN) so we can use them for load balancing // or because they are extremely useful. // // Note that if the counters are all on the time, we could just // as easily use AtomicLongs rather than AggregateProfileCounters. ProfileLevel level = ProfileLevel.MEDIUM; // These statistics are reported to the profile reports // directly, with the ProfileCollector. // They should not be reported as TASK_AND_AGGREGATE because, // for nested tasks, we don't want to merge their values into // the parent's value. ProfileDataType type = ProfileDataType.AGGREGATE; numTasks = (AggregateProfileCounter) consumer.createCounter("numTasks", type, level); numTransactionalTasks = (AggregateProfileCounter) consumer.createCounter("numTransactionalTasks", type, level); numFailedTasks = (AggregateProfileCounter) consumer.createCounter("numFailedTasks", type, level); readyCount = (AggregateProfileSample) consumer.createSample("readyCount", type, level); runtime = (AggregateProfileSample) consumer.createSample("runtime", type, level); lagtime = (AggregateProfileSample) consumer.createSample("lagTime", type, level); latency = (AggregateProfileSample) consumer.createSample("latency", type, level); setSmoothing(smoothingFactor); } // This is how we'd send a notification - this is not yet well defined. // Need to determine what notifications make sense here and figure out // how to decide when they should be sent. // Also, perhaps JMX monitors would work better here? /** * Send a notification that the task queue is falling behind. This * method is not used yet. */ void notifyTaskQueue() { sendNotification( new Notification("com.sun.sgs.task.queue.behind", MXBEAN_NAME, seqNumber.incrementAndGet(), System.currentTimeMillis(), "Task queue is behind")); } /* * Implement MBean. */ /** {@inheritDoc} */ public long getTaskCount() { return numTasks.getCount(); } /** {@inheritDoc} */ public long getTransactionalTaskCount() { return numTransactionalTasks.getCount(); } /** {@inheritDoc} */ public long getTaskFailureCount() { return numFailedTasks.getCount(); } /** {@inheritDoc} */ public double getSmoothingFactor() { return smoothingFactor; } /** {@inheritDoc} */ public void setSmoothingFactor(double newFactor) { setSmoothing(newFactor); } /** {@inheritDoc} */ public long getSuccessfulRuntimeMax() { return runtime.getMaxSample(); } /** {@inheritDoc} */ public double getSuccessfulRuntimeAvg() { return runtime.getAverage(); } /** {@inheritDoc} */ public double getTaskFailurePercentage() { return (getTaskFailureCount() * 100) / (double) getTaskCount(); } /** {@inheritDoc} */ public double getReadyCountAvg() { return readyCount.getAverage(); } /** {@inheritDoc} */ public double getSuccessfulLagTimeAvg() { return lagtime.getAverage(); } /** {@inheritDoc} */ public double getSuccessfulLatencyAvg() { return latency.getAverage(); } /** {@inheritDoc} */ public void clear() { lastClear = System.currentTimeMillis(); numTasks.clearCount(); numTransactionalTasks.clearCount(); numFailedTasks.clearCount(); readyCount.clearSamples(); runtime.clearSamples(); lagtime.clearSamples(); latency.clearSamples(); } /** {@inheritDoc} */ public long getLastClearTime() { return lastClear; } // Methods used by ProfileCollector to update our values when // tasks complete void taskFinishedSuccess(boolean trans, long ready, long run, long lag) { // Don't include unbounded tasks in our statistics. if (firstTask) { firstTask = false; ConfigManager config = (ConfigManager) collector.getRegisteredMBean(ConfigManager.MXBEAN_NAME); standardTimeout = config.getStandardTxnTimeout(); } if (run > standardTimeout) { return; } taskFinishedCommon(trans, ready); runtime.addSample(run); lagtime.addSample(lag); latency.addSample(run + lag); } void taskFinishedFail(boolean trans, long ready) { taskFinishedCommon(trans, ready); numFailedTasks.incrementCount(); } void taskFinishedCommon(boolean trans, long ready) { numTasks.incrementCount(); if (trans) { numTransactionalTasks.incrementCount(); } readyCount.addSample(ready); } /** * Set the smoothing factor for each sample. * @param smooth the new smoothing factor * @throws IllegalArgumentException if {@code smooth} is not between * 0.0 and 1.0, inclusive */ private void setSmoothing(double smooth) { readyCount.setSmoothingFactor(smooth); runtime.setSmoothingFactor(smooth); lagtime.setSmoothingFactor(smooth); latency.setSmoothingFactor(smooth); smoothingFactor = smooth; } }