/*
* Galaxy
* Copyright (c) 2012-2014, Parallel Universe Software Co. All rights reserved.
*
* This program and the accompanying materials are dual-licensed under
* either the terms of the Eclipse Public License v1.0 as published by
* the Eclipse Foundation
*
* or (per the licensee's choosing)
*
* under the terms of the GNU Lesser General Public License version 3.0
* as published by the Free Software Foundation.
*/
package co.paralleluniverse.galaxy.core;
import co.paralleluniverse.common.monitoring.PeriodicMonitor;
import co.paralleluniverse.galaxy.monitoring.CacheMXBean;
import co.paralleluniverse.galaxy.monitoring.Counter;
import java.beans.ConstructorProperties;
import java.util.EnumMap;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;
/**
*
* @author pron
*/
class JMXCacheMonitor extends PeriodicMonitor implements CacheMonitor, CacheMXBean {
private static final long[] histogramBins = {500, 1000, 10000, 100000}; // in microseconds
private final EnumMap<Message.Type, MessageMonitor> messageMonitors = new EnumMap<Message.Type, MessageMonitor>(Message.Type.class);
private final EnumMap<Op.Type, OpMonitor> opMonitors = new EnumMap<Op.Type, OpMonitor>(Op.Type.class);
private final EnumMap<MessageDelayReason, DelayedMessageMonitor> messageDelayMonitors = new EnumMap<MessageDelayReason, DelayedMessageMonitor>(MessageDelayReason.class);
//
private final Counter hitsCounter = new Counter();
private final Counter staleHitsCounter = new Counter();
private final Counter missesCounter = new Counter();
private final Counter invalidatesCounter = new Counter();
private final Counter stalePurgesCounter = new Counter();
private int hits;
private int staleHits;
private int misses;
private int invalidates;
private int stalePurges;
@ConstructorProperties({"name"})
public JMXCacheMonitor(String name) {
super(CacheMXBean.class, "co.paralleluniverse.galaxy.core:type=Cache");
}
@Override
protected void initCounters() {
for (Op.Type op : Op.Type.values())
opMonitors.put(op, new OpMonitor(histogramBins));
for (Message.Type m : Message.Type.values())
messageMonitors.put(m, new MessageMonitor());
for (MessageDelayReason reason : MessageDelayReason.values())
messageDelayMonitors.put(reason, new DelayedMessageMonitor());
}
@Override
protected void collectAndResetCounters() {
for (MessageMonitor mm : messageMonitors.values())
mm.collectAndResetCounters();
for (OpMonitor om : opMonitors.values())
om.collectAndResetCounters();
for (DelayedMessageMonitor dm : messageDelayMonitors.values())
dm.collectAndResetCounters();
hits = (int) hitsCounter.get();
staleHits = (int) staleHitsCounter.get();
misses = (int) missesCounter.get();
invalidates = (int) invalidatesCounter.get();
stalePurges = (int)stalePurgesCounter.get();
hitsCounter.reset();
staleHitsCounter.reset();
missesCounter.reset();
invalidatesCounter.reset();
stalePurgesCounter.reset();
}
@Override
protected void resetCounters() {
for (MessageMonitor mm : messageMonitors.values())
mm.reset();
for (OpMonitor om : opMonitors.values())
om.reset();
for (DelayedMessageMonitor dm : messageDelayMonitors.values())
dm.reset();
hitsCounter.reset();
staleHitsCounter.reset();
missesCounter.reset();
invalidatesCounter.reset();
stalePurgesCounter.reset();
}
@Override
public void addHit() {
hitsCounter.inc();
}
@Override
public void addStaleHit() {
staleHitsCounter.inc();
}
@Override
public void addMiss() {
missesCounter.inc();
}
@Override
public void addInvalidate(int num) {
invalidatesCounter.add(num);
}
@Override
public void addMessageSent(Message.Type msg) {
messageMonitors.get(msg).addSent();
}
@Override
public void addMessageReceived(Message.Type msg) {
messageMonitors.get(msg).addReceived();
}
@Override
public void addOp(Op.Type type, long durationMicroSeconds) {
if (durationMicroSeconds > 0)
opMonitors.get(type).addOp(durationMicroSeconds);
}
@Override
public void addMessageHandlingDelay(int numDelayed, long totalDelayNanos, MessageDelayReason reason) {
final DelayedMessageMonitor m = messageDelayMonitors.get(reason);
m.addMessages(numDelayed);
m.addDelay(totalDelayNanos);
}
@Override
public void addStalePurge(int num) {
stalePurgesCounter.add(num);
}
////////////////////////////////////////////
private static class MessageMonitor {
private final Counter messagesSentCounter = new Counter();
private final Counter messagesReceivedCounter = new Counter();
private int messagesSent;
private int messagesReceived;
void addSent() {
messagesSentCounter.inc();
}
void addReceived() {
messagesReceivedCounter.inc();
}
int getSent() {
return messagesSent;
}
int getReceived() {
return messagesReceived;
}
void collectAndResetCounters() {
collect();
reset();
}
void collect() {
messagesSent = (int) messagesSentCounter.get();
messagesReceived = (int) messagesReceivedCounter.get();
}
void reset() {
messagesSentCounter.reset();
messagesReceivedCounter.reset();
}
}
private static class OpMonitor {
private final Counter numOpsCounter = new Counter();
private final AtomicLong maxTimeCounter = new AtomicLong();
private final long[] histogramBins;
private final Counter[] histogramCounters;
private int numOps;
private long maxTime;
private final long[] rawHistogram;
private final float[] histogram;
OpMonitor(long... histogramBins) {
this.histogramBins = histogramBins;
this.histogramCounters = new Counter[histogramBins.length + 1];
this.rawHistogram = new long[histogramCounters.length];
this.histogram = new float[histogramCounters.length];
for (int i = 0; i < histogramCounters.length; i++)
this.histogramCounters[i] = new Counter();
}
int getNumOps() {
return numOps;
}
synchronized long getMaxTime() {
return maxTime;
}
synchronized float[] getHistogram() {
return histogram;
}
void addOp(long duration) {
numOpsCounter.inc();
if (duration == 0) {
histogramCounters[0].inc();
return;
}
for (;;) {
long currentMax = maxTimeCounter.get();
if (duration > currentMax) {
if (maxTimeCounter.compareAndSet(currentMax, duration))
break;
} else
break;
}
for (int i = 0; i < histogramBins.length; i++) {
if (duration < histogramBins[i]) {
histogramCounters[i].inc();
return;
}
}
histogramCounters[histogramBins.length].inc();
}
void collectAndResetCounters() {
collect();
reset();
}
synchronized void collect() {
numOps = (int) numOpsCounter.get();
maxTime = maxTimeCounter.get();
long sum = 0;
for (int i = 0; i < rawHistogram.length; i++) {
final long val = histogramCounters[i].get();
sum += val;
rawHistogram[i] = val;
}
for (int i = 0; i < histogram.length; i++)
histogram[i] = (float) ((double) rawHistogram[i] / sum * 100.0);
}
void reset() {
numOpsCounter.reset();
maxTimeCounter.set(0);
for (Counter histogramCounter : histogramCounters)
histogramCounter.reset();
}
}
private class DelayedMessageMonitor {
private final Counter messagesCounter = new Counter();
private final Counter delayCounter = new Counter();
private int messages;
private long totalDelay;
void addDelay(long nanos) {
delayCounter.add(TimeUnit.MICROSECONDS.convert(nanos, TimeUnit.NANOSECONDS));
}
void addMessages(int num) {
messagesCounter.add(num);
}
int getMessages() {
return messages;
}
long getTotalMicrosecondDelayPerSecond() {
return totalDelay;
}
void collectAndResetCounters() {
collect();
reset();
}
void collect() {
messages = (int) messagesCounter.get();
double secondsSinceLast = getMillisSinceLastCollect() / 1000.0;
totalDelay = (long) ((double)delayCounter.get() / secondsSinceLast);
}
void reset() {
messagesCounter.reset();
messagesCounter.reset();
}
}
private int getNumOp(Op.Type ot) {
return opMonitors.get(ot).getNumOps();
}
private float[] getOpHistogram(Op.Type ot) {
return opMonitors.get(ot).getHistogram();
}
private int getNumMessagesReceived(Message.Type mt) {
return messageMonitors.get(mt).getReceived();
}
private int getNumMessagesSent(Message.Type mt) {
return messageMonitors.get(mt).getSent();
}
@Override
public int getNumOpGet() {
return getNumOp(Op.Type.GET);
}
@Override
public float[] getOpHistogramGet() {
return getOpHistogram(Op.Type.GET);
}
@Override
public int getNumOpGetS() {
return getNumOp(Op.Type.GETS);
}
@Override
public float[] getOpHistogramGetS() {
return getOpHistogram(Op.Type.GETS);
}
@Override
public int getNumOpGetX() {
return getNumOp(Op.Type.GETX);
}
@Override
public float[] getOpHistogramGetX() {
return getOpHistogram(Op.Type.GETX);
}
@Override
public int getNumOpSet() {
return getNumOp(Op.Type.SET);
}
@Override
public float[] getOpHistogramSet() {
return getOpHistogram(Op.Type.SET);
}
@Override
public int getNumOpPut() {
return getNumOp(Op.Type.PUT);
}
@Override
public int getNumOpDel() {
return getNumOp(Op.Type.DEL);
}
@Override
public int getNumOpSend() {
return getNumOp(Op.Type.SEND);
}
@Override
public int getNumMessagesReceivedGET() {
return getNumMessagesReceived(Message.Type.GET);
}
@Override
public int getNumMessagesSentGET() {
return getNumMessagesSent(Message.Type.GET);
}
@Override
public int getNumMessagesReceivedGETX() {
return getNumMessagesReceived(Message.Type.GETX);
}
@Override
public int getNumMessagesSentGETX() {
return getNumMessagesSent(Message.Type.GETX);
}
@Override
public int getNumMessagesReceivedPUT() {
return getNumMessagesReceived(Message.Type.PUT);
}
@Override
public int getNumMessagesSentPUT() {
return getNumMessagesSent(Message.Type.PUT);
}
@Override
public int getNumMessagesReceivedPUTX() {
return getNumMessagesReceived(Message.Type.PUTX);
}
@Override
public int getNumMessagesSentPUTX() {
return getNumMessagesSent(Message.Type.PUTX);
}
@Override
public int getNumMessagesReceivedINV() {
return getNumMessagesReceived(Message.Type.INV);
}
@Override
public int getNumMessagesSentINV() {
return getNumMessagesSent(Message.Type.INV);
}
@Override
public int getNumMessagesReceivedINVACK() {
return getNumMessagesReceived(Message.Type.INVACK);
}
@Override
public int getNumMessagesSentINVACK() {
return getNumMessagesSent(Message.Type.INVACK);
}
@Override
public int getNumMessagesReceivedCHNGD_OWNR() {
return getNumMessagesReceived(Message.Type.CHNGD_OWNR);
}
@Override
public int getNumMessagesSentCHNGD_OWNR() {
return getNumMessagesSent(Message.Type.CHNGD_OWNR);
}
@Override
public int getNumMessagesReceivedMSG() {
return getNumMessagesReceived(Message.Type.MSG);
}
@Override
public int getNumMessagesSentMSG() {
return getNumMessagesSent(Message.Type.MSG);
}
@Override
public int getNumMessagesReceivedMSGACK() {
return getNumMessagesReceived(Message.Type.MSGACK);
}
@Override
public int getNumMessagesSentMSGACK() {
return getNumMessagesSent(Message.Type.MSGACK);
}
@Override
public int getNumMessagesDelayedDueLock() {
return messageDelayMonitors.get(MessageDelayReason.LOCK).getMessages();
}
@Override
public long getTotalMicrosecondDelayPerSecondDueLock() {
return messageDelayMonitors.get(MessageDelayReason.LOCK).getTotalMicrosecondDelayPerSecond();
}
@Override
public int getNumMessagesDelayedDueBackup() {
return messageDelayMonitors.get(MessageDelayReason.BACKUP).getMessages();
}
@Override
public long getTotalMicrosecondDelayPerSecondDueBackup() {
return messageDelayMonitors.get(MessageDelayReason.BACKUP).getTotalMicrosecondDelayPerSecond();
}
@Override
public int getNumMessagesDelayedDueOther() {
return messageDelayMonitors.get(MessageDelayReason.OTHER).getMessages();
}
@Override
public long getTotalMicrosecondDelayPerSecondDueOther() {
return messageDelayMonitors.get(MessageDelayReason.OTHER).getTotalMicrosecondDelayPerSecond();
}
@Override
public int getHits() {
return hits;
}
@Override
public int getStaleHits() {
return staleHits;
}
@Override
public int getMisses() {
return misses;
}
@Override
public int getInvalidates() {
return invalidates;
}
}