/*
* INESC-ID, Instituto de Engenharia de Sistemas e Computadores Investigação e Desevolvimento em Lisboa
* Copyright 2013 INESC-ID and/or its affiliates and other
* contributors as indicated by the @author tags. All rights reserved.
* See the copyright.txt in the distribution for a full listing of
* individual contributors.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 3.0 of
* the License, or (at your option) any later version.
*
* This software 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.infinispan.dataplacement.stats;
import org.infinispan.util.logging.Log;
import org.infinispan.util.logging.LogFactory;
import java.io.BufferedWriter;
import java.io.IOException;
import java.util.EnumMap;
import static java.lang.System.currentTimeMillis;
/**
* Keeps all the round stats values.
*
* @author Pedro Ruivo
* @since 5.2
*/
public class Stats {
private static final Log log = LogFactory.getLog(Stats.class);
private final long roundId;
private static enum TimeStamp {
/**
* the start timestamp
*/
START,
/**
* the timestamp when it finishes to calculate the local and remote accesses
*/
ACCESSES,
/**
* the timestamp when it received all the object requests and start to calculate the new owners
*/
RECEIVED_ACCESSES,
/**
* the timestamp when it finishes to calculate the new owners
*/
NEW_OWNERS,
/**
* the timestamp when all objects lookup are received
*/
RECEIVED_OBJECT_LOOKUP,
/**
* the timestamp when the acks are received
*/
ACKS,
/**
* the timestamp when the state transfer starts
*/
STATE_TRANSFER_START,
/**
* the timestamp when the state transfer ends
*/
STATE_TRANSFER_END
}
private static enum Counter {
/**
* the number of keys that are moved to a wrong node
*/
WRONG_OWNER,
/**
* the number of keys that are moved but they aren't supposed to be moved
*/
WRONG_MOVE,
/**
* the total number of keys to move
*/
TOTAL_KEYS_TO_MOVE
}
private static enum Size {
/**
* the sum of size of the object requests
*/
ACCESSES,
/**
* the size of the object lookup
*/
OBJECT_LOOKUP,
/**
* bloom filter size
*/
BLOOM_FILTER,
MACHINE_LEARNER_1,
MACHINE_LEARNER_2
}
private static enum Duration {
/**
* time (in nanoseconds) to create the object lookup
*/
OBJECT_LOOKUP_CREATION
}
private final IncrementableLong[] durations;
private final EnumMap<Counter, IncrementalInteger> counters;
private final EnumMap<TimeStamp, Long> timestamps;
private final EnumMap<Size, Integer> messageSizes;
private final EnumMap<Duration, Long> duration;
public Stats(long roundId, int size) {
this.roundId = roundId;
counters = new EnumMap<Counter, IncrementalInteger>(Counter.class);
for (Counter counter : Counter.values()) {
counters.put(counter, new IncrementalInteger());
}
timestamps = new EnumMap<TimeStamp, Long>(TimeStamp.class);
timestamps.put(TimeStamp.START, currentTimeMillis());
messageSizes = new EnumMap<Size, Integer>(Size.class);
durations = new IncrementableLong[size];
for (int i = 0; i < size; ++i) {
durations[i] = new IncrementableLong();
}
duration = new EnumMap<Duration, Long>(Duration.class);
}
public final IncrementableLong[] createQueryPhaseDurationsArray() {
IncrementableLong[] array = new IncrementableLong[durations.length];
for (int i = 0; i < array.length; ++i) {
array[i] = new IncrementableLong();
}
return array;
}
public final void collectedAccesses() {
timestamps.put(TimeStamp.ACCESSES, currentTimeMillis());
}
public final void receivedAccesses() {
timestamps.put(TimeStamp.RECEIVED_ACCESSES, currentTimeMillis());
}
public final void calculatedNewOwners() {
timestamps.put(TimeStamp.NEW_OWNERS, currentTimeMillis());
}
public final void receivedObjectLookup() {
timestamps.put(TimeStamp.RECEIVED_OBJECT_LOOKUP, currentTimeMillis());
}
public final void receivedAcks() {
timestamps.put(TimeStamp.ACKS, currentTimeMillis());
}
public final void startStateTransfer() {
timestamps.put(TimeStamp.STATE_TRANSFER_START, currentTimeMillis());
}
public final void endStateTransfer() {
timestamps.put(TimeStamp.STATE_TRANSFER_END, currentTimeMillis());
}
public final void wrongOwnersErrors(int value) {
counters.get(Counter.WRONG_OWNER).increment(value);
}
public final void wrongKeyMovedErrors(int value) {
counters.get(Counter.WRONG_MOVE).increment(value);
}
public final void totalKeysMoved(int value) {
counters.get(Counter.TOTAL_KEYS_TO_MOVE).increment(value);
}
public final void accessesSize(int value) {
messageSizes.put(Size.ACCESSES, value);
}
public final void objectLookupSize(int value) {
messageSizes.put(Size.OBJECT_LOOKUP, value);
}
public final void setBloomFilterSize(int value) {
messageSizes.put(Size.BLOOM_FILTER, value);
}
public final void setMachineLearner1(int value) {
messageSizes.put(Size.MACHINE_LEARNER_1, value);
}
public final void setMachineLearner2(int value) {
messageSizes.put(Size.MACHINE_LEARNER_2, value);
}
public final void queryDuration(IncrementableLong[] values) {
int size = Math.min(values.length, durations.length);
for (int i = 0; i < size; ++i) {
durations[i].add(values[i]);
}
}
public final void setObjectLookupCreationDuration(long duration) {
this.duration.put(Duration.OBJECT_LOOKUP_CREATION, duration);
}
public final void saveTo(BufferedWriter writer, boolean printHeader) {
if (log.isTraceEnabled()) {
log.tracef("Saving statistics to %s. print headers? %s", writer, printHeader);
}
try {
if (printHeader) {
writer.write("RoundId");
for (TimeStamp timeStamp : TimeStamp.values()) {
writer.write(",");
writer.write(timeStamp.toString());
}
for (Counter counter : Counter.values()) {
writer.write(",");
writer.write(counter.toString());
}
for (Size size : Size.values()) {
writer.write(",");
writer.write(size.toString());
}
for (Duration duration : Duration.values()) {
writer.write(",");
writer.write(duration.toString());
}
for (int i = 0; i < durations.length; ++i) {
writer.write(",");
writer.write("QUERY_DURATION_PHASE_" + i);
}
writer.newLine();
}
writer.write(Long.toString(roundId));
write(writer, timestamps, TimeStamp.values());
write(writer, counters, Counter.values());
write(writer, messageSizes, Size.values());
write(writer, duration, Duration.values());
for (IncrementableLong duration : durations) {
writer.write(",");
writer.write(duration == null ? "N/A" : duration.toString());
}
writer.newLine();
writer.flush();
} catch (Exception e) {
log.errorf(e, "Error saving stats %s.", this);
}
}
@Override
public String toString() {
return "Stats{" +
"roundId=" + roundId +
", counters=" + counters +
", timestamps=" + timestamps +
'}';
}
private void write(BufferedWriter writer, EnumMap map, Enum[] values) throws IOException {
for (Enum e : values) {
writer.write(",");
Object value = map.get(e);
writer.write(value == null ? "N/A" : value.toString());
}
}
private class IncrementalInteger {
private int value;
public final void increment() {
increment(1);
}
public final void increment(int value) {
this.value += value;
}
@Override
public String toString() {
return Integer.toString(value);
}
}
}