
org.glowroot.storage.simplerepo.TraceDao Maven / Gradle / Ivy
/*
* Copyright 2011-2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.glowroot.storage.simplerepo;
import java.io.IOException;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;
import java.util.Locale;
import javax.annotation.Nullable;
import org.glowroot.agent.shaded.google.common.base.Strings;
import org.glowroot.agent.shaded.google.common.collect.ImmutableList;
import org.glowroot.agent.shaded.google.common.collect.Lists;
import org.checkerframework.checker.tainting.qual.Untainted;
import org.glowroot.agent.shaded.slf4j.Logger;
import org.glowroot.agent.shaded.slf4j.LoggerFactory;
import org.glowroot.common.live.ImmutableTracePoint;
import org.glowroot.common.live.LiveTraceRepository.Existence;
import org.glowroot.common.live.LiveTraceRepository.TracePoint;
import org.glowroot.common.live.LiveTraceRepository.TracePointQuery;
import org.glowroot.common.util.OnlyUsedByTests;
import org.glowroot.storage.repo.ImmutableErrorMessageCount;
import org.glowroot.storage.repo.ImmutableHeaderPlus;
import org.glowroot.storage.repo.ImmutableTraceErrorPoint;
import org.glowroot.storage.repo.Result;
import org.glowroot.storage.repo.TraceRepository;
import org.glowroot.storage.simplerepo.TracePointQueryBuilder.ParameterizedSql;
import org.glowroot.storage.simplerepo.util.CappedDatabase;
import org.glowroot.storage.simplerepo.util.DataSource;
import org.glowroot.storage.simplerepo.util.DataSource.PreparedStatementBinder;
import org.glowroot.storage.simplerepo.util.DataSource.ResultSetExtractor;
import org.glowroot.storage.simplerepo.util.DataSource.RowMapper;
import org.glowroot.storage.simplerepo.util.ImmutableColumn;
import org.glowroot.storage.simplerepo.util.ImmutableIndex;
import org.glowroot.storage.simplerepo.util.RowMappers;
import org.glowroot.storage.simplerepo.util.Schema;
import org.glowroot.storage.simplerepo.util.Schema.Column;
import org.glowroot.storage.simplerepo.util.Schema.ColumnType;
import org.glowroot.storage.simplerepo.util.Schema.Index;
import org.glowroot.wire.api.model.ProfileTreeOuterClass.ProfileTree;
import org.glowroot.wire.api.model.TraceOuterClass.Trace;
import static org.glowroot.agent.shaded.google.common.base.Preconditions.checkNotNull;
import static org.glowroot.storage.simplerepo.util.Checkers.castUntainted;
public class TraceDao implements TraceRepository {
private static final Logger logger = LoggerFactory.getLogger(TraceDao.class);
private static final ImmutableList traceColumns = ImmutableList.of(
ImmutableColumn.of("server_id", ColumnType.VARCHAR),
ImmutableColumn.of("id", ColumnType.VARCHAR),
ImmutableColumn.of("partial", ColumnType.BOOLEAN),
ImmutableColumn.of("slow", ColumnType.BOOLEAN),
ImmutableColumn.of("error", ColumnType.BOOLEAN),
ImmutableColumn.of("start_time", ColumnType.BIGINT),
ImmutableColumn.of("capture_time", ColumnType.BIGINT),
ImmutableColumn.of("duration_nanos", ColumnType.BIGINT), // nanoseconds
ImmutableColumn.of("transaction_type", ColumnType.VARCHAR),
ImmutableColumn.of("transaction_name", ColumnType.VARCHAR),
ImmutableColumn.of("headline", ColumnType.VARCHAR),
ImmutableColumn.of("user_id", ColumnType.VARCHAR), // user is a postgres reserved word
ImmutableColumn.of("error_message", ColumnType.VARCHAR),
ImmutableColumn.of("header", ColumnType.VARBINARY), // protobuf
ImmutableColumn.of("entries_capped_id", ColumnType.BIGINT),
ImmutableColumn.of("profile_capped_id", ColumnType.BIGINT));
// capture_time column is used for expiring records without using FK with on delete cascade
private static final ImmutableList traceAttributeColumns =
ImmutableList.of(ImmutableColumn.of("server_id", ColumnType.VARCHAR),
ImmutableColumn.of("id", ColumnType.VARCHAR),
ImmutableColumn.of("name", ColumnType.VARCHAR),
ImmutableColumn.of("value", ColumnType.VARCHAR),
ImmutableColumn.of("capture_time", ColumnType.BIGINT));
private static final ImmutableList traceIndexes = ImmutableList.of(
// trace_transaction_type_idx is used by readTransactionTypes
ImmutableIndex.of("trace_transaction_type_idx",
ImmutableList.of("server_id", "transaction_type")),
// duration_nanos, id and error columns are included so database can return the
// result set directly from the index without having to reference the table for each row
//
// trace_slow_idx is for slow trace point query and for readOverallSlowCount()
ImmutableIndex.of("trace_slow_idx",
ImmutableList.of("server_id", "transaction_type", "slow", "capture_time",
"duration_nanos", "error", "id")),
// trace_transaction_slow_idx is for slow trace point query and for
// readTransactionSlowCount()
ImmutableIndex.of("trace_transaction_slow_idx",
ImmutableList.of("server_id", "transaction_type", "transaction_name", "slow",
"capture_time", "duration_nanos", "error", "id")),
// trace_error_idx is for error trace point query and for readOverallErrorCount()
ImmutableIndex.of("trace_error_idx",
ImmutableList.of("server_id", "transaction_type", "error", "capture_time",
"duration_nanos", "error", "id")),
// trace_transaction_error_idx is for error trace point query and for
// readTransactionErrorCount()
ImmutableIndex.of("trace_transaction_error_idx",
ImmutableList.of("server_id", "transaction_type", "transaction_name", "error",
"capture_time", "duration_nanos", "id")),
// trace_idx is for trace header lookup
ImmutableIndex.of("trace_idx", ImmutableList.of("server_id", "id")));
private static final ImmutableList traceAttributeIndexes = ImmutableList.of(
ImmutableIndex.of("trace_attribute_idx", ImmutableList.of("server_id", "id")));
private final DataSource dataSource;
private final CappedDatabase traceCappedDatabase;
private final TraceAttributeNameDao traceAttributeNameDao;
private final TransactionTypeDao transactionTypeDao;
TraceDao(DataSource dataSource, CappedDatabase traceCappedDatabase,
TransactionTypeDao transactionTypeDao) throws Exception {
this.dataSource = dataSource;
this.traceCappedDatabase = traceCappedDatabase;
traceAttributeNameDao = new TraceAttributeNameDao(dataSource);
this.transactionTypeDao = transactionTypeDao;
Schema schema = dataSource.getSchema();
schema.syncTable("trace", traceColumns);
schema.syncIndexes("trace", traceIndexes);
schema.syncTable("trace_attribute", traceAttributeColumns);
schema.syncIndexes("trace_attribute", traceAttributeIndexes);
}
@Override
public void collect(final String serverId, final Trace trace) throws Exception {
final Trace.Header header = trace.getHeader();
boolean exists = dataSource.queryForExists(
"select 1 from trace where server_id = ? and id = ?", serverId, trace.getId());
if (exists) {
dataSource.update("update trace set partial = ?, slow = ?, error = ?, start_time = ?,"
+ " capture_time = ?, duration_nanos = ?, transaction_type = ?,"
+ " transaction_name = ?, headline = ?, user_id = ?, error_message = ?,"
+ " header = ?, entries_capped_id = ?, profile_capped_id = ?"
+ " where server_id = ? and id = ?", new TraceBinder(serverId, trace));
} else {
dataSource.update(
"insert into trace (partial, slow, error, start_time, capture_time,"
+ " duration_nanos, transaction_type, transaction_name, headline,"
+ " user_id, error_message, header, entries_capped_id,"
+ " profile_capped_id, server_id, id)"
+ " values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
new TraceBinder(serverId, trace));
}
if (header.getAttributeCount() > 0) {
if (exists) {
dataSource.update("delete from trace_attribute where server_id = ? and id = ?",
serverId, trace.getId());
}
dataSource.batchUpdate(
"insert into trace_attribute (server_id, id, name, value, capture_time)"
+ " values (?, ?, ?, ?, ?)",
new PreparedStatementBinder() {
@Override
public void bind(PreparedStatement preparedStatement) throws SQLException {
for (Trace.Attribute attribute : header.getAttributeList()) {
for (String value : attribute.getValueList()) {
preparedStatement.setString(1, serverId);
preparedStatement.setString(2, trace.getId());
preparedStatement.setString(3, attribute.getName());
preparedStatement.setString(4, value);
preparedStatement.setLong(5, header.getCaptureTime());
preparedStatement.addBatch();
}
}
}
});
for (Trace.Attribute attribute : header.getAttributeList()) {
traceAttributeNameDao.updateLastCaptureTime(serverId, attribute.getName(),
header.getCaptureTime());
}
}
// FIXME maintain table of server_id/server_rollup associations and update transaction type
// for all server rollups
transactionTypeDao.updateLastCaptureTime(serverId, trace.getHeader().getTransactionType(),
trace.getHeader().getCaptureTime());
}
@Override
public List readTraceAttributeNames(String serverRollup) throws Exception {
// FIXME maintain table of server_id/server_rollup associations and join that here
// (maintain this table on agent "Hello", wipe out prior associations and add new ones)
return traceAttributeNameDao.readTraceAttributeNames(serverRollup);
}
@Override
public Result readPoints(TracePointQuery query) throws Exception {
ParameterizedSql parameterizedSql = new TracePointQueryBuilder(query).getParameterizedSql();
List points = dataSource.query(parameterizedSql.sql(),
new TracePointRowMapper(), parameterizedSql.argsAsArray());
// one extra record over the limit is fetched above to identify if the limit was hit
return Result.from(points, query.limit());
}
@Override
public long readOverallSlowCount(String serverId, String transactionType, long captureTimeFrom,
long captureTimeTo) throws Exception {
return dataSource.queryForLong(
"select count(*) from trace where server_id = ? and transaction_type = ?"
+ " and capture_time > ? and capture_time <= ? and slow = ?",
serverId, transactionType, captureTimeFrom, captureTimeTo, true);
}
@Override
public long readTransactionSlowCount(String serverId, String transactionType,
String transactionName, long captureTimeFrom, long captureTimeTo) throws Exception {
return dataSource.queryForLong(
"select count(*) from trace where server_id = ? and transaction_type = ?"
+ " and transaction_name = ? and capture_time > ? and capture_time <= ?"
+ " and slow = ?",
serverId, transactionType, transactionName, captureTimeFrom, captureTimeTo, true);
}
@Override
public long readOverallErrorCount(String serverId, String transactionType, long captureTimeFrom,
long captureTimeTo) throws Exception {
return dataSource.queryForLong(
"select count(*) from trace where server_id = ? and transaction_type = ?"
+ " and capture_time > ? and capture_time <= ? and error = ?",
serverId, transactionType, captureTimeFrom, captureTimeTo, true);
}
@Override
public long readTransactionErrorCount(String serverId, String transactionType,
String transactionName, long captureTimeFrom, long captureTimeTo) throws Exception {
return dataSource.queryForLong(
"select count(*) from trace where server_id = ? and transaction_type = ?"
+ " and transaction_name = ? and capture_time > ? and capture_time <= ?"
+ " and error = ?",
serverId, transactionType, transactionName, captureTimeFrom, captureTimeTo, true);
}
@Override
public List readErrorPoints(ErrorMessageQuery query, long resolutionMillis,
long liveCaptureTime) throws Exception {
// need ".0" to force double result
String captureTimeSql = castUntainted(
"ceil(capture_time / " + resolutionMillis + ".0) * " + resolutionMillis);
ParameterizedSql parameterizedSql =
buildErrorMessageQuery(query, "select " + captureTimeSql + ", count(*) from trace",
"group by " + captureTimeSql + " order by " + captureTimeSql);
return dataSource.query(parameterizedSql.sql(), new ErrorPointRowMapper(liveCaptureTime),
parameterizedSql.argsAsArray());
}
@Override
public Result readErrorMessageCounts(ErrorMessageQuery query)
throws Exception {
ParameterizedSql parameterizedSql =
buildErrorMessageQuery(query, "select error_message, count(*) from trace",
"group by error_message order by count(*) desc");
List points = dataSource.query(parameterizedSql.sql(),
new ErrorMessageCountRowMapper(), parameterizedSql.argsAsArray());
// one extra record over the limit is fetched above to identify if the limit was hit
return Result.from(points, query.limit());
}
@Override
public @Nullable HeaderPlus readHeader(String serverId, String traceId) throws Exception {
List traces = dataSource.query(
"select header, entries_capped_id, profile_capped_id from trace"
+ " where server_id = ? and id = ?",
new TraceHeaderRowMapper(), serverId, traceId);
if (traces.isEmpty()) {
return null;
}
if (traces.size() > 1) {
logger.error("multiple records returned for server_id '{}' and trace id '{}'", serverId,
traceId);
}
return traces.get(0);
}
@Override
public List readEntries(String serverId, String traceId) throws Exception {
List entries = dataSource.query(
"select entries_capped_id from trace where server_id = ? and id = ?",
new EntriesResultExtractor(), serverId, traceId);
if (entries == null) {
// data source is closing
return ImmutableList.of();
}
return entries;
}
@Override
public @Nullable ProfileTree readProfileTree(String serverId, String traceId) throws Exception {
return dataSource.query(
"select profile_capped_id from trace where server_id = ? and id = ?",
new ProfileTreeResultExtractor(), serverId, traceId);
}
@Override
public void deleteAll(String serverId) throws Exception {
traceAttributeNameDao.deleteAll(serverId);
dataSource.deleteAll("trace", "server_id", serverId);
dataSource.deleteAll("trace_attribute", "server_id", serverId);
}
void deleteBefore(String serverId, long captureTime) throws Exception {
traceAttributeNameDao.deleteBefore(serverId, captureTime);
dataSource.deleteBefore("trace", "server_id", serverId, captureTime);
dataSource.deleteBefore("trace_attribute", "server_id", serverId, captureTime);
}
@Override
@OnlyUsedByTests
public long count(String serverId) throws Exception {
return dataSource.queryForLong("select count(*) from trace where server_id = ?", serverId);
}
private ParameterizedSql buildErrorMessageQuery(ErrorMessageQuery query,
@Untainted String selectClause, @Untainted String groupByClause) {
String sql = selectClause;
List
© 2015 - 2025 Weber Informatics LLC | Privacy Policy