/**
* Copyright (C) 2009-2013 FoundationDB, LLC
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.foundationdb.server.service.monitor;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
public abstract class SessionMonitorBase implements SessionMonitor {
private final int sessionID;
private final long startTimeMillis;
private MonitorStage currentStage;
private long currentStageStartNanos;
private long[] lastNanos = new long[MonitorStage.values().length];
private long[] totalNanos = new long[MonitorStage.values().length];
private String currentStatement, currentStatementPreparedName;
private long currentStatementStartTime = -1;
private long currentStatementEndTime = -1;
private int rowsProcessed = 0;
private UserMonitor user = null;
private long[] statementCounters = new long[StatementTypes.values().length];
// TODO: In theory this needs to be a thread-safe data structure for adding/removing listeners
// In practice, this is only executed in one thread.
private Set<SessionEventListener> eventListeners = new HashSet<>();
protected SessionMonitorBase(int sessionID) {
this.sessionID = sessionID;
this.startTimeMillis = System.currentTimeMillis();
}
/* SessionMonitorBase methods */
public void startStatement(String statement) {
startStatement(statement, System.currentTimeMillis());
}
public void startStatement(String statement, long startTime) {
startStatement(statement, null, startTime);
}
public void startStatement(String statement, String preparedName) {
startStatement(statement, preparedName, System.currentTimeMillis());
}
public void startStatement(String statement, String preparedName, long startTime) {
countEvent(StatementTypes.STATEMENT);
currentStatement = statement;
currentStatementPreparedName = preparedName;
currentStatementStartTime = startTime;
currentStatementEndTime = -1;
rowsProcessed = -1;
}
public void endStatement() {
endStatement(-1);
}
public void endStatement(int rowsProcessed) {
currentStatementEndTime = System.currentTimeMillis();
this.rowsProcessed = rowsProcessed;
if (user != null) {
user.statementRun();
}
}
public void countEvent(StatementTypes type) {
statementCounters[type.ordinal()]++;
for (SessionEventListener listen : eventListeners) {
listen.countEvent(type);
}
}
public void failStatement(Throwable failure) {
countEvent(StatementTypes.FAILED);
endStatement();
}
// Caller can sequence all stages and avoid any gaps at the cost of more complicated
// exception handling, or just enter & leave and accept a tiny bit
// unaccounted for.
public void enterStage(MonitorStage stage) {
long now = System.nanoTime();
if (currentStage != null) {
long delta = now - currentStageStartNanos;
lastNanos[currentStage.ordinal()] = delta;
totalNanos[currentStage.ordinal()] += delta;
}
currentStage = stage;
currentStageStartNanos = now;
}
public void leaveStage() {
enterStage(null);
}
/* SessionMonitor */
@Override
public int getSessionId() {
return sessionID;
}
@Override
public long getStartTimeMillis() {
return startTimeMillis;
}
@Override
public long getStatementCount() {
return statementCounters[StatementTypes.STATEMENT.ordinal()];
}
@Override
public long getCount(StatementTypes type) {
return statementCounters[type.ordinal()];
}
@Override
public String getCurrentStatement() {
return currentStatement;
}
@Override
public String getCurrentStatementPreparedName() {
return currentStatementPreparedName;
}
@Override
public long getCurrentStatementStartTimeMillis() {
return currentStatementStartTime;
}
@Override
public long getCurrentStatementEndTimeMillis() {
return currentStatementEndTime;
}
@Override
public long getCurrentStatementDurationMillis() {
if (currentStatementEndTime < 0)
return -1;
else
return currentStatementEndTime - currentStatementStartTime;
}
@Override
public int getRowsProcessed() {
return rowsProcessed;
}
@Override
public MonitorStage getCurrentStage() {
return currentStage;
}
@Override
public long getLastTimeStageNanos(MonitorStage stage) {
return lastNanos[stage.ordinal()];
}
@Override
public long getTotalTimeStageNanos(MonitorStage stage) {
return lastNanos[stage.ordinal()];
}
@Override
public long getNonIdleTimeNanos() {
long total = 0;
for (int i = 1; i < totalNanos.length; i++) {
total += totalNanos[i];
}
return total;
}
@Override
public void addSessionEventListener(SessionEventListener listener) {
eventListeners.add(listener);
}
@Override
public void removeSessionEventListener (SessionEventListener listener) {
eventListeners.remove(listener);
}
public List<CursorMonitor> getCursors() {
return Collections.emptyList();
}
public List<PreparedStatementMonitor> getPreparedStatements() {
return Collections.emptyList();
}
public void setUserMonitor(UserMonitor monitor) {
this.user = monitor;
}
public UserMonitor getUserMonitor() {
return this.user;
}
}