/*
* Copyright 2015-2016 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.common.repo;
import java.io.IOException;
import java.util.List;
import java.util.zip.DataFormatException;
import javax.annotation.Nullable;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
import org.glowroot.common.live.ImmutableOverviewAggregate;
import org.glowroot.common.live.ImmutablePercentileAggregate;
import org.glowroot.common.live.LiveAggregateRepository.OverviewAggregate;
import org.glowroot.common.live.LiveAggregateRepository.PercentileAggregate;
import org.glowroot.common.model.LazyHistogram;
import org.glowroot.common.model.LazyHistogram.ScratchBuffer;
import org.glowroot.common.model.MutableProfile;
import org.glowroot.common.model.QueryCollector;
import org.glowroot.common.model.ServiceCallCollector;
import org.glowroot.common.util.Styles;
import org.glowroot.wire.api.model.AggregateOuterClass.Aggregate;
import org.glowroot.wire.api.model.ProfileOuterClass.Profile;
@Styles.Private
public class MutableAggregate {
private double totalDurationNanos;
private long transactionCount;
private long errorCount;
private boolean asyncTransactions;
private final List<MutableTimer> mainThreadRootTimers = Lists.newArrayList();
private final List<MutableTimer> auxThreadRootTimers = Lists.newArrayList();
private final List<MutableTimer> asyncTimers = Lists.newArrayList();
private final MutableThreadStats mainThreadStats = new MutableThreadStats();
private final MutableThreadStats auxThreadStats = new MutableThreadStats();
// histogram values are in nanoseconds, but with microsecond precision to reduce the number of
// buckets (and memory) required
private final LazyHistogram durationNanosHistogram = new LazyHistogram();
// lazy instantiated to reduce memory footprint
private @MonotonicNonNull QueryCollector queries;
private @MonotonicNonNull ServiceCallCollector serviceCalls;
private @MonotonicNonNull MutableProfile mainThreadProfile;
private @MonotonicNonNull MutableProfile auxThreadProfile;
private final int maxAggregateQueriesPerType;
private final int maxAggregateServiceCallsPerType;
public MutableAggregate(int maxAggregateQueriesPerType, int maxAggregateServiceCallsPerType) {
this.maxAggregateQueriesPerType = maxAggregateQueriesPerType;
this.maxAggregateServiceCallsPerType = maxAggregateServiceCallsPerType;
}
public double getTotalDurationNanos() {
return totalDurationNanos;
}
public long getTransactionCount() {
return transactionCount;
}
public long getErrorCount() {
return errorCount;
}
public boolean isAsyncTransactions() {
return asyncTransactions;
}
public List<Aggregate.Timer> getMainThreadRootTimersProto() {
return toProto(mainThreadRootTimers);
}
public List<Aggregate.Timer> getAuxThreadRootTimersProto() {
return toProto(auxThreadRootTimers);
}
public List<Aggregate.Timer> getAsyncTimersProto() {
return toProto(asyncTimers);
}
public MutableThreadStats getMainThreadStats() {
return mainThreadStats;
}
public MutableThreadStats getAuxThreadStats() {
return auxThreadStats;
}
public LazyHistogram getDurationNanosHistogram() {
return durationNanosHistogram;
}
public @Nullable QueryCollector getQueries() {
return queries;
}
public List<Aggregate.ServiceCallsByType> getServiceCallsProto() {
if (serviceCalls == null) {
return ImmutableList.of();
} else {
return serviceCalls.toProto();
}
}
public @Nullable MutableProfile getMainThreadProfile() {
return mainThreadProfile;
}
public @Nullable MutableProfile getAuxThreadProfile() {
return auxThreadProfile;
}
public boolean isEmpty() {
return transactionCount == 0;
}
public void addTotalDurationNanos(double totalDurationNanos) {
this.totalDurationNanos += totalDurationNanos;
}
public void addTransactionCount(long transactionCount) {
this.transactionCount += transactionCount;
}
public void addErrorCount(long errorCount) {
this.errorCount += errorCount;
}
public void addAsyncTransactions(boolean asyncTransactions) {
if (asyncTransactions) {
this.asyncTransactions = true;
}
}
public void mergeMainThreadRootTimers(List<Aggregate.Timer> toBeMergedRootTimers) {
mergeRootTimers(toBeMergedRootTimers, mainThreadRootTimers);
}
public void mergeAuxThreadRootTimers(List<Aggregate.Timer> toBeMergedRootTimers) {
mergeRootTimers(toBeMergedRootTimers, auxThreadRootTimers);
}
public void mergeAsyncTimers(List<Aggregate.Timer> toBeMergedRootTimers) {
mergeRootTimers(toBeMergedRootTimers, asyncTimers);
}
public void addMainThreadTotalCpuNanos(@Nullable Double totalCpuNanos) {
mainThreadStats.addTotalCpuNanos(totalCpuNanos);
}
public void addMainThreadTotalBlockedNanos(@Nullable Double totalBlockedNanos) {
mainThreadStats.addTotalBlockedNanos(totalBlockedNanos);
}
public void addMainThreadTotalWaitedNanos(@Nullable Double totalWaitedNanos) {
mainThreadStats.addTotalWaitedNanos(totalWaitedNanos);
}
public void addMainThreadTotalAllocatedBytes(@Nullable Double totalAllocatedBytes) {
mainThreadStats.addTotalAllocatedBytes(totalAllocatedBytes);
}
public void addAuxThreadTotalCpuNanos(@Nullable Double totalCpuNanos) {
auxThreadStats.addTotalCpuNanos(totalCpuNanos);
}
public void addAuxThreadTotalBlockedNanos(@Nullable Double totalBlockedNanos) {
auxThreadStats.addTotalBlockedNanos(totalBlockedNanos);
}
public void addAuxThreadTotalWaitedNanos(@Nullable Double totalWaitedNanos) {
auxThreadStats.addTotalWaitedNanos(totalWaitedNanos);
}
public void addAuxThreadTotalAllocatedBytes(@Nullable Double totalAllocatedBytes) {
auxThreadStats.addTotalAllocatedBytes(totalAllocatedBytes);
}
public void mergeMainThreadStats(@Nullable Aggregate.ThreadStats threadStats) {
mainThreadStats.addThreadStats(threadStats);
}
public void mergeAuxThreadStats(@Nullable Aggregate.ThreadStats threadStats) {
auxThreadStats.addThreadStats(threadStats);
}
public void mergeDurationNanosHistogram(Aggregate.Histogram toBeMergedDurationNanosHistogram)
throws DataFormatException {
durationNanosHistogram.merge(toBeMergedDurationNanosHistogram);
}
public OverviewAggregate toOverviewAggregate(long captureTime) throws IOException {
ImmutableOverviewAggregate.Builder builder = ImmutableOverviewAggregate.builder()
.captureTime(captureTime)
.totalDurationNanos(totalDurationNanos)
.transactionCount(transactionCount)
.asyncTransactions(asyncTransactions)
.mainThreadRootTimers(toProto(mainThreadRootTimers))
.auxThreadRootTimers(toProto(auxThreadRootTimers))
.asyncTimers(toProto(asyncTimers));
if (!mainThreadStats.isNA()) {
builder.mainThreadStats(mainThreadStats.toProto());
}
if (!auxThreadStats.isNA()) {
builder.auxThreadStats(auxThreadStats.toProto());
}
return builder.build();
}
public PercentileAggregate toPercentileAggregate(long captureTime) throws IOException {
return ImmutablePercentileAggregate.builder()
.captureTime(captureTime)
.totalDurationNanos(totalDurationNanos)
.transactionCount(transactionCount)
.durationNanosHistogram(durationNanosHistogram.toProto(new ScratchBuffer()))
.build();
}
public void mergeQuery(String queryType, String truncatedQueryText,
@Nullable String fullQueryTextSha1, double totalDurationNanos, long executionCount,
boolean hasTotalRows, long totalRows) {
if (queries == null) {
queries = new QueryCollector(maxAggregateQueriesPerType);
}
queries.mergeQuery(queryType, truncatedQueryText, fullQueryTextSha1, totalDurationNanos,
executionCount, hasTotalRows, totalRows);
}
public void mergeServiceCalls(List<Aggregate.ServiceCallsByType> toBeMergedServiceCalls)
throws IOException {
if (serviceCalls == null) {
serviceCalls = new ServiceCallCollector(maxAggregateServiceCallsPerType, 0);
}
serviceCalls.mergeServiceCalls(toBeMergedServiceCalls);
}
public void mergeMainThreadProfile(Profile toBeMergedProfile) throws IOException {
if (mainThreadProfile == null) {
mainThreadProfile = new MutableProfile();
}
mainThreadProfile.merge(toBeMergedProfile);
}
public void mergeAuxThreadProfile(Profile toBeMergedProfile) throws IOException {
if (auxThreadProfile == null) {
auxThreadProfile = new MutableProfile();
}
auxThreadProfile.merge(toBeMergedProfile);
}
public static void mergeRootTimers(List<Aggregate.Timer> toBeMergedRootTimers,
List<MutableTimer> rootTimers) {
for (Aggregate.Timer toBeMergedRootTimer : toBeMergedRootTimers) {
mergeRootTimer(toBeMergedRootTimer, rootTimers);
}
}
public static List<Aggregate.Timer> toProto(List<MutableTimer> rootTimers) {
List<Aggregate.Timer> protobufRootTimers =
Lists.newArrayListWithCapacity(rootTimers.size());
for (MutableTimer rootTimer : rootTimers) {
protobufRootTimers.add(rootTimer.toProto());
}
return protobufRootTimers;
}
private static void mergeRootTimer(Aggregate.Timer toBeMergedRootTimer,
List<MutableTimer> rootTimers) {
for (MutableTimer rootTimer : rootTimers) {
if (toBeMergedRootTimer.getName().equals(rootTimer.getName())) {
rootTimer.merge(toBeMergedRootTimer);
return;
}
}
MutableTimer rootTimer = MutableTimer.createRootTimer(toBeMergedRootTimer.getName(),
toBeMergedRootTimer.getExtended());
rootTimer.merge(toBeMergedRootTimer);
rootTimers.add(rootTimer);
}
}