package org.infinispan.stats;
import static java.util.concurrent.TimeUnit.NANOSECONDS;
import static org.infinispan.stats.container.ExtendedStatistic.NUM_ABORTED_RO_TX;
import static org.infinispan.stats.container.ExtendedStatistic.NUM_ABORTED_WR_TX;
import static org.infinispan.stats.container.ExtendedStatistic.NUM_COMMITTED_RO_TX;
import static org.infinispan.stats.container.ExtendedStatistic.NUM_COMMITTED_WR_TX;
import static org.infinispan.stats.container.ExtendedStatistic.RO_TX_ABORTED_EXECUTION_TIME;
import static org.infinispan.stats.container.ExtendedStatistic.RO_TX_SUCCESSFUL_EXECUTION_TIME;
import static org.infinispan.stats.container.ExtendedStatistic.WR_TX_ABORTED_EXECUTION_TIME;
import static org.infinispan.stats.container.ExtendedStatistic.WR_TX_SUCCESSFUL_EXECUTION_TIME;
import org.infinispan.stats.container.ConcurrentGlobalContainer;
import org.infinispan.stats.container.ExtendedStatistic;
import org.infinispan.stats.container.ExtendedStatisticsContainer;
import org.infinispan.stats.logging.Log;
import org.infinispan.util.TimeService;
import org.infinispan.util.logging.LogFactory;
/**
* Keeps the temporary statistics for a transaction. Also, it has the common logic for the local and remote
* transactions
*
* @author Roberto Palmieri
* @author Sebastiano Peluso
* @author Diego Didona
* @author Pedro Ruivo
* @since 6.0
*/
public abstract class TransactionStatistics {
//Here the elements which are common for local and remote transactions
protected final long initTime;
protected final Log log = LogFactory.getLog(getClass(), Log.class);
protected final boolean trace = log.isTraceEnabled();
protected final TimeService timeService;
private final ExtendedStatisticsContainer container;
private boolean readOnly;
private boolean committed;
protected TransactionStatistics(ExtendedStatisticsContainer container, TimeService timeService) {
this.timeService = timeService;
this.initTime = timeService.time();
this.readOnly = true; //as far as it does not tries to perform a put operation
this.container = container;
if (trace) {
log.tracef("Created transaction statistics. Start time=%s", initTime);
}
}
/**
* @return {@code true} if the transaction committed successfully, {@code false} otherwise
*/
public final boolean isCommitted() {
return this.committed;
}
/**
* Sets the transaction outcome. See {@link #isCommitted()}.
*
* @param commit {@code true} if the transaction is committed successfully.
*/
public final void setOutcome(boolean commit) {
committed = commit;
}
/**
* @return {@code true} if this transaction is a read-only transaction.
*/
public final boolean isReadOnly() {
return this.readOnly;
}
/**
* Sets this transaction as a write transaction. See also {@link #isReadOnly()}.
*/
public final void markAsUpdateTransaction() {
this.readOnly = false;
}
/**
* Adds a value to a statistic collected for this transaction.
*/
public final void addValue(ExtendedStatistic stat, double value) {
container.addValue(stat, value);
if (trace) {
log.tracef("Add %s to %s", value, stat);
}
}
/**
* @return a value collected for this transaction.
* @throws ExtendedStatisticNotFoundException
* if the statistic collected was not found.
*/
public final double getValue(ExtendedStatistic stat) throws ExtendedStatisticNotFoundException {
double value = container.getValue(stat);
if (trace) {
log.tracef("Value of %s is %s", stat, value);
}
return value;
}
/**
* Increments a statistic value. It is equivalent to {@code addValue(stat, 1)}.
*/
public final void incrementValue(ExtendedStatistic stat) {
this.addValue(stat, 1);
}
/**
* Signals this transaction as completed and updates the statistics to the final values ready to be merged in the
* cache statistics.
*/
public final void terminateTransaction() {
if (trace) {
log.tracef("Terminating transaction. Is read only? %s. Is commit? %s", readOnly, committed);
}
long execTime = timeService.timeDuration(initTime, NANOSECONDS);
if (readOnly) {
if (committed) {
incrementValue(NUM_COMMITTED_RO_TX);
addValue(RO_TX_SUCCESSFUL_EXECUTION_TIME, execTime);
} else {
incrementValue(NUM_ABORTED_RO_TX);
addValue(RO_TX_ABORTED_EXECUTION_TIME, execTime);
}
} else {
if (committed) {
incrementValue(NUM_COMMITTED_WR_TX);
addValue(WR_TX_SUCCESSFUL_EXECUTION_TIME, execTime);
} else {
incrementValue(NUM_ABORTED_WR_TX);
addValue(WR_TX_ABORTED_EXECUTION_TIME, execTime);
}
}
terminate();
}
/**
* Merges this statistics in the global container.
*/
public final void flushTo(ConcurrentGlobalContainer globalContainer) {
if (trace) {
log.tracef("Flush this [%s] to %s", this, globalContainer);
}
container.mergeTo(globalContainer);
}
@Override
public String toString() {
return "initTime=" + initTime +
", readOnly=" + readOnly +
", committed=" + committed +
'}';
}
/**
* Signals the reception of the {@link org.infinispan.commands.tx.PrepareCommand}.
*/
public abstract void onPrepareCommand();
/**
* @return {@code true} if this transaction statistics is for a local transaction.
*/
public abstract boolean isLocalTransaction();
/**
* Signals this transaction as completed and updates the statistics to the final values ready to be merged in the
* cache statistics. This method is abstract in order to be override for the local and the remote transactions.
*/
protected abstract void terminate();
/**
* Copies a statistic value and adds it to another statistic.
*/
protected final void copyValue(ExtendedStatistic from, ExtendedStatistic to) {
try {
double value = container.getValue(from);
container.addValue(to, value);
if (log.isDebugEnabled()) {
log.debugf("Copy value [%s] from [%s] to [%s]", value, from, to);
}
} catch (ExtendedStatisticNotFoundException e) {
log.unableToCopyValue(from, to, e);
}
}
}